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 }