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