Home | History | Annotate | Download | only in axes
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: NodeSequence.java 469367 2006-10-31 04:41:08Z minchau $
     20  */
     21 package org.apache.xpath.axes;
     22 
     23 import java.util.Vector;
     24 
     25 import org.apache.xml.dtm.DTM;
     26 import org.apache.xml.dtm.DTMFilter;
     27 import org.apache.xml.dtm.DTMIterator;
     28 import org.apache.xml.dtm.DTMManager;
     29 import org.apache.xml.utils.NodeVector;
     30 import org.apache.xpath.NodeSetDTM;
     31 import org.apache.xpath.XPathContext;
     32 import org.apache.xpath.objects.XObject;
     33 
     34 /**
     35  * This class is the dynamic wrapper for a Xalan DTMIterator instance, and
     36  * provides random access capabilities.
     37  */
     38 public class NodeSequence extends XObject
     39   implements DTMIterator, Cloneable, PathComponent
     40 {
     41     static final long serialVersionUID = 3866261934726581044L;
     42   /** The index of the last node in the iteration. */
     43   protected int m_last = -1;
     44 
     45   /**
     46    * The index of the next node to be fetched.  Useful if this
     47    * is a cached iterator, and is being used as random access
     48    * NodeList.
     49    */
     50   protected int m_next = 0;
     51 
     52   /**
     53    * A cache of a list of nodes obtained from the iterator so far.
     54    * This list is appended to until the iterator is exhausted and
     55    * the cache is complete.
     56    * <p>
     57    * Multiple NodeSequence objects may share the same cache.
     58    */
     59   private IteratorCache m_cache;
     60 
     61   /**
     62    * If this iterator needs to cache nodes that are fetched, they
     63    * are stored in the Vector in the generic object.
     64    */
     65   protected NodeVector getVector() {
     66       NodeVector nv = (m_cache != null) ?  m_cache.getVector() : null;
     67       return nv;
     68   }
     69 
     70   /**
     71    * Get the cache (if any) of nodes obtained from
     72    * the iterator so far. Note that the cache keeps
     73    * growing until the iterator is walked to exhaustion,
     74    * at which point the cache is "complete".
     75    */
     76   private IteratorCache getCache() {
     77       return m_cache;
     78   }
     79 
     80   /**
     81    * Set the vector where nodes will be cached.
     82    */
     83   protected void SetVector(NodeVector v)
     84   {
     85   	setObject(v);
     86   }
     87 
     88 
     89   /**
     90    * If the iterator needs to cache nodes as they are fetched,
     91    * then this method returns true.
     92    */
     93   public boolean hasCache()
     94   {
     95     final NodeVector nv = getVector();
     96   	return (nv != null);
     97   }
     98 
     99   /**
    100    * If this NodeSequence has a cache, and that cache is
    101    * fully populated then this method returns true, otherwise
    102    * if there is no cache or it is not complete it returns false.
    103    */
    104   private boolean cacheComplete() {
    105       final boolean complete;
    106       if (m_cache != null) {
    107           complete = m_cache.isComplete();
    108       } else {
    109           complete = false;
    110       }
    111       return complete;
    112   }
    113 
    114   /**
    115    * If this NodeSequence has a cache, mark that it is complete.
    116    * This method should be called after the iterator is exhausted.
    117    */
    118   private void markCacheComplete() {
    119       NodeVector nv = getVector();
    120       if (nv != null) {
    121           m_cache.setCacheComplete(true);
    122       }
    123   }
    124 
    125 
    126   /**
    127    * The functional iterator that fetches nodes.
    128    */
    129   protected DTMIterator m_iter;
    130 
    131   /**
    132    * Set the functional iterator that fetches nodes.
    133    * @param iter The iterator that is to be contained.
    134    */
    135   public final void setIter(DTMIterator iter)
    136   {
    137   	m_iter = iter;
    138   }
    139 
    140   /**
    141    * Get the functional iterator that fetches nodes.
    142    * @return The contained iterator.
    143    */
    144   public final DTMIterator getContainedIter()
    145   {
    146   	return m_iter;
    147   }
    148 
    149   /**
    150    * The DTMManager to use if we're using a NodeVector only.
    151    * We may well want to do away with this, and store it in the NodeVector.
    152    */
    153   protected DTMManager m_dtmMgr;
    154 
    155   // ==== Constructors ====
    156 
    157   /**
    158    * Create a new NodeSequence from a (already cloned) iterator.
    159    *
    160    * @param iter Cloned (not static) DTMIterator.
    161    * @param context The initial context node.
    162    * @param xctxt The execution context.
    163    * @param shouldCacheNodes True if this sequence can random access.
    164    */
    165   private NodeSequence(DTMIterator iter, int context, XPathContext xctxt, boolean shouldCacheNodes)
    166   {
    167   	setIter(iter);
    168   	setRoot(context, xctxt);
    169   	setShouldCacheNodes(shouldCacheNodes);
    170   }
    171 
    172   /**
    173    * Create a new NodeSequence from a (already cloned) iterator.
    174    *
    175    * @param nodeVector
    176    */
    177   public NodeSequence(Object nodeVector)
    178   {
    179   	super(nodeVector);
    180     if (nodeVector instanceof NodeVector) {
    181         SetVector((NodeVector) nodeVector);
    182     }
    183   	if(null != nodeVector)
    184   	{
    185   		assertion(nodeVector instanceof NodeVector,
    186   			"Must have a NodeVector as the object for NodeSequence!");
    187   		if(nodeVector instanceof DTMIterator)
    188   		{
    189   			setIter((DTMIterator)nodeVector);
    190   			m_last = ((DTMIterator)nodeVector).getLength();
    191   		}
    192 
    193   	}
    194   }
    195 
    196   /**
    197    * Construct an empty XNodeSet object.  This is used to create a mutable
    198    * nodeset to which random nodes may be added.
    199    */
    200   private NodeSequence(DTMManager dtmMgr)
    201   {
    202     super(new NodeVector());
    203     m_last = 0;
    204     m_dtmMgr = dtmMgr;
    205   }
    206 
    207 
    208   /**
    209    * Create a new NodeSequence in an invalid (null) state.
    210    */
    211   public NodeSequence()
    212   {
    213       return;
    214   }
    215 
    216 
    217   /**
    218    * @see DTMIterator#getDTM(int)
    219    */
    220   public DTM getDTM(int nodeHandle)
    221   {
    222   	DTMManager mgr = getDTMManager();
    223   	if(null != mgr)
    224     	return getDTMManager().getDTM(nodeHandle);
    225     else
    226     {
    227     	assertion(false, "Can not get a DTM Unless a DTMManager has been set!");
    228     	return null;
    229     }
    230   }
    231 
    232   /**
    233    * @see DTMIterator#getDTMManager()
    234    */
    235   public DTMManager getDTMManager()
    236   {
    237     return m_dtmMgr;
    238   }
    239 
    240   /**
    241    * @see DTMIterator#getRoot()
    242    */
    243   public int getRoot()
    244   {
    245   	if(null != m_iter)
    246     	return m_iter.getRoot();
    247   	else
    248   	{
    249   		// NodeSetDTM will call this, and so it's not a good thing to throw
    250   		// an assertion here.
    251   		// assertion(false, "Can not get the root from a non-iterated NodeSequence!");
    252   		return DTM.NULL;
    253   	}
    254   }
    255 
    256   /**
    257    * @see DTMIterator#setRoot(int, Object)
    258    */
    259   public void setRoot(int nodeHandle, Object environment)
    260   {
    261   	if(null != m_iter)
    262   	{
    263   		XPathContext xctxt = (XPathContext)environment;
    264   		m_dtmMgr = xctxt.getDTMManager();
    265   		m_iter.setRoot(nodeHandle, environment);
    266   		if(!m_iter.isDocOrdered())
    267   		{
    268   			if(!hasCache())
    269   				setShouldCacheNodes(true);
    270   			runTo(-1);
    271   			m_next=0;
    272   		}
    273   	}
    274   	else
    275   		assertion(false, "Can not setRoot on a non-iterated NodeSequence!");
    276   }
    277 
    278   /**
    279    * @see DTMIterator#reset()
    280    */
    281   public void reset()
    282   {
    283   	m_next = 0;
    284   	// not resetting the iterator on purpose!!!
    285   }
    286 
    287   /**
    288    * @see DTMIterator#getWhatToShow()
    289    */
    290   public int getWhatToShow()
    291   {
    292     return hasCache() ? (DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE)
    293     	: m_iter.getWhatToShow();
    294   }
    295 
    296   /**
    297    * @see DTMIterator#getExpandEntityReferences()
    298    */
    299   public boolean getExpandEntityReferences()
    300   {
    301   	if(null != m_iter)
    302   		return m_iter.getExpandEntityReferences();
    303   	else
    304     	return true;
    305   }
    306 
    307   /**
    308    * @see DTMIterator#nextNode()
    309    */
    310   public int nextNode()
    311   {
    312     // If the cache is on, and the node has already been found, then
    313     // just return from the list.
    314     NodeVector vec = getVector();
    315     if (null != vec)
    316     {
    317         // There is a cache
    318     	if(m_next < vec.size())
    319     	{
    320             // The node is in the cache, so just return it.
    321 			int next = vec.elementAt(m_next);
    322 	    	m_next++;
    323 	    	return next;
    324     	}
    325     	else if(cacheComplete() || (-1 != m_last) || (null == m_iter))
    326     	{
    327     		m_next++;
    328     		return DTM.NULL;
    329     	}
    330     }
    331 
    332   if (null == m_iter)
    333     return DTM.NULL;
    334 
    335  	int next = m_iter.nextNode();
    336     if(DTM.NULL != next)
    337     {
    338     	if(hasCache())
    339     	{
    340     		if(m_iter.isDocOrdered())
    341     	    {
    342     			getVector().addElement(next);
    343     			m_next++;
    344     		}
    345     		else
    346     		{
    347     			int insertIndex = addNodeInDocOrder(next);
    348     			if(insertIndex >= 0)
    349     				m_next++;
    350     		}
    351     	}
    352     	else
    353     		m_next++;
    354     }
    355     else
    356     {
    357         // We have exhausted the iterator, and if there is a cache
    358         // it must have all nodes in it by now, so let the cache
    359         // know that it is complete.
    360         markCacheComplete();
    361 
    362     	m_last = m_next;
    363     	m_next++;
    364     }
    365 
    366     return next;
    367   }
    368 
    369   /**
    370    * @see DTMIterator#previousNode()
    371    */
    372   public int previousNode()
    373   {
    374   	if(hasCache())
    375   	{
    376   		if(m_next <= 0)
    377   			return DTM.NULL;
    378   		else
    379   		{
    380   			m_next--;
    381   			return item(m_next);
    382   		}
    383   	}
    384   	else
    385   	{
    386 	    int n = m_iter.previousNode();
    387 	    m_next = m_iter.getCurrentPos();
    388 	    return m_next;
    389   	}
    390   }
    391 
    392   /**
    393    * @see DTMIterator#detach()
    394    */
    395   public void detach()
    396   {
    397   	if(null != m_iter)
    398   		m_iter.detach();
    399   	super.detach();
    400   }
    401 
    402   /**
    403    * Calling this with a value of false will cause the nodeset
    404    * to be cached.
    405    * @see DTMIterator#allowDetachToRelease(boolean)
    406    */
    407   public void allowDetachToRelease(boolean allowRelease)
    408   {
    409   	if((false == allowRelease) && !hasCache())
    410   	{
    411   		setShouldCacheNodes(true);
    412   	}
    413 
    414   	if(null != m_iter)
    415   		m_iter.allowDetachToRelease(allowRelease);
    416   	super.allowDetachToRelease(allowRelease);
    417   }
    418 
    419   /**
    420    * @see DTMIterator#getCurrentNode()
    421    */
    422   public int getCurrentNode()
    423   {
    424   	if(hasCache())
    425   	{
    426   		int currentIndex = m_next-1;
    427   		NodeVector vec = getVector();
    428   		if((currentIndex >= 0) && (currentIndex < vec.size()))
    429   			return vec.elementAt(currentIndex);
    430   		else
    431   			return DTM.NULL;
    432   	}
    433 
    434   	if(null != m_iter)
    435   	{
    436     	return m_iter.getCurrentNode();
    437   	}
    438   	else
    439   		return DTM.NULL;
    440   }
    441 
    442   /**
    443    * @see DTMIterator#isFresh()
    444    */
    445   public boolean isFresh()
    446   {
    447     return (0 == m_next);
    448   }
    449 
    450   /**
    451    * @see DTMIterator#setShouldCacheNodes(boolean)
    452    */
    453   public void setShouldCacheNodes(boolean b)
    454   {
    455     if (b)
    456     {
    457       if(!hasCache())
    458       {
    459         SetVector(new NodeVector());
    460       }
    461 //	  else
    462 //	    getVector().RemoveAllNoClear();  // Is this good?
    463     }
    464     else
    465       SetVector(null);
    466   }
    467 
    468   /**
    469    * @see DTMIterator#isMutable()
    470    */
    471   public boolean isMutable()
    472   {
    473     return hasCache(); // though may be surprising if it also has an iterator!
    474   }
    475 
    476   /**
    477    * @see DTMIterator#getCurrentPos()
    478    */
    479   public int getCurrentPos()
    480   {
    481     return m_next;
    482   }
    483 
    484   /**
    485    * @see DTMIterator#runTo(int)
    486    */
    487   public void runTo(int index)
    488   {
    489     int n;
    490 
    491     if (-1 == index)
    492     {
    493       int pos = m_next;
    494       while (DTM.NULL != (n = nextNode()));
    495       m_next = pos;
    496     }
    497     else if(m_next == index)
    498     {
    499       return;
    500     }
    501     else if(hasCache() && m_next < getVector().size())
    502     {
    503       m_next = index;
    504     }
    505     else if((null == getVector()) && (index < m_next))
    506     {
    507       while ((m_next >= index) && DTM.NULL != (n = previousNode()));
    508     }
    509     else
    510     {
    511       while ((m_next < index) && DTM.NULL != (n = nextNode()));
    512     }
    513 
    514   }
    515 
    516   /**
    517    * @see DTMIterator#setCurrentPos(int)
    518    */
    519   public void setCurrentPos(int i)
    520   {
    521   	runTo(i);
    522   }
    523 
    524   /**
    525    * @see DTMIterator#item(int)
    526    */
    527   public int item(int index)
    528   {
    529   	setCurrentPos(index);
    530   	int n = nextNode();
    531   	m_next = index;
    532   	return n;
    533   }
    534 
    535   /**
    536    * @see DTMIterator#setItem(int, int)
    537    */
    538   public void setItem(int node, int index)
    539   {
    540   	NodeVector vec = getVector();
    541   	if(null != vec)
    542   	{
    543         int oldNode = vec.elementAt(index);
    544         if (oldNode != node && m_cache.useCount() > 1) {
    545             /* If we are going to set the node at the given index
    546              * to a different value, and the cache is shared
    547              * (has a use count greater than 1)
    548              * then make a copy of the cache and use it
    549              * so we don't overwrite the value for other
    550              * users of the cache.
    551              */
    552             IteratorCache newCache = new IteratorCache();
    553             final NodeVector nv;
    554             try {
    555                 nv = (NodeVector) vec.clone();
    556             } catch (CloneNotSupportedException e) {
    557                 // This should never happen
    558                 e.printStackTrace();
    559                 RuntimeException rte = new RuntimeException(e.getMessage());
    560                 throw rte;
    561             }
    562             newCache.setVector(nv);
    563             newCache.setCacheComplete(true);
    564             m_cache = newCache;
    565             vec = nv;
    566 
    567             // Keep our superclass informed of the current NodeVector
    568             super.setObject(nv);
    569 
    570             /* When we get to here the new cache has
    571              * a use count of 1 and when setting a
    572              * bunch of values on the same NodeSequence,
    573              * such as when sorting, we will keep setting
    574              * values in that same copy which has a use count of 1.
    575              */
    576         }
    577   		vec.setElementAt(node, index);
    578   		m_last = vec.size();
    579   	}
    580   	else
    581   		m_iter.setItem(node, index);
    582   }
    583 
    584   /**
    585    * @see DTMIterator#getLength()
    586    */
    587   public int getLength()
    588   {
    589     IteratorCache cache = getCache();
    590 
    591   	if(cache != null)
    592   	{
    593         // Nodes from the iterator are cached
    594         if (cache.isComplete()) {
    595             // All of the nodes from the iterator are cached
    596             // so just return the number of nodes in the cache
    597             NodeVector nv = cache.getVector();
    598             return nv.size();
    599         }
    600 
    601         // If this NodeSequence wraps a mutable nodeset, then
    602         // m_last will not reflect the size of the nodeset if
    603         // it has been mutated...
    604         if (m_iter instanceof NodeSetDTM)
    605         {
    606             return m_iter.getLength();
    607         }
    608 
    609 	  	if(-1 == m_last)
    610 	  	{
    611 	  		int pos = m_next;
    612 	  		runTo(-1);
    613 	  		m_next = pos;
    614 	  	}
    615 	    return m_last;
    616   	}
    617   	else
    618   	{
    619   		return (-1 == m_last) ? (m_last = m_iter.getLength()) : m_last;
    620   	}
    621   }
    622 
    623   /**
    624    * Note: Not a deep clone.
    625    * @see DTMIterator#cloneWithReset()
    626    */
    627   public DTMIterator cloneWithReset() throws CloneNotSupportedException
    628   {
    629   	NodeSequence seq = (NodeSequence)super.clone();
    630     seq.m_next = 0;
    631     if (m_cache != null) {
    632         // In making this clone of an iterator we are making
    633         // another NodeSequence object it has a reference
    634         // to the same IteratorCache object as the original
    635         // so we need to remember that more than one
    636         // NodeSequence object shares the cache.
    637         m_cache.increaseUseCount();
    638     }
    639 
    640     return seq;
    641   }
    642 
    643   /**
    644    * Get a clone of this iterator, but don't reset the iteration in the
    645    * process, so that it may be used from the current position.
    646    * Note: Not a deep clone.
    647    *
    648    * @return A clone of this object.
    649    *
    650    * @throws CloneNotSupportedException
    651    */
    652   public Object clone() throws CloneNotSupportedException
    653   {
    654           NodeSequence clone = (NodeSequence) super.clone();
    655           if (null != m_iter) clone.m_iter = (DTMIterator) m_iter.clone();
    656           if (m_cache != null) {
    657               // In making this clone of an iterator we are making
    658               // another NodeSequence object it has a reference
    659               // to the same IteratorCache object as the original
    660               // so we need to remember that more than one
    661               // NodeSequence object shares the cache.
    662               m_cache.increaseUseCount();
    663           }
    664 
    665           return clone;
    666   }
    667 
    668 
    669   /**
    670    * @see DTMIterator#isDocOrdered()
    671    */
    672   public boolean isDocOrdered()
    673   {
    674   	if(null != m_iter)
    675   		return m_iter.isDocOrdered();
    676   	else
    677     	return true; // can't be sure?
    678   }
    679 
    680   /**
    681    * @see DTMIterator#getAxis()
    682    */
    683   public int getAxis()
    684   {
    685   	if(null != m_iter)
    686     	return m_iter.getAxis();
    687     else
    688     {
    689     	assertion(false, "Can not getAxis from a non-iterated node sequence!");
    690     	return 0;
    691     }
    692   }
    693 
    694   /**
    695    * @see PathComponent#getAnalysisBits()
    696    */
    697   public int getAnalysisBits()
    698   {
    699   	if((null != m_iter) && (m_iter instanceof PathComponent))
    700     	return ((PathComponent)m_iter).getAnalysisBits();
    701     else
    702     	return 0;
    703   }
    704 
    705   /**
    706    * @see org.apache.xpath.Expression#fixupVariables(Vector, int)
    707    */
    708   public void fixupVariables(Vector vars, int globalsSize)
    709   {
    710   	super.fixupVariables(vars, globalsSize);
    711   }
    712 
    713   /**
    714    * Add the node into a vector of nodes where it should occur in
    715    * document order.
    716    * @param node The node to be added.
    717    * @return insertIndex.
    718    * @throws RuntimeException thrown if this NodeSetDTM is not of
    719    * a mutable type.
    720    */
    721    protected int addNodeInDocOrder(int node)
    722    {
    723       assertion(hasCache(), "addNodeInDocOrder must be done on a mutable sequence!");
    724 
    725       int insertIndex = -1;
    726 
    727       NodeVector vec = getVector();
    728 
    729       // This needs to do a binary search, but a binary search
    730       // is somewhat tough because the sequence test involves
    731       // two nodes.
    732       int size = vec.size(), i;
    733 
    734       for (i = size - 1; i >= 0; i--)
    735       {
    736         int child = vec.elementAt(i);
    737 
    738         if (child == node)
    739         {
    740           i = -2; // Duplicate, suppress insert
    741 
    742           break;
    743         }
    744 
    745         DTM dtm = m_dtmMgr.getDTM(node);
    746         if (!dtm.isNodeAfter(node, child))
    747         {
    748           break;
    749         }
    750       }
    751 
    752       if (i != -2)
    753       {
    754         insertIndex = i + 1;
    755 
    756         vec.insertElementAt(node, insertIndex);
    757       }
    758 
    759       // checkDups();
    760       return insertIndex;
    761     } // end addNodeInDocOrder(Vector v, Object obj)
    762 
    763    /**
    764     * It used to be that many locations in the code simply
    765     * did an assignment to this.m_obj directly, rather than
    766     * calling the setObject(Object) method. The problem is
    767     * that our super-class would be updated on what the
    768     * cache associated with this NodeSequence, but
    769     * we wouldn't know ourselves.
    770     * <p>
    771     * All setting of m_obj is done through setObject() now,
    772     * and this method over-rides the super-class method.
    773     * So now we are in the loop have an opportunity
    774     * to update some caching information.
    775     *
    776     */
    777    protected void setObject(Object obj) {
    778        if (obj instanceof NodeVector) {
    779            // Keep our superclass informed of the current NodeVector
    780            // ... if we don't the smoketest fails (don't know why).
    781            super.setObject(obj);
    782 
    783            // A copy of the code of what SetVector() would do.
    784            NodeVector v = (NodeVector)obj;
    785            if (m_cache != null) {
    786                m_cache.setVector(v);
    787            } else if (v!=null) {
    788                m_cache = new IteratorCache();
    789                m_cache.setVector(v);
    790            }
    791        } else if (obj instanceof IteratorCache) {
    792            IteratorCache cache = (IteratorCache) obj;
    793            m_cache = cache;
    794            m_cache.increaseUseCount();
    795 
    796            // Keep our superclass informed of the current NodeVector
    797            super.setObject(cache.getVector());
    798        } else {
    799            super.setObject(obj);
    800        }
    801 
    802    }
    803 
    804    /**
    805     * Each NodeSequence object has an iterator which is "walked".
    806     * As an iterator is walked one obtains nodes from it.
    807     * As those nodes are obtained they may be cached, making
    808     * the next walking of a copy or clone of the iterator faster.
    809     * This field (m_cache) is a reference to such a cache,
    810     * which is populated as the iterator is walked.
    811     * <p>
    812     * Note that multiple NodeSequence objects may hold a
    813     * reference to the same cache, and also
    814     * (and this is important) the same iterator.
    815     * The iterator and its cache may be shared among
    816     * many NodeSequence objects.
    817     * <p>
    818     * If one of the NodeSequence objects walks ahead
    819     * of the others it fills in the cache.
    820     * As the others NodeSequence objects catch up they
    821     * get their values from
    822     * the cache rather than the iterator itself, so
    823     * the iterator is only ever walked once and everyone
    824     * benefits from the cache.
    825     * <p>
    826     * At some point the cache may be
    827     * complete due to walking to the end of one of
    828     * the copies of the iterator, and the cache is
    829     * then marked as "complete".
    830     * and the cache will have no more nodes added to it.
    831     * <p>
    832     * Its use-count is the number of NodeSequence objects that use it.
    833     */
    834    private final static class IteratorCache {
    835        /**
    836         * A list of nodes already obtained from the iterator.
    837         * As the iterator is walked the nodes obtained from
    838         * it are appended to this list.
    839         * <p>
    840         * Both an iterator and its corresponding cache can
    841         * be shared by multiple NodeSequence objects.
    842         * <p>
    843         * For example, consider three NodeSequence objects
    844         * ns1, ns2 and ns3 doing such sharing, and the
    845         * nodes to be obtaind from the iterator being
    846         * the sequence { 33, 11, 44, 22, 55 }.
    847         * <p>
    848         * If ns3.nextNode() is called 3 times the the
    849         * underlying iterator will have walked through
    850         * 33, 11, 55 and these three nodes will have been put
    851         * in the cache.
    852         * <p>
    853         * If ns2.nextNode() is called 2 times it will return
    854         * 33 and 11 from the cache, leaving the iterator alone.
    855         * <p>
    856         * If ns1.nextNode() is called 6 times it will return
    857         * 33 and 11 from the cache, then get 44, 22, 55 from
    858         * the iterator, and appending 44, 22, 55 to the cache.
    859         * On the sixth call it is found that the iterator is
    860         * exhausted and the cache is marked complete.
    861         * <p>
    862         * Should ns2 or ns3 have nextNode() called they will
    863         * know that the cache is complete, and they will
    864         * obtain all subsequent nodes from the cache.
    865         * <p>
    866         * Note that the underlying iterator, though shared
    867         * is only ever walked once.
    868         */
    869         private NodeVector m_vec2;
    870 
    871         /**
    872          * true if the associated iterator is exhausted and
    873          * all nodes obtained from it are in the cache.
    874          */
    875         private boolean m_isComplete2;
    876 
    877         private int m_useCount2;
    878 
    879         IteratorCache() {
    880             m_vec2 = null;
    881             m_isComplete2 = false;
    882             m_useCount2 = 1;
    883             return;
    884         }
    885 
    886         /**
    887          * Returns count of how many NodeSequence objects share this
    888          * IteratorCache object.
    889          */
    890         private int useCount() {
    891             return m_useCount2;
    892         }
    893 
    894         /**
    895          * This method is called when yet another
    896          * NodeSequence object uses, or shares
    897          * this same cache.
    898          *
    899          */
    900         private void increaseUseCount() {
    901             if (m_vec2 != null)
    902                 m_useCount2++;
    903 
    904         }
    905 
    906         /**
    907          * Sets the NodeVector that holds the
    908          * growing list of nodes as they are appended
    909          * to the cached list.
    910          */
    911         private void setVector(NodeVector nv) {
    912             m_vec2 = nv;
    913             m_useCount2 = 1;
    914         }
    915 
    916         /**
    917          * Get the cached list of nodes obtained from
    918          * the iterator so far.
    919          */
    920         private NodeVector getVector() {
    921             return m_vec2;
    922         }
    923 
    924         /**
    925          * Call this method with 'true' if the
    926          * iterator is exhausted and the cached list
    927          * is complete, or no longer growing.
    928          */
    929         private void setCacheComplete(boolean b) {
    930             m_isComplete2 = b;
    931 
    932         }
    933 
    934         /**
    935          * Returns true if no cache is complete
    936          * and immutable.
    937          */
    938         private boolean isComplete() {
    939             return m_isComplete2;
    940         }
    941     }
    942 
    943     /**
    944      * Get the cached list of nodes appended with
    945      * values obtained from the iterator as
    946      * a NodeSequence is walked when its
    947      * nextNode() method is called.
    948      */
    949     protected IteratorCache getIteratorCache() {
    950         return m_cache;
    951     }
    952 }
    953 
    954