001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: StepPattern.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath.patterns;
022    
023    import org.apache.xml.dtm.Axis;
024    import org.apache.xml.dtm.DTM;
025    import org.apache.xml.dtm.DTMAxisTraverser;
026    import org.apache.xml.dtm.DTMFilter;
027    import org.apache.xpath.Expression;
028    import org.apache.xpath.ExpressionOwner;
029    import org.apache.xpath.XPathContext;
030    import org.apache.xpath.XPathVisitor;
031    import org.apache.xpath.axes.SubContextList;
032    import org.apache.xpath.compiler.PsuedoNames;
033    import org.apache.xpath.objects.XObject;
034    
035    /**
036     * This class represents a single pattern match step.
037     * @xsl.usage advanced
038     */
039    public class StepPattern extends NodeTest implements SubContextList, ExpressionOwner
040    {
041        static final long serialVersionUID = 9071668960168152644L;
042    
043      /** The axis for this test. */
044      protected int m_axis;
045    
046      /**
047       * Construct a StepPattern that tests for namespaces and node names.
048       *
049       *
050       * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
051       * @param namespace The namespace to be tested.
052       * @param name The local name to be tested.
053       * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
054       * @param axisForPredicate No longer used.
055       */
056      public StepPattern(int whatToShow, String namespace, String name, int axis,
057                         int axisForPredicate)
058      {
059    
060        super(whatToShow, namespace, name);
061    
062        m_axis = axis;
063      }
064    
065      /**
066       * Construct a StepPattern that doesn't test for node names.
067       *
068       *
069       * @param whatToShow Bit set defined mainly by {@link org.w3c.dom.traversal.NodeFilter}.
070       * @param axis The Axis for this test, one of of Axes.ANCESTORORSELF, etc.
071       * @param axisForPredicate No longer used.
072       */
073      public StepPattern(int whatToShow, int axis, int axisForPredicate)
074      {
075    
076        super(whatToShow);
077    
078        m_axis = axis;
079      }
080    
081      /**
082       * The target local name or psuedo name, for hash table lookup optimization.
083       *  @serial
084       */
085      String m_targetString;  // only calculate on head
086    
087      /**
088       * Calculate the local name or psuedo name of the node that this pattern will test,
089       * for hash table lookup optimization.
090       *
091       * @see org.apache.xpath.compiler.PsuedoNames
092       */
093      public void calcTargetString()
094      {
095    
096        int whatToShow = getWhatToShow();
097    
098        switch (whatToShow)
099        {
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    }