StepPattern.java revision 9f8118474e9513f7a5b7d2a05e4a0fb15d1a6569
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: StepPattern.java 468655 2006-10-28 07:12:06Z minchau $
20 */
21package org.apache.xpath.patterns;
22
23import org.apache.xml.dtm.Axis;
24import org.apache.xml.dtm.DTM;
25import org.apache.xml.dtm.DTMAxisTraverser;
26import org.apache.xml.dtm.DTMFilter;
27import org.apache.xpath.Expression;
28import org.apache.xpath.ExpressionOwner;
29import org.apache.xpath.XPathContext;
30import org.apache.xpath.XPathVisitor;
31import org.apache.xpath.axes.SubContextList;
32import org.apache.xpath.compiler.PsuedoNames;
33import org.apache.xpath.objects.XObject;
34
35/**
36 * This class represents a single pattern match step.
37 * @xsl.usage advanced
38 */
39public class StepPattern extends NodeTest implements SubContextList, ExpressionOwner
40{
41    static final long serialVersionUID = 9071668960168152644L;
42
43  /** The axis for this test. */
44  protected int m_axis;
45
46  /**
47   * Construct a StepPattern that tests for namespaces and node names.
48   *
49   *
50   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
51   * @param namespace The namespace to be tested.
52   * @param name The local name to be tested.
53   * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
54   * @param axisForPredicate No longer used.
55   */
56  public StepPattern(int whatToShow, String namespace, String name, int axis,
57                     int axisForPredicate)
58  {
59
60    super(whatToShow, namespace, name);
61
62    m_axis = axis;
63  }
64
65  /**
66   * Construct a StepPattern that doesn't test for node names.
67   *
68   *
69   * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
70   * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
71   * @param axisForPredicate No longer used.
72   */
73  public StepPattern(int whatToShow, int axis, int axisForPredicate)
74  {
75
76    super(whatToShow);
77
78    m_axis = axis;
79  }
80
81  /**
82   * The target local name or psuedo name, for hash table lookup optimization.
83   *  @serial
84   */
85  String m_targetString;  // only calculate on head
86
87  /**
88   * Calculate the local name or psuedo name of the node that this pattern will test,
89   * for hash table lookup optimization.
90   *
91   * @see org.apache.xpath.compiler.PsuedoNames
92   */
93  public void calcTargetString()
94  {
95
96    int whatToShow = getWhatToShow();
97
98    switch (whatToShow)
99    {
100    case DTMFilter.SHOW_COMMENT :
101      m_targetString = PsuedoNames.PSEUDONAME_COMMENT;
102      break;
103    case DTMFilter.SHOW_TEXT :
104    case DTMFilter.SHOW_CDATA_SECTION :
105    case (DTMFilter.SHOW_TEXT | DTMFilter.SHOW_CDATA_SECTION) :
106      m_targetString = PsuedoNames.PSEUDONAME_TEXT;
107      break;
108    case DTMFilter.SHOW_ALL :
109      m_targetString = PsuedoNames.PSEUDONAME_ANY;
110      break;
111    case DTMFilter.SHOW_DOCUMENT :
112    case DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT :
113      m_targetString = PsuedoNames.PSEUDONAME_ROOT;
114      break;
115    case DTMFilter.SHOW_ELEMENT :
116      if (this.WILD == m_name)
117        m_targetString = PsuedoNames.PSEUDONAME_ANY;
118      else
119        m_targetString = m_name;
120      break;
121    default :
122      m_targetString = PsuedoNames.PSEUDONAME_ANY;
123      break;
124    }
125  }
126
127  /**
128   * Get the local name or psuedo name of the node that this pattern will test,
129   * for hash table lookup optimization.
130   *
131   *
132   * @return local name or psuedo name of the node.
133   * @see org.apache.xpath.compiler.PsuedoNames
134   */
135  public String getTargetString()
136  {
137    return m_targetString;
138  }
139
140  /**
141   * Reference to nodetest and predicate for
142   * parent or ancestor.
143   * @serial
144   */
145  StepPattern m_relativePathPattern;
146
147  /**
148   * This function is used to fixup variables from QNames to stack frame
149   * indexes at stylesheet build time.
150   * @param vars List of QNames that correspond to variables.  This list
151   * should be searched backwards for the first qualified name that
152   * corresponds to the variable reference qname.  The position of the
153   * QName in the vector from the start of the vector will be its position
154   * in the stack frame (but variables above the globalsTop value will need
155   * to be offset to the current stack frame).
156   * @param globalsSize The number of variables in the global variable area.
157   */
158  public void fixupVariables(java.util.Vector vars, int globalsSize)
159  {
160
161    super.fixupVariables(vars, globalsSize);
162
163    if (null != m_predicates)
164    {
165      for (int i = 0; i < m_predicates.length; i++)
166      {
167        m_predicates[i].fixupVariables(vars, globalsSize);
168      }
169    }
170
171    if (null != m_relativePathPattern)
172    {
173      m_relativePathPattern.fixupVariables(vars, globalsSize);
174    }
175  }
176
177  /**
178   * Set the reference to nodetest and predicate for
179   * parent or ancestor.
180   *
181   *
182   * @param expr The relative pattern expression.
183   */
184  public void setRelativePathPattern(StepPattern expr)
185  {
186
187    m_relativePathPattern = expr;
188    expr.exprSetParent(this);
189
190    calcScore();
191  }
192
193  /**
194   * Get the reference to nodetest and predicate for
195   * parent or ancestor.
196   *
197   *
198   * @return The relative pattern expression.
199   */
200  public StepPattern getRelativePathPattern()
201  {
202    return m_relativePathPattern;
203  }
204
205  //  /**
206  //   * Set the list of predicate expressions for this pattern step.
207  //   * @param predicates List of expression objects.
208  //   */
209  //  public void setPredicates(Expression[] predicates)
210  //  {
211  //    m_predicates = predicates;
212  //  }
213
214  /**
215   * Set the list of predicate expressions for this pattern step.
216   * @return List of expression objects.
217   */
218  public Expression[] getPredicates()
219  {
220    return m_predicates;
221  }
222
223  /**
224   * The list of predicate expressions for this pattern step.
225   *  @serial
226   */
227  Expression[] m_predicates;
228
229  /**
230   * Tell if this expression or it's subexpressions can traverse outside
231   * the current subtree.
232   *
233   * NOTE: Ancestors tests with predicates are problematic, and will require
234   * special treatment.
235   *
236   * @return true if traversal outside the context node's subtree can occur.
237   */
238  public boolean canTraverseOutsideSubtree()
239  {
240
241    int n = getPredicateCount();
242
243    for (int i = 0; i < n; i++)
244    {
245      if (getPredicate(i).canTraverseOutsideSubtree())
246        return true;
247    }
248
249    return false;
250  }
251
252  /**
253   * Get a predicate expression.
254   *
255   *
256   * @param i The index of the predicate.
257   *
258   * @return A predicate expression.
259   */
260  public Expression getPredicate(int i)
261  {
262    return m_predicates[i];
263  }
264
265  /**
266   * Get the number of predicates for this match pattern step.
267   *
268   *
269   * @return the number of predicates for this match pattern step.
270   */
271  public final int getPredicateCount()
272  {
273    return (null == m_predicates) ? 0 : m_predicates.length;
274  }
275
276  /**
277   * Set the predicates for this match pattern step.
278   *
279   *
280   * @param predicates An array of expressions that define predicates
281   *                   for this step.
282   */
283  public void setPredicates(Expression[] predicates)
284  {
285
286    m_predicates = predicates;
287    if(null != predicates)
288    {
289    	for(int i = 0; i < predicates.length; i++)
290    	{
291    		predicates[i].exprSetParent(this);
292    	}
293    }
294
295    calcScore();
296  }
297
298  /**
299   * Static calc of match score.
300   */
301  public void calcScore()
302  {
303
304    if ((getPredicateCount() > 0) || (null != m_relativePathPattern))
305    {
306      m_score = SCORE_OTHER;
307    }
308    else
309      super.calcScore();
310
311    if (null == m_targetString)
312      calcTargetString();
313  }
314
315  /**
316   * Execute this pattern step, including predicates.
317   *
318   *
319   * @param xctxt XPath runtime context.
320   * @param currentNode The current node context.
321   *
322   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
323   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
324   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
325   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
326   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
327   *
328   * @throws javax.xml.transform.TransformerException
329   */
330  public XObject execute(XPathContext xctxt, int currentNode)
331          throws javax.xml.transform.TransformerException
332  {
333
334    DTM dtm = xctxt.getDTM(currentNode);
335
336    if (dtm != null)
337    {
338      int expType = dtm.getExpandedTypeID(currentNode);
339
340      return execute(xctxt, currentNode, dtm, expType);
341    }
342
343    return NodeTest.SCORE_NONE;
344  }
345
346  /**
347   * Execute this pattern step, including predicates.
348   *
349   *
350   * @param xctxt XPath runtime context.
351   *
352   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
353   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
354   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
355   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
356   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
357   *
358   * @throws javax.xml.transform.TransformerException
359   */
360  public XObject execute(XPathContext xctxt)
361          throws javax.xml.transform.TransformerException
362  {
363    return execute(xctxt, xctxt.getCurrentNode());
364  }
365
366  /**
367   * Execute an expression in the XPath runtime context, and return the
368   * result of the expression.
369   *
370   *
371   * @param xctxt The XPath runtime context.
372   * @param currentNode The currentNode.
373   * @param dtm The DTM of the current node.
374   * @param expType The expanded type ID of the current node.
375   *
376   * @return The result of the expression in the form of a <code>XObject</code>.
377   *
378   * @throws javax.xml.transform.TransformerException if a runtime exception
379   *         occurs.
380   */
381  public XObject execute(
382          XPathContext xctxt, int currentNode, DTM dtm, int expType)
383            throws javax.xml.transform.TransformerException
384  {
385
386    if (m_whatToShow == NodeTest.SHOW_BYFUNCTION)
387    {
388      if (null != m_relativePathPattern)
389      {
390        return m_relativePathPattern.execute(xctxt);
391      }
392      else
393        return NodeTest.SCORE_NONE;
394    }
395
396    XObject score;
397
398    score = super.execute(xctxt, currentNode, dtm, expType);
399
400    if (score == NodeTest.SCORE_NONE)
401      return NodeTest.SCORE_NONE;
402
403    if (getPredicateCount() != 0)
404    {
405      if (!executePredicates(xctxt, dtm, currentNode))
406        return NodeTest.SCORE_NONE;
407    }
408
409    if (null != m_relativePathPattern)
410      return m_relativePathPattern.executeRelativePathPattern(xctxt, dtm,
411              currentNode);
412
413    return score;
414  }
415
416  /**
417   * New Method to check whether the current node satisfies a position predicate
418   *
419   * @param xctxt The XPath runtime context.
420   * @param predPos Which predicate we're evaluating of foo[1][2][3].
421   * @param dtm The DTM of the current node.
422   * @param context The currentNode.
423   * @param pos The position being requested, i.e. the value returned by
424   *            m_predicates[predPos].execute(xctxt).
425   *
426   * @return true of the position of the context matches pos, false otherwise.
427   */
428  private final boolean checkProximityPosition(XPathContext xctxt,
429          int predPos, DTM dtm, int context, int pos)
430  {
431
432    try
433    {
434      DTMAxisTraverser traverser =
435        dtm.getAxisTraverser(Axis.PRECEDINGSIBLING);
436
437      for (int child = traverser.first(context); DTM.NULL != child;
438              child = traverser.next(context, child))
439      {
440        try
441        {
442          xctxt.pushCurrentNode(child);
443
444          if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
445          {
446            boolean pass = true;
447
448            try
449            {
450              xctxt.pushSubContextList(this);
451
452              for (int i = 0; i < predPos; i++)
453              {
454                xctxt.pushPredicatePos(i);
455                try
456                {
457                  XObject pred = m_predicates[i].execute(xctxt);
458
459                  try
460                  {
461                    if (XObject.CLASS_NUMBER == pred.getType())
462                    {
463                      throw new Error("Why: Should never have been called");
464                    }
465                    else if (!pred.boolWithSideEffects())
466                    {
467                      pass = false;
468
469                      break;
470                    }
471                  }
472                  finally
473                  {
474                    pred.detach();
475                  }
476                }
477                finally
478                {
479                  xctxt.popPredicatePos();
480                }
481              }
482            }
483            finally
484            {
485              xctxt.popSubContextList();
486            }
487
488            if (pass)
489              pos--;
490
491            if (pos < 1)
492              return false;
493          }
494        }
495        finally
496        {
497          xctxt.popCurrentNode();
498        }
499      }
500    }
501    catch (javax.xml.transform.TransformerException se)
502    {
503
504      // TODO: should keep throw sax exception...
505      throw new java.lang.RuntimeException(se.getMessage());
506    }
507
508    return (pos == 1);
509  }
510
511  /**
512   * Get the proximity position index of the current node based on this
513   * node test.
514   *
515   *
516   * @param xctxt XPath runtime context.
517   * @param predPos Which predicate we're evaluating of foo[1][2][3].
518   * @param findLast If true, don't terminate when the context node is found.
519   *
520   * @return the proximity position index of the current node based on the
521   *         node test.
522   */
523  private final int getProximityPosition(XPathContext xctxt, int predPos,
524                    boolean findLast)
525  {
526
527    int pos = 0;
528    int context = xctxt.getCurrentNode();
529    DTM dtm = xctxt.getDTM(context);
530    int parent = dtm.getParent(context);
531
532    try
533    {
534      DTMAxisTraverser traverser = dtm.getAxisTraverser(Axis.CHILD);
535
536      for (int child = traverser.first(parent); DTM.NULL != child;
537              child = traverser.next(parent, child))
538      {
539        try
540        {
541          xctxt.pushCurrentNode(child);
542
543          if (NodeTest.SCORE_NONE != super.execute(xctxt, child))
544          {
545            boolean pass = true;
546
547            try
548            {
549              xctxt.pushSubContextList(this);
550
551              for (int i = 0; i < predPos; i++)
552              {
553                xctxt.pushPredicatePos(i);
554                try
555                {
556                  XObject pred = m_predicates[i].execute(xctxt);
557
558                  try
559                  {
560                    if (XObject.CLASS_NUMBER == pred.getType())
561                    {
562                      if ((pos + 1) != (int) pred.numWithSideEffects())
563                      {
564                        pass = false;
565
566                        break;
567                      }
568                    }
569                    else if (!pred.boolWithSideEffects())
570                    {
571                      pass = false;
572
573                      break;
574                    }
575                  }
576                  finally
577                  {
578                    pred.detach();
579                  }
580                }
581                finally
582                {
583                  xctxt.popPredicatePos();
584                }
585              }
586            }
587            finally
588            {
589              xctxt.popSubContextList();
590            }
591
592            if (pass)
593              pos++;
594
595            if (!findLast && child == context)
596            {
597              return pos;
598            }
599          }
600        }
601        finally
602        {
603          xctxt.popCurrentNode();
604        }
605      }
606    }
607    catch (javax.xml.transform.TransformerException se)
608    {
609
610      // TODO: should keep throw sax exception...
611      throw new java.lang.RuntimeException(se.getMessage());
612    }
613
614    return pos;
615  }
616
617  /**
618   * Get the proximity position index of the current node based on this
619   * node test.
620   *
621   *
622   * @param xctxt XPath runtime context.
623   *
624   * @return the proximity position index of the current node based on the
625   *         node test.
626   */
627  public int getProximityPosition(XPathContext xctxt)
628  {
629    return getProximityPosition(xctxt, xctxt.getPredicatePos(), false);
630  }
631
632  /**
633   * Get the count of the nodes that match the test, which is the proximity
634   * position of the last node that can pass this test in the sub context
635   * selection.  In XSLT 1-based indexing, this count is the index of the last
636   * node.
637   *
638   *
639   * @param xctxt XPath runtime context.
640   *
641   * @return the count of the nodes that match the test.
642   */
643  public int getLastPos(XPathContext xctxt)
644  {
645    return getProximityPosition(xctxt, xctxt.getPredicatePos(), true);
646  }
647
648  /**
649   * Execute the match pattern step relative to another step.
650   *
651   *
652   * @param xctxt The XPath runtime context.
653   * @param dtm The DTM of the current node.
654   * @param currentNode The current node context.
655   *
656   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
657   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
658   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
659   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
660   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
661   *
662   * @throws javax.xml.transform.TransformerException
663   */
664  protected final XObject executeRelativePathPattern(
665          XPathContext xctxt, DTM dtm, int currentNode)
666            throws javax.xml.transform.TransformerException
667  {
668
669    XObject score = NodeTest.SCORE_NONE;
670    int context = currentNode;
671    DTMAxisTraverser traverser;
672
673    traverser = dtm.getAxisTraverser(m_axis);
674
675    for (int relative = traverser.first(context); DTM.NULL != relative;
676            relative = traverser.next(context, relative))
677    {
678      try
679      {
680        xctxt.pushCurrentNode(relative);
681
682        score = execute(xctxt);
683
684        if (score != NodeTest.SCORE_NONE)
685          break;
686      }
687      finally
688      {
689        xctxt.popCurrentNode();
690      }
691    }
692
693    return score;
694  }
695
696  /**
697   * Execute the predicates on this step to determine if the current node
698   * should be filtered or accepted.
699   *
700   * @param xctxt The XPath runtime context.
701   * @param dtm The DTM of the current node.
702   * @param currentNode The current node context.
703   *
704   * @return true if the node should be accepted, false otherwise.
705   *
706   * @throws javax.xml.transform.TransformerException
707   */
708  protected final boolean executePredicates(
709          XPathContext xctxt, DTM dtm, int currentNode)
710            throws javax.xml.transform.TransformerException
711  {
712
713    boolean result = true;
714    boolean positionAlreadySeen = false;
715    int n = getPredicateCount();
716
717    try
718    {
719      xctxt.pushSubContextList(this);
720
721      for (int i = 0; i < n; i++)
722      {
723        xctxt.pushPredicatePos(i);
724
725        try
726        {
727          XObject pred = m_predicates[i].execute(xctxt);
728
729          try
730          {
731            if (XObject.CLASS_NUMBER == pred.getType())
732            {
733              int pos = (int) pred.num();
734
735              if (positionAlreadySeen)
736              {
737                result = (pos == 1);
738
739                break;
740              }
741              else
742              {
743                positionAlreadySeen = true;
744
745                if (!checkProximityPosition(xctxt, i, dtm, currentNode, pos))
746                {
747                  result = false;
748
749                  break;
750                }
751              }
752
753            }
754            else if (!pred.boolWithSideEffects())
755            {
756              result = false;
757
758              break;
759            }
760          }
761          finally
762          {
763            pred.detach();
764          }
765        }
766        finally
767        {
768          xctxt.popPredicatePos();
769        }
770      }
771    }
772    finally
773    {
774      xctxt.popSubContextList();
775    }
776
777    return result;
778  }
779
780  /**
781   * Get the string represenentation of this step for diagnostic purposes.
782   *
783   *
784   * @return A string representation of this step, built by reverse-engineering
785   * the contained info.
786   */
787  public String toString()
788  {
789
790    StringBuffer buf = new StringBuffer();
791
792    for (StepPattern pat = this; pat != null; pat = pat.m_relativePathPattern)
793    {
794      if (pat != this)
795        buf.append("/");
796
797      buf.append(Axis.getNames(pat.m_axis));
798      buf.append("::");
799
800      if (0x000005000 == pat.m_whatToShow)
801      {
802        buf.append("doc()");
803      }
804      else if (DTMFilter.SHOW_BYFUNCTION == pat.m_whatToShow)
805      {
806        buf.append("function()");
807      }
808      else if (DTMFilter.SHOW_ALL == pat.m_whatToShow)
809      {
810        buf.append("node()");
811      }
812      else if (DTMFilter.SHOW_TEXT == pat.m_whatToShow)
813      {
814        buf.append("text()");
815      }
816      else if (DTMFilter.SHOW_PROCESSING_INSTRUCTION == pat.m_whatToShow)
817      {
818        buf.append("processing-instruction(");
819
820        if (null != pat.m_name)
821        {
822          buf.append(pat.m_name);
823        }
824
825        buf.append(")");
826      }
827      else if (DTMFilter.SHOW_COMMENT == pat.m_whatToShow)
828      {
829        buf.append("comment()");
830      }
831      else if (null != pat.m_name)
832      {
833        if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
834        {
835          buf.append("@");
836        }
837
838        if (null != pat.m_namespace)
839        {
840          buf.append("{");
841          buf.append(pat.m_namespace);
842          buf.append("}");
843        }
844
845        buf.append(pat.m_name);
846      }
847      else if (DTMFilter.SHOW_ATTRIBUTE == pat.m_whatToShow)
848      {
849        buf.append("@");
850      }
851      else if ((DTMFilter.SHOW_DOCUMENT | DTMFilter.SHOW_DOCUMENT_FRAGMENT)
852               == pat.m_whatToShow)
853      {
854        buf.append("doc-root()");
855      }
856      else
857      {
858        buf.append("?" + Integer.toHexString(pat.m_whatToShow));
859      }
860
861      if (null != pat.m_predicates)
862      {
863        for (int i = 0; i < pat.m_predicates.length; i++)
864        {
865          buf.append("[");
866          buf.append(pat.m_predicates[i]);
867          buf.append("]");
868        }
869      }
870    }
871
872    return buf.toString();
873  }
874
875  /** Set to true to send diagnostics about pattern matches to the consol. */
876  private static final boolean DEBUG_MATCHES = false;
877
878  /**
879   * Get the match score of the given node.
880   *
881   * @param xctxt The XPath runtime context.
882   * @param context The node to be tested.
883   *
884   * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
885   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
886   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
887   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
888   *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}.
889   *
890   * @throws javax.xml.transform.TransformerException
891   */
892  public double getMatchScore(XPathContext xctxt, int context)
893          throws javax.xml.transform.TransformerException
894  {
895
896    xctxt.pushCurrentNode(context);
897    xctxt.pushCurrentExpressionNode(context);
898
899    try
900    {
901      XObject score = execute(xctxt);
902
903      return score.num();
904    }
905    finally
906    {
907      xctxt.popCurrentNode();
908      xctxt.popCurrentExpressionNode();
909    }
910
911    // return XPath.MATCH_SCORE_NONE;
912  }
913
914  /**
915   * Set the axis that this step should follow.
916   *
917   *
918   * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
919   */
920  public void setAxis(int axis)
921  {
922    m_axis = axis;
923  }
924
925  /**
926   * Get the axis that this step follows.
927   *
928   *
929   * @return The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
930   */
931  public int getAxis()
932  {
933    return m_axis;
934  }
935
936  class PredOwner implements ExpressionOwner
937  {
938  	int m_index;
939
940  	PredOwner(int index)
941  	{
942  		m_index = index;
943  	}
944
945    /**
946     * @see ExpressionOwner#getExpression()
947     */
948    public Expression getExpression()
949    {
950      return m_predicates[m_index];
951    }
952
953
954    /**
955     * @see ExpressionOwner#setExpression(Expression)
956     */
957    public void setExpression(Expression exp)
958    {
959    	exp.exprSetParent(StepPattern.this);
960    	m_predicates[m_index] = exp;
961    }
962  }
963
964  /**
965   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
966   */
967  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
968  {
969  	 	if(visitor.visitMatchPattern(owner, this))
970  	 	{
971  	 		callSubtreeVisitors(visitor);
972  	 	}
973  }
974
975  /**
976   * Call the visitors on the subtree.  Factored out from callVisitors
977   * so it may be called by derived classes.
978   */
979  protected void callSubtreeVisitors(XPathVisitor visitor)
980  {
981    if (null != m_predicates)
982    {
983      int n = m_predicates.length;
984      for (int i = 0; i < n; i++)
985      {
986        ExpressionOwner predOwner = new PredOwner(i);
987        if (visitor.visitPredicate(predOwner, m_predicates[i]))
988        {
989          m_predicates[i].callVisitors(predOwner, visitor);
990        }
991      }
992    }
993    if (null != m_relativePathPattern)
994    {
995      m_relativePathPattern.callVisitors(this, visitor);
996    }
997  }
998
999
1000  /**
1001   * @see ExpressionOwner#getExpression()
1002   */
1003  public Expression getExpression()
1004  {
1005    return m_relativePathPattern;
1006  }
1007
1008  /**
1009   * @see ExpressionOwner#setExpression(Expression)
1010   */
1011  public void setExpression(Expression exp)
1012  {
1013    exp.exprSetParent(this);
1014  	m_relativePathPattern = (StepPattern)exp;
1015  }
1016
1017  /**
1018   * @see Expression#deepEquals(Expression)
1019   */
1020  public boolean deepEquals(Expression expr)
1021  {
1022  	if(!super.deepEquals(expr))
1023  		return false;
1024
1025  	StepPattern sp = (StepPattern)expr;
1026
1027    if (null != m_predicates)
1028    {
1029        int n = m_predicates.length;
1030        if ((null == sp.m_predicates) || (sp.m_predicates.length != n))
1031              return false;
1032        for (int i = 0; i < n; i++)
1033        {
1034          if (!m_predicates[i].deepEquals(sp.m_predicates[i]))
1035          	return false;
1036        }
1037    }
1038    else if (null != sp.m_predicates)
1039    	return false;
1040
1041  	if(null != m_relativePathPattern)
1042  	{
1043  		if(!m_relativePathPattern.deepEquals(sp.m_relativePathPattern))
1044  			return false;
1045  	}
1046  	else if(sp.m_relativePathPattern != null)
1047  		return false;
1048
1049  	return true;
1050  }
1051
1052
1053}
1054