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  */
21 package org.apache.xpath.patterns;
22 
23 import org.apache.xml.dtm.Axis;
24 import org.apache.xml.dtm.DTM;
25 import org.apache.xml.dtm.DTMAxisTraverser;
26 import org.apache.xml.dtm.DTMFilter;
27 import org.apache.xpath.Expression;
28 import org.apache.xpath.ExpressionOwner;
29 import org.apache.xpath.XPathContext;
30 import org.apache.xpath.XPathVisitor;
31 import org.apache.xpath.axes.SubContextList;
32 import org.apache.xpath.compiler.PsuedoNames;
33 import org.apache.xpath.objects.XObject;
34 
35 /**
36  * This class represents a single pattern match step.
37  * @xsl.usage advanced
38  */
39 public 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    */
StepPattern(int whatToShow, String namespace, String name, int axis, int axisForPredicate)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    */
StepPattern(int whatToShow, int axis, int axisForPredicate)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    */
calcTargetString()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    */
getTargetString()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    */
fixupVariables(java.util.Vector vars, int globalsSize)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    */
setRelativePathPattern(StepPattern expr)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    */
getRelativePathPattern()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    */
getPredicates()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    */
canTraverseOutsideSubtree()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    */
getPredicate(int i)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    */
getPredicateCount()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    */
setPredicates(Expression[] predicates)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    */
calcScore()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    */
execute(XPathContext xctxt, int currentNode)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    */
execute(XPathContext xctxt)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    */
execute( XPathContext xctxt, int currentNode, DTM dtm, int expType)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    */
checkProximityPosition(XPathContext xctxt, int predPos, DTM dtm, int context, int pos)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    */
getProximityPosition(XPathContext xctxt, int predPos, boolean findLast)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    */
getProximityPosition(XPathContext xctxt)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    */
getLastPos(XPathContext xctxt)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    */
executeRelativePathPattern( XPathContext xctxt, DTM dtm, int currentNode)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    */
executePredicates( XPathContext xctxt, DTM dtm, int currentNode)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    */
toString()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    */
getMatchScore(XPathContext xctxt, int context)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    */
setAxis(int axis)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    */
getAxis()931   public int getAxis()
932   {
933     return m_axis;
934   }
935 
936   class PredOwner implements ExpressionOwner
937   {
938   	int m_index;
939 
PredOwner(int index)940   	PredOwner(int index)
941   	{
942   		m_index = index;
943   	}
944 
945     /**
946      * @see ExpressionOwner#getExpression()
947      */
getExpression()948     public Expression getExpression()
949     {
950       return m_predicates[m_index];
951     }
952 
953 
954     /**
955      * @see ExpressionOwner#setExpression(Expression)
956      */
setExpression(Expression exp)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    */
callVisitors(ExpressionOwner owner, XPathVisitor visitor)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    */
callSubtreeVisitors(XPathVisitor visitor)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    */
getExpression()1003   public Expression getExpression()
1004   {
1005     return m_relativePathPattern;
1006   }
1007 
1008   /**
1009    * @see ExpressionOwner#setExpression(Expression)
1010    */
setExpression(Expression exp)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    */
deepEquals(Expression expr)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