001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: UnionPathIterator.java 469314 2006-10-30 23:31:59Z minchau $
020 */
021 package org.apache.xpath.axes;
022
023 import org.apache.xml.dtm.Axis;
024 import org.apache.xml.dtm.DTM;
025 import org.apache.xml.dtm.DTMIterator;
026 import org.apache.xpath.Expression;
027 import org.apache.xpath.ExpressionOwner;
028 import org.apache.xpath.XPathVisitor;
029 import org.apache.xpath.compiler.Compiler;
030 import org.apache.xpath.compiler.OpCodes;
031 import org.apache.xpath.compiler.OpMap;
032
033 /**
034 * This class extends NodeSetDTM, which implements DTMIterator,
035 * and fetches nodes one at a time in document order based on a XPath
036 * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
037 * As each node is iterated via nextNode(), the node is also stored
038 * in the NodeVector, so that previousNode() can easily be done.
039 * @xsl.usage advanced
040 */
041 public class UnionPathIterator extends LocPathIterator
042 implements Cloneable, DTMIterator, java.io.Serializable, PathComponent
043 {
044 static final long serialVersionUID = -3910351546843826781L;
045
046 /**
047 * Constructor to create an instance which you can add location paths to.
048 */
049 public UnionPathIterator()
050 {
051
052 super();
053
054 // m_mutable = false;
055 // m_cacheNodes = false;
056 m_iterators = null;
057 m_exprs = null;
058 }
059
060 /**
061 * Initialize the context values for this expression
062 * after it is cloned.
063 *
064 * @param context The XPath runtime context for this
065 * transformation.
066 */
067 public void setRoot(int context, Object environment)
068 {
069 super.setRoot(context, environment);
070
071 try
072 {
073 if (null != m_exprs)
074 {
075 int n = m_exprs.length;
076 DTMIterator newIters[] = new DTMIterator[n];
077
078 for (int i = 0; i < n; i++)
079 {
080 DTMIterator iter = m_exprs[i].asIterator(m_execContext, context);
081 newIters[i] = iter;
082 iter.nextNode();
083 }
084 m_iterators = newIters;
085 }
086 }
087 catch(Exception e)
088 {
089 throw new org.apache.xml.utils.WrappedRuntimeException(e);
090 }
091 }
092
093 /**
094 * Add an iterator to the union list.
095 *
096 * @param expr non-null reference to a location path iterator.
097 */
098 public void addIterator(DTMIterator expr)
099 {
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 }