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