blob: 2f1b13b08f3c3371fe009adf1fe8df76565a5420 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xpath.axes;
import java.util.Vector;
import javax.xml.transform.TransformerException;
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;
/**
* Location path iterator that uses Walkers.
*/
public class WalkingIterator extends LocPathIterator implements ExpressionOwner
{
/**
* 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 = compiler.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 execContext 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;
}
}
/**
* <meta name="usage" content="advanced"/>
* Get the head of the walker list.
*
* @return The head of the walker list, or null
* if this iterator does not implement walkers.
*/
public final AxesWalker getFirstWalker()
{
return m_firstWalker;
}
/**
* <meta name="usage" content="advanced"/>
* Set the head of the walker list.
*
* @param walker Should be a valid AxesWalker.
*/
public final void setFirstWalker(AxesWalker walker)
{
m_firstWalker = walker;
}
/**
* <meta name="usage" content="advanced"/>
* Set the last used walker.
*
* @param walker The last used walker, or null.
*/
public final void setLastUsedWalker(AxesWalker walker)
{
m_lastUsedWalker = walker;
}
/**
* <meta name="usage" content="advanced"/>
* Get the last used walker.
*
* @return The last used walker, or null.
*/
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 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;
}
}