blob: dc2d608d7315b3bbf8041c9cfaa9e7c253cc4bfc [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 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;
// Java library imports
import java.util.Vector;
import java.util.Stack;
// DOM imports
//import org.w3c.dom.traversal.NodeIterator;
//import org.w3c.dom.traversal.TreeWalker;
//import org.w3c.dom.traversal.NodeFilter;
//import org.w3c.dom.Node;
//import org.w3c.dom.NamedNodeMap;
//import org.w3c.dom.DOMException;
//import org.w3c.dom.NodeList;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.dtm.DTMFilter;
import org.apache.xml.dtm.DTMManager;
// Xalan imports
import org.apache.xpath.res.XPATHErrorResources;
import org.apache.xpath.XPath;
import org.apache.xpath.compiler.OpMap;
import org.apache.xpath.compiler.Compiler;
import org.apache.xpath.DOMHelper;
import org.apache.xpath.compiler.OpCodes;
import org.apache.xpath.compiler.PsuedoNames;
import org.apache.xpath.NodeSet;
import org.apache.xpath.Expression;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XObject;
import org.apache.xml.utils.IntStack;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xml.utils.ObjectPool;
import org.apache.xpath.objects.XNodeSet;
import org.apache.xpath.axes.AxesWalker;
import org.apache.xpath.VariableStack;
/**
* <meta name="usage" content="advanced"/>
* This class extends NodeSet, which implements NodeIterator,
* and fetches nodes one at a time in document order based on a XPath
* <a href="http://www.w3.org/TR/xpath#NT-LocationPath>LocationPath</a>.
*
* <p>If setShouldCacheNodes(true) is called,
* as each node is iterated via nextNode(), the node is also stored
* in the NodeVector, so that previousNode() can easily be done, except in
* the case where the LocPathIterator is "owned" by a UnionPathIterator,
* in which case the UnionPathIterator will cache the nodes.</p>
*/
public class LocPathIterator extends PredicatedNodeTest
implements Cloneable, DTMIterator, java.io.Serializable
{
/**
* Get the waiting walker at the given index.
*
*
* @param i The walker index.
*
* @return non-null reference to an AxesWalker.
*/
AxesWalker getWaiting(int i)
{
return (AxesWalker) m_waiting.elementAt(i);
}
/**
* Get the number of waiters waiting in the current expression execution.
* Note that this may not be the same as the total number of waiters in
* the waiting list.
*
*
* @return the number of waiters waiting in the current expression execution.
*/
int getWaitingCount()
{
if(null == m_waiting)
return 0;
else
return m_waiting.size() - m_waitingBottom;
}
/**
* Create a LocPathIterator object.
*
* @param nscontext The namespace context for this iterator,
* should be OK if null.
*/
public LocPathIterator(PrefixResolver nscontext)
{
setLocPathIterator(this);
this.m_prefixResolver = nscontext;
}
/**
* Create a LocPathIterator object, 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.
*
* @throws javax.xml.transform.TransformerException
*/
public LocPathIterator(Compiler compiler, int opPos, int analysis)
throws javax.xml.transform.TransformerException
{
this(compiler, opPos, analysis, true);
}
/**
* Create a LocPathIterator object, 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
*/
public LocPathIterator(
Compiler compiler, int opPos, int analysis, boolean shouldLoadWalkers)
throws javax.xml.transform.TransformerException
{
m_analysis = analysis;
setLocPathIterator(this);
int firstStepPos = compiler.getFirstChildPos(opPos);
if (shouldLoadWalkers)
{
m_firstWalker = WalkerFactory.loadWalkers(this, compiler, firstStepPos,
0);
m_lastUsedWalker = m_firstWalker;
}
}
/**
* Set the environment in which this iterator operates, which should provide:
* a node (the context node... same value as "root" defined below)
* a pair of non-zero positive integers (the context position and the context size)
* a set of variable bindings
* a function library
* the set of namespace declarations in scope for the expression.
*
* <p>At this time the exact implementation of this environment is application
* dependent. Probably a proper interface will be created fairly soon.</p>
*
* @param environment The environment object.
*/
public void setEnvironment(Object environment)
{
// no-op for now.
}
/**
* Get an instance of a DTM that "owns" a node handle. Since a node
* iterator may be passed without a DTMManager, this allows the
* caller to easily get the DTM using just the iterator.
*
* @param nodeHandle the nodeHandle.
*
* @return a non-null DTM reference.
*/
public DTM getDTM(int nodeHandle)
{
// %OPT%
return m_execContext.getDTM(nodeHandle);
}
/**
* Get an instance of the DTMManager. Since a node
* iterator may be passed without a DTMManager, this allows the
* caller to easily get the DTMManager using just the iterator.
*
* @return a non-null DTMManager reference.
*/
public DTMManager getDTMManager()
{
return m_execContext.getDTMManager();
}
/**
* Execute this iterator, meaning create a clone that can
* store state, and initialize it for fast execution from
* the current runtime state. When this is called, no actual
* query from the current context node is performed.
*
* @param xctxt The XPath execution context.
*
* @return An XNodeSet reference that holds this iterator.
*
* @throws javax.xml.transform.TransformerException
*/
public XObject execute(XPathContext xctxt)
throws javax.xml.transform.TransformerException
{
try
{
// LocPathIterator clone = (LocPathIterator) m_pool.getInstanceIfFree();
// if (null == clone)
LocPathIterator clone = (LocPathIterator) this.clone();
clone.initContext(xctxt);
return new XNodeSet(clone);
}
catch (CloneNotSupportedException ncse)
{
throw new javax.xml.transform.TransformerException(ncse);
}
}
/**
* <meta name="usage" content="advanced"/>
* Set if this is an iterator at the upper level of
* the XPath.
*
* @param b true if this location path is at the top level of the
* expression.
*/
public void setIsTopLevel(boolean b)
{
m_isTopLevel = b;
}
/**
* <meta name="usage" content="advanced"/>
* Get if this is an iterator at the upper level of
* the XPath.
*
* @return true if this location path is at the top level of the
* expression.
*/
public boolean getIsTopLevel()
{
return m_isTopLevel;
}
/**
* Initialize the context values for this expression
* after it is cloned.
*
* @param execContext The XPath runtime context for this
* transformation.
*/
public void initContext(XPathContext execContext)
{
this.m_context = execContext.getCurrentNode();
m_cdtm = execContext.getDTM(m_context);
this.m_currentContextNode = execContext.getCurrentExpressionNode();
this.m_execContext = execContext;
this.m_prefixResolver = execContext.getNamespaceContext();
this.m_dhelper = execContext.getDOMHelper();
if (m_isTopLevel)
{
VariableStack vars = execContext.getVarStack();
this.m_varStackPos = vars.getSearchStartOrTop();
this.m_varStackContext = vars.getContextPos();
}
}
/**
* Initialize the context values for this expression
* after it is cloned.
*
* @param execContext The XPath runtime context for this
* transformation.
*/
public void initContext(XPathContext execContext, int context)
{
this.m_context = context;
m_cdtm = execContext.getDTM(m_context);
this.m_currentContextNode = context;
this.m_execContext = execContext;
this.m_prefixResolver = execContext.getNamespaceContext();
this.m_dhelper = execContext.getDOMHelper();
if (m_isTopLevel)
{
VariableStack vars = execContext.getVarStack();
this.m_varStackPos = vars.getSearchStartOrTop();
this.m_varStackContext = vars.getContextPos();
}
}
/**
* Set the next position index of this iterator.
*
* @param next A value greater than or equal to zero that indicates the next
* node position to fetch.
*/
protected void setNextPosition(int next)
{
m_next = next;
}
/**
* Get the current position, which is one less than
* the next nextNode() call will retrieve. i.e. if
* you call getCurrentPos() and the return is 0, the next
* fetch will take place at index 1.
*
* @return A value greater than or equal to zero that indicates the next
* node position to fetch.
*/
public final int getCurrentPos()
{
return m_next;
}
/**
* Add one to the current node index.
*/
void incrementNextPosition()
{
m_next++;
}
/**
* If setShouldCacheNodes(true) is called, then nodes will
* be cached. They are not cached by default.
*
* @param b True if this iterator should cache nodes.
*/
public void setShouldCacheNodes(boolean b)
{
if (b)
m_cachedNodes = new NodeSet();
else
m_cachedNodes = null;
}
/**
* Tells if this iterator can have nodes added to it or set via
* the <code>setItem(int node, int index)</code> method.
*
* @return True if the nodelist can be mutated.
*/
public boolean isMutable()
{
return (m_cachedNodes != null);
}
/**
* Get cached nodes.
*
* @return Cached nodes.
*/
public NodeSet getCachedNodes()
{
return m_cachedNodes;
}
/**
* Set the current position in the node set.
*
* @param i Must be a valid index greater
* than or equal to zero and less than m_cachedNodes.size().
*/
public void setCurrentPos(int i)
{
// System.out.println("setCurrentPos: "+i);
if (null == m_cachedNodes)
throw new RuntimeException(
"This NodeSet can not do indexing or counting functions!");
setNextPosition(i);
m_cachedNodes.setCurrentPos(i);
// throw new RuntimeException("Who's resetting this thing?");
}
/**
* Get the length of the cached nodes.
*
* <p>Note: for the moment at least, this only returns
* the size of the nodes that have been fetched to date,
* it doesn't attempt to run to the end to make sure we
* have found everything. This should be reviewed.</p>
*
* @return The size of the current cache list.
*/
public int size()
{
if (null == m_cachedNodes)
return 0;
return m_cachedNodes.size();
}
/**
* Returns the <code>index</code> th item in the collection. If
* <code>index</code> is greater than or equal to the number of nodes in
* the list, this returns <code>null</code> .
* @param index Index into the collection.
* @return The node at the <code>index</code> th position in the
* <code>NodeList</code> , or <code>null</code> if that is not a valid
* index.
*/
public int item(int index)
{
// resetToCachedList();
return m_cachedNodes.item(index);
}
/**
* Sets the node at the specified index of this vector to be the
* specified node. The previous component at that position is discarded.
*
* <p>The index must be a value greater than or equal to 0 and less
* than the current size of the vector.
* The iterator must be in cached mode.</p>
*
* <p>Meant to be used for sorted iterators.</p>
*
* @param node Node to set
* @param index Index of where to set the node
*/
public void setItem(int node, int index)
{
m_cachedNodes.setElementAt(node, index);
}
/**
* The number of nodes in the list. The range of valid child node indices
* is 0 to <code>length-1</code> inclusive.
*
* @return The number of nodes in the list, always greater or equal to zero.
*/
public int getLength()
{
// resetToCachedList();
if(m_last > 0)
return m_last;
else if(null == m_cachedNodes || !m_foundLast)
{
m_last = getLastPos(m_execContext);
}
else
{
m_last = m_cachedNodes.getLength();
}
return m_last;
}
/**
* In order to implement NodeList (for extensions), try to reset
* to a cached list for random access.
*/
private void resetToCachedList()
{
// %REVIEW% ? This doesn't seem to work so well...
int pos = this.getCurrentPos();
if ((null == m_cachedNodes) || (pos != 0))
this.setShouldCacheNodes(true);
runTo(-1);
this.setCurrentPos(pos);
}
/**
* Tells if this NodeSet is "fresh", in other words, if
* the first nextNode() that is called will return the
* first node in the set.
*
* @return true of nextNode has not been called.
*/
public boolean isFresh()
{
return (m_next == 0);
}
/**
* Returns the previous node in the set and moves the position of the
* iterator backwards in the set.
* @return The previous <code>Node</code> in the set being iterated over,
* or<code>null</code> if there are no more members in that set.
*/
public int previousNode()
{
if (null == m_cachedNodes)
throw new RuntimeException(
"This NodeSet can not iterate to a previous node!");
return m_cachedNodes.previousNode();
}
/**
* This attribute determines which node types are presented via the
* iterator. The available set of constants is defined in the
* <code>NodeFilter</code> interface.
*
* <p>This is somewhat useless at this time, since it doesn't
* really return information that tells what this iterator will
* show. It is here only to fullfill the DOM NodeIterator
* interface.</p>
*
* @return For now, always NodeFilter.SHOW_ALL & ~NodeFilter.SHOW_ENTITY_REFERENCE.
* @see org.w3c.dom.traversal.NodeIterator
*/
public int getWhatToShow()
{
// TODO: ??
return DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE;
}
/**
* The filter used to screen nodes. Not used at this time,
* this is here only to fullfill the DOM NodeIterator
* interface.
*
* @return Always null.
* @see org.w3c.dom.traversal.NodeIterator
*/
public DTMFilter getFilter()
{
return null;
}
/**
* The root node of the Iterator, as specified when it was created.
*
* @return The "root" of this iterator, which, in XPath terms,
* is the node context for this iterator.
*/
public int getRoot()
{
return m_context;
}
/**
* The value of this flag determines whether the children of entity
* reference nodes are visible to the iterator. If false, they will be
* skipped over.
* <br> To produce a view of the document that has entity references
* expanded and does not expose the entity reference node itself, use the
* whatToShow flags to hide the entity reference node and set
* expandEntityReferences to true when creating the iterator. To produce
* a view of the document that has entity reference nodes but no entity
* expansion, use the whatToShow flags to show the entity reference node
* and set expandEntityReferences to false.
*
* @return Always true, since entity reference nodes are not
* visible in the XPath model.
*/
public boolean getExpandEntityReferences()
{
return true;
}
/**
* 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()
{
this.m_context = DTM.NULL;
m_cdtm = null;
this.m_execContext = null;
this.m_prefixResolver = null;
this.m_dhelper = null;
this.m_varStackPos = -1;
this.m_varStackContext = 0;
// m_pool.freeInstance(this);
}
/**
* Get a cloned Iterator that is reset to the beginning
* of the query.
*
* @return A cloned NodeIterator set of the start of the query.
*
* @throws CloneNotSupportedException
*/
public DTMIterator cloneWithReset() throws CloneNotSupportedException
{
LocPathIterator clone = (LocPathIterator) clone();
clone.reset();
return clone;
}
/**
* Get a cloned LocPathIterator 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
{
LocPathIterator clone = (LocPathIterator) super.clone();
// clone.m_varStackPos = this.m_varStackPos;
// clone.m_varStackContext = this.m_varStackContext;
if (null != m_firstWalker)
{
// If we have waiting walkers, we have to check for duplicates.
Vector clones = (null != m_waiting) ? new Vector() : null;
clone.m_firstWalker = m_firstWalker.cloneDeep(clone, clones);
if (null != m_waiting)
{
clone.m_waiting = (Vector) m_waiting.clone(); // or is new Vector faster?
int n = m_waiting.size();
for (int i = 0; i < n; i++)
{
AxesWalker waiting = (AxesWalker) m_waiting.elementAt(i);
clone.m_waiting.setElementAt(waiting.cloneDeep(clone, clones), i);
}
}
}
return clone;
}
/**
* Reset the iterator.
*/
public void reset()
{
// super.reset();
m_foundLast = false;
m_lastFetched = DTM.NULL;
m_next = 0;
m_last = 0;
m_waitingBottom = 0;
if (null != m_firstWalker)
{
m_lastUsedWalker = m_firstWalker;
m_firstWalker.setRoot(m_context);
if(null != m_waiting)
m_waiting.removeAllElements();
}
}
/**
* 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 the cache is on, and the node has already been found, then
// just return from the list.
if ((null != m_cachedNodes)
&& (m_next < m_cachedNodes.size()))
{
int next = m_cachedNodes.elementAt(m_next);
incrementNextPosition();
m_currentContextNode = next;
return next;
}
// 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 initContext, which is called
// from the execute method.
if (-1 == m_varStackPos)
{
if (DTM.NULL == m_firstWalker.getRoot())
{
this.setNextPosition(0);
m_firstWalker.setRoot(m_context);
m_lastUsedWalker = m_firstWalker;
}
return returnNextNode(m_firstWalker.nextNode());
}
else
{
VariableStack vars = m_execContext.getVarStack();
// These three statements need to be combined into one operation.
int savedStart = vars.getSearchStart();
vars.setSearchStart(m_varStackPos);
vars.pushContextPosition(m_varStackContext);
if (DTM.NULL == m_firstWalker.getRoot())
{
this.setNextPosition(0);
m_firstWalker.setRoot(m_context);
m_lastUsedWalker = m_firstWalker;
}
int n = returnNextNode(m_firstWalker.nextNode());
// These two statements need to be combined into one operation.
vars.setSearchStart(savedStart);
vars.popContextPosition();
return n;
}
}
/**
* Bottleneck the return of a next node, to make returns
* easier from nextNode().
*
* @param nextNode The next node found, may be null.
*
* @return The same node that was passed as an argument.
*/
protected int returnNextNode(int nextNode)
{
if (DTM.NULL != nextNode)
{
if (null != m_cachedNodes)
m_cachedNodes.addElement(nextNode);
this.incrementNextPosition();
}
m_lastFetched = nextNode;
if (DTM.NULL == nextNode)
m_foundLast = true;
return nextNode;
}
/**
* Return the last fetched node. Needed to support the UnionPathIterator.
*
* @return The last fetched node, or null if the last fetch was null.
*/
public int getCurrentNode()
{
return m_lastFetched;
}
/**
* If an index is requested, NodeSet will call this method
* to run the iterator to the index. By default this sets
* m_next to the index. If the index argument is -1, this
* signals that the iterator should be run to the end.
*
* @param index The index to run to, or -1 if the iterator
* should run to the end.
*/
public void runTo(int index)
{
if (m_foundLast || ((index >= 0) && (index <= getCurrentPos())))
return;
int n;
if (-1 == index)
{
while (DTM.NULL != (n = nextNode()));
}
else
{
while (DTM.NULL != (n = nextNode()))
{
if (getCurrentPos() >= index)
break;
}
}
}
/**
* <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 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;
}
/**
* <meta name="usage" content="advanced"/>
* Add a walker to the waiting list.
*
* @param walker A walker that is waiting for
* other step walkers to complete, before it can
* continue.
*
* @see org.apache.xpath.axes.AxesWalker
*/
public final void addToWaitList(AxesWalker walker)
{
if (null == m_waiting)
{
m_waiting = new Vector();
}
m_waiting.addElement(walker);
}
/**
* <meta name="usage" content="advanced"/>
* Remove a walker from the waiting list.
*
* @param walker A walker that is no longer waiting.
*
* @see org.apache.xpath.axes.AxesWalker
*/
public final void removeFromWaitList(AxesWalker walker)
{
if(null != m_waiting) // defensive check.
m_waiting.removeElement(walker);
}
/**
* Tells if we've found the last node yet.
*
* @return true if the last nextNode returned null.
*/
public final boolean getFoundLast()
{
return m_foundLast;
}
/**
* The XPath execution context we are operating on.
*
* @return XPath execution context this iterator is operating on,
* or null if initContext has not been called.
*/
public final XPathContext getXPathContext()
{
return m_execContext;
}
/**
* The DOM helper for the given context;
*
* @return The DOMHelper that should be used,
* or null if initContext has not been called.
*/
public final DOMHelper getDOMHelper()
{
return m_dhelper;
}
/**
* The node context for the iterator.
*
* @return The node context, same as getRoot().
*/
public final int getContext()
{
return m_context;
}
/**
* The node context from where the expression is being
* executed from (i.e. for current() support).
*
* @return The top-level node context of the entire expression.
*/
public final int getCurrentContextNode()
{
return m_currentContextNode;
}
/**
* Set the current context node for this iterator.
*
* @param n Must be a non-null reference to the node context.
*/
public final void setCurrentContextNode(int n)
{
m_currentContextNode = n;
}
/**
* Set the current context node for this iterator.
*
* @param n Must be a non-null reference to the node context.
*/
public final void setRoot(int n)
{
m_context = n;
m_cdtm = m_execContext.getDTM(n);
}
/**
* Return the saved reference to the prefix resolver that
* was in effect when this iterator was created.
*
* @return The prefix resolver or this iterator, which may be null.
*/
public final PrefixResolver getPrefixResolver()
{
return m_prefixResolver;
}
/**
* Get the index of the last node in the iteration.
*
*
* @return the index of the last node in the iteration.
*/
public int getLast()
{
return getLength();
}
/**
* Set the index of the last node in the iteration.
*
*
* @param last the index of the last node in the iteration.
*/
public void setLast(int last)
{
m_last = last;
}
/**
* Get the index of the last node that can be itterated to.
* This probably will need to be overridded by derived classes.
*
* @param xctxt XPath runtime context.
*
* @return the index of the last node that can be itterated to.
*/
public int getLastPos(XPathContext xctxt)
{
int pos = getProximityPosition();
LocPathIterator clone;
try
{
// %REVIEW% %OPT%
if(0 == pos && m_currentContextNode != DTM.NULL)
clone = (LocPathIterator) cloneWithReset();
else
clone = (LocPathIterator) clone();
}
catch (CloneNotSupportedException cnse)
{
return -1;
}
// %REVIEW% Commented this out, as it was messing up pos68 test. count-1?
// System.out.println("clone.getPredicateCount(): "+clone.getPredicateCount());
// clone.setPredicateCount(clone.getPredicateCount() - 1);
int next;
while (DTM.NULL != (next = clone.nextNode()))
{
pos++;
}
// System.out.println("pos: "+pos);
return pos;
}
/**
* Get the analysis pattern built by the WalkerFactory.
*
* @return The analysis pattern built by the WalkerFactory.
*/
int getAnalysis()
{
return m_analysis;
}
/**
* Set the analysis pattern built by the WalkerFactory.
*
* @param a The analysis pattern built by the WalkerFactory.
*/
void setAnalysis(int a)
{
m_analysis = a;
}
/**
* Tell if this expression or it's subexpressions can traverse outside
* the current subtree.
*
* @return true if traversal outside the context node's subtree can occur.
*/
public boolean canTraverseOutsideSubtree()
{
if((m_analysis & WalkerFactory.BITMASK_TRAVERSES_OUTSIDE_SUBTREE) != 0)
{
return true;
}
// We have to ask subwalkers about their predicates.
if(null != m_firstWalker)
{
if(m_firstWalker.canTraverseOutsideSubtree())
return true;
}
return super.canTraverseOutsideSubtree();
}
//============= State Data =============
/**
* The dtm of the context node. Careful about using this... it may not
* be the dtm of the current node.
*/
transient protected DTM m_cdtm;
/** The starting point in m_waiting where the waiting step walkers are. */
transient int m_waitingBottom = 0;
/**
* An index to the point in the variable stack where we should
* begin variable searches for this iterator.
* This is -1 if m_isTopLevel is false.
*/
transient int m_varStackPos = -1;
/**
* An index into the variable stack where the variable context
* ends, i.e. at the point we should terminate the search and
* go looking for global variables.
*/
transient int m_varStackContext;
/**
* Value determined at compile time, indicates that this is an
* iterator at the top level of the expression, rather than inside
* a predicate.
* @serial
*/
private boolean m_isTopLevel = false;
/** The index of the last node in the iteration. */
transient private int m_last = 0;
/* The pool for cloned iterators. Iterators need to be cloned
* because the hold running state, and thus the original iterator
* expression from the stylesheet pool can not be used. */
// ObjectPool m_pool = new ObjectPool(this.getClass());
/** The last node that was fetched, usually by nextNode. */
transient public int m_lastFetched = DTM.NULL;
/**
* If this iterator needs to cache nodes that are fetched, they
* are stored here.
*/
transient NodeSet m_cachedNodes;
/** 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;
/** This is true if nextNode returns null. */
transient protected boolean m_foundLast = false;
/**
* Quicker access to the DOM helper than going through the
* XPathContext object.
*/
transient protected DOMHelper m_dhelper;
/**
* The context node for this iterator, which doesn't change through
* the course of the iteration.
*/
transient protected int m_context = DTM.NULL;
/**
* The node context from where the expression is being
* executed from (i.e. for current() support). Different
* from m_context in that this is the context for the entire
* expression, rather than the context for the subexpression.
*/
transient protected int m_currentContextNode = DTM.NULL;
/**
* Fast access to the current prefix resolver. It isn't really
* clear that this is needed.
* @serial
*/
protected PrefixResolver m_prefixResolver;
/**
* The XPathContext reference, needed for execution of many
* operations.
*/
transient protected XPathContext m_execContext;
/**
* The index of the next node to be fetched. Useful if this
* is a cached iterator, and is being used as random access
* NodeList.
*/
transient protected int m_next = 0;
/**
* The list of "waiting" step walkers.
* @see org.apache.xpath.axes.AxesWalker
*/
transient private Vector m_waiting = null;
/**
* The analysis pattern built by the WalkerFactory.
* TODO: Move to LocPathIterator.
* @see org.apache.xpath.axes.WalkerFactory
* @serial
*/
protected int m_analysis = 0x00000000;
}