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.xalan.internal.res.XSLMessages; 24 import com.sun.org.apache.xml.internal.dtm.DTM; 25 import com.sun.org.apache.xml.internal.dtm.DTMAxisTraverser; 26 import com.sun.org.apache.xml.internal.dtm.DTMIterator; 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.res.XPATHErrorResources; 33 import java.util.List; 34 35 /** 36 * Serves as common interface for axes Walkers, and stores common 37 * state variables. 38 * 39 * @LastModified: Oct 2017 40 */ 41 public class AxesWalker extends PredicatedNodeTest 42 implements Cloneable, PathComponent, ExpressionOwner 43 { 44 static final long serialVersionUID = -2966031951306601247L; 45 46 /** 47 * Construct an AxesWalker using a LocPathIterator. 48 * 49 * @param locPathIterator non-null reference to the parent iterator. 50 */ 51 public AxesWalker(LocPathIterator locPathIterator, int axis) 52 { 53 super( locPathIterator ); 54 m_axis = axis; 55 } 56 57 public final WalkingIterator wi() 58 { 59 return (WalkingIterator)m_lpi; 60 } 61 62 /** 63 * Initialize an AxesWalker during the parse of the XPath expression. 64 * 65 * @param compiler The Compiler object that has information about this 66 * walker in the op map. 67 * @param opPos The op code position of this location step. 68 * @param stepType The type of location step. 69 * 70 * @throws javax.xml.transform.TransformerException 71 */ 72 public void init(Compiler compiler, int opPos, int stepType) 73 throws javax.xml.transform.TransformerException 74 { 75 76 initPredicateInfo(compiler, opPos); 77 78 // int testType = compiler.getOp(nodeTestOpPos); 79 } 80 81 /** 82 * Get a cloned AxesWalker. 83 * 84 * @return A new AxesWalker that can be used without mutating this one. 85 * 86 * @throws CloneNotSupportedException 87 */ 88 public Object clone() throws CloneNotSupportedException 89 { 90 // Do not access the location path itterator during this operation! 91 92 AxesWalker clone = (AxesWalker) super.clone(); 93 94 //clone.setCurrentNode(clone.m_root); 95 96 // clone.m_isFresh = true; 97 98 return clone; 99 } 100 101 /** 102 * Do a deep clone of this walker, including next and previous walkers. 103 * If the this AxesWalker is on the clone list, don't clone but 104 * return the already cloned version. 105 * 106 * @param cloneOwner non-null reference to the cloned location path 107 * iterator to which this clone will be added. 108 * @param cloneList non-null vector of sources in odd elements, and the 109 * corresponding clones in even vectors. 110 * 111 * @return non-null clone, which may be a new clone, or may be a clone 112 * contained on the cloneList. 113 */ 114 AxesWalker cloneDeep(WalkingIterator cloneOwner, List<AxesWalker> cloneList) 115 throws CloneNotSupportedException 116 { 117 AxesWalker clone = findClone(this, cloneList); 118 if(null != clone) 119 return clone; 120 clone = (AxesWalker)this.clone(); 121 clone.setLocPathIterator(cloneOwner); 122 if(null != cloneList) 123 { 124 cloneList.add(this); 125 cloneList.add(clone); 126 } 127 128 if(wi().m_lastUsedWalker == this) 129 cloneOwner.m_lastUsedWalker = clone; 130 131 if(null != m_nextWalker) 132 clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner, cloneList); 133 134 // If you don't check for the cloneList here, you'll go into an 135 // recursive infinate loop. 136 if(null != cloneList) 137 { 138 if(null != m_prevWalker) 139 clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner, cloneList); 140 } 141 else 142 { 143 if(null != m_nextWalker) 144 clone.m_nextWalker.m_prevWalker = clone; 145 } 146 return clone; 147 } 148 149 /** 150 * Find a clone that corresponds to the key argument. 151 * 152 * @param key The original AxesWalker for which there may be a clone. 153 * @param cloneList vector of sources in odd elements, and the 154 * corresponding clones in even vectors, may be null. 155 * 156 * @return A clone that corresponds to the key, or null if key not found. 157 */ 158 static AxesWalker findClone(AxesWalker key, List<AxesWalker> cloneList) 159 { 160 if(null != cloneList) 161 { 162 // First, look for clone on list. 163 int n = cloneList.size(); 164 for (int i = 0; i < n; i+=2) 165 { 166 if(key == cloneList.get(i)) 167 return cloneList.get(i+1); 168 } 169 } 170 return null; 171 } 172 173 /** 174 * Detaches the walker from the set which it iterated over, releasing 175 * any computational resources and placing the iterator in the INVALID 176 * state. 177 */ 178 public void detach() 179 { 180 m_currentNode = DTM.NULL; 181 m_dtm = null; 182 m_traverser = null; 183 m_isFresh = true; 184 m_root = DTM.NULL; 185 } 186 187 //=============== TreeWalker Implementation =============== 188 189 /** 190 * The root node of the TreeWalker, as specified in setRoot(int root). 191 * Note that this may actually be below the current node. 192 * 193 * @return The context node of the step. 194 */ 195 public int getRoot() 196 { 197 return m_root; 198 } 199 200 /** 201 * Get the analysis bits for this walker, as defined in the WalkerFactory. 202 * @return One of WalkerFactory#BIT_DESCENDANT, etc. 203 */ 204 public int getAnalysisBits() 205 { 206 int axis = getAxis(); 207 int bit = WalkerFactory.getAnalysisBitFromAxes(axis); 208 return bit; 209 } 210 211 /** 212 * Set the root node of the TreeWalker. 213 * (Not part of the DOM2 TreeWalker interface). 214 * 215 * @param root The context node of this step. 216 */ 217 public void setRoot(int root) 218 { 219 // %OPT% Get this directly from the lpi. 220 XPathContext xctxt = wi().getXPathContext(); 221 m_dtm = xctxt.getDTM(root); 222 m_traverser = m_dtm.getAxisTraverser(m_axis); 223 m_isFresh = true; 224 m_foundLast = false; 225 m_root = root; 226 m_currentNode = root; 227 228 if (DTM.NULL == root) 229 { 230 throw new RuntimeException( 231 XSLMessages.createXPATHMessage(XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL, null)); //"\n !!!! Error! Setting the root of a walker to null!!!"); 232 } 233 234 resetProximityPositions(); 235 } 236 237 /** 238 * The node at which the TreeWalker is currently positioned. 239 * <br> The value must not be null. Alterations to the DOM tree may cause 240 * the current node to no longer be accepted by the TreeWalker's 241 * associated filter. currentNode may also be explicitly set to any node, 242 * whether or not it is within the subtree specified by the root node or 243 * would be accepted by the filter and whatToShow flags. Further 244 * traversal occurs relative to currentNode even if it is not part of the 245 * current view by applying the filters in the requested direction (not 246 * changing currentNode where no traversal is possible). 247 * 248 * @return The node at which the TreeWalker is currently positioned, only null 249 * if setRoot has not yet been called. 250 */ 251 public final int getCurrentNode() 252 { 253 return m_currentNode; 254 } 255 256 /** 257 * Set the next walker in the location step chain. 258 * 259 * 260 * @param walker Reference to AxesWalker derivative, or may be null. 261 */ 262 public void setNextWalker(AxesWalker walker) 263 { 264 m_nextWalker = walker; 265 } 266 267 /** 268 * Get the next walker in the location step chain. 269 * 270 * 271 * @return Reference to AxesWalker derivative, or null. 272 */ 273 public AxesWalker getNextWalker() 274 { 275 return m_nextWalker; 276 } 277 278 /** 279 * Set or clear the previous walker reference in the location step chain. 280 * 281 * 282 * @param walker Reference to previous walker reference in the location 283 * step chain, or null. 284 */ 285 public void setPrevWalker(AxesWalker walker) 286 { 287 m_prevWalker = walker; 288 } 289 290 /** 291 * Get the previous walker reference in the location step chain. 292 * 293 * 294 * @return Reference to previous walker reference in the location 295 * step chain, or null. 296 */ 297 public AxesWalker getPrevWalker() 298 { 299 return m_prevWalker; 300 } 301 302 /** 303 * This is simply a way to bottle-neck the return of the next node, for 304 * diagnostic purposes. 305 * 306 * @param n Node to return, or null. 307 * 308 * @return The argument. 309 */ 310 private int returnNextNode(int n) 311 { 312 313 return n; 314 } 315 316 /** 317 * Get the next node in document order on the axes. 318 * 319 * @return the next node in document order on the axes, or null. 320 */ 321 protected int getNextNode() 322 { 323 if (m_foundLast) 324 return DTM.NULL; 325 326 if (m_isFresh) 327 { 328 m_currentNode = m_traverser.first(m_root); 329 m_isFresh = false; 330 } 331 // I shouldn't have to do this the check for current node, I think. 332 // numbering\numbering24.xsl fails if I don't do this. I think 333 // it occurs as the walkers are backing up. -sb 334 else if(DTM.NULL != m_currentNode) 335 { 336 m_currentNode = m_traverser.next(m_root, m_currentNode); 337 } 338 339 if (DTM.NULL == m_currentNode) 340 this.m_foundLast = true; 341 342 return m_currentNode; 343 } 344 345 /** 346 * Moves the <code>TreeWalker</code> to the next visible node in document 347 * order relative to the current node, and returns the new node. If the 348 * current node has no next node, or if the search for nextNode attempts 349 * to step upward from the TreeWalker's root node, returns 350 * <code>null</code> , and retains the current node. 351 * @return The new node, or <code>null</code> if the current node has no 352 * next node in the TreeWalker's logical view. 353 */ 354 public int nextNode() 355 { 356 int nextNode = DTM.NULL; 357 AxesWalker walker = wi().getLastUsedWalker(); 358 359 while (true) 360 { 361 if (null == walker) 362 break; 363 364 nextNode = walker.getNextNode(); 365 366 if (DTM.NULL == nextNode) 367 { 368 369 walker = walker.m_prevWalker; 370 } 371 else 372 { 373 if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT) 374 { 375 continue; 376 } 377 378 if (null == walker.m_nextWalker) 379 { 380 wi().setLastUsedWalker(walker); 381 382 // return walker.returnNextNode(nextNode); 383 break; 384 } 385 else 386 { 387 AxesWalker prev = walker; 388 389 walker = walker.m_nextWalker; 390 391 walker.setRoot(nextNode); 392 393 walker.m_prevWalker = prev; 394 395 continue; 396 } 397 } // if(null != nextNode) 398 } // while(null != walker) 399 400 return nextNode; 401 } 402 403 //============= End TreeWalker Implementation ============= 404 405 /** 406 * Get the index of the last node that can be itterated to. 407 * 408 * 409 * @param xctxt XPath runtime context. 410 * 411 * @return the index of the last node that can be itterated to. 412 */ 413 public int getLastPos(XPathContext xctxt) 414 { 415 416 int pos = getProximityPosition(); 417 418 AxesWalker walker; 419 420 try 421 { 422 walker = (AxesWalker) clone(); 423 } 424 catch (CloneNotSupportedException cnse) 425 { 426 return -1; 427 } 428 429 walker.setPredicateCount(m_predicateIndex); 430 walker.setNextWalker(null); 431 walker.setPrevWalker(null); 432 433 WalkingIterator lpi = wi(); 434 AxesWalker savedWalker = lpi.getLastUsedWalker(); 435 436 try 437 { 438 lpi.setLastUsedWalker(walker); 439 440 int next; 441 442 while (DTM.NULL != (next = walker.nextNode())) 443 { 444 pos++; 445 } 446 447 // TODO: Should probably save this in the iterator. 448 } 449 finally 450 { 451 lpi.setLastUsedWalker(savedWalker); 452 } 453 454 // System.out.println("pos: "+pos); 455 return pos; 456 } 457 458 //============= State Data ============= 459 460 /** 461 * The DTM for the root. This can not be used, or must be changed, 462 * for the filter walker, or any walker that can have nodes 463 * from multiple documents. 464 * Never, ever, access this value without going through getDTM(int node). 465 */ 466 private DTM m_dtm; 467 468 /** 469 * Set the DTM for this walker. 470 * 471 * @param dtm Non-null reference to a DTM. 472 */ 473 public void setDefaultDTM(DTM dtm) 474 { 475 m_dtm = dtm; 476 } 477 478 /** 479 * Get the DTM for this walker. 480 * 481 * @return Non-null reference to a DTM. 482 */ 483 public DTM getDTM(int node) 484 { 485 // 486 return wi().getXPathContext().getDTM(node); 487 } 488 489 /** 490 * Returns true if all the nodes in the iteration well be returned in document 491 * order. 492 * Warning: This can only be called after setRoot has been called! 493 * 494 * @return true as a default. 495 */ 496 public boolean isDocOrdered() 497 { 498 return true; 499 } 500 501 /** 502 * Returns the axis being iterated, if it is known. 503 * 504 * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 505 * types. 506 */ 507 public int getAxis() 508 { 509 return m_axis; 510 } 511 512 /** 513 * This will traverse the heararchy, calling the visitor for 514 * each member. If the called visitor method returns 515 * false, the subtree should not be called. 516 * 517 * @param owner The owner of the visitor, where that path may be 518 * rewritten if needed. 519 * @param visitor The visitor whose appropriate method will be called. 520 */ 521 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 522 { 523 if(visitor.visitStep(owner, this)) 524 { 525 callPredicateVisitors(visitor); 526 if(null != m_nextWalker) 527 { 528 m_nextWalker.callVisitors(this, visitor); 529 } 530 } 531 } 532 533 /** 534 * @see ExpressionOwner#getExpression() 535 */ 536 public Expression getExpression() 537 { 538 return m_nextWalker; 539 } 540 541 /** 542 * @see ExpressionOwner#setExpression(Expression) 543 */ 544 public void setExpression(Expression exp) 545 { 546 exp.exprSetParent(this); 547 m_nextWalker = (AxesWalker)exp; 548 } 549 550 /** 551 * @see Expression#deepEquals(Expression) 552 */ 553 public boolean deepEquals(Expression expr) 554 { 555 if (!super.deepEquals(expr)) 556 return false; 557 558 AxesWalker walker = (AxesWalker)expr; 559 if(this.m_axis != walker.m_axis) 560 return false; 561 562 return true; 563 } 564 565 /** 566 * The root node of the TreeWalker, as specified when it was created. 567 */ 568 transient int m_root = DTM.NULL; 569 570 /** 571 * The node at which the TreeWalker is currently positioned. 572 */ 573 private transient int m_currentNode = DTM.NULL; 574 575 /** True if an itteration has not begun. */ 576 transient boolean m_isFresh; 577 578 /** The next walker in the location step chain. 579 * @serial */ 580 protected AxesWalker m_nextWalker; 581 582 /** The previous walker in the location step chain, or null. 583 * @serial */ 584 AxesWalker m_prevWalker; 585 586 /** The traversal axis from where the nodes will be filtered. */ 587 protected int m_axis = -1; 588 589 /** The DTM inner traversal class, that corresponds to the super axis. */ 590 protected DTMAxisTraverser m_traverser; 591 }