blob: e58bd3e9ce5bb508adfa839dbc4d9ff1f31d982f [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2002 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.io.Serializable;
import java.util.Vector;
import javax.xml.transform.TransformerException;
import org.apache.xml.dtm.DTM;
import org.apache.xml.dtm.DTMIterator;
import org.apache.xml.dtm.DTMManager;
import org.apache.xml.dtm.DTMFilter;
import org.apache.xml.utils.NodeVector;
import org.apache.xpath.Expression;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XObject;
/**
* This class is the dynamic wrapper for a Xalan DTMIterator instance, and
* provides random access capabilities.
*/
public class NodeSequence extends XObject
implements DTMIterator, Cloneable, PathComponent
{
/** The index of the last node in the iteration. */
protected int m_last = -1;
/**
* The index of the next node to be fetched. Useful if this
* is a cached iterator, and is being used as random access
* NodeList.
*/
protected int m_next = 0;
/**
* If this iterator needs to cache nodes that are fetched, they
* are stored in the Vector in the generic object.
*/
protected NodeVector getVector()
{
return (NodeVector)m_obj;
}
/**
* Set the vector where nodes will be stored.
*/
protected void SetVector(NodeVector v)
{
m_obj = v;
}
/**
* If this iterator needs to cache nodes that are fetched, they
* are stored here.
*/
public boolean hasCache()
{
return (m_obj != null);
}
/**
* The functional iterator that fetches nodes.
*/
protected DTMIterator m_iter;
/**
* Set the functional iterator that fetches nodes.
* @param iter The iterator that is to be contained.
*/
public final void setIter(DTMIterator iter)
{
m_iter = iter;
}
/**
* Get the functional iterator that fetches nodes.
* @return The contained iterator.
*/
public final DTMIterator getContainedIter()
{
return m_iter;
}
/**
* The DTMManager to use if we're using a NodeVector only.
* We may well want to do away with this, and store it in the NodeVector.
*/
protected DTMManager m_dtmMgr;
// ==== Constructors ====
/**
* Create a new NodeSequence from a (already cloned) iterator.
*
* @param iter Cloned (not static) DTMIterator.
* @param context The initial context node.
* @param xctxt The execution context.
* @param shouldCacheNodes True if this sequence can random access.
*/
public NodeSequence(DTMIterator iter, int context, XPathContext xctxt, boolean shouldCacheNodes)
{
setIter(iter);
setRoot(context, xctxt);
setShouldCacheNodes(shouldCacheNodes);
}
/**
* Create a new NodeSequence from a (already cloned) iterator.
*
* @param iter Cloned (not static) DTMIterator.
*/
public NodeSequence(Object nodeVector)
{
super(nodeVector);
if(null != nodeVector)
{
assertion(nodeVector instanceof NodeVector,
"Must have a NodeVector as the object for NodeSequence!");
if(nodeVector instanceof DTMIterator)
{
setIter((DTMIterator)nodeVector);
m_last = ((DTMIterator)nodeVector).getLength();
}
}
}
/**
* Construct an empty XNodeSet object. This is used to create a mutable
* nodeset to which random nodes may be added.
*/
public NodeSequence(DTMManager dtmMgr)
{
super(new NodeVector());
m_last = 0;
m_dtmMgr = dtmMgr;
}
/**
* Create a new NodeSequence in an invalid (null) state.
*/
public NodeSequence()
{
}
/**
* @see DTMIterator#getDTM(int)
*/
public DTM getDTM(int nodeHandle)
{
DTMManager mgr = getDTMManager();
if(null != mgr)
return getDTMManager().getDTM(nodeHandle);
else
{
assertion(false, "Can not get a DTM Unless a DTMManager has been set!");
return null;
}
}
/**
* @see DTMIterator#getDTMManager()
*/
public DTMManager getDTMManager()
{
return m_dtmMgr;
}
/**
* @see DTMIterator#getRoot()
*/
public int getRoot()
{
if(null != m_iter)
return m_iter.getRoot();
else
{
// NodeSetDTM will call this, and so it's not a good thing to throw
// an assertion here.
// assertion(false, "Can not get the root from a non-iterated NodeSequence!");
return DTM.NULL;
}
}
/**
* @see DTMIterator#setRoot(int, Object)
*/
public void setRoot(int nodeHandle, Object environment)
{
if(null != m_iter)
{
XPathContext xctxt = (XPathContext)environment;
m_dtmMgr = xctxt.getDTMManager();
m_iter.setRoot(nodeHandle, environment);
if(!m_iter.isDocOrdered())
{
if(!hasCache())
setShouldCacheNodes(true);
runTo(-1);
m_next=0;
}
}
else
assertion(false, "Can not setRoot on a non-iterated NodeSequence!");
}
/**
* @see DTMIterator#reset()
*/
public void reset()
{
m_next = 0;
// not resetting the iterator on purpose!!!
}
/**
* @see DTMIterator#getWhatToShow()
*/
public int getWhatToShow()
{
return hasCache() ? (DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE)
: m_iter.getWhatToShow();
}
/**
* @see DTMIterator#getExpandEntityReferences()
*/
public boolean getExpandEntityReferences()
{
if(null != m_iter)
return m_iter.getExpandEntityReferences();
else
return true;
}
/**
* @see DTMIterator#nextNode()
*/
public int nextNode()
{
// If the cache is on, and the node has already been found, then
// just return from the list.
NodeVector vec = getVector();
if (null != vec)
{
if(m_next < vec.size())
{
int next = vec.elementAt(m_next);
m_next++;
return next;
}
else if((-1 != m_last) || (null == m_iter))
{
m_next++;
return DTM.NULL;
}
}
if (null == m_iter)
return DTM.NULL;
int next = m_iter.nextNode();
if(DTM.NULL != next)
{
if(hasCache())
{
if(m_iter.isDocOrdered())
{
getVector().addElement(next);
m_next++;
}
else
{
int insertIndex = addNodeInDocOrder(next);
if(insertIndex >= 0)
m_next++;
}
}
else
m_next++;
}
else
{
m_last = m_next;
m_next++;
}
return next;
}
/**
* @see DTMIterator#previousNode()
*/
public int previousNode()
{
if(hasCache())
{
if(m_next <= 0)
return DTM.NULL;
else
{
m_next--;
return item(m_next);
}
}
else
{
int n = m_iter.previousNode();
m_next = m_iter.getCurrentPos();
return m_next;
}
}
/**
* @see DTMIterator#detach()
*/
public void detach()
{
if(null != m_iter)
m_iter.detach();
super.detach();
}
/**
* Calling this with a value of false will cause the nodeset
* to be cached.
* @see DTMIterator#allowDetachToRelease(boolean)
*/
public void allowDetachToRelease(boolean allowRelease)
{
if((false == allowRelease) && !hasCache())
{
setShouldCacheNodes(true);
}
if(null != m_iter)
m_iter.allowDetachToRelease(allowRelease);
super.allowDetachToRelease(allowRelease);
}
/**
* @see DTMIterator#getCurrentNode()
*/
public int getCurrentNode()
{
if(hasCache())
{
int currentIndex = m_next-1;
NodeVector vec = getVector();
if((currentIndex >= 0) && (currentIndex < vec.size()))
return vec.elementAt(currentIndex);
else
return DTM.NULL;
}
if(null != m_iter)
{
return m_iter.getCurrentNode();
}
else
return DTM.NULL;
}
/**
* @see DTMIterator#isFresh()
*/
public boolean isFresh()
{
return (0 == m_next);
}
/**
* @see DTMIterator#setShouldCacheNodes(boolean)
*/
public void setShouldCacheNodes(boolean b)
{
if (b)
{
if(!hasCache())
{
SetVector(new NodeVector());
}
// else
// getVector().RemoveAllNoClear(); // Is this good?
}
else
SetVector(null);
}
/**
* @see DTMIterator#isMutable()
*/
public boolean isMutable()
{
return hasCache(); // though may be surprising if it also has an iterator!
}
/**
* @see DTMIterator#getCurrentPos()
*/
public int getCurrentPos()
{
return m_next;
}
/**
* @see DTMIterator#runTo(int)
*/
public void runTo(int index)
{
int n;
if (-1 == index)
{
int pos = m_next;
while (DTM.NULL != (n = nextNode()));
m_next = pos;
}
else if(m_next == index)
{
return;
}
else if(hasCache() && m_next < getVector().size())
{
m_next = index;
}
else if((null == getVector()) && (index < m_next))
{
while ((m_next >= index) && DTM.NULL != (n = previousNode()));
}
else
{
while ((m_next < index) && DTM.NULL != (n = nextNode()));
}
}
/**
* @see DTMIterator#setCurrentPos(int)
*/
public void setCurrentPos(int i)
{
runTo(i);
}
/**
* @see DTMIterator#item(int)
*/
public int item(int index)
{
setCurrentPos(index);
int n = nextNode();
m_next = index;
return n;
}
/**
* @see DTMIterator#setItem(int, int)
*/
public void setItem(int node, int index)
{
NodeVector vec = getVector();
if(null != vec)
{
vec.setElementAt(node, index);
m_last = vec.size();
}
else
m_iter.setItem(node, index);
}
/**
* @see DTMIterator#getLength()
*/
public int getLength()
{
if(hasCache())
{
if(-1 == m_last)
{
int pos = m_next;
runTo(-1);
m_next = pos;
}
return m_last;
}
else
{
return m_iter.getLength();
}
}
/**
* Note: Not a deep clone.
* @see DTMIterator#cloneWithReset()
*/
public DTMIterator cloneWithReset() throws CloneNotSupportedException
{
NodeSequence seq = (NodeSequence)super.clone();
seq.m_next = 0;
return seq;
}
/**
* Get a clone of this iterator, but don't reset the iteration in the
* process, so that it may be used from the current position.
* Note: Not a deep clone.
*
* @return A clone of this object.
*
* @throws CloneNotSupportedException
*/
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
/**
* @see DTMIterator#isDocOrdered()
*/
public boolean isDocOrdered()
{
if(null != m_iter)
return m_iter.isDocOrdered();
else
return true; // can't be sure?
}
/**
* @see DTMIterator#getAxis()
*/
public int getAxis()
{
if(null != m_iter)
return m_iter.getAxis();
else
{
assertion(false, "Can not getAxis from a non-iterated node sequence!");
return 0;
}
}
/**
* @see PathComponent#getAnalysisBits()
*/
public int getAnalysisBits()
{
if((null != m_iter) && (m_iter instanceof PathComponent))
return ((PathComponent)m_iter).getAnalysisBits();
else
return 0;
}
/**
* @see Expression#fixupVariables(Vector, int)
*/
public void fixupVariables(Vector vars, int globalsSize)
{
super.fixupVariables(vars, globalsSize);
}
/**
* Add the node into a vector of nodes where it should occur in
* document order.
* @param v Vector of nodes, presumably containing Nodes
* @param obj Node object.
*
* @param node The node to be added.
* @param test true if we should test for doc order
* @param support The XPath runtime context.
* @return insertIndex.
* @throws RuntimeException thrown if this NodeSetDTM is not of
* a mutable type.
*/
protected int addNodeInDocOrder(int node)
{
assertion(hasCache(), "addNodeInDocOrder must be done on a mutable sequence!");
int insertIndex = -1;
NodeVector vec = getVector();
// This needs to do a binary search, but a binary search
// is somewhat tough because the sequence test involves
// two nodes.
int size = vec.size(), i;
for (i = size - 1; i >= 0; i--)
{
int child = vec.elementAt(i);
if (child == node)
{
i = -2; // Duplicate, suppress insert
break;
}
DTM dtm = m_dtmMgr.getDTM(node);
if (!dtm.isNodeAfter(node, child))
{
break;
}
}
if (i != -2)
{
insertIndex = i + 1;
vec.insertElementAt(node, insertIndex);
}
// checkDups();
return insertIndex;
} // end addNodeInDocOrder(Vector v, Object obj)
}