1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: DescendantIterator.java 469314 2006-10-30 23:31:59Z minchau $
20 */
21package org.apache.xpath.axes;
22
23import org.apache.xml.dtm.Axis;
24import org.apache.xml.dtm.DTM;
25import org.apache.xml.dtm.DTMAxisTraverser;
26import org.apache.xml.dtm.DTMFilter;
27import org.apache.xml.dtm.DTMIterator;
28import org.apache.xpath.Expression;
29import org.apache.xpath.XPathContext;
30import org.apache.xpath.compiler.Compiler;
31import org.apache.xpath.compiler.OpCodes;
32import org.apache.xpath.compiler.OpMap;
33import org.apache.xpath.patterns.NodeTest;
34
35/**
36 * This class implements an optimized iterator for
37 * descendant, descendant-or-self, or "//foo" patterns.
38 * @see org.apache.xpath.axes.LocPathIterator
39 * @xsl.usage advanced
40 */
41public class DescendantIterator extends LocPathIterator
42{
43    static final long serialVersionUID = -1190338607743976938L;
44  /**
45   * Create a DescendantIterator object.
46   *
47   * @param compiler A reference to the Compiler that contains the op map.
48   * @param opPos The position within the op map, which contains the
49   * location path expression for this itterator.
50   *
51   * @throws javax.xml.transform.TransformerException
52   */
53  DescendantIterator(Compiler compiler, int opPos, int analysis)
54          throws javax.xml.transform.TransformerException
55  {
56
57    super(compiler, opPos, analysis, false);
58
59    int firstStepPos = OpMap.getFirstChildPos(opPos);
60    int stepType = compiler.getOp(firstStepPos);
61
62    boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType);
63    boolean fromRoot = false;
64    if (OpCodes.FROM_SELF == stepType)
65    {
66      orSelf = true;
67      // firstStepPos += 8;
68    }
69    else if(OpCodes.FROM_ROOT == stepType)
70    {
71      fromRoot = true;
72      // Ugly code... will go away when AST work is done.
73      int nextStepPos = compiler.getNextStepPos(firstStepPos);
74      if(compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF)
75        orSelf = true;
76      // firstStepPos += 8;
77    }
78
79    // Find the position of the last step.
80    int nextStepPos = firstStepPos;
81    while(true)
82    {
83      nextStepPos = compiler.getNextStepPos(nextStepPos);
84      if(nextStepPos > 0)
85      {
86        int stepOp = compiler.getOp(nextStepPos);
87        if(OpCodes.ENDOP != stepOp)
88          firstStepPos = nextStepPos;
89        else
90          break;
91      }
92      else
93        break;
94
95    }
96
97    // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336
98    if((analysis & WalkerFactory.BIT_CHILD) != 0)
99      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}
381