001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: UnionPathIterator.java 469314 2006-10-30 23:31:59Z minchau $
020     */
021    package org.apache.xpath.axes;
022    
023    import org.apache.xml.dtm.Axis;
024    import org.apache.xml.dtm.DTM;
025    import org.apache.xml.dtm.DTMIterator;
026    import org.apache.xpath.Expression;
027    import org.apache.xpath.ExpressionOwner;
028    import org.apache.xpath.XPathVisitor;
029    import org.apache.xpath.compiler.Compiler;
030    import org.apache.xpath.compiler.OpCodes;
031    import org.apache.xpath.compiler.OpMap;
032    
033    /**
034     * This class extends NodeSetDTM, which implements DTMIterator,
035     * and fetches nodes one at a time in document order based on a XPath
036     * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
037     * As each node is iterated via nextNode(), the node is also stored
038     * in the NodeVector, so that previousNode() can easily be done.
039     * @xsl.usage advanced
040     */
041    public class UnionPathIterator extends LocPathIterator
042            implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
043    {
044        static final long serialVersionUID = -3910351546843826781L;
045    
046      /**
047       * Constructor to create an instance which you can add location paths to.
048       */
049      public UnionPathIterator()
050      {
051    
052        super();
053    
054        // m_mutable = false;
055        // m_cacheNodes = false;
056        m_iterators = null;
057        m_exprs = null;
058      }
059    
060      /**
061       * Initialize the context values for this expression 
062       * after it is cloned.
063       *
064       * @param context The XPath runtime context for this 
065       * transformation.
066       */
067      public void setRoot(int context, Object environment)
068      {
069        super.setRoot(context, environment);
070    
071        try
072        {
073          if (null != m_exprs)
074          {
075            int n = m_exprs.length;
076            DTMIterator newIters[] = new DTMIterator[n];
077      
078            for (int i = 0; i < n; i++)
079            {
080              DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
081              newIters[i] = iter;
082              iter.nextNode();
083            }
084            m_iterators = newIters;
085          }
086        }
087        catch(Exception e)
088        {
089          throw new org.apache.xml.utils.WrappedRuntimeException(e);
090        }
091      }
092      
093      /**
094       * Add an iterator to the union list.
095       *
096       * @param expr non-null reference to a location path iterator.
097       */
098      public void addIterator(DTMIterator expr)
099      {
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    }