1 /*
2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xpath.internal.axes;
22 
23 import com.sun.org.apache.xml.internal.dtm.Axis;
24 import com.sun.org.apache.xml.internal.dtm.DTM;
25 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
26 import com.sun.org.apache.xml.internal.utils.QName;
27 import com.sun.org.apache.xpath.internal.Expression;
28 import com.sun.org.apache.xpath.internal.ExpressionOwner;
29 import com.sun.org.apache.xpath.internal.XPathVisitor;
30 import com.sun.org.apache.xpath.internal.compiler.Compiler;
31 import com.sun.org.apache.xpath.internal.compiler.OpCodes;
32 import com.sun.org.apache.xpath.internal.compiler.OpMap;
33 import java.util.List;
34 
35 /**
36  * This class extends NodeSetDTM, which implements DTMIterator,
37  * and fetches nodes one at a time in document order based on a XPath
38  * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
39  * As each node is iterated via nextNode(), the node is also stored
40  * in the NodeVector, so that previousNode() can easily be done.
41  * @xsl.usage advanced
42  * @LastModified: Oct 2017
43  */
44 public class UnionPathIterator extends LocPathIterator
45         implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
46 {
47     static final long serialVersionUID = -3910351546843826781L;
48 
49   /**
50    * Constructor to create an instance which you can add location paths to.
51    */
UnionPathIterator()52   public UnionPathIterator()
53   {
54 
55     super();
56 
57     // m_mutable = false;
58     // m_cacheNodes = false;
59     m_iterators = null;
60     m_exprs = null;
61   }
62 
63   /**
64    * Initialize the context values for this expression
65    * after it is cloned.
66    *
67    * @param context The XPath runtime context for this
68    * transformation.
69    */
setRoot(int context, Object environment)70   public void setRoot(int context, Object environment)
71   {
72     super.setRoot(context, environment);
73 
74     try
75     {
76       if (null != m_exprs)
77       {
78         int n = m_exprs.length;
79         DTMIterator newIters[] = new DTMIterator[n];
80 
81         for (int i = 0; i < n; i++)
82         {
83           DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
84           newIters[i] = iter;
85           iter.nextNode();
86         }
87         m_iterators = newIters;
88       }
89     }
90     catch(Exception e)
91     {
92       throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
93     }
94   }
95 
96   /**
97    * Add an iterator to the union list.
98    *
99    * @param expr non-null reference to a location path iterator.
100    */
addIterator(DTMIterator expr)101   public void addIterator(DTMIterator expr)
102   {
103 
104     // Increase array size by only 1 at a time.  Fix this
105     // if it looks to be a problem.
106     if (null == m_iterators)
107     {
108       m_iterators = new DTMIterator[1];
109       m_iterators[0] = expr;
110     }
111     else
112     {
113       DTMIterator[] exprs = m_iterators;
114       int len = m_iterators.length;
115 
116       m_iterators = new DTMIterator[len + 1];
117 
118       System.arraycopy(exprs, 0, m_iterators, 0, len);
119 
120       m_iterators[len] = expr;
121     }
122     expr.nextNode();
123     if(expr instanceof Expression)
124         ((Expression)expr).exprSetParent(this);
125   }
126 
127   /**
128    *  Detaches the iterator from the set which it iterated over, releasing
129    * any computational resources and placing the iterator in the INVALID
130    * state. After<code>detach</code> has been invoked, calls to
131    * <code>nextNode</code> or<code>previousNode</code> will raise the
132    * exception INVALID_STATE_ERR.
133    */
detach()134   public void detach()
135   {
136           if(m_allowDetach && null != m_iterators){
137                   int n = m_iterators.length;
138                   for(int i = 0; i < n; i++)
139                   {
140                           m_iterators[i].detach();
141                   }
142                   m_iterators = null;
143           }
144   }
145 
146 
147   /**
148    * Create a UnionPathIterator object, including creation
149    * of location path iterators from the opcode list, and call back
150    * into the Compiler to create predicate expressions.
151    *
152    * @param compiler The Compiler which is creating
153    * this expression.
154    * @param opPos The position of this iterator in the
155    * opcode list from the compiler.
156    *
157    * @throws javax.xml.transform.TransformerException
158    */
UnionPathIterator(Compiler compiler, int opPos)159   public UnionPathIterator(Compiler compiler, int opPos)
160           throws javax.xml.transform.TransformerException
161   {
162 
163     super();
164 
165     opPos = OpMap.getFirstChildPos(opPos);
166 
167     loadLocationPaths(compiler, opPos, 0);
168   }
169 
170   /**
171    * This will return an iterator capable of handling the union of paths given.
172    *
173    * @param compiler The Compiler which is creating
174    * this expression.
175    * @param opPos The position of this iterator in the
176    * opcode list from the compiler.
177    *
178    * @return Object that is derived from LocPathIterator.
179    *
180    * @throws javax.xml.transform.TransformerException
181    */
createUnionIterator(Compiler compiler, int opPos)182   public static LocPathIterator createUnionIterator(Compiler compiler, int opPos)
183           throws javax.xml.transform.TransformerException
184   {
185         // For the moment, I'm going to first create a full UnionPathIterator, and
186         // then see if I can reduce it to a UnionChildIterator.  It would obviously
187         // be more effecient to just test for the conditions for a UnionChildIterator,
188         // and then create that directly.
189         UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
190         int nPaths = upi.m_exprs.length;
191         boolean isAllChildIterators = true;
192         for(int i = 0; i < nPaths; i++)
193         {
194                 LocPathIterator lpi = upi.m_exprs[i];
195 
196                 if(lpi.getAxis() != Axis.CHILD)
197                 {
198                         isAllChildIterators = false;
199                         break;
200                 }
201                 else
202                 {
203                         // check for positional predicates or position function, which won't work.
204                         if(HasPositionalPredChecker.check(lpi))
205                         {
206                                 isAllChildIterators = false;
207                                 break;
208                         }
209                 }
210         }
211         if(isAllChildIterators)
212         {
213                 UnionChildIterator uci = new UnionChildIterator();
214 
215                 for(int i = 0; i < nPaths; i++)
216                 {
217                         PredicatedNodeTest lpi = upi.m_exprs[i];
218                         // I could strip the lpi down to a pure PredicatedNodeTest, but
219                         // I don't think it's worth it.  Note that the test can be used
220                         // as a static object... so it doesn't have to be cloned.
221                         uci.addNodeTest(lpi);
222                 }
223                 return uci;
224 
225         }
226         else
227                 return upi;
228   }
229 
230   /**
231    * Get the analysis bits for this walker, as defined in the WalkerFactory.
232    * @return One of WalkerFactory#BIT_DESCENDANT, etc.
233    */
getAnalysisBits()234   public int getAnalysisBits()
235   {
236     int bits = 0;
237 
238     if (m_exprs != null)
239     {
240       int n = m_exprs.length;
241 
242       for (int i = 0; i < n; i++)
243       {
244         int bit = m_exprs[i].getAnalysisBits();
245         bits |= bit;
246       }
247     }
248 
249     return bits;
250   }
251 
252   /**
253    * Read the object from a serialization stream.
254    *
255    * @param stream Input stream to read from
256    *
257    * @throws java.io.IOException
258    * @throws javax.xml.transform.TransformerException
259    */
readObject(java.io.ObjectInputStream stream)260   private void readObject(java.io.ObjectInputStream stream)
261           throws java.io.IOException, javax.xml.transform.TransformerException
262   {
263     try
264     {
265       stream.defaultReadObject();
266       m_clones =  new IteratorPool(this);
267     }
268     catch (ClassNotFoundException cnfe)
269     {
270       throw new javax.xml.transform.TransformerException(cnfe);
271     }
272   }
273 
274   /**
275    * Get a cloned LocPathIterator that holds the same
276    * position as this iterator.
277    *
278    * @return A clone of this iterator that holds the same node position.
279    *
280    * @throws CloneNotSupportedException
281    */
clone()282   public Object clone() throws CloneNotSupportedException
283   {
284 
285     UnionPathIterator clone = (UnionPathIterator) super.clone();
286     if (m_iterators != null)
287     {
288       int n = m_iterators.length;
289 
290       clone.m_iterators = new DTMIterator[n];
291 
292       for (int i = 0; i < n; i++)
293       {
294         clone.m_iterators[i] = (DTMIterator)m_iterators[i].clone();
295       }
296     }
297 
298     return clone;
299   }
300 
301 
302   /**
303    * Create a new location path iterator.
304    *
305    * @param compiler The Compiler which is creating
306    * this expression.
307    * @param opPos The position of this iterator in the
308    *
309    * @return New location path iterator.
310    *
311    * @throws javax.xml.transform.TransformerException
312    */
createDTMIterator( Compiler compiler, int opPos)313   protected LocPathIterator createDTMIterator(
314           Compiler compiler, int opPos) throws javax.xml.transform.TransformerException
315   {
316     LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos,
317                                       (compiler.getLocationPathDepth() <= 0));
318     return lpi;
319   }
320 
321   /**
322    * Initialize the location path iterators.  Recursive.
323    *
324    * @param compiler The Compiler which is creating
325    * this expression.
326    * @param opPos The position of this iterator in the
327    * opcode list from the compiler.
328    * @param count The insert position of the iterator.
329    *
330    * @throws javax.xml.transform.TransformerException
331    */
loadLocationPaths(Compiler compiler, int opPos, int count)332   protected void loadLocationPaths(Compiler compiler, int opPos, int count)
333           throws javax.xml.transform.TransformerException
334   {
335 
336     // TODO: Handle unwrapped FilterExpr
337     int steptype = compiler.getOp(opPos);
338 
339     if (steptype == OpCodes.OP_LOCATIONPATH)
340     {
341       loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
342 
343       m_exprs[count] = createDTMIterator(compiler, opPos);
344       m_exprs[count].exprSetParent(this);
345     }
346     else
347     {
348 
349       // Have to check for unwrapped functions, which the LocPathIterator
350       // doesn't handle.
351       switch (steptype)
352       {
353       case OpCodes.OP_VARIABLE :
354       case OpCodes.OP_EXTFUNCTION :
355       case OpCodes.OP_FUNCTION :
356       case OpCodes.OP_GROUP :
357         loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1);
358 
359         WalkingIterator iter =
360           new WalkingIterator(compiler.getNamespaceContext());
361         iter.exprSetParent(this);
362 
363         if(compiler.getLocationPathDepth() <= 0)
364           iter.setIsTopLevel(true);
365 
366         iter.m_firstWalker = new com.sun.org.apache.xpath.internal.axes.FilterExprWalker(iter);
367 
368         iter.m_firstWalker.init(compiler, opPos, steptype);
369 
370         m_exprs[count] = iter;
371         break;
372       default :
373         m_exprs = new LocPathIterator[count];
374       }
375     }
376   }
377 
378   /**
379    *  Returns the next node in the set and advances the position of the
380    * iterator in the set. After a DTMIterator is created, the first call
381    * to nextNode() returns the first node in the set.
382    * @return  The next <code>Node</code> in the set being iterated over, or
383    *   <code>null</code> if there are no more members in that set.
384    */
nextNode()385   public int nextNode()
386   {
387         if(m_foundLast)
388                 return DTM.NULL;
389 
390     // Loop through the iterators getting the current fetched
391     // node, and get the earliest occuring in document order
392     int earliestNode = DTM.NULL;
393 
394     if (null != m_iterators)
395     {
396       int n = m_iterators.length;
397       int iteratorUsed = -1;
398 
399       for (int i = 0; i < n; i++)
400       {
401         int node = m_iterators[i].getCurrentNode();
402 
403         if (DTM.NULL == node)
404           continue;
405         else if (DTM.NULL == earliestNode)
406         {
407           iteratorUsed = i;
408           earliestNode = node;
409         }
410         else
411         {
412           if (node == earliestNode)
413           {
414 
415             // Found a duplicate, so skip past it.
416             m_iterators[i].nextNode();
417           }
418           else
419           {
420             DTM dtm = getDTM(node);
421 
422             if (dtm.isNodeAfter(node, earliestNode))
423             {
424               iteratorUsed = i;
425               earliestNode = node;
426             }
427           }
428         }
429       }
430 
431       if (DTM.NULL != earliestNode)
432       {
433         m_iterators[iteratorUsed].nextNode();
434 
435         incrementCurrentPos();
436       }
437       else
438         m_foundLast = true;
439     }
440 
441     m_lastFetched = earliestNode;
442 
443     return earliestNode;
444   }
445 
446   /**
447    * This function is used to fixup variables from QNames to stack frame
448    * indexes at stylesheet build time.
449    * @param vars List of QNames that correspond to variables.  This list
450    * should be searched backwards for the first qualified name that
451    * corresponds to the variable reference qname.  The position of the
452    * QName in the vector from the start of the vector will be its position
453    * in the stack frame (but variables above the globalsTop value will need
454    * to be offset to the current stack frame).
455    */
fixupVariables(List<QName> vars, int globalsSize)456   public void fixupVariables(List<QName> vars, int globalsSize)
457   {
458     for (int i = 0; i < m_exprs.length; i++)
459     {
460       m_exprs[i].fixupVariables(vars, globalsSize);
461     }
462 
463   }
464 
465   /**
466    * The location path iterators, one for each
467    * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
468    * path</a> contained in the union expression.
469    * @serial
470    */
471   protected LocPathIterator[] m_exprs;
472 
473 
474   /**
475    * The location path iterators, one for each
476    * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
477    * path</a> contained in the union expression.
478    * @serial
479    */
480   protected DTMIterator[] m_iterators;
481 
482   /**
483    * Returns the axis being iterated, if it is known.
484    *
485    * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
486    * types.
487    */
getAxis()488   public int getAxis()
489   {
490     // Could be smarter.
491     return -1;
492   }
493 
494   class iterOwner implements ExpressionOwner
495   {
496         int m_index;
497 
iterOwner(int index)498         iterOwner(int index)
499         {
500                 m_index = index;
501         }
502 
503     /**
504      * @see ExpressionOwner#getExpression()
505      */
getExpression()506     public Expression getExpression()
507     {
508       return m_exprs[m_index];
509     }
510 
511     /**
512      * @see ExpressionOwner#setExpression(Expression)
513      */
setExpression(Expression exp)514     public void setExpression(Expression exp)
515     {
516 
517         if(!(exp instanceof LocPathIterator))
518         {
519                 // Yuck.  Need FilterExprIter.  Or make it so m_exprs can be just
520                 // plain expressions?
521                 WalkingIterator wi = new WalkingIterator(getPrefixResolver());
522                 FilterExprWalker few = new FilterExprWalker(wi);
523                 wi.setFirstWalker(few);
524                 few.setInnerExpression(exp);
525                 wi.exprSetParent(UnionPathIterator.this);
526                 few.exprSetParent(wi);
527                 exp.exprSetParent(few);
528                 exp = wi;
529         }
530         else
531                 exp.exprSetParent(UnionPathIterator.this);
532         m_exprs[m_index] = (LocPathIterator)exp;
533     }
534 
535   }
536 
537   /**
538    * @see com.sun.org.apache.xpath.internal.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
539    */
callVisitors(ExpressionOwner owner, XPathVisitor visitor)540   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
541   {
542                 if(visitor.visitUnionPath(owner, this))
543                 {
544                         if(null != m_exprs)
545                         {
546                                 int n = m_exprs.length;
547                                 for(int i = 0; i < n; i++)
548                                 {
549                                         m_exprs[i].callVisitors(new iterOwner(i), visitor);
550                                 }
551                         }
552                 }
553   }
554 
555     /**
556      * @see Expression#deepEquals(Expression)
557      */
deepEquals(Expression expr)558     public boolean deepEquals(Expression expr)
559     {
560       if (!super.deepEquals(expr))
561             return false;
562 
563       UnionPathIterator upi = (UnionPathIterator) expr;
564 
565       if (null != m_exprs)
566       {
567         int n = m_exprs.length;
568 
569         if((null == upi.m_exprs) || (upi.m_exprs.length != n))
570                 return false;
571 
572         for (int i = 0; i < n; i++)
573         {
574           if(!m_exprs[i].deepEquals(upi.m_exprs[i]))
575                 return false;
576         }
577       }
578       else if (null != upi.m_exprs)
579       {
580           return false;
581       }
582 
583       return true;
584     }
585 
586 
587 }
588