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