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: AxesWalker.java,v 1.29 2004/08/17 19:25:34 jycli Exp $
018: */
019: package org.apache.xpath.axes;
020:
021: import java.util.Vector;
022:
023: import org.apache.xalan.res.XSLMessages;
024: import org.apache.xml.dtm.DTM;
025: import org.apache.xml.dtm.DTMAxisTraverser;
026: import org.apache.xml.dtm.DTMIterator;
027: import org.apache.xpath.Expression;
028: import org.apache.xpath.ExpressionOwner;
029: import org.apache.xpath.XPathContext;
030: import org.apache.xpath.XPathVisitor;
031: import org.apache.xpath.compiler.Compiler;
032: import org.apache.xpath.res.XPATHErrorResources;
033:
034: /**
035: * Serves as common interface for axes Walkers, and stores common
036: * state variables.
037: */
038: public class AxesWalker extends PredicatedNodeTest implements
039: Cloneable, PathComponent, ExpressionOwner {
040: static final long serialVersionUID = -2966031951306601247L;
041:
042: /**
043: * Construct an AxesWalker using a LocPathIterator.
044: *
045: * @param locPathIterator non-null reference to the parent iterator.
046: */
047: public AxesWalker(LocPathIterator locPathIterator, int axis) {
048: super (locPathIterator);
049: m_axis = axis;
050: }
051:
052: public final WalkingIterator wi() {
053: return (WalkingIterator) m_lpi;
054: }
055:
056: /**
057: * Initialize an AxesWalker during the parse of the XPath expression.
058: *
059: * @param compiler The Compiler object that has information about this
060: * walker in the op map.
061: * @param opPos The op code position of this location step.
062: * @param stepType The type of location step.
063: *
064: * @throws javax.xml.transform.TransformerException
065: */
066: public void init(Compiler compiler, int opPos, int stepType)
067: throws javax.xml.transform.TransformerException {
068:
069: initPredicateInfo(compiler, opPos);
070:
071: // int testType = compiler.getOp(nodeTestOpPos);
072: }
073:
074: /**
075: * Get a cloned AxesWalker.
076: *
077: * @return A new AxesWalker that can be used without mutating this one.
078: *
079: * @throws CloneNotSupportedException
080: */
081: public Object clone() throws CloneNotSupportedException {
082: // Do not access the location path itterator during this operation!
083:
084: AxesWalker clone = (AxesWalker) super .clone();
085:
086: //clone.setCurrentNode(clone.m_root);
087:
088: // clone.m_isFresh = true;
089:
090: return clone;
091: }
092:
093: /**
094: * Do a deep clone of this walker, including next and previous walkers.
095: * If the this AxesWalker is on the clone list, don't clone but
096: * return the already cloned version.
097: *
098: * @param cloneOwner non-null reference to the cloned location path
099: * iterator to which this clone will be added.
100: * @param cloneList non-null vector of sources in odd elements, and the
101: * corresponding clones in even vectors.
102: *
103: * @return non-null clone, which may be a new clone, or may be a clone
104: * contained on the cloneList.
105: */
106: AxesWalker cloneDeep(WalkingIterator cloneOwner, Vector cloneList)
107: throws CloneNotSupportedException {
108: AxesWalker clone = findClone(this , cloneList);
109: if (null != clone)
110: return clone;
111: clone = (AxesWalker) this .clone();
112: clone.setLocPathIterator(cloneOwner);
113: if (null != cloneList) {
114: cloneList.addElement(this );
115: cloneList.addElement(clone);
116: }
117:
118: if (wi().m_lastUsedWalker == this )
119: cloneOwner.m_lastUsedWalker = clone;
120:
121: if (null != m_nextWalker)
122: clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner,
123: cloneList);
124:
125: // If you don't check for the cloneList here, you'll go into an
126: // recursive infinate loop.
127: if (null != cloneList) {
128: if (null != m_prevWalker)
129: clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner,
130: cloneList);
131: } else {
132: if (null != m_nextWalker)
133: clone.m_nextWalker.m_prevWalker = clone;
134: }
135: return clone;
136: }
137:
138: /**
139: * Find a clone that corresponds to the key argument.
140: *
141: * @param key The original AxesWalker for which there may be a clone.
142: * @param cloneList vector of sources in odd elements, and the
143: * corresponding clones in even vectors, may be null.
144: *
145: * @return A clone that corresponds to the key, or null if key not found.
146: */
147: static AxesWalker findClone(AxesWalker key, Vector cloneList) {
148: if (null != cloneList) {
149: // First, look for clone on list.
150: int n = cloneList.size();
151: for (int i = 0; i < n; i += 2) {
152: if (key == cloneList.elementAt(i))
153: return (AxesWalker) cloneList.elementAt(i + 1);
154: }
155: }
156: return null;
157: }
158:
159: /**
160: * Detaches the walker from the set which it iterated over, releasing
161: * any computational resources and placing the iterator in the INVALID
162: * state.
163: */
164: public void detach() {
165: m_currentNode = DTM.NULL;
166: m_dtm = null;
167: m_traverser = null;
168: m_isFresh = true;
169: m_root = DTM.NULL;
170: }
171:
172: //=============== TreeWalker Implementation ===============
173:
174: /**
175: * The root node of the TreeWalker, as specified in setRoot(int root).
176: * Note that this may actually be below the current node.
177: *
178: * @return The context node of the step.
179: */
180: public int getRoot() {
181: return m_root;
182: }
183:
184: /**
185: * Get the analysis bits for this walker, as defined in the WalkerFactory.
186: * @return One of WalkerFactory#BIT_DESCENDANT, etc.
187: */
188: public int getAnalysisBits() {
189: int axis = getAxis();
190: int bit = WalkerFactory.getAnalysisBitFromAxes(axis);
191: return bit;
192: }
193:
194: /**
195: * Set the root node of the TreeWalker.
196: * (Not part of the DOM2 TreeWalker interface).
197: *
198: * @param root The context node of this step.
199: */
200: public void setRoot(int root) {
201: // %OPT% Get this directly from the lpi.
202: XPathContext xctxt = wi().getXPathContext();
203: m_dtm = xctxt.getDTM(root);
204: m_traverser = m_dtm.getAxisTraverser(m_axis);
205: m_isFresh = true;
206: m_foundLast = false;
207: m_root = root;
208: m_currentNode = root;
209:
210: if (DTM.NULL == root) {
211: throw new RuntimeException(XSLMessages.createXPATHMessage(
212: XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL,
213: null)); //"\n !!!! Error! Setting the root of a walker to null!!!");
214: }
215:
216: resetProximityPositions();
217: }
218:
219: /**
220: * The node at which the TreeWalker is currently positioned.
221: * <br> The value must not be null. Alterations to the DOM tree may cause
222: * the current node to no longer be accepted by the TreeWalker's
223: * associated filter. currentNode may also be explicitly set to any node,
224: * whether or not it is within the subtree specified by the root node or
225: * would be accepted by the filter and whatToShow flags. Further
226: * traversal occurs relative to currentNode even if it is not part of the
227: * current view by applying the filters in the requested direction (not
228: * changing currentNode where no traversal is possible).
229: *
230: * @return The node at which the TreeWalker is currently positioned, only null
231: * if setRoot has not yet been called.
232: */
233: public final int getCurrentNode() {
234: return m_currentNode;
235: }
236:
237: /**
238: * Set the next walker in the location step chain.
239: *
240: *
241: * @param walker Reference to AxesWalker derivative, or may be null.
242: */
243: public void setNextWalker(AxesWalker walker) {
244: m_nextWalker = walker;
245: }
246:
247: /**
248: * Get the next walker in the location step chain.
249: *
250: *
251: * @return Reference to AxesWalker derivative, or null.
252: */
253: public AxesWalker getNextWalker() {
254: return m_nextWalker;
255: }
256:
257: /**
258: * Set or clear the previous walker reference in the location step chain.
259: *
260: *
261: * @param walker Reference to previous walker reference in the location
262: * step chain, or null.
263: */
264: public void setPrevWalker(AxesWalker walker) {
265: m_prevWalker = walker;
266: }
267:
268: /**
269: * Get the previous walker reference in the location step chain.
270: *
271: *
272: * @return Reference to previous walker reference in the location
273: * step chain, or null.
274: */
275: public AxesWalker getPrevWalker() {
276: return m_prevWalker;
277: }
278:
279: /**
280: * This is simply a way to bottle-neck the return of the next node, for
281: * diagnostic purposes.
282: *
283: * @param n Node to return, or null.
284: *
285: * @return The argument.
286: */
287: private int returnNextNode(int n) {
288:
289: return n;
290: }
291:
292: /**
293: * Get the next node in document order on the axes.
294: *
295: * @return the next node in document order on the axes, or null.
296: */
297: protected int getNextNode() {
298: if (m_foundLast)
299: return DTM.NULL;
300:
301: if (m_isFresh) {
302: m_currentNode = m_traverser.first(m_root);
303: m_isFresh = false;
304: }
305: // I shouldn't have to do this the check for current node, I think.
306: // numbering\numbering24.xsl fails if I don't do this. I think
307: // it occurs as the walkers are backing up. -sb
308: else if (DTM.NULL != m_currentNode) {
309: m_currentNode = m_traverser.next(m_root, m_currentNode);
310: }
311:
312: if (DTM.NULL == m_currentNode)
313: this .m_foundLast = true;
314:
315: return m_currentNode;
316: }
317:
318: /**
319: * Moves the <code>TreeWalker</code> to the next visible node in document
320: * order relative to the current node, and returns the new node. If the
321: * current node has no next node, or if the search for nextNode attempts
322: * to step upward from the TreeWalker's root node, returns
323: * <code>null</code> , and retains the current node.
324: * @return The new node, or <code>null</code> if the current node has no
325: * next node in the TreeWalker's logical view.
326: */
327: public int nextNode() {
328: int nextNode = DTM.NULL;
329: AxesWalker walker = wi().getLastUsedWalker();
330:
331: while (true) {
332: if (null == walker)
333: break;
334:
335: nextNode = walker.getNextNode();
336:
337: if (DTM.NULL == nextNode) {
338:
339: walker = walker.m_prevWalker;
340: } else {
341: if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT) {
342: continue;
343: }
344:
345: if (null == walker.m_nextWalker) {
346: wi().setLastUsedWalker(walker);
347:
348: // return walker.returnNextNode(nextNode);
349: break;
350: } else {
351: AxesWalker prev = walker;
352:
353: walker = walker.m_nextWalker;
354:
355: walker.setRoot(nextNode);
356:
357: walker.m_prevWalker = prev;
358:
359: continue;
360: }
361: } // if(null != nextNode)
362: } // while(null != walker)
363:
364: return nextNode;
365: }
366:
367: //============= End TreeWalker Implementation =============
368:
369: /**
370: * Get the index of the last node that can be itterated to.
371: *
372: *
373: * @param xctxt XPath runtime context.
374: *
375: * @return the index of the last node that can be itterated to.
376: */
377: public int getLastPos(XPathContext xctxt) {
378:
379: int pos = getProximityPosition();
380:
381: AxesWalker walker;
382:
383: try {
384: walker = (AxesWalker) clone();
385: } catch (CloneNotSupportedException cnse) {
386: return -1;
387: }
388:
389: walker.setPredicateCount(walker.getPredicateCount() - 1);
390: walker.setNextWalker(null);
391: walker.setPrevWalker(null);
392:
393: WalkingIterator lpi = wi();
394: AxesWalker savedWalker = lpi.getLastUsedWalker();
395:
396: try {
397: lpi.setLastUsedWalker(walker);
398:
399: int next;
400:
401: while (DTM.NULL != (next = walker.nextNode())) {
402: pos++;
403: }
404:
405: // TODO: Should probably save this in the iterator.
406: } finally {
407: lpi.setLastUsedWalker(savedWalker);
408: }
409:
410: // System.out.println("pos: "+pos);
411: return pos;
412: }
413:
414: //============= State Data =============
415:
416: /**
417: * The DTM for the root. This can not be used, or must be changed,
418: * for the filter walker, or any walker that can have nodes
419: * from multiple documents.
420: * Never, ever, access this value without going through getDTM(int node).
421: */
422: private DTM m_dtm;
423:
424: /**
425: * Set the DTM for this walker.
426: *
427: * @param dtm Non-null reference to a DTM.
428: */
429: public void setDefaultDTM(DTM dtm) {
430: m_dtm = dtm;
431: }
432:
433: /**
434: * Get the DTM for this walker.
435: *
436: * @return Non-null reference to a DTM.
437: */
438: public DTM getDTM(int node) {
439: //
440: return wi().getXPathContext().getDTM(node);
441: }
442:
443: /**
444: * Returns true if all the nodes in the iteration well be returned in document
445: * order.
446: * Warning: This can only be called after setRoot has been called!
447: *
448: * @return true as a default.
449: */
450: public boolean isDocOrdered() {
451: return true;
452: }
453:
454: /**
455: * Returns the axis being iterated, if it is known.
456: *
457: * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
458: * types.
459: */
460: public int getAxis() {
461: return m_axis;
462: }
463:
464: /**
465: * This will traverse the heararchy, calling the visitor for
466: * each member. If the called visitor method returns
467: * false, the subtree should not be called.
468: *
469: * @param owner The owner of the visitor, where that path may be
470: * rewritten if needed.
471: * @param visitor The visitor whose appropriate method will be called.
472: */
473: public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) {
474: if (visitor.visitStep(owner, this )) {
475: callPredicateVisitors(visitor);
476: if (null != m_nextWalker) {
477: m_nextWalker.callVisitors(this , visitor);
478: }
479: }
480: }
481:
482: /**
483: * @see ExpressionOwner#getExpression()
484: */
485: public Expression getExpression() {
486: return m_nextWalker;
487: }
488:
489: /**
490: * @see ExpressionOwner#setExpression(Expression)
491: */
492: public void setExpression(Expression exp) {
493: exp.exprSetParent(this );
494: m_nextWalker = (AxesWalker) exp;
495: }
496:
497: /**
498: * @see Expression#deepEquals(Expression)
499: */
500: public boolean deepEquals(Expression expr) {
501: if (!super .deepEquals(expr))
502: return false;
503:
504: AxesWalker walker = (AxesWalker) expr;
505: if (this .m_axis != walker.m_axis)
506: return false;
507:
508: return true;
509: }
510:
511: /**
512: * The root node of the TreeWalker, as specified when it was created.
513: */
514: transient int m_root = DTM.NULL;
515:
516: /**
517: * The node at which the TreeWalker is currently positioned.
518: */
519: private transient int m_currentNode = DTM.NULL;
520:
521: /** True if an itteration has not begun. */
522: transient boolean m_isFresh;
523:
524: /** The next walker in the location step chain.
525: * @serial */
526: protected AxesWalker m_nextWalker;
527:
528: /** The previous walker in the location step chain, or null.
529: * @serial */
530: AxesWalker m_prevWalker;
531:
532: /** The traversal axis from where the nodes will be filtered. */
533: protected int m_axis = -1;
534:
535: /** The DTM inner traversal class, that corresponds to the super axis. */
536: protected DTMAxisTraverser m_traverser;
537: }
|