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