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