UnionPathIterator.java revision 9f8118474e9513f7a5b7d2a05e4a0fb15d1a6569
1/*
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
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
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.
17 */
18/*
19 * $Id: UnionPathIterator.java 469314 2006-10-30 23:31:59Z minchau $
20 */
21package org.apache.xpath.axes;
22
23import org.apache.xml.dtm.Axis;
24import org.apache.xml.dtm.DTM;
25import org.apache.xml.dtm.DTMIterator;
26import org.apache.xpath.Expression;
27import org.apache.xpath.ExpressionOwner;
28import org.apache.xpath.XPathVisitor;
29import org.apache.xpath.compiler.Compiler;
30import org.apache.xpath.compiler.OpCodes;
31import org.apache.xpath.compiler.OpMap;
32
33/**
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.
39 * @xsl.usage advanced
40 */
41public class UnionPathIterator extends LocPathIterator
42        implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
43{
44    static final long serialVersionUID = -3910351546843826781L;
45
46  /**
47   * Constructor to create an instance which you can add location paths to.
48   */
49  public UnionPathIterator()
50  {
51
52    super();
53
54    // m_mutable = false;
55    // m_cacheNodes = false;
56    m_iterators = null;
57    m_exprs = null;
58  }
59
60  /**
61   * Initialize the context values for this expression
62   * after it is cloned.
63   *
64   * @param context The XPath runtime context for this
65   * transformation.
66   */
67  public void setRoot(int context, Object environment)
68  {
69    super.setRoot(context, environment);
70
71    try
72    {
73      if (null != m_exprs)
74      {
75        int n = m_exprs.length;
76        DTMIterator newIters[] = new DTMIterator[n];
77
78        for (int i = 0; i < n; i++)
79        {
80          DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
81          newIters[i] = iter;
82          iter.nextNode();
83        }
84        m_iterators = newIters;
85      }
86    }
87    catch(Exception e)
88    {
89      throw new org.apache.xml.utils.WrappedRuntimeException(e);
90    }
91  }
92
93  /**
94   * Add an iterator to the union list.
95   *
96   * @param expr non-null reference to a location path iterator.
97   */
98  public void addIterator(DTMIterator expr)
99  {
100
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)
104    {
105      m_iterators = new DTMIterator[1];
106      m_iterators[0] = expr;
107    }
108    else
109    {
110      DTMIterator[] exprs = m_iterators;
111      int len = m_iterators.length;
112
113      m_iterators = new DTMIterator[len + 1];
114
115      System.arraycopy(exprs, 0, m_iterators, 0, len);
116
117      m_iterators[len] = expr;
118    }
119    expr.nextNode();
120    if(expr instanceof Expression)
121    	((Expression)expr).exprSetParent(this);
122  }
123
124  /**
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.
130   */
131  public void detach()
132  {
133          if(m_allowDetach && null != m_iterators){
134                  int n = m_iterators.length;
135                  for(int i = 0; i < n; i++)
136                  {
137                          m_iterators[i].detach();
138                  }
139                  m_iterators = null;
140          }
141  }
142
143
144  /**
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.
148   *
149   * @param compiler The Compiler which is creating
150   * this expression.
151   * @param opPos The position of this iterator in the
152   * opcode list from the compiler.
153   *
154   * @throws javax.xml.transform.TransformerException
155   */
156  public UnionPathIterator(Compiler compiler, int opPos)
157          throws javax.xml.transform.TransformerException
158  {
159
160    super();
161
162    opPos = OpMap.getFirstChildPos(opPos);
163
164    loadLocationPaths(compiler, opPos, 0);
165  }
166
167  /**
168   * This will return an iterator capable of handling the union of paths given.
169   *
170   * @param compiler The Compiler which is creating
171   * this expression.
172   * @param opPos The position of this iterator in the
173   * opcode list from the compiler.
174   *
175   * @return Object that is derived from LocPathIterator.
176   *
177   * @throws javax.xml.transform.TransformerException
178   */
179  public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
180          throws javax.xml.transform.TransformerException
181  {
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++)
190  	{
191  		LocPathIterator lpi = upi.m_exprs[i];
192
193  		if(lpi.getAxis() != Axis.CHILD)
194  		{
195  			isAllChildIterators = false;
196  			break;
197  		}
198  		else
199  		{
200  			// check for positional predicates or position function, which won't work.
201  			if(HasPositionalPredChecker.check(lpi))
202  			{
203  				isAllChildIterators = false;
204  				break;
205  			}
206  		}
207  	}
208  	if(isAllChildIterators)
209  	{
210  		UnionChildIterator uci = new UnionChildIterator();
211
212	  	for(int i = 0; i < nPaths; i++)
213	  	{
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);
219	  	}
220	  	return uci;
221
222  	}
223  	else
224  		return upi;
225  }
226
227  /**
228   * Get the analysis bits for this walker, as defined in the WalkerFactory.
229   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
230   */
231  public int getAnalysisBits()
232  {
233    int bits = 0;
234
235    if (m_exprs != null)
236    {
237      int n = m_exprs.length;
238
239      for (int i = 0; i < n; i++)
240      {
241      	int bit = m_exprs[i].getAnalysisBits();
242        bits |= bit;
243      }
244    }
245
246    return bits;
247  }
248
249  /**
250   * Read the object from a serialization stream.
251   *
252   * @param stream Input stream to read from
253   *
254   * @throws java.io.IOException
255   * @throws javax.xml.transform.TransformerException
256   */
257  private void readObject(java.io.ObjectInputStream stream)
258          throws java.io.IOException, javax.xml.transform.TransformerException
259  {
260    try
261    {
262      stream.defaultReadObject();
263      m_clones =  new IteratorPool(this);
264    }
265    catch (ClassNotFoundException cnfe)
266    {
267      throw new javax.xml.transform.TransformerException(cnfe);
268    }
269  }
270
271  /**
272   * Get a cloned LocPathIterator that holds the same
273   * position as this iterator.
274   *
275   * @return A clone of this iterator that holds the same node position.
276   *
277   * @throws CloneNotSupportedException
278   */
279  public Object clone() throws CloneNotSupportedException
280  {
281
282    UnionPathIterator clone = (UnionPathIterator) super.clone();
283    if (m_iterators != null)
284    {
285      int n = m_iterators.length;
286
287      clone.m_iterators = new DTMIterator[n];
288
289      for (int i = 0; i < n; i++)
290      {
291        clone.m_iterators[i] = (DTMIterator)m_iterators[i].clone();
292      }
293    }
294
295    return clone;
296  }
297
298
299  /**
300   * Create a new location path iterator.
301   *
302   * @param compiler The Compiler which is creating
303   * this expression.
304   * @param opPos The position of this iterator in the
305   *
306   * @return New location path iterator.
307   *
308   * @throws javax.xml.transform.TransformerException
309   */
310  protected LocPathIterator createDTMIterator(
311          Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
312  {
313    LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos,
314                                      (compiler.getLocationPathDepth() <= 0));
315    return lpi;
316  }
317
318  /**
319   * Initialize the location path iterators.  Recursive.
320   *
321   * @param compiler The Compiler which is creating
322   * this expression.
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.
326   *
327   * @throws javax.xml.transform.TransformerException
328   */
329  protected void loadLocationPaths(Compiler compiler, int opPos, int count)
330          throws javax.xml.transform.TransformerException
331  {
332
333    // TODO: Handle unwrapped FilterExpr
334    int steptype = compiler.getOp(opPos);
335
336    if (steptype == OpCodes.OP_LOCATIONPATH)
337    {
338      loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
339
340      m_exprs[count] = createDTMIterator(compiler, opPos);
341      m_exprs[count].exprSetParent(this);
342    }
343    else
344    {
345
346      // Have to check for unwrapped functions, which the LocPathIterator
347      // doesn't handle.
348      switch (steptype)
349      {
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);
355
356        WalkingIterator iter =
357          new WalkingIterator(compiler.getNamespaceContext());
358        iter.exprSetParent(this);
359
360        if(compiler.getLocationPathDepth() <= 0)
361          iter.setIsTopLevel(true);
362
363        iter.m_firstWalker = new org.apache.xpath.axes.FilterExprWalker(iter);
364
365        iter.m_firstWalker.init(compiler, opPos, steptype);
366
367        m_exprs[count] = iter;
368        break;
369      default :
370        m_exprs = new LocPathIterator[count];
371      }
372    }
373  }
374
375  /**
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.
381   */
382  public int nextNode()
383  {
384  	if(m_foundLast)
385  		return DTM.NULL;
386
387    // Loop through the iterators getting the current fetched
388    // node, and get the earliest occuring in document order
389    int earliestNode = DTM.NULL;
390
391    if (null != m_iterators)
392    {
393      int n = m_iterators.length;
394      int iteratorUsed = -1;
395
396      for (int i = 0; i < n; i++)
397      {
398        int node = m_iterators[i].getCurrentNode();
399
400        if (DTM.NULL == node)
401          continue;
402        else if (DTM.NULL == earliestNode)
403        {
404          iteratorUsed = i;
405          earliestNode = node;
406        }
407        else
408        {
409          if (node == earliestNode)
410          {
411
412            // Found a duplicate, so skip past it.
413            m_iterators[i].nextNode();
414          }
415          else
416          {
417            DTM dtm = getDTM(node);
418
419            if (dtm.isNodeAfter(node, earliestNode))
420            {
421              iteratorUsed = i;
422              earliestNode = node;
423            }
424          }
425        }
426      }
427
428      if (DTM.NULL != earliestNode)
429      {
430        m_iterators[iteratorUsed].nextNode();
431
432        incrementCurrentPos();
433      }
434      else
435        m_foundLast = true;
436    }
437
438    m_lastFetched = earliestNode;
439
440    return earliestNode;
441  }
442
443  /**
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).
452   */
453  public void fixupVariables(java.util.Vector vars, int globalsSize)
454  {
455    for (int i = 0; i < m_exprs.length; i++)
456    {
457      m_exprs[i].fixupVariables(vars, globalsSize);
458    }
459
460  }
461
462  /**
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.
466   * @serial
467   */
468  protected LocPathIterator[] m_exprs;
469
470
471  /**
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.
475   * @serial
476   */
477  protected DTMIterator[] m_iterators;
478
479  /**
480   * Returns the axis being iterated, if it is known.
481   *
482   * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
483   * types.
484   */
485  public int getAxis()
486  {
487    // Could be smarter.
488    return -1;
489  }
490
491  class iterOwner implements ExpressionOwner
492  {
493  	int m_index;
494
495  	iterOwner(int index)
496  	{
497  		m_index = index;
498  	}
499
500    /**
501     * @see ExpressionOwner#getExpression()
502     */
503    public Expression getExpression()
504    {
505      return m_exprs[m_index];
506    }
507
508    /**
509     * @see ExpressionOwner#setExpression(Expression)
510     */
511    public void setExpression(Expression exp)
512    {
513
514    	if(!(exp instanceof LocPathIterator))
515    	{
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);
525    		exp = wi;
526    	}
527    	else
528    		exp.exprSetParent(UnionPathIterator.this);
529    	m_exprs[m_index] = (LocPathIterator)exp;
530    }
531
532  }
533
534  /**
535   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
536   */
537  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
538  {
539  	 	if(visitor.visitUnionPath(owner, this))
540  	 	{
541  	 		if(null != m_exprs)
542  	 		{
543  	 			int n = m_exprs.length;
544  	 			for(int i = 0; i < n; i++)
545  	 			{
546  	 				m_exprs[i].callVisitors(new iterOwner(i), visitor);
547  	 			}
548  	 		}
549  	 	}
550  }
551
552    /**
553     * @see Expression#deepEquals(Expression)
554     */
555    public boolean deepEquals(Expression expr)
556    {
557      if (!super.deepEquals(expr))
558            return false;
559
560      UnionPathIterator upi = (UnionPathIterator) expr;
561
562      if (null != m_exprs)
563      {
564        int n = m_exprs.length;
565
566        if((null == upi.m_exprs) || (upi.m_exprs.length != n))
567        	return false;
568
569        for (int i = 0; i < n; i++)
570        {
571          if(!m_exprs[i].deepEquals(upi.m_exprs[i]))
572          	return false;
573        }
574      }
575      else if (null != upi.m_exprs)
576      {
577          return false;
578      }
579
580      return true;
581    }
582
583
584}
585