1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 /* 19 * $Id: NodeSequence.java 469367 2006-10-31 04:41:08Z minchau $ 20 */ 21 package org.apache.xpath.axes; 22 23 import java.util.Vector; 24 25 import org.apache.xml.dtm.DTM; 26 import org.apache.xml.dtm.DTMFilter; 27 import org.apache.xml.dtm.DTMIterator; 28 import org.apache.xml.dtm.DTMManager; 29 import org.apache.xml.utils.NodeVector; 30 import org.apache.xpath.NodeSetDTM; 31 import org.apache.xpath.XPathContext; 32 import org.apache.xpath.objects.XObject; 33 34 /** 35 * This class is the dynamic wrapper for a Xalan DTMIterator instance, and 36 * provides random access capabilities. 37 */ 38 public class NodeSequence extends XObject 39 implements DTMIterator, Cloneable, PathComponent 40 { 41 static final long serialVersionUID = 3866261934726581044L; 42 /** The index of the last node in the iteration. */ 43 protected int m_last = -1; 44 45 /** 46 * The index of the next node to be fetched. Useful if this 47 * is a cached iterator, and is being used as random access 48 * NodeList. 49 */ 50 protected int m_next = 0; 51 52 /** 53 * A cache of a list of nodes obtained from the iterator so far. 54 * This list is appended to until the iterator is exhausted and 55 * the cache is complete. 56 * <p> 57 * Multiple NodeSequence objects may share the same cache. 58 */ 59 private IteratorCache m_cache; 60 61 /** 62 * If this iterator needs to cache nodes that are fetched, they 63 * are stored in the Vector in the generic object. 64 */ 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