- /*
- * Copyright 2002-2004 The Apache Software Foundation.
- *
- * Licensed 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: NodeSequence.java,v 1.11 2004/02/17 04:32:08 minchau Exp $
- */
- package com.sun.org.apache.xpath.internal.axes;
-
- import java.util.Vector;
-
- import com.sun.org.apache.xml.internal.dtm.DTM;
- import com.sun.org.apache.xml.internal.dtm.DTMFilter;
- import com.sun.org.apache.xml.internal.dtm.DTMIterator;
- import com.sun.org.apache.xml.internal.dtm.DTMManager;
- import com.sun.org.apache.xml.internal.utils.NodeVector;
- import com.sun.org.apache.xpath.internal.NodeSetDTM;
- import com.sun.org.apache.xpath.internal.XPathContext;
- import com.sun.org.apache.xpath.internal.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 this NodeSequence wraps a mutable nodeset, then
- // m_last will not reflect the size of the nodeset if
- // it has been mutated...
- if (m_iter instanceof NodeSetDTM)
- {
- return m_iter.getLength();
- }
-
- if(-1 == m_last)
- {
- int pos = m_next;
- runTo(-1);
- m_next = pos;
- }
- return m_last;
- }
- else
- {
- return (-1 == m_last) ? (m_last = m_iter.getLength()) : m_last;
- }
- }
-
- /**
- * 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 com.sun.org.apache.xpath.internal.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)
- }
-