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