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