1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Copyright 1999-2004 The Apache Software Foundation. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 /* 20 * $Id: PredicatedNodeTest.java,v 1.2.4.2 2005/09/14 19:45:20 jeffsuttor Exp $ 21 */ 22 package com.sun.org.apache.xpath.internal.axes; 23 24 import com.sun.org.apache.xml.internal.dtm.DTM; 25 import com.sun.org.apache.xml.internal.dtm.DTMIterator; 26 import com.sun.org.apache.xml.internal.utils.PrefixResolver; 27 import com.sun.org.apache.xpath.internal.Expression; 28 import com.sun.org.apache.xpath.internal.ExpressionOwner; 29 import com.sun.org.apache.xpath.internal.XPathContext; 30 import com.sun.org.apache.xpath.internal.XPathVisitor; 31 import com.sun.org.apache.xpath.internal.compiler.Compiler; 32 import com.sun.org.apache.xpath.internal.objects.XObject; 33 import com.sun.org.apache.xpath.internal.patterns.NodeTest; 34 35 public abstract class PredicatedNodeTest extends NodeTest implements SubContextList 36 { 37 static final long serialVersionUID = -6193530757296377351L; 38 39 /** 40 * Construct an AxesWalker using a LocPathIterator. 41 * 42 * @param locPathIterator non-null reference to the parent iterator. 43 */ PredicatedNodeTest(LocPathIterator locPathIterator)44 PredicatedNodeTest(LocPathIterator locPathIterator) 45 { 46 m_lpi = locPathIterator; 47 } 48 49 /** 50 * Construct an AxesWalker. The location path iterator will have to be set 51 * before use. 52 */ PredicatedNodeTest()53 PredicatedNodeTest() 54 { 55 } 56 57 /** 58 * Read the object from a serialization stream. 59 * 60 * @param stream Input stream to read from 61 * 62 * @throws java.io.IOException 63 * @throws javax.xml.transform.TransformerException 64 */ readObject(java.io.ObjectInputStream stream)65 private void readObject(java.io.ObjectInputStream stream) 66 throws java.io.IOException, javax.xml.transform.TransformerException 67 { 68 try 69 { 70 stream.defaultReadObject(); 71 m_predicateIndex = -1; 72 73 /** 74 * Initialize to the declared value. 75 * As noted at declaration, this variable is used only for clones for getLastPos, 76 * it should have been excluded from serialization. For compatibility, we'll 77 * keep it as is but initializing to the declared value. 78 */ 79 m_predCount = -1; 80 resetProximityPositions(); 81 } 82 catch (ClassNotFoundException cnfe) 83 { 84 throw new javax.xml.transform.TransformerException(cnfe); 85 } 86 } 87 88 /** 89 * Get a cloned PrdicatedNodeTest. 90 * 91 * @return A new PredicatedNodeTest that can be used without mutating this one. 92 * 93 * @throws CloneNotSupportedException 94 */ clone()95 public Object clone() throws CloneNotSupportedException 96 { 97 // Do not access the location path itterator during this operation! 98 99 PredicatedNodeTest clone = (PredicatedNodeTest) super.clone(); 100 101 if ((null != this.m_proximityPositions) 102 && (this.m_proximityPositions == clone.m_proximityPositions)) 103 { 104 clone.m_proximityPositions = new int[this.m_proximityPositions.length]; 105 106 System.arraycopy(this.m_proximityPositions, 0, 107 clone.m_proximityPositions, 0, 108 this.m_proximityPositions.length); 109 } 110 111 if(clone.m_lpi == this) 112 clone.m_lpi = (LocPathIterator)clone; 113 114 return clone; 115 } 116 117 // Only for clones for findLastPos. See bug4638. 118 protected int m_predCount = -1; 119 120 /** 121 * Get the number of predicates that this walker has. 122 * 123 * @return the number of predicates that this walker has. 124 */ getPredicateCount()125 public int getPredicateCount() 126 { 127 if(-1 == m_predCount) 128 return (null == m_predicates) ? 0 : m_predicates.length; 129 else 130 return m_predCount; 131 } 132 133 /** 134 * Set the number of predicates that this walker has. This does more 135 * that one would think, as it creates a new predicate array of the 136 * size of the count argument, and copies count predicates into the new 137 * one from the old, and then reassigns the predicates value. All this 138 * to keep from having to have a predicate count value. 139 * 140 * @param count The number of predicates, which must be equal or less 141 * than the existing count. 142 */ setPredicateCount(int count)143 public void setPredicateCount(int count) 144 { 145 if(count > 0) 146 { 147 Expression[] newPredicates = new Expression[count]; 148 for (int i = 0; i < count; i++) 149 { 150 newPredicates[i] = m_predicates[i]; 151 } 152 m_predicates = newPredicates; 153 } 154 else 155 m_predicates = null; 156 157 } 158 159 /** 160 * Init predicate info. 161 * 162 * @param compiler The Compiler object that has information about this 163 * walker in the op map. 164 * @param opPos The op code position of this location step. 165 * 166 * @throws javax.xml.transform.TransformerException 167 */ initPredicateInfo(Compiler compiler, int opPos)168 protected void initPredicateInfo(Compiler compiler, int opPos) 169 throws javax.xml.transform.TransformerException 170 { 171 172 int pos = compiler.getFirstPredicateOpPos(opPos); 173 174 if(pos > 0) 175 { 176 m_predicates = compiler.getCompiledPredicates(pos); 177 if(null != m_predicates) 178 { 179 for(int i = 0; i < m_predicates.length; i++) 180 { 181 m_predicates[i].exprSetParent(this); 182 } 183 } 184 } 185 } 186 187 /** 188 * Get a predicate expression at the given index. 189 * 190 * 191 * @param index Index of the predicate. 192 * 193 * @return A predicate expression. 194 */ getPredicate(int index)195 public Expression getPredicate(int index) 196 { 197 return m_predicates[index]; 198 } 199 200 /** 201 * Get the current sub-context position. 202 * 203 * @return The node position of this walker in the sub-context node list. 204 */ getProximityPosition()205 public int getProximityPosition() 206 { 207 208 // System.out.println("getProximityPosition - m_predicateIndex: "+m_predicateIndex); 209 return getProximityPosition(m_predicateIndex); 210 } 211 212 /** 213 * Get the current sub-context position. 214 * 215 * @param xctxt The XPath runtime context. 216 * 217 * @return The node position of this walker in the sub-context node list. 218 */ getProximityPosition(XPathContext xctxt)219 public int getProximityPosition(XPathContext xctxt) 220 { 221 return getProximityPosition(); 222 } 223 224 /** 225 * Get the index of the last node that can be itterated to. 226 * 227 * 228 * @param xctxt XPath runtime context. 229 * 230 * @return the index of the last node that can be itterated to. 231 */ getLastPos(XPathContext xctxt)232 public abstract int getLastPos(XPathContext xctxt); 233 234 /** 235 * Get the current sub-context position. 236 * 237 * @param predicateIndex The index of the predicate where the proximity 238 * should be taken from. 239 * 240 * @return The node position of this walker in the sub-context node list. 241 */ getProximityPosition(int predicateIndex)242 protected int getProximityPosition(int predicateIndex) 243 { 244 return (predicateIndex >= 0) ? m_proximityPositions[predicateIndex] : 0; 245 } 246 247 /** 248 * Reset the proximity positions counts. 249 */ resetProximityPositions()250 public void resetProximityPositions() 251 { 252 int nPredicates = getPredicateCount(); 253 if (nPredicates > 0) 254 { 255 if (null == m_proximityPositions) 256 m_proximityPositions = new int[nPredicates]; 257 258 for (int i = 0; i < nPredicates; i++) 259 { 260 try 261 { 262 initProximityPosition(i); 263 } 264 catch(Exception e) 265 { 266 // TODO: Fix this... 267 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e); 268 } 269 } 270 } 271 } 272 273 /** 274 * Init the proximity position to zero for a forward axes. 275 * 276 * @param i The index into the m_proximityPositions array. 277 * 278 * @throws javax.xml.transform.TransformerException 279 */ initProximityPosition(int i)280 public void initProximityPosition(int i) throws javax.xml.transform.TransformerException 281 { 282 m_proximityPositions[i] = 0; 283 } 284 285 /** 286 * Count forward one proximity position. 287 * 288 * @param i The index into the m_proximityPositions array, where the increment 289 * will occur. 290 */ countProximityPosition(int i)291 protected void countProximityPosition(int i) 292 { 293 // Note that in the case of a UnionChildIterator, this may be a 294 // static object and so m_proximityPositions may indeed be null! 295 int[] pp = m_proximityPositions; 296 if ((null != pp) && (i < pp.length)) 297 pp[i]++; 298 } 299 300 /** 301 * Tells if this is a reverse axes. 302 * 303 * @return false, unless a derived class overrides. 304 */ isReverseAxes()305 public boolean isReverseAxes() 306 { 307 return false; 308 } 309 310 /** 311 * Get which predicate is executing. 312 * 313 * @return The current predicate index, or -1 if no predicate is executing. 314 */ getPredicateIndex()315 public int getPredicateIndex() 316 { 317 return m_predicateIndex; 318 } 319 320 /** 321 * Process the predicates. 322 * 323 * @param context The current context node. 324 * @param xctxt The XPath runtime context. 325 * 326 * @return the result of executing the predicate expressions. 327 * 328 * @throws javax.xml.transform.TransformerException 329 */ executePredicates(int context, XPathContext xctxt)330 boolean executePredicates(int context, XPathContext xctxt) 331 throws javax.xml.transform.TransformerException 332 { 333 334 int nPredicates = getPredicateCount(); 335 // System.out.println("nPredicates: "+nPredicates); 336 if (nPredicates == 0) 337 return true; 338 339 PrefixResolver savedResolver = xctxt.getNamespaceContext(); 340 341 try 342 { 343 m_predicateIndex = 0; 344 xctxt.pushSubContextList(this); 345 xctxt.pushNamespaceContext(m_lpi.getPrefixResolver()); 346 xctxt.pushCurrentNode(context); 347 348 for (int i = 0; i < nPredicates; i++) 349 { 350 // System.out.println("Executing predicate expression - waiting count: "+m_lpi.getWaitingCount()); 351 XObject pred = m_predicates[i].execute(xctxt); 352 // System.out.println("\nBack from executing predicate expression - waiting count: "+m_lpi.getWaitingCount()); 353 // System.out.println("pred.getType(): "+pred.getType()); 354 if (XObject.CLASS_NUMBER == pred.getType()) 355 { 356 if (DEBUG_PREDICATECOUNTING) 357 { 358 System.out.flush(); 359 System.out.println("\n===== start predicate count ========"); 360 System.out.println("m_predicateIndex: " + m_predicateIndex); 361 // System.out.println("getProximityPosition(m_predicateIndex): " 362 // + getProximityPosition(m_predicateIndex)); 363 System.out.println("pred.num(): " + pred.num()); 364 } 365 366 int proxPos = this.getProximityPosition(m_predicateIndex); 367 int predIndex = (int) pred.num(); 368 if (proxPos != predIndex) 369 { 370 if (DEBUG_PREDICATECOUNTING) 371 { 372 System.out.println("\nnode context: "+nodeToString(context)); 373 System.out.println("index predicate is false: "+proxPos); 374 System.out.println("\n===== end predicate count ========"); 375 } 376 return false; 377 } 378 else if (DEBUG_PREDICATECOUNTING) 379 { 380 System.out.println("\nnode context: "+nodeToString(context)); 381 System.out.println("index predicate is true: "+proxPos); 382 System.out.println("\n===== end predicate count ========"); 383 } 384 385 // If there is a proximity index that will not change during the 386 // course of itteration, then we know there can be no more true 387 // occurances of this predicate, so flag that we're done after 388 // this. 389 // 390 // bugzilla 14365 391 // We can't set m_foundLast = true unless we're sure that -all- 392 // remaining parameters are stable, or else last() fails. Fixed so 393 // only sets m_foundLast if on the last predicate 394 if(m_predicates[i].isStableNumber() && i == nPredicates - 1) 395 { 396 m_foundLast = true; 397 } 398 } 399 else if (!pred.bool()) 400 return false; 401 402 countProximityPosition(++m_predicateIndex); 403 } 404 } 405 finally 406 { 407 xctxt.popCurrentNode(); 408 xctxt.popNamespaceContext(); 409 xctxt.popSubContextList(); 410 m_predicateIndex = -1; 411 } 412 413 return true; 414 } 415 416 /** 417 * This function is used to fixup variables from QNames to stack frame 418 * indexes at stylesheet build time. 419 * @param vars List of QNames that correspond to variables. This list 420 * should be searched backwards for the first qualified name that 421 * corresponds to the variable reference qname. The position of the 422 * QName in the vector from the start of the vector will be its position 423 * in the stack frame (but variables above the globalsTop value will need 424 * to be offset to the current stack frame). 425 */ fixupVariables(java.util.Vector vars, int globalsSize)426 public void fixupVariables(java.util.Vector vars, int globalsSize) 427 { 428 super.fixupVariables(vars, globalsSize); 429 430 int nPredicates = getPredicateCount(); 431 432 for (int i = 0; i < nPredicates; i++) 433 { 434 m_predicates[i].fixupVariables(vars, globalsSize); 435 } 436 } 437 438 439 /** 440 * Diagnostics. 441 * 442 * @param n Node to give diagnostic information about, or null. 443 * 444 * @return Informative string about the argument. 445 */ nodeToString(int n)446 protected String nodeToString(int n) 447 { 448 if(DTM.NULL != n) 449 { 450 DTM dtm = m_lpi.getXPathContext().getDTM(n); 451 return dtm.getNodeName(n) + "{" + (n+1) + "}"; 452 } 453 else 454 { 455 return "null"; 456 } 457 } 458 459 //=============== NodeFilter Implementation =============== 460 461 /** 462 * Test whether a specified node is visible in the logical view of a 463 * TreeWalker or NodeIterator. This function will be called by the 464 * implementation of TreeWalker and NodeIterator; it is not intended to 465 * be called directly from user code. 466 * @param n The node to check to see if it passes the filter or not. 467 * @return a constant to determine whether the node is accepted, 468 * rejected, or skipped, as defined above . 469 */ acceptNode(int n)470 public short acceptNode(int n) 471 { 472 473 XPathContext xctxt = m_lpi.getXPathContext(); 474 475 try 476 { 477 xctxt.pushCurrentNode(n); 478 479 XObject score = execute(xctxt, n); 480 481 // System.out.println("\n::acceptNode - score: "+score.num()+"::"); 482 if (score != NodeTest.SCORE_NONE) 483 { 484 if (getPredicateCount() > 0) 485 { 486 countProximityPosition(0); 487 488 if (!executePredicates(n, xctxt)) 489 return DTMIterator.FILTER_SKIP; 490 } 491 492 return DTMIterator.FILTER_ACCEPT; 493 } 494 } 495 catch (javax.xml.transform.TransformerException se) 496 { 497 498 // TODO: Fix this. 499 throw new RuntimeException(se.getMessage()); 500 } 501 finally 502 { 503 xctxt.popCurrentNode(); 504 } 505 506 return DTMIterator.FILTER_SKIP; 507 } 508 509 510 /** 511 * Get the owning location path iterator. 512 * 513 * @return the owning location path iterator, which should not be null. 514 */ getLocPathIterator()515 public LocPathIterator getLocPathIterator() 516 { 517 return m_lpi; 518 } 519 520 /** 521 * Set the location path iterator owner for this walker. Besides 522 * initialization, this function is called during cloning operations. 523 * 524 * @param li non-null reference to the owning location path iterator. 525 */ setLocPathIterator(LocPathIterator li)526 public void setLocPathIterator(LocPathIterator li) 527 { 528 m_lpi = li; 529 if(this != li) 530 li.exprSetParent(this); 531 } 532 533 /** 534 * Tell if this expression or it's subexpressions can traverse outside 535 * the current subtree. 536 * 537 * @return true if traversal outside the context node's subtree can occur. 538 */ canTraverseOutsideSubtree()539 public boolean canTraverseOutsideSubtree() 540 { 541 int n = getPredicateCount(); 542 for (int i = 0; i < n; i++) 543 { 544 if(getPredicate(i).canTraverseOutsideSubtree()) 545 return true; 546 } 547 return false; 548 } 549 550 /** 551 * This will traverse the heararchy, calling the visitor for 552 * each member. If the called visitor method returns 553 * false, the subtree should not be called. 554 * 555 * @param visitor The visitor whose appropriate method will be called. 556 */ callPredicateVisitors(XPathVisitor visitor)557 public void callPredicateVisitors(XPathVisitor visitor) 558 { 559 if (null != m_predicates) 560 { 561 int n = m_predicates.length; 562 for (int i = 0; i < n; i++) 563 { 564 ExpressionOwner predOwner = new PredOwner(i); 565 if (visitor.visitPredicate(predOwner, m_predicates[i])) 566 { 567 m_predicates[i].callVisitors(predOwner, visitor); 568 } 569 570 } 571 } 572 } 573 574 /** 575 * @see Expression#deepEquals(Expression) 576 */ deepEquals(Expression expr)577 public boolean deepEquals(Expression expr) 578 { 579 if (!super.deepEquals(expr)) 580 return false; 581 582 PredicatedNodeTest pnt = (PredicatedNodeTest) expr; 583 if (null != m_predicates) 584 { 585 586 int n = m_predicates.length; 587 if ((null == pnt.m_predicates) || (pnt.m_predicates.length != n)) 588 return false; 589 for (int i = 0; i < n; i++) 590 { 591 if (!m_predicates[i].deepEquals(pnt.m_predicates[i])) 592 return false; 593 } 594 } 595 else if (null != pnt.m_predicates) 596 return false; 597 598 return true; 599 } 600 601 /** This is true if nextNode returns null. */ 602 transient protected boolean m_foundLast = false; 603 604 /** The owning location path iterator. 605 * @serial */ 606 protected LocPathIterator m_lpi; 607 608 /** 609 * Which predicate we are executing. 610 */ 611 transient int m_predicateIndex = -1; 612 613 /** The list of predicate expressions. Is static and does not need 614 * to be deep cloned. 615 * @serial 616 */ 617 private Expression[] m_predicates; 618 619 /** 620 * An array of counts that correspond to the number 621 * of predicates the step contains. 622 */ 623 transient protected int[] m_proximityPositions; 624 625 /** If true, diagnostic messages about predicate execution will be posted. */ 626 static final boolean DEBUG_PREDICATECOUNTING = false; 627 628 class PredOwner implements ExpressionOwner 629 { 630 int m_index; 631 PredOwner(int index)632 PredOwner(int index) 633 { 634 m_index = index; 635 } 636 637 /** 638 * @see ExpressionOwner#getExpression() 639 */ getExpression()640 public Expression getExpression() 641 { 642 return m_predicates[m_index]; 643 } 644 645 646 /** 647 * @see ExpressionOwner#setExpression(Expression) 648 */ setExpression(Expression exp)649 public void setExpression(Expression exp) 650 { 651 exp.exprSetParent(PredicatedNodeTest.this); 652 m_predicates[m_index] = exp; 653 } 654 } 655 656 } 657