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 */
21package org.apache.xpath.axes;
22
23import org.apache.xml.dtm.DTM;
24import org.apache.xml.dtm.DTMIterator;
25import org.apache.xml.utils.PrefixResolver;
26import org.apache.xpath.Expression;
27import org.apache.xpath.ExpressionOwner;
28import org.apache.xpath.XPathContext;
29import org.apache.xpath.XPathVisitor;
30import org.apache.xpath.compiler.Compiler;
31import org.apache.xpath.objects.XObject;
32import org.apache.xpath.patterns.NodeTest;
33
34public 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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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	 */
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     */
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
623  	PredOwner(int index)
624  	{
625  		m_index = index;
626  	}
627
628    /**
629     * @see ExpressionOwner#getExpression()
630     */
631    public Expression getExpression()
632    {
633      return m_predicates[m_index];
634    }
635
636
637    /**
638     * @see ExpressionOwner#setExpression(Expression)
639     */
640    public void setExpression(Expression exp)
641    {
642    	exp.exprSetParent(PredicatedNodeTest.this);
643    	m_predicates[m_index] = exp;
644    }
645  }
646
647}
648