| /* |
| * reserved comment block |
| * DO NOT REMOVE OR ALTER! |
| */ |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| 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 |
| { |
| static final long serialVersionUID = 3866261934726581044L; |
| /** 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; |
| |
| /** |
| * 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. |
| */ |
| private IteratorCache m_cache; |
| |
| /** |
| * If this iterator needs to cache nodes that are fetched, they |
| * are stored in the Vector in the generic object. |
| */ |
| protected NodeVector getVector() { |
| NodeVector nv = (m_cache != null) ? m_cache.getVector() : null; |
| 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". |
| */ |
| private IteratorCache getCache() { |
| return m_cache; |
| } |
| |
| /** |
| * Set the vector where nodes will be cached. |
| */ |
| protected void SetVector(NodeVector v) |
| { |
| setObject(v); |
| } |
| |
| |
| /** |
| * If the iterator needs to cache nodes as they are fetched, |
| * then this method returns true. |
| */ |
| public boolean hasCache() |
| { |
| final NodeVector nv = getVector(); |
| return (nv != null); |
| } |
| |
| /** |
| * 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; |
| if (m_cache != null) { |
| complete = m_cache.isComplete(); |
| } 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() { |
| NodeVector nv = getVector(); |
| if (nv != null) { |
| m_cache.setCacheComplete(true); |
| } |
| } |
| |
| |
| /** |
| * 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. |
| */ |
| private 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 nodeVector |
| */ |
| public NodeSequence(Object nodeVector) |
| { |
| super(nodeVector); |
| if (nodeVector instanceof NodeVector) { |
| SetVector((NodeVector) 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. |
| */ |
| private NodeSequence(DTMManager dtmMgr) |
| { |
| super(new NodeVector()); |
| m_last = 0; |
| m_dtmMgr = dtmMgr; |
| } |
| |
| |
| /** |
| * Create a new NodeSequence in an invalid (null) state. |
| */ |
| public NodeSequence() |
| { |
| return; |
| } |
| |
| |
| /** |
| * @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 root is DTM.NULL, then something's wrong with the context |
| if (nodeHandle == DTM.NULL) |
| { |
| throw new RuntimeException("Unable to evaluate expression using " + |
| "this context"); |
| } |
| |
| 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) |
| { |
| // There is a cache |
| if(m_next < vec.size()) |
| { |
| // The node is in the cache, so just return it. |
| int next = vec.elementAt(m_next); |
| m_next++; |
| return next; |
| } |
| else if(cacheComplete() || (-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 |
| { |
| // 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. |
| markCacheComplete(); |
| |
| 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() && index < 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) |
| { |
| int oldNode = vec.elementAt(index); |
| if (oldNode != node && m_cache.useCount() > 1) { |
| /* 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. |
| */ |
| IteratorCache newCache = new IteratorCache(); |
| final NodeVector nv; |
| try { |
| nv = (NodeVector) vec.clone(); |
| } catch (CloneNotSupportedException e) { |
| // This should never happen |
| e.printStackTrace(); |
| RuntimeException rte = new RuntimeException(e.getMessage()); |
| throw rte; |
| } |
| newCache.setVector(nv); |
| newCache.setCacheComplete(true); |
| m_cache = newCache; |
| vec = nv; |
| |
| // Keep our superclass informed of the current NodeVector |
| super.setObject(nv); |
| |
| /* 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. |
| */ |
| } |
| vec.setElementAt(node, index); |
| m_last = vec.size(); |
| } |
| else |
| m_iter.setItem(node, index); |
| } |
| |
| /** |
| * @see DTMIterator#getLength() |
| */ |
| public int getLength() |
| { |
| IteratorCache cache = getCache(); |
| |
| if(cache != null) |
| { |
| // 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 |
| NodeVector nv = cache.getVector(); |
| return nv.size(); |
| } |
| |
| // 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; |
| if (m_cache != null) { |
| // 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. |
| m_cache.increaseUseCount(); |
| } |
| |
| 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 |
| { |
| NodeSequence clone = (NodeSequence) super.clone(); |
| if (null != m_iter) clone.m_iter = (DTMIterator) m_iter.clone(); |
| if (m_cache != null) { |
| // 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. |
| m_cache.increaseUseCount(); |
| } |
| |
| return 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 org.apache.xpath.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 node The node to be added. |
| * @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) |
| |
| /** |
| * 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. |
| * |
| */ |
| protected void setObject(Object obj) { |
| if (obj instanceof NodeVector) { |
| // Keep our superclass informed of the current NodeVector |
| // ... if we don't the smoketest fails (don't know why). |
| super.setObject(obj); |
| |
| // A copy of the code of what SetVector() would do. |
| NodeVector v = (NodeVector)obj; |
| if (m_cache != null) { |
| m_cache.setVector(v); |
| } else if (v!=null) { |
| m_cache = new IteratorCache(); |
| m_cache.setVector(v); |
| } |
| } else if (obj instanceof IteratorCache) { |
| IteratorCache cache = (IteratorCache) obj; |
| m_cache = cache; |
| m_cache.increaseUseCount(); |
| |
| // Keep our superclass informed of the current NodeVector |
| super.setObject(cache.getVector()); |
| } else { |
| super.setObject(obj); |
| } |
| |
| } |
| |
| /** |
| * 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. |
| */ |
| private NodeVector m_vec2; |
| |
| /** |
| * 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_vec2 = null; |
| 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() { |
| if (m_vec2 != null) |
| m_useCount2++; |
| |
| } |
| |
| /** |
| * Sets the NodeVector that holds the |
| * growing list of nodes as they are appended |
| * to the cached list. |
| */ |
| private void setVector(NodeVector nv) { |
| m_vec2 = nv; |
| m_useCount2 = 1; |
| } |
| |
| /** |
| * Get the cached list of nodes obtained from |
| * the iterator so far. |
| */ |
| private NodeVector getVector() { |
| 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. |
| */ |
| protected IteratorCache getIteratorCache() { |
| return m_cache; |
| } |
| } |