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: WalkingIterator.java 469314 2006-10-30 23:31:59Z minchau $
20 */
21package org.apache.xpath.axes;
22
23import org.apache.xml.dtm.DTM;
24import org.apache.xml.utils.PrefixResolver;
25import org.apache.xpath.Expression;
26import org.apache.xpath.ExpressionOwner;
27import org.apache.xpath.VariableStack;
28import org.apache.xpath.XPathVisitor;
29import org.apache.xpath.compiler.Compiler;
30import org.apache.xpath.compiler.OpMap;
31
32/**
33 * Location path iterator that uses Walkers.
34 */
35
36public class WalkingIterator extends LocPathIterator implements ExpressionOwner
37{
38    static final long serialVersionUID = 9110225941815665906L;
39  /**
40   * Create a WalkingIterator iterator, including creation
41   * of step walkers from the opcode list, and call back
42   * into the Compiler to create predicate expressions.
43   *
44   * @param compiler The Compiler which is creating
45   * this expression.
46   * @param opPos The position of this iterator in the
47   * opcode list from the compiler.
48   * @param shouldLoadWalkers True if walkers should be
49   * loaded, or false if this is a derived iterator and
50   * it doesn't wish to load child walkers.
51   *
52   * @throws javax.xml.transform.TransformerException
53   */
54  WalkingIterator(
55          Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
56            throws javax.xml.transform.TransformerException
57  {
58    super(compiler, opPos, analysis, shouldLoadWalkers);
59
60    int firstStepPos = OpMap.getFirstChildPos(opPos);
61
62    if (shouldLoadWalkers)
63    {
64      m_firstWalker = WalkerFactory.loadWalkers(this, compiler, firstStepPos, 0);
65      m_lastUsedWalker = m_firstWalker;
66    }
67  }
68
69  /**
70   * Create a WalkingIterator object.
71   *
72   * @param nscontext The namespace context for this iterator,
73   * should be OK if null.
74   */
75  public WalkingIterator(PrefixResolver nscontext)
76  {
77
78    super(nscontext);
79  }
80
81
82  /**
83   * Get the analysis bits for this walker, as defined in the WalkerFactory.
84   * @return One of WalkerFactory#BIT_DESCENDANT, etc.
85   */
86  public int getAnalysisBits()
87  {
88    int bits = 0;
89    if (null != m_firstWalker)
90    {
91      AxesWalker walker = m_firstWalker;
92
93      while (null != walker)
94      {
95        int bit = walker.getAnalysisBits();
96        bits |= bit;
97        walker = walker.getNextWalker();
98      }
99    }
100    return bits;
101  }
102
103  /**
104   * Get a cloned WalkingIterator that holds the same
105   * position as this iterator.
106   *
107   * @return A clone of this iterator that holds the same node position.
108   *
109   * @throws CloneNotSupportedException
110   */
111  public Object clone() throws CloneNotSupportedException
112  {
113
114    WalkingIterator clone = (WalkingIterator) super.clone();
115
116    //    clone.m_varStackPos = this.m_varStackPos;
117    //    clone.m_varStackContext = this.m_varStackContext;
118    if (null != m_firstWalker)
119    {
120      clone.m_firstWalker = m_firstWalker.cloneDeep(clone, null);
121    }
122
123    return clone;
124  }
125
126  /**
127   * Reset the iterator.
128   */
129  public void reset()
130  {
131
132    super.reset();
133
134    if (null != m_firstWalker)
135    {
136      m_lastUsedWalker = m_firstWalker;
137
138      m_firstWalker.setRoot(m_context);
139    }
140
141  }
142
143  /**
144   * Initialize the context values for this expression
145   * after it is cloned.
146   *
147   * @param context The XPath runtime context for this
148   * transformation.
149   */
150  public void setRoot(int context, Object environment)
151  {
152
153    super.setRoot(context, environment);
154
155    if(null != m_firstWalker)
156    {
157      m_firstWalker.setRoot(context);
158      m_lastUsedWalker = m_firstWalker;
159    }
160  }
161
162  /**
163   *  Returns the next node in the set and advances the position of the
164   * iterator in the set. After a NodeIterator is created, the first call
165   * to nextNode() returns the first node in the set.
166   * @return  The next <code>Node</code> in the set being iterated over, or
167   *   <code>null</code> if there are no more members in that set.
168   */
169  public int nextNode()
170  {
171  	if(m_foundLast)
172  		return DTM.NULL;
173
174    // If the variable stack position is not -1, we'll have to
175    // set our position in the variable stack, so our variable access
176    // will be correct.  Iterators that are at the top level of the
177    // expression need to reset the variable stack, while iterators
178    // in predicates do not need to, and should not, since their execution
179    // may be much later than top-level iterators.
180    // m_varStackPos is set in setRoot, which is called
181    // from the execute method.
182    if (-1 == m_stackFrame)
183    {
184      return returnNextNode(m_firstWalker.nextNode());
185    }
186    else
187    {
188      VariableStack vars = m_execContext.getVarStack();
189
190      // These three statements need to be combined into one operation.
191      int savedStart = vars.getStackFrame();
192
193      vars.setStackFrame(m_stackFrame);
194
195      int n = returnNextNode(m_firstWalker.nextNode());
196
197      // These two statements need to be combined into one operation.
198      vars.setStackFrame(savedStart);
199
200      return n;
201    }
202  }
203
204
205  /**
206   * Get the head of the walker list.
207   *
208   * @return The head of the walker list, or null
209   * if this iterator does not implement walkers.
210   * @xsl.usage advanced
211   */
212  public final AxesWalker getFirstWalker()
213  {
214    return m_firstWalker;
215  }
216
217  /**
218   * Set the head of the walker list.
219   *
220   * @param walker Should be a valid AxesWalker.
221   * @xsl.usage advanced
222   */
223  public final void setFirstWalker(AxesWalker walker)
224  {
225    m_firstWalker = walker;
226  }
227
228
229  /**
230   * Set the last used walker.
231   *
232   * @param walker The last used walker, or null.
233   * @xsl.usage advanced
234   */
235  public final void setLastUsedWalker(AxesWalker walker)
236  {
237    m_lastUsedWalker = walker;
238  }
239
240  /**
241   * Get the last used walker.
242   *
243   * @return The last used walker, or null.
244   * @xsl.usage advanced
245   */
246  public final AxesWalker getLastUsedWalker()
247  {
248    return m_lastUsedWalker;
249  }
250
251  /**
252   *  Detaches the iterator from the set which it iterated over, releasing
253   * any computational resources and placing the iterator in the INVALID
254   * state. After<code>detach</code> has been invoked, calls to
255   * <code>nextNode</code> or<code>previousNode</code> will raise the
256   * exception INVALID_STATE_ERR.
257   */
258  public void detach()
259  {
260    if(m_allowDetach)
261    {
262	  	AxesWalker walker = m_firstWalker;
263	    while (null != walker)
264	    {
265	      walker.detach();
266	      walker = walker.getNextWalker();
267	    }
268
269	    m_lastUsedWalker = null;
270
271	    // Always call the superclass detach last!
272	    super.detach();
273    }
274  }
275
276  /**
277   * This function is used to fixup variables from QNames to stack frame
278   * indexes at stylesheet build time.
279   * @param vars List of QNames that correspond to variables.  This list
280   * should be searched backwards for the first qualified name that
281   * corresponds to the variable reference qname.  The position of the
282   * QName in the vector from the start of the vector will be its position
283   * in the stack frame (but variables above the globalsTop value will need
284   * to be offset to the current stack frame).
285   */
286  public void fixupVariables(java.util.Vector vars, int globalsSize)
287  {
288    m_predicateIndex = -1;
289
290    AxesWalker walker = m_firstWalker;
291
292    while (null != walker)
293    {
294      walker.fixupVariables(vars, globalsSize);
295      walker = walker.getNextWalker();
296    }
297  }
298
299  /**
300   * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
301   */
302  public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
303  {
304  	 	if(visitor.visitLocationPath(owner, this))
305  	 	{
306  	 		if(null != m_firstWalker)
307  	 		{
308  	 			m_firstWalker.callVisitors(this, visitor);
309  	 		}
310  	 	}
311  }
312
313
314  /** The last used step walker in the walker list.
315   *  @serial */
316  protected AxesWalker m_lastUsedWalker;
317
318  /** The head of the step walker list.
319   *  @serial */
320  protected AxesWalker m_firstWalker;
321
322  /**
323   * @see ExpressionOwner#getExpression()
324   */
325  public Expression getExpression()
326  {
327    return m_firstWalker;
328  }
329
330  /**
331   * @see ExpressionOwner#setExpression(Expression)
332   */
333  public void setExpression(Expression exp)
334  {
335  	exp.exprSetParent(this);
336  	m_firstWalker = (AxesWalker)exp;
337  }
338
339    /**
340     * @see Expression#deepEquals(Expression)
341     */
342    public boolean deepEquals(Expression expr)
343    {
344      if (!super.deepEquals(expr))
345                return false;
346
347      AxesWalker walker1 = m_firstWalker;
348      AxesWalker walker2 = ((WalkingIterator)expr).m_firstWalker;
349      while ((null != walker1) && (null != walker2))
350      {
351        if(!walker1.deepEquals(walker2))
352        	return false;
353        walker1 = walker1.getNextWalker();
354        walker2 = walker2.getNextWalker();
355      }
356
357      if((null != walker1) || (null != walker2))
358      	return false;
359
360      return true;
361    }
362
363}
364