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.Axis; 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.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.XPathVisitor; 30 import com.sun.org.apache.xpath.internal.compiler.Compiler; 31 import com.sun.org.apache.xpath.internal.compiler.OpCodes; 32 import com.sun.org.apache.xpath.internal.compiler.OpMap; 33 import java.util.List; 34 35 /** 36 * This class extends NodeSetDTM, which implements DTMIterator, 37 * and fetches nodes one at a time in document order based on a XPath 38 * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>. 39 * As each node is iterated via nextNode(), the node is also stored 40 * in the NodeVector, so that previousNode() can easily be done. 41 * @xsl.usage advanced 42 * @LastModified: Oct 2017 43 */ 44 public class UnionPathIterator extends LocPathIterator 45 implements Cloneable, DTMIterator, java.io.Serializable, PathComponent 46 { 47 static final long serialVersionUID = -3910351546843826781L; 48 49 /** 50 * Constructor to create an instance which you can add location paths to. 51 */ 52 public UnionPathIterator() 53 { 54 55 super(); 56 57 // m_mutable = false; 58 // m_cacheNodes = false; 59 m_iterators = null; 60 m_exprs = null; 61 } 62 63 /** 64 * Initialize the context values for this expression 65 * after it is cloned. 66 * 67 * @param context The XPath runtime context for this 68 * transformation. 69 */ 70 public void setRoot(int context, Object environment) 71 { 72 super.setRoot(context, environment); 73 74 try 75 { 76 if (null != m_exprs) 77 { 78 int n = m_exprs.length; 79 DTMIterator newIters[] = new DTMIterator[n]; 80 81 for (int i = 0; i < n; i++) 82 { 83 DTMIterator iter = m_exprs[i].asIterator(m_execContext, context); 84 newIters[i] = iter; 85 iter.nextNode(); 86 } 87 m_iterators = newIters; 88 } 89 } 90 catch(Exception e) 91 { 92 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e); 93 } 94 } 95 96 /** 97 * Add an iterator to the union list. 98 * 99 * @param expr non-null reference to a location path iterator. 100 */ 101 public void addIterator(DTMIterator expr) 102 { 103 104 // Increase array size by only 1 at a time. Fix this 105 // if it looks to be a problem. 106 if (null == m_iterators) 107 { 108 m_iterators = new DTMIterator[1]; 109 m_iterators[0] = expr; 110 } 111 else 112 { 113 DTMIterator[] exprs = m_iterators; 114 int len = m_iterators.length; 115 116 m_iterators = new DTMIterator[len + 1]; 117 118 System.arraycopy(exprs, 0, m_iterators, 0, len); 119 120 m_iterators[len] = expr; 121 } 122 expr.nextNode(); 123 if(expr instanceof Expression) 124 ((Expression)expr).exprSetParent(this); 125 } 126 127 /** 128 * Detaches the iterator from the set which it iterated over, releasing 129 * any computational resources and placing the iterator in the INVALID 130 * state. After<code>detach</code> has been invoked, calls to 131 * <code>nextNode</code> or<code>previousNode</code> will raise the 132 * exception INVALID_STATE_ERR. 133 */ 134 public void detach() 135 { 136 if(m_allowDetach && null != m_iterators){ 137 int n = m_iterators.length; 138 for(int i = 0; i < n; i++) 139 { 140 m_iterators[i].detach(); 141 } 142 m_iterators = null; 143 } 144 } 145 146 147 /** 148 * Create a UnionPathIterator object, including creation 149 * of location path iterators from the opcode list, and call back 150 * into the Compiler to create predicate expressions. 151 * 152 * @param compiler The Compiler which is creating 153 * this expression. 154 * @param opPos The position of this iterator in the 155 * opcode list from the compiler. 156 * 157 * @throws javax.xml.transform.TransformerException 158 */ 159 public UnionPathIterator(Compiler compiler, int opPos) 160 throws javax.xml.transform.TransformerException 161 { 162 163 super(); 164 165 opPos = OpMap.getFirstChildPos(opPos); 166 167 loadLocationPaths(compiler, opPos, 0); 168 } 169 170 /** 171 * This will return an iterator capable of handling the union of paths given. 172 * 173 * @param compiler The Compiler which is creating 174 * this expression. 175 * @param opPos The position of this iterator in the 176 * opcode list from the compiler. 177 * 178 * @return Object that is derived from LocPathIterator. 179 * 180 * @throws javax.xml.transform.TransformerException 181 */ 182 public static LocPathIterator createUnionIterator(Compiler compiler, int opPos) 183 throws javax.xml.transform.TransformerException 184 { 185 // For the moment, I'm going to first create a full UnionPathIterator, and 186 // then see if I can reduce it to a UnionChildIterator. It would obviously 187 // be more effecient to just test for the conditions for a UnionChildIterator, 188 // and then create that directly. 189 UnionPathIterator upi = new UnionPathIterator(compiler, opPos); 190 int nPaths = upi.m_exprs.length; 191 boolean isAllChildIterators = true; 192 for(int i = 0; i < nPaths; i++) 193 { 194 LocPathIterator lpi = upi.m_exprs[i]; 195 196 if(lpi.getAxis() != Axis.CHILD) 197 { 198 isAllChildIterators = false; 199 break; 200 } 201 else 202 { 203 // check for positional predicates or position function, which won't work. 204 if(HasPositionalPredChecker.check(lpi)) 205 { 206 isAllChildIterators = false; 207 break; 208 } 209 } 210 } 211 if(isAllChildIterators) 212 { 213 UnionChildIterator uci = new UnionChildIterator(); 214 215 for(int i = 0; i < nPaths; i++) 216 { 217 PredicatedNodeTest lpi = upi.m_exprs[i]; 218 // I could strip the lpi down to a pure PredicatedNodeTest, but 219 // I don't think it's worth it. Note that the test can be used 220 // as a static object... so it doesn't have to be cloned. 221 uci.addNodeTest(lpi); 222 } 223 return uci; 224 225 } 226 else 227 return upi; 228 } 229 230 /** 231 * Get the analysis bits for this walker, as defined in the WalkerFactory. 232 * @return One of WalkerFactory#BIT_DESCENDANT, etc. 233 */ 234 public int getAnalysisBits() 235 { 236 int bits = 0; 237 238 if (m_exprs != null) 239 { 240 int n = m_exprs.length; 241 242 for (int i = 0; i < n; i++) 243 { 244 int bit = m_exprs[i].getAnalysisBits(); 245 bits |= bit; 246 } 247 } 248 249 return bits; 250 } 251 252 /** 253 * Read the object from a serialization stream. 254 * 255 * @param stream Input stream to read from 256 * 257 * @throws java.io.IOException 258 * @throws javax.xml.transform.TransformerException 259 */ 260 private void readObject(java.io.ObjectInputStream stream) 261 throws java.io.IOException, javax.xml.transform.TransformerException 262 { 263 try 264 { 265 stream.defaultReadObject(); 266 m_clones = new IteratorPool(this); 267 } 268 catch (ClassNotFoundException cnfe) 269 { 270 throw new javax.xml.transform.TransformerException(cnfe); 271 } 272 } 273 274 /** 275 * Get a cloned LocPathIterator that holds the same 276 * position as this iterator. 277 * 278 * @return A clone of this iterator that holds the same node position. 279 * 280 * @throws CloneNotSupportedException 281 */ 282 public Object clone() throws CloneNotSupportedException 283 { 284 285 UnionPathIterator clone = (UnionPathIterator) super.clone(); 286 if (m_iterators != null) 287 { 288 int n = m_iterators.length; 289 290 clone.m_iterators = new DTMIterator[n]; 291 292 for (int i = 0; i < n; i++) 293 { 294 clone.m_iterators[i] = (DTMIterator)m_iterators[i].clone(); 295 } 296 } 297 298 return clone; 299 } 300 301 302 /** 303 * Create a new location path iterator. 304 * 305 * @param compiler The Compiler which is creating 306 * this expression. 307 * @param opPos The position of this iterator in the 308 * 309 * @return New location path iterator. 310 * 311 * @throws javax.xml.transform.TransformerException 312 */ 313 protected LocPathIterator createDTMIterator( 314 Compiler compiler, int opPos) throws javax.xml.transform.TransformerException 315 { 316 LocPathIterator lpi = (LocPathIterator)WalkerFactory.newDTMIterator(compiler, opPos, 317 (compiler.getLocationPathDepth() <= 0)); 318 return lpi; 319 } 320 321 /** 322 * Initialize the location path iterators. Recursive. 323 * 324 * @param compiler The Compiler which is creating 325 * this expression. 326 * @param opPos The position of this iterator in the 327 * opcode list from the compiler. 328 * @param count The insert position of the iterator. 329 * 330 * @throws javax.xml.transform.TransformerException 331 */ 332 protected void loadLocationPaths(Compiler compiler, int opPos, int count) 333 throws javax.xml.transform.TransformerException 334 { 335 336 // TODO: Handle unwrapped FilterExpr 337 int steptype = compiler.getOp(opPos); 338 339 if (steptype == OpCodes.OP_LOCATIONPATH) 340 { 341 loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1); 342 343 m_exprs[count] = createDTMIterator(compiler, opPos); 344 m_exprs[count].exprSetParent(this); 345 } 346 else 347 { 348 349 // Have to check for unwrapped functions, which the LocPathIterator 350 // doesn't handle. 351 switch (steptype) 352 { 353 case OpCodes.OP_VARIABLE : 354 case OpCodes.OP_EXTFUNCTION : 355 case OpCodes.OP_FUNCTION : 356 case OpCodes.OP_GROUP : 357 loadLocationPaths(compiler, compiler.getNextOpPos(opPos), count + 1); 358 359 WalkingIterator iter = 360 new WalkingIterator(compiler.getNamespaceContext()); 361 iter.exprSetParent(this); 362 363 if(compiler.getLocationPathDepth() <= 0) 364 iter.setIsTopLevel(true); 365 366 iter.m_firstWalker = new com.sun.org.apache.xpath.internal.axes.FilterExprWalker(iter); 367 368 iter.m_firstWalker.init(compiler, opPos, steptype); 369 370 m_exprs[count] = iter; 371 break; 372 default : 373 m_exprs = new LocPathIterator[count]; 374 } 375 } 376 } 377 378 /** 379 * Returns the next node in the set and advances the position of the 380 * iterator in the set. After a DTMIterator is created, the first call 381 * to nextNode() returns the first node in the set. 382 * @return The next <code>Node</code> in the set being iterated over, or 383 * <code>null</code> if there are no more members in that set. 384 */ 385 public int nextNode() 386 { 387 if(m_foundLast) 388 return DTM.NULL; 389 390 // Loop through the iterators getting the current fetched 391 // node, and get the earliest occuring in document order 392 int earliestNode = DTM.NULL; 393 394 if (null != m_iterators) 395 { 396 int n = m_iterators.length; 397 int iteratorUsed = -1; 398 399 for (int i = 0; i < n; i++) 400 { 401 int node = m_iterators[i].getCurrentNode(); 402 403 if (DTM.NULL == node) 404 continue; 405 else if (DTM.NULL == earliestNode) 406 { 407 iteratorUsed = i; 408 earliestNode = node; 409 } 410 else 411 { 412 if (node == earliestNode) 413 { 414 415 // Found a duplicate, so skip past it. 416 m_iterators[i].nextNode(); 417 } 418 else 419 { 420 DTM dtm = getDTM(node); 421 422 if (dtm.isNodeAfter(node, earliestNode)) 423 { 424 iteratorUsed = i; 425 earliestNode = node; 426 } 427 } 428 } 429 } 430 431 if (DTM.NULL != earliestNode) 432 { 433 m_iterators[iteratorUsed].nextNode(); 434 435 incrementCurrentPos(); 436 } 437 else 438 m_foundLast = true; 439 } 440 441 m_lastFetched = earliestNode; 442 443 return earliestNode; 444 } 445 446 /** 447 * This function is used to fixup variables from QNames to stack frame 448 * indexes at stylesheet build time. 449 * @param vars List of QNames that correspond to variables. This list 450 * should be searched backwards for the first qualified name that 451 * corresponds to the variable reference qname. The position of the 452 * QName in the vector from the start of the vector will be its position 453 * in the stack frame (but variables above the globalsTop value will need 454 * to be offset to the current stack frame). 455 */ 456 public void fixupVariables(List<QName> vars, int globalsSize) 457 { 458 for (int i = 0; i < m_exprs.length; i++) 459 { 460 m_exprs[i].fixupVariables(vars, globalsSize); 461 } 462 463 } 464 465 /** 466 * The location path iterators, one for each 467 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location 468 * path</a> contained in the union expression. 469 * @serial 470 */ 471 protected LocPathIterator[] m_exprs; 472 473 474 /** 475 * The location path iterators, one for each 476 * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location 477 * path</a> contained in the union expression. 478 * @serial 479 */ 480 protected DTMIterator[] m_iterators; 481 482 /** 483 * Returns the axis being iterated, if it is known. 484 * 485 * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 486 * types. 487 */ 488 public int getAxis() 489 { 490 // Could be smarter. 491 return -1; 492 } 493 494 class iterOwner implements ExpressionOwner 495 { 496 int m_index; 497 498 iterOwner(int index) 499 { 500 m_index = index; 501 } 502 503 /** 504 * @see ExpressionOwner#getExpression() 505 */ 506 public Expression getExpression() 507 { 508 return m_exprs[m_index]; 509 } 510 511 /** 512 * @see ExpressionOwner#setExpression(Expression) 513 */ 514 public void setExpression(Expression exp) 515 { 516 517 if(!(exp instanceof LocPathIterator)) 518 { 519 // Yuck. Need FilterExprIter. Or make it so m_exprs can be just 520 // plain expressions? 521 WalkingIterator wi = new WalkingIterator(getPrefixResolver()); 522 FilterExprWalker few = new FilterExprWalker(wi); 523 wi.setFirstWalker(few); 524 few.setInnerExpression(exp); 525 wi.exprSetParent(UnionPathIterator.this); 526 few.exprSetParent(wi); 527 exp.exprSetParent(few); 528 exp = wi; 529 } 530 else 531 exp.exprSetParent(UnionPathIterator.this); 532 m_exprs[m_index] = (LocPathIterator)exp; 533 } 534 535 } 536 537 /** 538 * @see com.sun.org.apache.xpath.internal.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor) 539 */ 540 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 541 { 542 if(visitor.visitUnionPath(owner, this)) 543 { 544 if(null != m_exprs) 545 { 546 int n = m_exprs.length; 547 for(int i = 0; i < n; i++) 548 { 549 m_exprs[i].callVisitors(new iterOwner(i), visitor); 550 } 551 } 552 } 553 } 554 555 /** 556 * @see Expression#deepEquals(Expression) 557 */ 558 public boolean deepEquals(Expression expr) 559 { 560 if (!super.deepEquals(expr)) 561 return false; 562 563 UnionPathIterator upi = (UnionPathIterator) expr; 564 565 if (null != m_exprs) 566 { 567 int n = m_exprs.length; 568 569 if((null == upi.m_exprs) || (upi.m_exprs.length != n)) 570 return false; 571 572 for (int i = 0; i < n; i++) 573 { 574 if(!m_exprs[i].deepEquals(upi.m_exprs[i])) 575 return false; 576 } 577 } 578 else if (null != upi.m_exprs) 579 { 580 return false; 581 } 582 583 return true; 584 } 585 586 587 }