2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 * $Id: UnionPathIterator.java 469314 2006-10-30 23:31:59Z minchau $
21 package org.apache.xpath.axes;
23 import org.apache.xml.dtm.Axis;
24 import org.apache.xml.dtm.DTM;
25 import org.apache.xml.dtm.DTMIterator;
26 import org.apache.xpath.Expression;
27 import org.apache.xpath.ExpressionOwner;
28 import org.apache.xpath.XPathVisitor;
29 import org.apache.xpath.compiler.Compiler;
30 import org.apache.xpath.compiler.OpCodes;
31 import org.apache.xpath.compiler.OpMap;
34 * This class extends NodeSetDTM, which implements DTMIterator,
35 * and fetches nodes one at a time in document order based on a XPath
36 * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
37 * As each node is iterated via nextNode(), the node is also stored
38 * in the NodeVector, so that previousNode() can easily be done.
41 public class UnionPathIterator extends LocPathIterator
42 implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
44 static final long serialVersionUID = -3910351546843826781L;
47 * Constructor to create an instance which you can add location paths to.
49 public UnionPathIterator()
55 // m_cacheNodes = false;
61 * Initialize the context values for this expression
64 * @param context The XPath runtime context for this
67 public void setRoot(int context, Object environment)
69 super.setRoot(context, environment);
75 int n = m_exprs.length;
76 DTMIterator newIters[] = new DTMIterator[n];
78 for (int i = 0; i < n; i++)
80 DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
84 m_iterators = newIters;
89 throw new org.apache.xml.utils.WrappedRuntimeException(e);
94 * Add an iterator to the union list.
96 * @param expr non-null reference to a location path iterator.
98 public void addIterator(DTMIterator expr)
101 // Increase array size by only 1 at a time. Fix this
102 // if it looks to be a problem.
103 if (null == m_iterators)
105 m_iterators = new DTMIterator[1];
106 m_iterators[0] = expr;
110 DTMIterator[] exprs = m_iterators;
111 int len = m_iterators.length;
113 m_iterators = new DTMIterator[len + 1];
115 System.arraycopy(exprs, 0, m_iterators, 0, len);
117 m_iterators[len] = expr;
120 if(expr instanceof Expression)
121 ((Expression)expr).exprSetParent(this);
125 * Detaches the iterator from the set which it iterated over, releasing
126 * any computational resources and placing the iterator in the INVALID
127 * state. After<code>detach</code> has been invoked, calls to
128 * <code>nextNode</code> or<code>previousNode</code> will raise the
129 * exception INVALID_STATE_ERR.
133 if(m_allowDetach && null != m_iterators){
134 int n = m_iterators.length;
135 for(int i = 0; i < n; i++)
137 m_iterators[i].detach();
145 * Create a UnionPathIterator object, including creation
146 * of location path iterators from the opcode list, and call back
147 * into the Compiler to create predicate expressions.
149 * @param compiler The Compiler which is creating
151 * @param opPos The position of this iterator in the
152 * opcode list from the compiler.
154 * @throws javax.xml.transform.TransformerException
156 public UnionPathIterator(Compiler compiler, int opPos)
157 throws javax.xml.transform.TransformerException
162 opPos = OpMap.getFirstChildPos(opPos);
164 loadLocationPaths(compiler, opPos, 0);
168 * This will return an iterator capable of handling the union of paths given.
170 * @param compiler The Compiler which is creating
172 * @param opPos The position of this iterator in the
173 * opcode list from the compiler.
175 * @return Object that is derived from LocPathIterator.
177 * @throws javax.xml.transform.TransformerException
179 public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
180 throws javax.xml.transform.TransformerException
182 // For the moment, I'm going to first create a full UnionPathIterator, and
183 // then see if I can reduce it to a UnionChildIterator. It would obviously
184 // be more effecient to just test for the conditions for a UnionChildIterator,
185 // and then create that directly.
186 UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
187 int nPaths = upi.m_exprs.length;
188 boolean isAllChildIterators = true;
189 for(int i = 0; i < nPaths; i++)
191 LocPathIterator lpi = upi.m_exprs[i];
193 if(lpi.getAxis() != Axis.CHILD)
195 isAllChildIterators = false;
200 // check for positional predicates or position function, which won't work.
201 if(HasPositionalPredChecker.check(lpi))
203 isAllChildIterators = false;
208 if(isAllChildIterators)
210 UnionChildIterator uci = new UnionChildIterator();
212 for(int i = 0; i < nPaths; i++)
214 PredicatedNodeTest lpi = upi.m_exprs[i];
215 // I could strip the lpi down to a pure PredicatedNodeTest, but
216 // I don't think it's worth it. Note that the test can be used
217 // as a static object... so it doesn't have to be cloned.
218 uci.addNodeTest(lpi);
228 * Get the analysis bits for this walker, as defined in the WalkerFactory.
229 * @return One of WalkerFactory#BIT_DESCENDANT, etc.
231 public int getAnalysisBits()
237 int n = m_exprs.length;
239 for (int i = 0; i < n; i++)
241 int bit = m_exprs[i].getAnalysisBits();
250 * Read the object from a serialization stream.
252 * @param stream Input stream to read from
254 * @throws java.io.IOException
255 * @throws javax.xml.transform.TransformerException
257 private void readObject(java.io.ObjectInputStream stream)
258 throws java.io.IOException, javax.xml.transform.TransformerException
262 stream.defaultReadObject();
263 m_clones = new IteratorPool(this);
265 catch (ClassNotFoundException cnfe)
267 throw new javax.xml.transform.TransformerException(cnfe);
272 * Get a cloned LocPathIterator that holds the same
273 * position as this iterator.
275 * @return A clone of this iterator that holds the same node position.
277 * @throws CloneNotSupportedException
279 public Object clone() throws CloneNotSupportedException
282 UnionPathIterator clone = (UnionPathIterator) super.clone();
283 if (m_iterators != null)
285 int n = m_iterators.length;
287 clone.m_iterators = new DTMIterator[n];
289 for (int i = 0; i < n; i++)
291 clone.m_iterators[i] = (DTMIterator)m_iterators[i].clone();
300 * Create a new location path iterator.
302 * @param compiler The Compiler which is creating
304 * @param opPos The position of this iterator in the
306 * @return New location path iterator.
308 * @throws javax.xml.transform.TransformerException
310 protected LocPathIterator createDTMIterator(
311 Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
313 LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos,
314 (compiler.getLocationPathDepth() <= 0));
319 * Initialize the location path iterators. Recursive.
321 * @param compiler The Compiler which is creating
323 * @param opPos The position of this iterator in the
324 * opcode list from the compiler.
325 * @param count The insert position of the iterator.
327 * @throws javax.xml.transform.TransformerException
329 protected void loadLocationPaths(Compiler compiler, int opPos, int count)
330 throws javax.xml.transform.TransformerException
333 // TODO: Handle unwrapped FilterExpr
334 int steptype = compiler.getOp(opPos);
336 if (steptype == OpCodes.OP_LOCATIONPATH)
338 loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
340 m_exprs[count] = createDTMIterator(compiler, opPos);
341 m_exprs[count].exprSetParent(this);
346 // Have to check for unwrapped functions, which the LocPathIterator
350 case OpCodes.OP_VARIABLE :
351 case OpCodes.OP_EXTFUNCTION :
352 case OpCodes.OP_FUNCTION :
353 case OpCodes.OP_GROUP :
354 loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
356 WalkingIterator iter =
357 new WalkingIterator(compiler.getNamespaceContext());
358 iter.exprSetParent(this);
360 if(compiler.getLocationPathDepth() <= 0)
361 iter.setIsTopLevel(true);
363 iter.m_firstWalker = new org.apache.xpath.axes.FilterExprWalker(iter);
365 iter.m_firstWalker.init(compiler, opPos, steptype);
367 m_exprs[count] = iter;
370 m_exprs = new LocPathIterator[count];
376 * Returns the next node in the set and advances the position of the
377 * iterator in the set. After a DTMIterator is created, the first call
378 * to nextNode() returns the first node in the set.
379 * @return The next <code>Node</code> in the set being iterated over, or
380 * <code>null</code> if there are no more members in that set.
382 public int nextNode()
387 // Loop through the iterators getting the current fetched
388 // node, and get the earliest occuring in document order
389 int earliestNode = DTM.NULL;
391 if (null != m_iterators)
393 int n = m_iterators.length;
394 int iteratorUsed = -1;
396 for (int i = 0; i < n; i++)
398 int node = m_iterators[i].getCurrentNode();
400 if (DTM.NULL == node)
402 else if (DTM.NULL == earliestNode)
409 if (node == earliestNode)
412 // Found a duplicate, so skip past it.
413 m_iterators[i].nextNode();
417 DTM dtm = getDTM(node);
419 if (dtm.isNodeAfter(node, earliestNode))
428 if (DTM.NULL != earliestNode)
430 m_iterators[iteratorUsed].nextNode();
432 incrementCurrentPos();
438 m_lastFetched = earliestNode;
444 * This function is used to fixup variables from QNames to stack frame
445 * indexes at stylesheet build time.
446 * @param vars List of QNames that correspond to variables. This list
447 * should be searched backwards for the first qualified name that
448 * corresponds to the variable reference qname. The position of the
449 * QName in the vector from the start of the vector will be its position
450 * in the stack frame (but variables above the globalsTop value will need
451 * to be offset to the current stack frame).
453 public void fixupVariables(java.util.Vector vars, int globalsSize)
455 for (int i = 0; i < m_exprs.length; i++)
457 m_exprs[i].fixupVariables(vars, globalsSize);
463 * The location path iterators, one for each
464 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
465 * path</a> contained in the union expression.
468 protected LocPathIterator[] m_exprs;
472 * The location path iterators, one for each
473 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
474 * path</a> contained in the union expression.
477 protected DTMIterator[] m_iterators;
480 * Returns the axis being iterated, if it is known.
482 * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
491 class iterOwner implements ExpressionOwner
501 * @see ExpressionOwner#getExpression()
503 public Expression getExpression()
505 return m_exprs[m_index];
509 * @see ExpressionOwner#setExpression(Expression)
511 public void setExpression(Expression exp)
514 if(!(exp instanceof LocPathIterator))
516 // Yuck. Need FilterExprIter. Or make it so m_exprs can be just
517 // plain expressions?
518 WalkingIterator wi = new WalkingIterator(getPrefixResolver());
519 FilterExprWalker few = new FilterExprWalker(wi);
520 wi.setFirstWalker(few);
521 few.setInnerExpression(exp);
522 wi.exprSetParent(UnionPathIterator.this);
523 few.exprSetParent(wi);
524 exp.exprSetParent(few);
528 exp.exprSetParent(UnionPathIterator.this);
529 m_exprs[m_index] = (LocPathIterator)exp;
535 * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
537 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
539 if(visitor.visitUnionPath(owner, this))
543 int n = m_exprs.length;
544 for(int i = 0; i < n; i++)
546 m_exprs[i].callVisitors(new iterOwner(i), visitor);
553 * @see Expression#deepEquals(Expression)
555 public boolean deepEquals(Expression expr)
557 if (!super.deepEquals(expr))
560 UnionPathIterator upi = (UnionPathIterator) expr;
564 int n = m_exprs.length;
566 if((null == upi.m_exprs) || (upi.m_exprs.length != n))
569 for (int i = 0; i < n; i++)
571 if(!m_exprs[i].deepEquals(upi.m_exprs[i]))
575 else if (null != upi.m_exprs)