1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xpath.internal.patterns;
  23 
  24 import com.sun.org.apache.xml.internal.dtm.Axis;
  25 import com.sun.org.apache.xml.internal.dtm.DTM;
  26 import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser;
  27 import com.sun.org.apache.xml.internal.dtm.DTMFilter;
  28 import com.sun.org.apache.xml.internal.utils.QName;
  29 import com.sun.org.apache.xpath.internal.Expression;
  30 import com.sun.org.apache.xpath.internal.ExpressionOwner;
  31 import com.sun.org.apache.xpath.internal.XPathContext;
  32 import com.sun.org.apache.xpath.internal.XPathVisitor;
  33 import com.sun.org.apache.xpath.internal.axes.SubContextList;
  34 import com.sun.org.apache.xpath.internal.compiler.PsuedoNames;
  35 import com.sun.org.apache.xpath.internal.objects.XObject;
  36 import java.util.List;
  37 
  38 /**
  39  * This class represents a single pattern match step.
  40  * @xsl.usage advanced
  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    */
  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    */
  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    */
  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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 934   public int getAxis()
 935   {
 936     return m_axis;
 937   }
 938 
 939   class PredOwner implements ExpressionOwner
 940   {
 941         int m_index;
 942 
 943         PredOwner(int index)
 944         {
 945                 m_index = index;
 946         }
 947 
 948     /**
 949      * @see ExpressionOwner#getExpression()
 950      */
 951     public Expression getExpression()
 952     {
 953       return m_predicates[m_index];
 954     }
 955 
 956 
 957     /**
 958      * @see ExpressionOwner#setExpression(Expression)
 959      */
 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    */
 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    */
 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    */
1006   public Expression getExpression()
1007   {
1008     return m_relativePathPattern;
1009   }
1010 
1011   /**
1012    * @see ExpressionOwner#setExpression(Expression)
1013    */
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    */
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 }