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    */
  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    */
  78   private IteratorCache getCache() {
  79       return m_cache;
  80   }
  81 
  82   /**
  83    * Set the vector where nodes will be cached.
  84    */
  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    */
  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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 213   public NodeSequence()
 214   {
 215       return;
 216   }
 217 
 218 
 219   /**
 220    * @see DTMIterator#getDTM(int)
 221    */
 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    */
 237   public DTMManager getDTMManager()
 238   {
 239     return m_dtmMgr;
 240   }
 241 
 242   /**
 243    * @see DTMIterator#getRoot()
 244    */
 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    */
 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    */
 290   public void reset()
 291   {
 292         m_next = 0;
 293         // not resetting the iterator on purpose!!!
 294   }
 295 
 296   /**
 297    * @see DTMIterator#getWhatToShow()
 298    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 454   public boolean isFresh()
 455   {
 456     return (0 == m_next);
 457   }
 458 
 459   /**
 460    * @see DTMIterator#setShouldCacheNodes(boolean)
 461    */
 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    */
 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    */
 488   public int getCurrentPos()
 489   {
 490     return m_next;
 491   }
 492 
 493   /**
 494    * @see DTMIterator#runTo(int)
 495    */
 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    */
 528   public void setCurrentPos(int i)
 529   {
 530         runTo(i);
 531   }
 532 
 533   /**
 534    * @see DTMIterator#item(int)
 535    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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    */
 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     */
 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 
 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          */
 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          */
 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          */
 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          */
 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          */
 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          */
 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      */
 958     protected IteratorCache getIteratorCache() {
 959         return m_cache;
 960     }
 961 }