/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* 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
*
*
* 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.6 2007/01/12 19:26:42 spericas Exp $
*/
/**
* This class is the dynamic wrapper for a Xalan DTMIterator instance, and
* provides random access capabilities.
*/
{
/** The index of the last node in the iteration. */
/**
* The index of the next node to be fetched. Useful if this
* is a cached iterator, and is being used as random access
* NodeList.
*/
/**
* A cache of a list of nodes obtained from the iterator so far.
* This list is appended to until the iterator is exhausted and
* the cache is complete.
* <p>
* Multiple NodeSequence objects may share the same cache.
*/
/**
* If this iterator needs to cache nodes that are fetched, they
* are stored in the Vector in the generic object.
*/
return nv;
}
/**
* Get the cache (if any) of nodes obtained from
* the iterator so far. Note that the cache keeps
* growing until the iterator is walked to exhaustion,
* at which point the cache is "complete".
*/
return m_cache;
}
/**
* Set the vector where nodes will be cached.
*/
{
setObject(v);
}
/**
* If the iterator needs to cache nodes as they are fetched,
* then this method returns true.
*/
public boolean hasCache()
{
}
/**
* If this NodeSequence has a cache, and that cache is
* fully populated then this method returns true, otherwise
* if there is no cache or it is not complete it returns false.
*/
private boolean cacheComplete() {
final boolean complete;
} else {
complete = false;
}
return complete;
}
/**
* If this NodeSequence has a cache, mark that it is complete.
* This method should be called after the iterator is exhausted.
*/
private void markCacheComplete() {
m_cache.setCacheComplete(true);
}
}
/**
* The functional iterator that fetches nodes.
*/
/**
* Set the functional iterator that fetches nodes.
* @param iter The iterator that is to be contained.
*/
{
}
/**
* Get the functional iterator that fetches nodes.
* @return The contained iterator.
*/
{
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.
*/
// ==== 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.
*/
{
}
/**
* Create a new NodeSequence from a (already cloned) iterator.
*
* @param nodeVector
*/
{
super(nodeVector);
if (nodeVector instanceof NodeVector) {
}
if(null != nodeVector)
{
"Must have a NodeVector as the object for NodeSequence!");
if(nodeVector instanceof DTMIterator)
{
}
}
}
/**
* Construct an empty XNodeSet object. This is used to create a mutable
* nodeset to which random nodes may be added.
*/
{
super(new NodeVector());
m_last = 0;
}
/**
* Create a new NodeSequence in an invalid (null) state.
*/
public NodeSequence()
{
return;
}
/**
* @see DTMIterator#getDTM(int)
*/
{
else
{
assertion(false, "Can not get a DTM Unless a DTMManager has been set!");
return null;
}
}
/**
* @see DTMIterator#getDTMManager()
*/
{
return m_dtmMgr;
}
/**
* @see DTMIterator#getRoot()
*/
public int 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!");
}
}
/**
* @see DTMIterator#setRoot(int, Object)
*/
{
// If root is DTM.NULL, then something's wrong with the context
{
throw new RuntimeException("Unable to evaluate expression using " +
"this context");
}
{
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()
{
: m_iter.getWhatToShow();
}
/**
* @see DTMIterator#getExpandEntityReferences()
*/
public boolean getExpandEntityReferences()
{
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.
{
// There is a cache
{
// The node is in the cache, so just return it.
m_next++;
return next;
}
{
m_next++;
}
}
{
if(hasCache())
{
if(m_iter.isDocOrdered())
{
m_next++;
}
else
{
if(insertIndex >= 0)
m_next++;
}
}
else
m_next++;
}
else
{
// We have exhausted the iterator, and if there is a cache
// it must have all nodes in it by now, so let the cache
// know that it is complete.
m_next++;
}
return next;
}
/**
* @see DTMIterator#previousNode()
*/
public int previousNode()
{
if(hasCache())
{
if(m_next <= 0)
else
{
m_next--;
}
}
else
{
int n = m_iter.previousNode();
return m_next;
}
}
/**
* @see DTMIterator#detach()
*/
public void detach()
{
super.detach();
}
/**
* Calling this with a value of false will cause the nodeset
* to be cached.
* @see DTMIterator#allowDetachToRelease(boolean)
*/
{
if((false == allowRelease) && !hasCache())
{
setShouldCacheNodes(true);
}
super.allowDetachToRelease(allowRelease);
}
/**
* @see DTMIterator#getCurrentNode()
*/
public int getCurrentNode()
{
if(hasCache())
{
else
}
{
return m_iter.getCurrentNode();
}
else
}
/**
* @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
}
/**
* @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)
*/
{
int n;
if (-1 == index)
{
}
{
return;
}
{
}
{
}
else
{
}
}
/**
* @see DTMIterator#setCurrentPos(int)
*/
public void setCurrentPos(int i)
{
runTo(i);
}
/**
* @see DTMIterator#item(int)
*/
{
int n = nextNode();
return n;
}
/**
* @see DTMIterator#setItem(int, int)
*/
{
{
/* If we are going to set the node at the given index
* to a different value, and the cache is shared
* (has a use count greater than 1)
* then make a copy of the cache and use it
* so we don't overwrite the value for other
* users of the cache.
*/
final NodeVector nv;
try {
} catch (CloneNotSupportedException e) {
// This should never happen
e.printStackTrace();
throw rte;
}
newCache.setCacheComplete(true);
// Keep our superclass informed of the current NodeVector
/* When we get to here the new cache has
* a use count of 1 and when setting a
* bunch of values on the same NodeSequence,
* such as when sorting, we will keep setting
* values in that same copy which has a use count of 1.
*/
}
}
else
}
/**
* @see DTMIterator#getLength()
*/
public int getLength()
{
{
// Nodes from the iterator are cached
if (cache.isComplete()) {
// All of the nodes from the iterator are cached
// so just return the number of nodes in the cache
}
// 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)
{
}
if(-1 == m_last)
{
runTo(-1);
}
return m_last;
}
else
{
}
}
/**
* Note: Not a deep clone.
* @see DTMIterator#cloneWithReset()
*/
{
// In making this clone of an iterator we are making
// another NodeSequence object it has a reference
// to the same IteratorCache object as the original
// so we need to remember that more than one
// NodeSequence object shares the cache.
}
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
*/
{
// In making this clone of an iterator we are making
// another NodeSequence object it has a reference
// to the same IteratorCache object as the original
// so we need to remember that more than one
// NodeSequence object shares the cache.
}
return clone;
}
/**
* @see DTMIterator#isDocOrdered()
*/
public boolean isDocOrdered()
{
return m_iter.isDocOrdered();
else
return true; // can't be sure?
}
/**
* @see DTMIterator#getAxis()
*/
public int getAxis()
{
else
{
assertion(false, "Can not getAxis from a non-iterated node sequence!");
return 0;
}
}
/**
* @see PathComponent#getAnalysisBits()
*/
public int getAnalysisBits()
{
else
return 0;
}
/**
* @see org.apache.xpath.Expression#fixupVariables(Vector, int)
*/
{
}
/**
* Add the node into a vector of nodes where it should occur in
* document order.
* @param node The node to be added.
* @return insertIndex.
* @throws RuntimeException thrown if this NodeSetDTM is not of
* a mutable type.
*/
{
int insertIndex = -1;
// This needs to do a binary search, but a binary search
// is somewhat tough because the sequence test involves
// two nodes.
{
{
i = -2; // Duplicate, suppress insert
break;
}
{
break;
}
}
if (i != -2)
{
insertIndex = i + 1;
}
// checkDups();
return insertIndex;
} // end addNodeInDocOrder(Vector v, Object obj)
/**
* It used to be that many locations in the code simply
* did an assignment to this.m_obj directly, rather than
* calling the setObject(Object) method. The problem is
* that our super-class would be updated on what the
* cache associated with this NodeSequence, but
* we wouldn't know ourselves.
* <p>
* All setting of m_obj is done through setObject() now,
* and this method over-rides the super-class method.
* So now we are in the loop have an opportunity
* to update some caching information.
*
*/
if (obj instanceof NodeVector) {
// Keep our superclass informed of the current NodeVector
// ... if we don't the smoketest fails (don't know why).
// A copy of the code of what SetVector() would do.
} else if (v!=null) {
m_cache = new IteratorCache();
}
} else if (obj instanceof IteratorCache) {
// Keep our superclass informed of the current NodeVector
} else {
}
}
/**
* Each NodeSequence object has an iterator which is "walked".
* As an iterator is walked one obtains nodes from it.
* As those nodes are obtained they may be cached, making
* the next walking of a copy or clone of the iterator faster.
* This field (m_cache) is a reference to such a cache,
* which is populated as the iterator is walked.
* <p>
* Note that multiple NodeSequence objects may hold a
* reference to the same cache, and also
* (and this is important) the same iterator.
* The iterator and its cache may be shared among
* many NodeSequence objects.
* <p>
* If one of the NodeSequence objects walks ahead
* of the others it fills in the cache.
* As the others NodeSequence objects catch up they
* get their values from
* the cache rather than the iterator itself, so
* the iterator is only ever walked once and everyone
* benefits from the cache.
* <p>
* At some point the cache may be
* complete due to walking to the end of one of
* the copies of the iterator, and the cache is
* then marked as "complete".
* and the cache will have no more nodes added to it.
* <p>
* Its use-count is the number of NodeSequence objects that use it.
*/
private final static class IteratorCache {
/**
* A list of nodes already obtained from the iterator.
* As the iterator is walked the nodes obtained from
* it are appended to this list.
* <p>
* Both an iterator and its corresponding cache can
* be shared by multiple NodeSequence objects.
* <p>
* For example, consider three NodeSequence objects
* ns1, ns2 and ns3 doing such sharing, and the
* nodes to be obtaind from the iterator being
* the sequence { 33, 11, 44, 22, 55 }.
* <p>
* If ns3.nextNode() is called 3 times the the
* underlying iterator will have walked through
* 33, 11, 55 and these three nodes will have been put
* in the cache.
* <p>
* If ns2.nextNode() is called 2 times it will return
* 33 and 11 from the cache, leaving the iterator alone.
* <p>
* If ns1.nextNode() is called 6 times it will return
* 33 and 11 from the cache, then get 44, 22, 55 from
* the iterator, and appending 44, 22, 55 to the cache.
* On the sixth call it is found that the iterator is
* exhausted and the cache is marked complete.
* <p>
* Should ns2 or ns3 have nextNode() called they will
* know that the cache is complete, and they will
* obtain all subsequent nodes from the cache.
* <p>
* Note that the underlying iterator, though shared
* is only ever walked once.
*/
/**
* true if the associated iterator is exhausted and
* all nodes obtained from it are in the cache.
*/
private boolean m_isComplete2;
private int m_useCount2;
IteratorCache() {
m_isComplete2 = false;
m_useCount2 = 1;
return;
}
/**
* Returns count of how many NodeSequence objects share this
* IteratorCache object.
*/
private int useCount() {
return m_useCount2;
}
/**
* This method is called when yet another
* NodeSequence object uses, or shares
* this same cache.
*
*/
private void increaseUseCount() {
m_useCount2++;
}
/**
* Sets the NodeVector that holds the
* growing list of nodes as they are appended
* to the cached list.
*/
m_useCount2 = 1;
}
/**
* Get the cached list of nodes obtained from
* the iterator so far.
*/
return m_vec2;
}
/**
* Call this method with 'true' if the
* iterator is exhausted and the cached list
* is complete, or no longer growing.
*/
private void setCacheComplete(boolean b) {
m_isComplete2 = b;
}
/**
* Returns true if no cache is complete
* and immutable.
*/
private boolean isComplete() {
return m_isComplete2;
}
}
/**
* Get the cached list of nodes appended with
* values obtained from the iterator as
* a NodeSequence is walked when its
* nextNode() method is called.
*/
return m_cache;
}
}