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    */
getVector()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    */
getCache()76   private IteratorCache getCache() {
77       return m_cache;
78   }
79 
80   /**
81    * Set the vector where nodes will be cached.
82    */
SetVector(NodeVector v)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    */
hasCache()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    */
cacheComplete()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    */
markCacheComplete()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    */
setIter(DTMIterator iter)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    */
getContainedIter()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    */
NodeSequence(DTMIterator iter, int context, XPathContext xctxt, boolean shouldCacheNodes)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    */
NodeSequence(Object nodeVector)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    */
NodeSequence(DTMManager dtmMgr)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    */
NodeSequence()211   public NodeSequence()
212   {
213       return;
214   }
215 
216 
217   /**
218    * @see DTMIterator#getDTM(int)
219    */
getDTM(int nodeHandle)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    */
getDTMManager()235   public DTMManager getDTMManager()
236   {
237     return m_dtmMgr;
238   }
239 
240   /**
241    * @see DTMIterator#getRoot()
242    */
getRoot()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    */
setRoot(int nodeHandle, Object environment)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    */
reset()281   public void reset()
282   {
283   	m_next = 0;
284   	// not resetting the iterator on purpose!!!
285   }
286 
287   /**
288    * @see DTMIterator#getWhatToShow()
289    */
getWhatToShow()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    */
getExpandEntityReferences()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    */
nextNode()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    */
previousNode()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    */
detach()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    */
allowDetachToRelease(boolean allowRelease)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    */
getCurrentNode()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    */
isFresh()445   public boolean isFresh()
446   {
447     return (0 == m_next);
448   }
449 
450   /**
451    * @see DTMIterator#setShouldCacheNodes(boolean)
452    */
setShouldCacheNodes(boolean b)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    */
isMutable()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    */
getCurrentPos()479   public int getCurrentPos()
480   {
481     return m_next;
482   }
483 
484   /**
485    * @see DTMIterator#runTo(int)
486    */
runTo(int index)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    */
setCurrentPos(int i)519   public void setCurrentPos(int i)
520   {
521   	runTo(i);
522   }
523 
524   /**
525    * @see DTMIterator#item(int)
526    */
item(int index)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    */
setItem(int node, int index)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    */
getLength()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    */
cloneWithReset()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    */
clone()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    */
isDocOrdered()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    */
getAxis()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    */
getAnalysisBits()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    */
fixupVariables(Vector vars, int globalsSize)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    */
addNodeInDocOrder(int node)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     */
setObject(Object obj)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 
IteratorCache()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          */
useCount()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          */
increaseUseCount()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          */
setVector(NodeVector nv)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          */
getVector()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          */
setCacheComplete(boolean b)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          */
isComplete()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      */
getIteratorCache()949     protected IteratorCache getIteratorCache() {
950         return m_cache;
951     }
952 }
953 
954