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.DTM;
24 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
25 import com.sun.org.apache.xml.internal.utils.PrefixResolver;
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.XPathContext;
30 import com.sun.org.apache.xpath.internal.XPathVisitor;
31 import com.sun.org.apache.xpath.internal.compiler.Compiler;
32 import com.sun.org.apache.xpath.internal.objects.XObject;
33 import com.sun.org.apache.xpath.internal.patterns.NodeTest;
34 import java.util.List;
35 
36 /**
37  * @LastModified: Oct 2017
38  */
39 public abstract class PredicatedNodeTest extends NodeTest implements SubContextList
40 {
41     static final long serialVersionUID = -6193530757296377351L;
42 
43   /**
44    * Construct an AxesWalker using a LocPathIterator.
45    *
46    * @param locPathIterator non-null reference to the parent iterator.
47    */
PredicatedNodeTest(LocPathIterator locPathIterator)48   PredicatedNodeTest(LocPathIterator locPathIterator)
49   {
50     m_lpi = locPathIterator;
51   }
52 
53   /**
54    * Construct an AxesWalker.  The location path iterator will have to be set
55    * before use.
56    */
PredicatedNodeTest()57   PredicatedNodeTest()
58   {
59   }
60 
61   /**
62    * Read the object from a serialization stream.
63    *
64    * @param stream Input stream to read from
65    *
66    * @throws java.io.IOException
67    * @throws javax.xml.transform.TransformerException
68    */
readObject(java.io.ObjectInputStream stream)69   private void readObject(java.io.ObjectInputStream stream)
70           throws java.io.IOException, javax.xml.transform.TransformerException
71   {
72     try
73     {
74       stream.defaultReadObject();
75       m_predicateIndex = -1;
76 
77       /**
78        * Initialize to the declared value.
79        * As noted at declaration, this variable is used only for clones for getLastPos,
80        * it should have been excluded from serialization. For compatibility, we'll
81        * keep it as is but initializing to the declared value.
82        */
83       m_predCount = -1;
84       resetProximityPositions();
85     }
86     catch (ClassNotFoundException cnfe)
87     {
88       throw new javax.xml.transform.TransformerException(cnfe);
89     }
90   }
91 
92   /**
93    * Get a cloned PrdicatedNodeTest.
94    *
95    * @return A new PredicatedNodeTest that can be used without mutating this one.
96    *
97    * @throws CloneNotSupportedException
98    */
clone()99   public Object clone() throws CloneNotSupportedException
100   {
101     // Do not access the location path itterator during this operation!
102 
103     PredicatedNodeTest clone = (PredicatedNodeTest) super.clone();
104 
105     if ((null != this.m_proximityPositions)
106             && (this.m_proximityPositions == clone.m_proximityPositions))
107     {
108       clone.m_proximityPositions = new int[this.m_proximityPositions.length];
109 
110       System.arraycopy(this.m_proximityPositions, 0,
111                        clone.m_proximityPositions, 0,
112                        this.m_proximityPositions.length);
113     }
114 
115     if(clone.m_lpi == this)
116       clone.m_lpi = (LocPathIterator)clone;
117 
118     return clone;
119   }
120 
121   // Only for clones for findLastPos.  See bug4638.
122   protected int m_predCount = -1;
123 
124   /**
125    * Get the number of predicates that this walker has.
126    *
127    * @return the number of predicates that this walker has.
128    */
getPredicateCount()129   public int getPredicateCount()
130   {
131     if(-1 == m_predCount)
132       return (null == m_predicates) ? 0 : m_predicates.length;
133     else
134       return m_predCount;
135   }
136 
137   /**
138    * Set the number of predicates that this walker has.  This does more
139    * that one would think, as it creates a new predicate array of the
140    * size of the count argument, and copies count predicates into the new
141    * one from the old, and then reassigns the predicates value.  All this
142    * to keep from having to have a predicate count value.
143    *
144    * @param count The number of predicates, which must be equal or less
145    *               than the existing count.
146    */
setPredicateCount(int count)147   public void setPredicateCount(int count)
148   {
149     if(count > 0)
150     {
151       Expression[] newPredicates = new Expression[count];
152       for (int i = 0; i < count; i++)
153       {
154         newPredicates[i] = m_predicates[i];
155       }
156       m_predicates = newPredicates;
157     }
158     else
159       m_predicates = null;
160 
161   }
162 
163   /**
164    * Init predicate info.
165    *
166    * @param compiler The Compiler object that has information about this
167    *                 walker in the op map.
168    * @param opPos The op code position of this location step.
169    *
170    * @throws javax.xml.transform.TransformerException
171    */
initPredicateInfo(Compiler compiler, int opPos)172   protected void initPredicateInfo(Compiler compiler, int opPos)
173           throws javax.xml.transform.TransformerException
174   {
175 
176     int pos = compiler.getFirstPredicateOpPos(opPos);
177 
178     if(pos > 0)
179     {
180       m_predicates = compiler.getCompiledPredicates(pos);
181       if(null != m_predicates)
182       {
183         for(int i = 0; i < m_predicates.length; i++)
184         {
185                 m_predicates[i].exprSetParent(this);
186         }
187       }
188     }
189   }
190 
191   /**
192    * Get a predicate expression at the given index.
193    *
194    *
195    * @param index Index of the predicate.
196    *
197    * @return A predicate expression.
198    */
getPredicate(int index)199   public Expression getPredicate(int index)
200   {
201     return m_predicates[index];
202   }
203 
204   /**
205    * Get the current sub-context position.
206    *
207    * @return The node position of this walker in the sub-context node list.
208    */
getProximityPosition()209   public int getProximityPosition()
210   {
211 
212     // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex);
213     return getProximityPosition(m_predicateIndex);
214   }
215 
216   /**
217    * Get the current sub-context position.
218    *
219    * @param xctxt The XPath runtime context.
220    *
221    * @return The node position of this walker in the sub-context node list.
222    */
getProximityPosition(XPathContext xctxt)223   public int getProximityPosition(XPathContext xctxt)
224   {
225     return getProximityPosition();
226   }
227 
228   /**
229    * Get the index of the last node that can be itterated to.
230    *
231    *
232    * @param xctxt XPath runtime context.
233    *
234    * @return the index of the last node that can be itterated to.
235    */
getLastPos(XPathContext xctxt)236   public abstract int getLastPos(XPathContext xctxt);
237 
238   /**
239    * Get the current sub-context position.
240    *
241    * @param predicateIndex The index of the predicate where the proximity
242    *                       should be taken from.
243    *
244    * @return The node position of this walker in the sub-context node list.
245    */
getProximityPosition(int predicateIndex)246   protected int getProximityPosition(int predicateIndex)
247   {
248     return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0;
249   }
250 
251   /**
252    * Reset the proximity positions counts.
253    */
resetProximityPositions()254   public void resetProximityPositions()
255   {
256     int nPredicates = getPredicateCount();
257     if (nPredicates > 0)
258     {
259       if (null == m_proximityPositions)
260         m_proximityPositions = new int[nPredicates];
261 
262       for (int i = 0; i < nPredicates; i++)
263       {
264         try
265         {
266           initProximityPosition(i);
267         }
268         catch(Exception e)
269         {
270           // TODO: Fix this...
271           throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
272         }
273       }
274     }
275   }
276 
277   /**
278    * Init the proximity position to zero for a forward axes.
279    *
280    * @param i The index into the m_proximityPositions array.
281    *
282    * @throws javax.xml.transform.TransformerException
283    */
initProximityPosition(int i)284   public void initProximityPosition(int i) throws javax.xml.transform.TransformerException
285   {
286     m_proximityPositions[i] = 0;
287   }
288 
289   /**
290    * Count forward one proximity position.
291    *
292    * @param i The index into the m_proximityPositions array, where the increment
293    *          will occur.
294    */
countProximityPosition(int i)295   protected void countProximityPosition(int i)
296   {
297         // Note that in the case of a UnionChildIterator, this may be a
298         // static object and so m_proximityPositions may indeed be null!
299         int[] pp = m_proximityPositions;
300     if ((null != pp) && (i < pp.length))
301       pp[i]++;
302   }
303 
304   /**
305    * Tells if this is a reverse axes.
306    *
307    * @return false, unless a derived class overrides.
308    */
isReverseAxes()309   public boolean isReverseAxes()
310   {
311     return false;
312   }
313 
314   /**
315    * Get which predicate is executing.
316    *
317    * @return The current predicate index, or -1 if no predicate is executing.
318    */
getPredicateIndex()319   public int getPredicateIndex()
320   {
321     return m_predicateIndex;
322   }
323 
324   /**
325    * Process the predicates.
326    *
327    * @param context The current context node.
328    * @param xctxt The XPath runtime context.
329    *
330    * @return the result of executing the predicate expressions.
331    *
332    * @throws javax.xml.transform.TransformerException
333    */
executePredicates(int context, XPathContext xctxt)334   boolean executePredicates(int context, XPathContext xctxt)
335           throws javax.xml.transform.TransformerException
336   {
337 
338     int nPredicates = getPredicateCount();
339     // System.out.println("nPredicates: "+nPredicates);
340     if (nPredicates == 0)
341       return true;
342 
343     PrefixResolver savedResolver = xctxt.getNamespaceContext();
344 
345     try
346     {
347       m_predicateIndex = 0;
348       xctxt.pushSubContextList(this);
349       xctxt.pushNamespaceContext(m_lpi.getPrefixResolver());
350       xctxt.pushCurrentNode(context);
351 
352       for (int i = 0; i < nPredicates; i++)
353       {
354         // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
355         XObject pred = m_predicates[i].execute(xctxt);
356         // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount());
357         // System.out.println("pred.getType(): "+pred.getType());
358         if (XObject.CLASS_NUMBER == pred.getType())
359         {
360           if (DEBUG_PREDICATECOUNTING)
361           {
362             System.out.flush();
363             System.out.println("\n===== start predicate count ========");
364             System.out.println("m_predicateIndex: " + m_predicateIndex);
365             // System.out.println("getProximityPosition(m_predicateIndex): "
366             //                   + getProximityPosition(m_predicateIndex));
367             System.out.println("pred.num(): " + pred.num());
368           }
369 
370           int proxPos = this.getProximityPosition(m_predicateIndex);
371           int predIndex = (int) pred.num();
372           if (proxPos != predIndex)
373           {
374             if (DEBUG_PREDICATECOUNTING)
375             {
376               System.out.println("\nnode context: "+nodeToString(context));
377               System.out.println("index predicate is false: "+proxPos);
378               System.out.println("\n===== end predicate count ========");
379             }
380             return false;
381           }
382           else if (DEBUG_PREDICATECOUNTING)
383           {
384             System.out.println("\nnode context: "+nodeToString(context));
385             System.out.println("index predicate is true: "+proxPos);
386             System.out.println("\n===== end predicate count ========");
387           }
388 
389           // If there is a proximity index that will not change during the
390           // course of itteration, then we know there can be no more true
391           // occurances of this predicate, so flag that we're done after
392           // this.
393           //
394           // bugzilla 14365
395           // We can't set m_foundLast = true unless we're sure that -all-
396           // remaining parameters are stable, or else last() fails. Fixed so
397           // only sets m_foundLast if on the last predicate
398           if(m_predicates[i].isStableNumber() && i == nPredicates - 1)
399           {
400             m_foundLast = true;
401           }
402         }
403         else if (!pred.bool())
404           return false;
405 
406         countProximityPosition(++m_predicateIndex);
407       }
408     }
409     finally
410     {
411       xctxt.popCurrentNode();
412       xctxt.popNamespaceContext();
413       xctxt.popSubContextList();
414       m_predicateIndex = -1;
415     }
416 
417     return true;
418   }
419 
420   /**
421    * This function is used to fixup variables from QNames to stack frame
422    * indexes at stylesheet build time.
423    * @param vars List of QNames that correspond to variables.  This list
424    * should be searched backwards for the first qualified name that
425    * corresponds to the variable reference qname.  The position of the
426    * QName in the vector from the start of the vector will be its position
427    * in the stack frame (but variables above the globalsTop value will need
428    * to be offset to the current stack frame).
429    */
fixupVariables(List<QName> vars, int globalsSize)430   public void fixupVariables(List<QName> vars, int globalsSize)
431   {
432     super.fixupVariables(vars, globalsSize);
433 
434     int nPredicates = getPredicateCount();
435 
436     for (int i = 0; i < nPredicates; i++)
437     {
438       m_predicates[i].fixupVariables(vars, globalsSize);
439     }
440   }
441 
442 
443   /**
444    * Diagnostics.
445    *
446    * @param n Node to give diagnostic information about, or null.
447    *
448    * @return Informative string about the argument.
449    */
nodeToString(int n)450   protected String nodeToString(int n)
451   {
452     if(DTM.NULL != n)
453     {
454       DTM dtm = m_lpi.getXPathContext().getDTM(n);
455       return dtm.getNodeName(n) + "{" + (n+1) + "}";
456     }
457     else
458     {
459       return "null";
460     }
461   }
462 
463   //=============== NodeFilter Implementation ===============
464 
465   /**
466    *  Test whether a specified node is visible in the logical view of a
467    * TreeWalker or NodeIterator. This function will be called by the
468    * implementation of TreeWalker and NodeIterator; it is not intended to
469    * be called directly from user code.
470    * @param n  The node to check to see if it passes the filter or not.
471    * @return  a constant to determine whether the node is accepted,
472    *   rejected, or skipped, as defined  above .
473    */
acceptNode(int n)474   public short acceptNode(int n)
475   {
476 
477     XPathContext xctxt = m_lpi.getXPathContext();
478 
479     try
480     {
481       xctxt.pushCurrentNode(n);
482 
483       XObject score = execute(xctxt, n);
484 
485       // System.out.println("\n::acceptNode - score: "+score.num()+"::");
486       if (score != NodeTest.SCORE_NONE)
487       {
488         if (getPredicateCount() > 0)
489         {
490           countProximityPosition(0);
491 
492           if (!executePredicates(n, xctxt))
493             return DTMIterator.FILTER_SKIP;
494         }
495 
496         return DTMIterator.FILTER_ACCEPT;
497       }
498     }
499     catch (javax.xml.transform.TransformerException se)
500     {
501 
502       // TODO: Fix this.
503       throw new RuntimeException(se.getMessage());
504     }
505     finally
506     {
507       xctxt.popCurrentNode();
508     }
509 
510     return DTMIterator.FILTER_SKIP;
511   }
512 
513 
514   /**
515    * Get the owning location path iterator.
516    *
517    * @return the owning location path iterator, which should not be null.
518    */
getLocPathIterator()519   public LocPathIterator getLocPathIterator()
520   {
521     return m_lpi;
522   }
523 
524   /**
525    * Set the location path iterator owner for this walker.  Besides
526    * initialization, this function is called during cloning operations.
527    *
528    * @param li non-null reference to the owning location path iterator.
529    */
setLocPathIterator(LocPathIterator li)530   public void setLocPathIterator(LocPathIterator li)
531   {
532     m_lpi = li;
533     if(this != li)
534       li.exprSetParent(this);
535   }
536 
537   /**
538    * Tell if this expression or it's subexpressions can traverse outside
539    * the current subtree.
540    *
541    * @return true if traversal outside the context node's subtree can occur.
542    */
canTraverseOutsideSubtree()543    public boolean canTraverseOutsideSubtree()
544    {
545     int n = getPredicateCount();
546     for (int i = 0; i < n; i++)
547     {
548       if(getPredicate(i).canTraverseOutsideSubtree())
549         return true;
550     }
551     return false;
552    }
553 
554         /**
555          * This will traverse the heararchy, calling the visitor for
556          * each member.  If the called visitor method returns
557          * false, the subtree should not be called.
558          *
559          * @param visitor The visitor whose appropriate method will be called.
560          */
callPredicateVisitors(XPathVisitor visitor)561         public void callPredicateVisitors(XPathVisitor visitor)
562         {
563           if (null != m_predicates)
564             {
565             int n = m_predicates.length;
566             for (int i = 0; i < n; i++)
567               {
568               ExpressionOwner predOwner = new PredOwner(i);
569               if (visitor.visitPredicate(predOwner, m_predicates[i]))
570                 {
571                 m_predicates[i].callVisitors(predOwner, visitor);
572               }
573 
574             }
575           }
576         }
577 
578     /**
579      * @see Expression#deepEquals(Expression)
580      */
deepEquals(Expression expr)581     public boolean deepEquals(Expression expr)
582     {
583       if (!super.deepEquals(expr))
584             return false;
585 
586       PredicatedNodeTest pnt = (PredicatedNodeTest) expr;
587       if (null != m_predicates)
588       {
589 
590         int n = m_predicates.length;
591         if ((null == pnt.m_predicates) || (pnt.m_predicates.length != n))
592               return false;
593         for (int i = 0; i < n; i++)
594         {
595           if (!m_predicates[i].deepEquals(pnt.m_predicates[i]))
596                 return false;
597         }
598       }
599       else if (null != pnt.m_predicates)
600               return false;
601 
602       return true;
603     }
604 
605   /** This is true if nextNode returns null. */
606   transient protected boolean m_foundLast = false;
607 
608   /** The owning location path iterator.
609    *  @serial */
610   protected LocPathIterator m_lpi;
611 
612   /**
613    * Which predicate we are executing.
614    */
615   transient int m_predicateIndex = -1;
616 
617   /** The list of predicate expressions. Is static and does not need
618    *  to be deep cloned.
619    *  @serial
620    */
621   private Expression[] m_predicates;
622 
623   /**
624    * An array of counts that correspond to the number
625    * of predicates the step contains.
626    */
627   transient protected int[] m_proximityPositions;
628 
629   /** If true, diagnostic messages about predicate execution will be posted.  */
630   static final boolean DEBUG_PREDICATECOUNTING = false;
631 
632   class PredOwner implements ExpressionOwner
633   {
634         int m_index;
635 
PredOwner(int index)636         PredOwner(int index)
637         {
638                 m_index = index;
639         }
640 
641     /**
642      * @see ExpressionOwner#getExpression()
643      */
getExpression()644     public Expression getExpression()
645     {
646       return m_predicates[m_index];
647     }
648 
649 
650     /**
651      * @see ExpressionOwner#setExpression(Expression)
652      */
setExpression(Expression exp)653     public void setExpression(Expression exp)
654     {
655         exp.exprSetParent(PredicatedNodeTest.this);
656         m_predicates[m_index] = exp;
657     }
658   }
659 
660 }
661