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