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