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 */
21package org.apache.xpath.axes;
22
23import java.util.Vector;
24
25import org.apache.xml.dtm.DTM;
26import org.apache.xml.dtm.DTMFilter;
27import org.apache.xml.dtm.DTMIterator;
28import org.apache.xml.dtm.DTMManager;
29import org.apache.xml.utils.NodeVector;
30import org.apache.xpath.NodeSetDTM;
31import org.apache.xpath.XPathContext;
32import 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 */
38public 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