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