001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: UnionPathIterator.java,v 1.36 2005/01/23 01:02:11 mcnamara Exp $
018: */
019: package org.apache.xpath.axes;
020:
021: import org.apache.xml.dtm.Axis;
022: import org.apache.xml.dtm.DTM;
023: import org.apache.xml.dtm.DTMIterator;
024: import org.apache.xpath.Expression;
025: import org.apache.xpath.ExpressionOwner;
026: import org.apache.xpath.XPathVisitor;
027: import org.apache.xpath.compiler.Compiler;
028: import org.apache.xpath.compiler.OpCodes;
029:
030: /**
031: * This class extends NodeSetDTM, which implements DTMIterator,
032: * and fetches nodes one at a time in document order based on a XPath
033: * <a href="http://www.w3.org/TR/xpath#NT-UnionExpr">UnionExpr</a>.
034: * As each node is iterated via nextNode(), the node is also stored
035: * in the NodeVector, so that previousNode() can easily be done.
036: * @xsl.usage advanced
037: */
038: public class UnionPathIterator extends LocPathIterator implements
039: Cloneable, DTMIterator, java.io.Serializable, PathComponent {
040: static final long serialVersionUID = -3910351546843826781L;
041:
042: /**
043: * Constructor to create an instance which you can add location paths to.
044: */
045: public UnionPathIterator() {
046:
047: super ();
048:
049: // m_mutable = false;
050: // m_cacheNodes = false;
051: m_iterators = null;
052: m_exprs = null;
053: }
054:
055: /**
056: * Initialize the context values for this expression
057: * after it is cloned.
058: *
059: * @param context The XPath runtime context for this
060: * transformation.
061: */
062: public void setRoot(int context, Object environment) {
063: super .setRoot(context, environment);
064:
065: try {
066: if (null != m_exprs) {
067: int n = m_exprs.length;
068: DTMIterator newIters[] = new DTMIterator[n];
069:
070: for (int i = 0; i < n; i++) {
071: DTMIterator iter = m_exprs[i].asIterator(
072: m_execContext, context);
073: newIters[i] = iter;
074: iter.nextNode();
075: }
076: m_iterators = newIters;
077: }
078: } catch (Exception e) {
079: throw new org.apache.xml.utils.WrappedRuntimeException(e);
080: }
081: }
082:
083: /**
084: * Add an iterator to the union list.
085: *
086: * @param expr non-null reference to a location path iterator.
087: */
088: public void addIterator(DTMIterator expr) {
089:
090: // Increase array size by only 1 at a time. Fix this
091: // if it looks to be a problem.
092: if (null == m_iterators) {
093: m_iterators = new DTMIterator[1];
094: m_iterators[0] = expr;
095: } else {
096: DTMIterator[] exprs = m_iterators;
097: int len = m_iterators.length;
098:
099: m_iterators = new DTMIterator[len + 1];
100:
101: System.arraycopy(exprs, 0, m_iterators, 0, len);
102:
103: m_iterators[len] = expr;
104: }
105: expr.nextNode();
106: if (expr instanceof Expression)
107: ((Expression) expr).exprSetParent(this );
108: }
109:
110: /**
111: * Detaches the iterator from the set which it iterated over, releasing
112: * any computational resources and placing the iterator in the INVALID
113: * state. After<code>detach</code> has been invoked, calls to
114: * <code>nextNode</code> or<code>previousNode</code> will raise the
115: * exception INVALID_STATE_ERR.
116: */
117: public void detach() {
118: if (m_allowDetach && null != m_iterators) {
119: int n = m_iterators.length;
120: for (int i = 0; i < n; i++) {
121: m_iterators[i].detach();
122: }
123: m_iterators = null;
124: }
125: }
126:
127: /**
128: * Create a UnionPathIterator object, including creation
129: * of location path iterators from the opcode list, and call back
130: * into the Compiler to create predicate expressions.
131: *
132: * @param compiler The Compiler which is creating
133: * this expression.
134: * @param opPos The position of this iterator in the
135: * opcode list from the compiler.
136: *
137: * @throws javax.xml.transform.TransformerException
138: */
139: public UnionPathIterator(Compiler compiler, int opPos)
140: throws javax.xml.transform.TransformerException {
141:
142: super ();
143:
144: opPos = compiler.getFirstChildPos(opPos);
145:
146: loadLocationPaths(compiler, opPos, 0);
147: }
148:
149: /**
150: * This will return an iterator capable of handling the union of paths given.
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: * @return Object that is derived from LocPathIterator.
158: *
159: * @throws javax.xml.transform.TransformerException
160: */
161: public static LocPathIterator createUnionIterator(
162: Compiler compiler, int opPos)
163: throws javax.xml.transform.TransformerException {
164: // For the moment, I'm going to first create a full UnionPathIterator, and
165: // then see if I can reduce it to a UnionChildIterator. It would obviously
166: // be more effecient to just test for the conditions for a UnionChildIterator,
167: // and then create that directly.
168: UnionPathIterator upi = new UnionPathIterator(compiler, opPos);
169: int nPaths = upi.m_exprs.length;
170: boolean isAllChildIterators = true;
171: for (int i = 0; i < nPaths; i++) {
172: LocPathIterator lpi = upi.m_exprs[i];
173:
174: if (lpi.getAxis() != Axis.CHILD) {
175: isAllChildIterators = false;
176: break;
177: } else {
178: // check for positional predicates or position function, which won't work.
179: if (HasPositionalPredChecker.check(lpi)) {
180: isAllChildIterators = false;
181: break;
182: }
183: }
184: }
185: if (isAllChildIterators) {
186: UnionChildIterator uci = new UnionChildIterator();
187:
188: for (int i = 0; i < nPaths; i++) {
189: PredicatedNodeTest lpi = upi.m_exprs[i];
190: // I could strip the lpi down to a pure PredicatedNodeTest, but
191: // I don't think it's worth it. Note that the test can be used
192: // as a static object... so it doesn't have to be cloned.
193: uci.addNodeTest(lpi);
194: }
195: return uci;
196:
197: } else
198: return upi;
199: }
200:
201: /**
202: * Get the analysis bits for this walker, as defined in the WalkerFactory.
203: * @return One of WalkerFactory#BIT_DESCENDANT, etc.
204: */
205: public int getAnalysisBits() {
206: int bits = 0;
207:
208: if (m_exprs != null) {
209: int n = m_exprs.length;
210:
211: for (int i = 0; i < n; i++) {
212: int bit = m_exprs[i].getAnalysisBits();
213: bits |= bit;
214: }
215: }
216:
217: return bits;
218: }
219:
220: /**
221: * Read the object from a serialization stream.
222: *
223: * @param stream Input stream to read from
224: *
225: * @throws java.io.IOException
226: * @throws javax.xml.transform.TransformerException
227: */
228: private void readObject(java.io.ObjectInputStream stream)
229: throws java.io.IOException,
230: javax.xml.transform.TransformerException {
231: try {
232: stream.defaultReadObject();
233: m_clones = new IteratorPool(this );
234: } catch (ClassNotFoundException cnfe) {
235: throw new javax.xml.transform.TransformerException(cnfe);
236: }
237: }
238:
239: /**
240: * Get a cloned LocPathIterator that holds the same
241: * position as this iterator.
242: *
243: * @return A clone of this iterator that holds the same node position.
244: *
245: * @throws CloneNotSupportedException
246: */
247: public Object clone() throws CloneNotSupportedException {
248:
249: UnionPathIterator clone = (UnionPathIterator) super .clone();
250: if (m_iterators != null) {
251: int n = m_iterators.length;
252:
253: clone.m_iterators = new DTMIterator[n];
254:
255: for (int i = 0; i < n; i++) {
256: clone.m_iterators[i] = (DTMIterator) m_iterators[i]
257: .clone();
258: }
259: }
260:
261: return clone;
262: }
263:
264: /**
265: * Create a new location path iterator.
266: *
267: * @param compiler The Compiler which is creating
268: * this expression.
269: * @param opPos The position of this iterator in the
270: *
271: * @return New location path iterator.
272: *
273: * @throws javax.xml.transform.TransformerException
274: */
275: protected LocPathIterator createDTMIterator(Compiler compiler,
276: int opPos) throws javax.xml.transform.TransformerException {
277: LocPathIterator lpi = (LocPathIterator) WalkerFactory
278: .newDTMIterator(compiler, opPos, (compiler
279: .getLocationPathDepth() <= 0));
280: return lpi;
281: }
282:
283: /**
284: * Initialize the location path iterators. Recursive.
285: *
286: * @param compiler The Compiler which is creating
287: * this expression.
288: * @param opPos The position of this iterator in the
289: * opcode list from the compiler.
290: * @param count The insert position of the iterator.
291: *
292: * @throws javax.xml.transform.TransformerException
293: */
294: protected void loadLocationPaths(Compiler compiler, int opPos,
295: int count) throws javax.xml.transform.TransformerException {
296:
297: // TODO: Handle unwrapped FilterExpr
298: int steptype = compiler.getOp(opPos);
299:
300: if (steptype == OpCodes.OP_LOCATIONPATH) {
301: loadLocationPaths(compiler, compiler.getNextOpPos(opPos),
302: count + 1);
303:
304: m_exprs[count] = createDTMIterator(compiler, opPos);
305: m_exprs[count].exprSetParent(this );
306: } else {
307:
308: // Have to check for unwrapped functions, which the LocPathIterator
309: // doesn't handle.
310: switch (steptype) {
311: case OpCodes.OP_VARIABLE:
312: case OpCodes.OP_EXTFUNCTION:
313: case OpCodes.OP_FUNCTION:
314: case OpCodes.OP_GROUP:
315: loadLocationPaths(compiler, compiler
316: .getNextOpPos(opPos), count + 1);
317:
318: WalkingIterator iter = new WalkingIterator(compiler
319: .getNamespaceContext());
320: iter.exprSetParent(this );
321:
322: if (compiler.getLocationPathDepth() <= 0)
323: iter.setIsTopLevel(true);
324:
325: iter.m_firstWalker = new org.apache.xpath.axes.FilterExprWalker(
326: iter);
327:
328: iter.m_firstWalker.init(compiler, opPos, steptype);
329:
330: m_exprs[count] = iter;
331: break;
332: default:
333: m_exprs = new LocPathIterator[count];
334: }
335: }
336: }
337:
338: /**
339: * Returns the next node in the set and advances the position of the
340: * iterator in the set. After a DTMIterator is created, the first call
341: * to nextNode() returns the first node in the set.
342: * @return The next <code>Node</code> in the set being iterated over, or
343: * <code>null</code> if there are no more members in that set.
344: */
345: public int nextNode() {
346: if (m_foundLast)
347: return DTM.NULL;
348:
349: // Loop through the iterators getting the current fetched
350: // node, and get the earliest occuring in document order
351: int earliestNode = DTM.NULL;
352:
353: if (null != m_iterators) {
354: int n = m_iterators.length;
355: int iteratorUsed = -1;
356:
357: for (int i = 0; i < n; i++) {
358: int node = m_iterators[i].getCurrentNode();
359:
360: if (DTM.NULL == node)
361: continue;
362: else if (DTM.NULL == earliestNode) {
363: iteratorUsed = i;
364: earliestNode = node;
365: } else {
366: if (node == earliestNode) {
367:
368: // Found a duplicate, so skip past it.
369: m_iterators[i].nextNode();
370: } else {
371: DTM dtm = getDTM(node);
372:
373: if (dtm.isNodeAfter(node, earliestNode)) {
374: iteratorUsed = i;
375: earliestNode = node;
376: }
377: }
378: }
379: }
380:
381: if (DTM.NULL != earliestNode) {
382: m_iterators[iteratorUsed].nextNode();
383:
384: incrementCurrentPos();
385: } else
386: m_foundLast = true;
387: }
388:
389: m_lastFetched = earliestNode;
390:
391: return earliestNode;
392: }
393:
394: /**
395: * This function is used to fixup variables from QNames to stack frame
396: * indexes at stylesheet build time.
397: * @param vars List of QNames that correspond to variables. This list
398: * should be searched backwards for the first qualified name that
399: * corresponds to the variable reference qname. The position of the
400: * QName in the vector from the start of the vector will be its position
401: * in the stack frame (but variables above the globalsTop value will need
402: * to be offset to the current stack frame).
403: */
404: public void fixupVariables(java.util.Vector vars, int globalsSize) {
405: for (int i = 0; i < m_exprs.length; i++) {
406: m_exprs[i].fixupVariables(vars, globalsSize);
407: }
408:
409: }
410:
411: /**
412: * The location path iterators, one for each
413: * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
414: * path</a> contained in the union expression.
415: * @serial
416: */
417: protected LocPathIterator[] m_exprs;
418:
419: /**
420: * The location path iterators, one for each
421: * <a href="http://www.w3.org/TR/xpath#NT-LocationPath">location
422: * path</a> contained in the union expression.
423: * @serial
424: */
425: protected DTMIterator[] m_iterators;
426:
427: /**
428: * Returns the axis being iterated, if it is known.
429: *
430: * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
431: * types.
432: */
433: public int getAxis() {
434: // Could be smarter.
435: return -1;
436: }
437:
438: class iterOwner implements ExpressionOwner {
439: int m_index;
440:
441: iterOwner(int index) {
442: m_index = index;
443: }
444:
445: /**
446: * @see ExpressionOwner#getExpression()
447: */
448: public Expression getExpression() {
449: return m_exprs[m_index];
450: }
451:
452: /**
453: * @see ExpressionOwner#setExpression(Expression)
454: */
455: public void setExpression(Expression exp) {
456:
457: if (!(exp instanceof LocPathIterator)) {
458: // Yuck. Need FilterExprIter. Or make it so m_exprs can be just
459: // plain expressions?
460: WalkingIterator wi = new WalkingIterator(
461: getPrefixResolver());
462: FilterExprWalker few = new FilterExprWalker(wi);
463: wi.setFirstWalker(few);
464: few.setInnerExpression(exp);
465: wi.exprSetParent(UnionPathIterator.this );
466: few.exprSetParent(wi);
467: exp.exprSetParent(few);
468: exp = wi;
469: } else
470: exp.exprSetParent(UnionPathIterator.this );
471: m_exprs[m_index] = (LocPathIterator) exp;
472: }
473:
474: }
475:
476: /**
477: * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
478: */
479: public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) {
480: if (visitor.visitUnionPath(owner, this )) {
481: if (null != m_exprs) {
482: int n = m_exprs.length;
483: for (int i = 0; i < n; i++) {
484: m_exprs[i].callVisitors(new iterOwner(i), visitor);
485: }
486: }
487: }
488: }
489:
490: /**
491: * @see Expression#deepEquals(Expression)
492: */
493: public boolean deepEquals(Expression expr) {
494: if (!super .deepEquals(expr))
495: return false;
496:
497: UnionPathIterator upi = (UnionPathIterator) expr;
498:
499: if (null != m_exprs) {
500: int n = m_exprs.length;
501:
502: if ((null == upi.m_exprs) || (upi.m_exprs.length != n))
503: return false;
504:
505: for (int i = 0; i < n; i++) {
506: if (!m_exprs[i].deepEquals(upi.m_exprs[i]))
507: return false;
508: }
509: } else if (null != upi.m_exprs) {
510: return false;
511: }
512:
513: return true;
514: }
515:
516: }
|