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