001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: NodeSequence.java 469367 2006-10-31 04:41:08Z minchau $
020     */
021    package org.apache.xpath.axes;
022    
023    import java.util.Vector;
024    
025    import org.apache.xml.dtm.DTM;
026    import org.apache.xml.dtm.DTMFilter;
027    import org.apache.xml.dtm.DTMIterator;
028    import org.apache.xml.dtm.DTMManager;
029    import org.apache.xml.utils.NodeVector;
030    import org.apache.xpath.NodeSetDTM;
031    import org.apache.xpath.XPathContext;
032    import org.apache.xpath.objects.XObject;
033    
034    /**
035     * This class is the dynamic wrapper for a Xalan DTMIterator instance, and 
036     * provides random access capabilities.
037     */
038    public class NodeSequence extends XObject
039      implements DTMIterator, Cloneable, PathComponent
040    {
041        static final long serialVersionUID = 3866261934726581044L;
042      /** The index of the last node in the iteration. */
043      protected int m_last = -1;
044      
045      /**
046       * The index of the next node to be fetched.  Useful if this
047       * is a cached iterator, and is being used as random access
048       * NodeList.
049       */
050      protected int m_next = 0;
051        
052      /**
053       * A cache of a list of nodes obtained from the iterator so far.
054       * This list is appended to until the iterator is exhausted and
055       * the cache is complete.
056       * <p>
057       * Multiple NodeSequence objects may share the same cache.
058       */
059      private IteratorCache m_cache;
060      
061      /**
062       * If this iterator needs to cache nodes that are fetched, they
063       * are stored in the Vector in the generic object.
064       */
065      protected NodeVector getVector() {
066          NodeVector nv = (m_cache != null) ?  m_cache.getVector() : null;
067          return nv;
068      }
069      
070      /**
071       * Get the cache (if any) of nodes obtained from
072       * the iterator so far. Note that the cache keeps
073       * growing until the iterator is walked to exhaustion,
074       * at which point the cache is "complete".
075       */
076      private IteratorCache getCache() {
077          return m_cache;
078      }
079      
080      /**
081       * Set the vector where nodes will be cached.
082       */
083      protected void SetVector(NodeVector v)
084      {
085            setObject(v);
086      }
087    
088      
089      /**
090       * If the iterator needs to cache nodes as they are fetched,
091       * then this method returns true. 
092       */
093      public boolean hasCache()
094      {
095        final NodeVector nv = getVector();
096            return (nv != null);
097      }
098      
099      /**
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