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: DescendantIterator.java,v 1.25 2005/01/23 01:02:10 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.DTMAxisTraverser;
024: import org.apache.xml.dtm.DTMFilter;
025: import org.apache.xml.dtm.DTMIterator;
026: import org.apache.xpath.Expression;
027: import org.apache.xpath.XPathContext;
028: import org.apache.xpath.compiler.Compiler;
029: import org.apache.xpath.compiler.OpCodes;
030: import org.apache.xpath.patterns.NodeTest;
031:
032: /**
033: * This class implements an optimized iterator for
034: * descendant, descendant-or-self, or "//foo" patterns.
035: * @see org.apache.xpath.axes.LocPathIterator
036: * @xsl.usage advanced
037: */
038: public class DescendantIterator extends LocPathIterator {
039: static final long serialVersionUID = -1190338607743976938L;
040:
041: /**
042: * Create a DescendantIterator object.
043: *
044: * @param compiler A reference to the Compiler that contains the op map.
045: * @param opPos The position within the op map, which contains the
046: * location path expression for this itterator.
047: *
048: * @throws javax.xml.transform.TransformerException
049: */
050: DescendantIterator(Compiler compiler, int opPos, int analysis)
051: throws javax.xml.transform.TransformerException {
052:
053: super (compiler, opPos, analysis, false);
054:
055: int firstStepPos = compiler.getFirstChildPos(opPos);
056: int stepType = compiler.getOp(firstStepPos);
057:
058: boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType);
059: boolean fromRoot = false;
060: if (OpCodes.FROM_SELF == stepType) {
061: orSelf = true;
062: // firstStepPos += 8;
063: } else if (OpCodes.FROM_ROOT == stepType) {
064: fromRoot = true;
065: // Ugly code... will go away when AST work is done.
066: int nextStepPos = compiler.getNextStepPos(firstStepPos);
067: if (compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF)
068: orSelf = true;
069: // firstStepPos += 8;
070: }
071:
072: // Find the position of the last step.
073: int nextStepPos = firstStepPos;
074: while (true) {
075: nextStepPos = compiler.getNextStepPos(nextStepPos);
076: if (nextStepPos > 0) {
077: int stepOp = compiler.getOp(nextStepPos);
078: if (OpCodes.ENDOP != stepOp)
079: firstStepPos = nextStepPos;
080: else
081: break;
082: } else
083: break;
084:
085: }
086:
087: // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336
088: if ((analysis & WalkerFactory.BIT_CHILD) != 0)
089: orSelf = false;
090:
091: if (fromRoot) {
092: if (orSelf)
093: m_axis = Axis.DESCENDANTSORSELFFROMROOT;
094: else
095: m_axis = Axis.DESCENDANTSFROMROOT;
096: } else if (orSelf)
097: m_axis = Axis.DESCENDANTORSELF;
098: else
099: m_axis = Axis.DESCENDANT;
100:
101: int whatToShow = compiler.getWhatToShow(firstStepPos);
102:
103: if ((0 == (whatToShow & (DTMFilter.SHOW_ATTRIBUTE
104: | DTMFilter.SHOW_ELEMENT | DTMFilter.SHOW_PROCESSING_INSTRUCTION)))
105: || (whatToShow == DTMFilter.SHOW_ALL))
106: initNodeTest(whatToShow);
107: else {
108: initNodeTest(whatToShow, compiler.getStepNS(firstStepPos),
109: compiler.getStepLocalName(firstStepPos));
110: }
111: initPredicateInfo(compiler, firstStepPos);
112: }
113:
114: /**
115: * Create a DescendantIterator object.
116: *
117: */
118: public DescendantIterator() {
119: super (null);
120: m_axis = Axis.DESCENDANTSORSELFFROMROOT;
121: int whatToShow = DTMFilter.SHOW_ALL;
122: initNodeTest(whatToShow);
123: }
124:
125: /**
126: * Get a cloned Iterator that is reset to the beginning
127: * of the query.
128: *
129: * @return A cloned NodeIterator set of the start of the query.
130: *
131: * @throws CloneNotSupportedException
132: */
133: public DTMIterator cloneWithReset()
134: throws CloneNotSupportedException {
135:
136: DescendantIterator clone = (DescendantIterator) super
137: .cloneWithReset();
138: clone.m_traverser = m_traverser;
139:
140: clone.resetProximityPositions();
141:
142: return clone;
143: }
144:
145: /**
146: * Returns the next node in the set and advances the position of the
147: * iterator in the set. After a NodeIterator is created, the first call
148: * to nextNode() returns the first node in the set.
149: *
150: * @return The next <code>Node</code> in the set being iterated over, or
151: * <code>null</code> if there are no more members in that set.
152: *
153: * @throws DOMException
154: * INVALID_STATE_ERR: Raised if this method is called after the
155: * <code>detach</code> method was invoked.
156: */
157: public int nextNode() {
158: if (m_foundLast)
159: return DTM.NULL;
160:
161: if (DTM.NULL == m_lastFetched) {
162: resetProximityPositions();
163: }
164:
165: int next;
166:
167: org.apache.xpath.VariableStack vars;
168: int savedStart;
169: if (-1 != m_stackFrame) {
170: vars = m_execContext.getVarStack();
171:
172: // These three statements need to be combined into one operation.
173: savedStart = vars.getStackFrame();
174:
175: vars.setStackFrame(m_stackFrame);
176: } else {
177: // Yuck. Just to shut up the compiler!
178: vars = null;
179: savedStart = 0;
180: }
181:
182: try {
183: do {
184: if (0 == m_extendedTypeID) {
185: next = m_lastFetched = (DTM.NULL == m_lastFetched) ? m_traverser
186: .first(m_context)
187: : m_traverser
188: .next(m_context, m_lastFetched);
189: } else {
190: next = m_lastFetched = (DTM.NULL == m_lastFetched) ? m_traverser
191: .first(m_context, m_extendedTypeID)
192: : m_traverser.next(m_context,
193: m_lastFetched, m_extendedTypeID);
194: }
195:
196: if (DTM.NULL != next) {
197: if (DTMIterator.FILTER_ACCEPT == acceptNode(next))
198: break;
199: else
200: continue;
201: } else
202: break;
203: } while (next != DTM.NULL);
204:
205: if (DTM.NULL != next) {
206: m_pos++;
207: return next;
208: } else {
209: m_foundLast = true;
210:
211: return DTM.NULL;
212: }
213: } finally {
214: if (-1 != m_stackFrame) {
215: // These two statements need to be combined into one operation.
216: vars.setStackFrame(savedStart);
217: }
218: }
219: }
220:
221: /**
222: * Initialize the context values for this expression
223: * after it is cloned.
224: *
225: * @param context The XPath runtime context for this
226: * transformation.
227: */
228: public void setRoot(int context, Object environment) {
229: super .setRoot(context, environment);
230: m_traverser = m_cdtm.getAxisTraverser(m_axis);
231:
232: String localName = getLocalName();
233: String namespace = getNamespace();
234: int what = m_whatToShow;
235: // System.out.println("what: ");
236: // NodeTest.debugWhatToShow(what);
237: if (DTMFilter.SHOW_ALL == what
238: || NodeTest.WILD.equals(localName)
239: || NodeTest.WILD.equals(namespace)) {
240: m_extendedTypeID = 0;
241: } else {
242: int type = getNodeTypeTest(what);
243: m_extendedTypeID = m_cdtm.getExpandedTypeID(namespace,
244: localName, type);
245: }
246:
247: }
248:
249: /**
250: * Return the first node out of the nodeset, if this expression is
251: * a nodeset expression. This is the default implementation for
252: * nodesets.
253: * <p>WARNING: Do not mutate this class from this function!</p>
254: * @param xctxt The XPath runtime context.
255: * @return the first node out of the nodeset, or DTM.NULL.
256: */
257: public int asNode(XPathContext xctxt)
258: throws javax.xml.transform.TransformerException {
259: if (getPredicateCount() > 0)
260: return super .asNode(xctxt);
261:
262: int current = xctxt.getCurrentNode();
263:
264: DTM dtm = xctxt.getDTM(current);
265: DTMAxisTraverser traverser = dtm.getAxisTraverser(m_axis);
266:
267: String localName = getLocalName();
268: String namespace = getNamespace();
269: int what = m_whatToShow;
270:
271: // System.out.print(" (DescendantIterator) ");
272:
273: // System.out.println("what: ");
274: // NodeTest.debugWhatToShow(what);
275: if (DTMFilter.SHOW_ALL == what || localName == NodeTest.WILD
276: || namespace == NodeTest.WILD) {
277: return traverser.first(current);
278: } else {
279: int type = getNodeTypeTest(what);
280: int extendedType = dtm.getExpandedTypeID(namespace,
281: localName, type);
282: return traverser.first(current, extendedType);
283: }
284: }
285:
286: /**
287: * Detaches the iterator from the set which it iterated over, releasing
288: * any computational resources and placing the iterator in the INVALID
289: * state. After<code>detach</code> has been invoked, calls to
290: * <code>nextNode</code> or<code>previousNode</code> will raise the
291: * exception INVALID_STATE_ERR.
292: */
293: public void detach() {
294: if (m_allowDetach) {
295: m_traverser = null;
296: m_extendedTypeID = 0;
297:
298: // Always call the superclass detach last!
299: super .detach();
300: }
301: }
302:
303: /**
304: * Returns the axis being iterated, if it is known.
305: *
306: * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
307: * types.
308: */
309: public int getAxis() {
310: return m_axis;
311: }
312:
313: /** The traverser to use to navigate over the descendants. */
314: transient protected DTMAxisTraverser m_traverser;
315:
316: /** The axis that we are traversing. */
317: protected int m_axis;
318:
319: /** The extended type ID, not set until setRoot. */
320: protected int m_extendedTypeID;
321:
322: /**
323: * @see Expression#deepEquals(Expression)
324: */
325: public boolean deepEquals(Expression expr) {
326: if (!super .deepEquals(expr))
327: return false;
328:
329: if (m_axis != ((DescendantIterator) expr).m_axis)
330: return false;
331:
332: return true;
333: }
334:
335: }
|