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