code: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the  "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id$
   */
  package org.apache.xpath.axes;
  
  import org.apache.xml.dtm.DTM;
  import org.apache.xml.utils.PrefixResolver;
  import org.apache.xpath.Expression;
  import org.apache.xpath.ExpressionOwner;
  import org.apache.xpath.VariableStack;
  import org.apache.xpath.XPathVisitor;
  import org.apache.xpath.compiler.Compiler;
  import org.apache.xpath.compiler.OpMap;
  
  /**
   * Location path iterator that uses Walkers.
   */
  
  public class WalkingIterator extends LocPathIterator implements ExpressionOwner
  {
      static final long serialVersionUID = 9110225941815665906L;
    /**
     * Create a WalkingIterator iterator, including creation
     * of step walkers from the opcode list, and call back
     * into the Compiler to create predicate expressions.
     *
     * @param compiler The Compiler which is creating
     * this expression.
     * @param opPos The position of this iterator in the
     * opcode list from the compiler.
     * @param shouldLoadWalkers True if walkers should be
     * loaded, or false if this is a derived iterator and
     * it doesn't wish to load child walkers.
     *
     * @throws javax.xml.transform.TransformerException
     */
    WalkingIterator(
            Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
              throws javax.xml.transform.TransformerException
    {
      super(compiler, opPos, analysis, shouldLoadWalkers);
      
      int firstStepPos = OpMap.getFirstChildPos(opPos);
  
      if (shouldLoadWalkers)
      {
        m_firstWalker = WalkerFactory.loadWalkers(this, compiler, firstStepPos, 0);
        m_lastUsedWalker = m_firstWalker;
      }
    }
    
    /**
     * Create a WalkingIterator object.
     *
     * @param nscontext The namespace context for this iterator,
     * should be OK if null.
     */
    public WalkingIterator(PrefixResolver nscontext)
    {
  
      super(nscontext);
    }
    
    
    /** 
     * Get the analysis bits for this walker, as defined in the WalkerFactory.
     * @return One of WalkerFactory#BIT_DESCENDANT, etc.
     */
    public int getAnalysisBits()
    {
      int bits = 0;
      if (null != m_firstWalker)
      {    	
        AxesWalker walker = m_firstWalker;
  
        while (null != walker)
        {
          int bit = walker.getAnalysisBits();
          bits |= bit;
          walker = walker.getNextWalker();
        }       
      }
      return bits;
    }
    
    /**
     * Get a cloned WalkingIterator that holds the same
     * position as this iterator.
     *
     * @return A clone of this iterator that holds the same node position.
     *
     * @throws CloneNotSupportedException
     */
    public Object clone() throws CloneNotSupportedException
    {
  
      WalkingIterator clone = (WalkingIterator) super.clone();
  
      //    clone.m_varStackPos = this.m_varStackPos;
      //    clone.m_varStackContext = this.m_varStackContext;
      if (null != m_firstWalker)
      {
        clone.m_firstWalker = m_firstWalker.cloneDeep(clone, null);
      }
  
      return clone;
    }
    
    /**
     * Reset the iterator.
     */
    public void reset()
    {
  
      super.reset();
  
      if (null != m_firstWalker)
      {
        m_lastUsedWalker = m_firstWalker;
  
        m_firstWalker.setRoot(m_context);
      }
  
    }
    
    /**
     * Initialize the context values for this expression
     * after it is cloned.
     *
     * @param context The XPath runtime context for this
     * transformation.
     */
    public void setRoot(int context, Object environment)
    {
  
      super.setRoot(context, environment);
      
      if(null != m_firstWalker)
      {
        m_firstWalker.setRoot(context);
        m_lastUsedWalker = m_firstWalker;
      }
    }
    
    /**
     *  Returns the next node in the set and advances the position of the
     * iterator in the set. After a NodeIterator is created, the first call
     * to nextNode() returns the first node in the set.
     * @return  The next <code>Node</code> in the set being iterated over, or
     *   <code>null</code> if there are no more members in that set.
     */
    public int nextNode()
    {
    	if(m_foundLast)
    		return DTM.NULL;
  
      // If the variable stack position is not -1, we'll have to 
      // set our position in the variable stack, so our variable access 
      // will be correct.  Iterators that are at the top level of the 
      // expression need to reset the variable stack, while iterators 
      // in predicates do not need to, and should not, since their execution
      // may be much later than top-level iterators.  
      // m_varStackPos is set in setRoot, which is called 
      // from the execute method.
      if (-1 == m_stackFrame)
      {
        return returnNextNode(m_firstWalker.nextNode());
      }
      else
      {
        VariableStack vars = m_execContext.getVarStack();
  
        // These three statements need to be combined into one operation.
        int savedStart = vars.getStackFrame();
  
        vars.setStackFrame(m_stackFrame);
  
        int n = returnNextNode(m_firstWalker.nextNode());
  
        // These two statements need to be combined into one operation.
        vars.setStackFrame(savedStart);
  
        return n;
      }
    }
  
    
    /**
     * Get the head of the walker list.
     *
     * @return The head of the walker list, or null
     * if this iterator does not implement walkers.
     * @xsl.usage advanced
     */
    public final AxesWalker getFirstWalker()
    {
      return m_firstWalker;
    }
    
    /**
     * Set the head of the walker list.
     * 
     * @param walker Should be a valid AxesWalker.
     * @xsl.usage advanced
     */
    public final void setFirstWalker(AxesWalker walker)
    {
      m_firstWalker = walker;
    }
  
  
    /**
     * Set the last used walker.
     *
     * @param walker The last used walker, or null.
     * @xsl.usage advanced
     */
    public final void setLastUsedWalker(AxesWalker walker)
    {
      m_lastUsedWalker = walker;
    }
  
    /**
     * Get the last used walker.
     *
     * @return The last used walker, or null.
     * @xsl.usage advanced
     */
    public final AxesWalker getLastUsedWalker()
    {
      return m_lastUsedWalker;
    }
    
    /**
     *  Detaches the iterator from the set which it iterated over, releasing
     * any computational resources and placing the iterator in the INVALID
     * state. After<code>detach</code> has been invoked, calls to
     * <code>nextNode</code> or<code>previousNode</code> will raise the
     * exception INVALID_STATE_ERR.
     */
    public void detach()
    {   
      if(m_allowDetach)
      {
  	  	AxesWalker walker = m_firstWalker; 
  	    while (null != walker)
  	    {
  	      walker.detach();
  	      walker = walker.getNextWalker();
  	    }
  	
  	    m_lastUsedWalker = null;
  	    
  	    // Always call the superclass detach last!
  	    super.detach();
      }
    }
    
    /**
     * This function is used to fixup variables from QNames to stack frame 
     * indexes at stylesheet build time.
     * @param vars List of QNames that correspond to variables.  This list 
     * should be searched backwards for the first qualified name that 
     * corresponds to the variable reference qname.  The position of the 
     * QName in the vector from the start of the vector will be its position 
     * in the stack frame (but variables above the globalsTop value will need 
     * to be offset to the current stack frame).
     */
    public void fixupVariables(java.util.Vector vars, int globalsSize)
    {
      m_predicateIndex = -1;
  
      AxesWalker walker = m_firstWalker;
  
      while (null != walker)
      {
        walker.fixupVariables(vars, globalsSize);
        walker = walker.getNextWalker();
      }
    }
    
    /**
     * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
     */
    public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
    {
    	 	if(visitor.visitLocationPath(owner, this))
    	 	{
    	 		if(null != m_firstWalker)
    	 		{
    	 			m_firstWalker.callVisitors(this, visitor);
    	 		}
    	 	}
    }
  
    
    /** The last used step walker in the walker list.
     *  @serial */
    protected AxesWalker m_lastUsedWalker;
  
    /** The head of the step walker list.
     *  @serial */
    protected AxesWalker m_firstWalker;
  
    /**
     * @see ExpressionOwner#getExpression()
     */
    public Expression getExpression()
    {
      return m_firstWalker;
    }
  
    /**
     * @see ExpressionOwner#setExpression(Expression)
     */
    public void setExpression(Expression exp)
    {
    	exp.exprSetParent(this);
    	m_firstWalker = (AxesWalker)exp;
    }
    
      /**
       * @see Expression#deepEquals(Expression)
       */
      public boolean deepEquals(Expression expr)
      {
        if (!super.deepEquals(expr))
                  return false;
  
        AxesWalker walker1 = m_firstWalker;
        AxesWalker walker2 = ((WalkingIterator)expr).m_firstWalker;
        while ((null != walker1) && (null != walker2))
        {
          if(!walker1.deepEquals(walker2))
          	return false;
          walker1 = walker1.getNextWalker();
          walker2 = walker2.getNextWalker();
        }
        
        if((null != walker1) || (null != walker2))
        	return false;
  
        return true;
      }
  
  }
  