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