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