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: NodeSetDTM.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath;
022    
023    import org.apache.xalan.res.XSLMessages;
024    import org.apache.xml.dtm.DTM;
025    import org.apache.xml.dtm.DTMFilter;
026    import org.apache.xml.dtm.DTMIterator;
027    import org.apache.xml.dtm.DTMManager;
028    import org.apache.xml.utils.NodeVector;
029    import org.apache.xpath.res.XPATHErrorResources;
030    
031    import org.w3c.dom.Node;
032    import org.w3c.dom.NodeList;
033    import org.w3c.dom.traversal.NodeIterator;
034    
035    
036    /**
037     * <p>The NodeSetDTM class can act as either a NodeVector,
038     * NodeList, or NodeIterator.  However, in order for it to
039     * act as a NodeVector or NodeList, it's required that
040     * setShouldCacheNodes(true) be called before the first
041     * nextNode() is called, in order that nodes can be added
042     * as they are fetched.  Derived classes that implement iterators
043     * must override runTo(int index), in order that they may
044     * run the iteration to the given index. </p>
045     * 
046     * <p>Note that we directly implement the DOM's NodeIterator
047     * interface. We do not emulate all the behavior of the
048     * standard NodeIterator. In particular, we do not guarantee
049     * to present a "live view" of the document ... but in XSLT,
050     * the source document should never be mutated, so this should
051     * never be an issue.</p>
052     * 
053     * <p>Thought: Should NodeSetDTM really implement NodeList and NodeIterator,
054     * or should there be specific subclasses of it which do so? The
055     * advantage of doing it all here is that all NodeSetDTMs will respond
056     * to the same calls; the disadvantage is that some of them may return
057     * less-than-enlightening results when you do so.</p>
058     * @xsl.usage advanced
059     */
060    public class NodeSetDTM extends NodeVector
061            implements /* NodeList, NodeIterator, */ DTMIterator, 
062            Cloneable
063    {
064        static final long serialVersionUID = 7686480133331317070L;
065    
066      /**
067       * Create an empty nodelist.
068       */
069      public NodeSetDTM(DTMManager dtmManager)
070      {
071        super();
072        m_manager = dtmManager;
073      }
074    
075      /**
076       * Create an empty, using the given block size.
077       *
078       * @param blocksize Size of blocks to allocate 
079       * @param dummy pass zero for right now...
080       */
081      public NodeSetDTM(int blocksize, int dummy, DTMManager dtmManager)
082      {
083        super(blocksize);
084        m_manager = dtmManager;
085      }
086    
087      // %TBD%
088    //  /**
089    //   * Create a NodeSetDTM, and copy the members of the
090    //   * given nodelist into it.
091    //   *
092    //   * @param nodelist List of Nodes to be made members of the new set.
093    //   */
094    //  public NodeSetDTM(NodeList nodelist)
095    //  {
096    //
097    //    super();
098    //
099    //    addNodes(nodelist);
100    //  }
101    
102      /**
103       * Create a NodeSetDTM, and copy the members of the
104       * given NodeSetDTM into it.
105       *
106       * @param nodelist Set of Nodes to be made members of the new set.
107       */
108      public NodeSetDTM(NodeSetDTM nodelist)
109      {
110    
111        super();
112        m_manager = nodelist.getDTMManager();
113        m_root = nodelist.getRoot();
114    
115        addNodes((DTMIterator) nodelist);
116      }
117    
118      /**
119       * Create a NodeSetDTM, and copy the members of the
120       * given DTMIterator into it.
121       *
122       * @param ni Iterator which yields Nodes to be made members of the new set.
123       */
124      public NodeSetDTM(DTMIterator ni)
125      {
126    
127        super();
128    
129        m_manager = ni.getDTMManager();
130        m_root = ni.getRoot();
131        addNodes(ni);
132      }
133      
134      /**
135       * Create a NodeSetDTM, and copy the members of the
136       * given DTMIterator into it.
137       *
138       * @param iterator Iterator which yields Nodes to be made members of the new set.
139       */
140      public NodeSetDTM(NodeIterator iterator, XPathContext xctxt)
141      {
142    
143        super();
144    
145        Node node;
146        m_manager = xctxt.getDTMManager();
147    
148        while (null != (node = iterator.nextNode()))
149        {
150          int handle = xctxt.getDTMHandleFromNode(node);
151          addNodeInDocOrder(handle, xctxt);
152        }
153      }
154      
155      /**
156       * Create a NodeSetDTM, and copy the members of the
157       * given DTMIterator into it.
158       *
159       */
160      public NodeSetDTM(NodeList nodeList, XPathContext xctxt)
161      {
162    
163        super();
164    
165        m_manager = xctxt.getDTMManager();
166    
167        int n = nodeList.getLength();
168        for (int i = 0; i < n; i++) 
169        {
170          Node node = nodeList.item(i);
171          int handle = xctxt.getDTMHandleFromNode(node);
172          // Do not reorder or strip duplicate nodes from the given DOM nodelist
173          addNode(handle); // addNodeInDocOrder(handle, xctxt);  
174        } 
175      }
176    
177    
178      /**
179       * Create a NodeSetDTM which contains the given Node.
180       *
181       * @param node Single node to be added to the new set.
182       */
183      public NodeSetDTM(int node, DTMManager dtmManager)
184      {
185    
186        super();
187        m_manager = dtmManager;
188    
189        addNode(node);
190      }
191      
192      /**
193       * Set the environment in which this iterator operates, which should provide:
194       * a node (the context node... same value as "root" defined below) 
195       * a pair of non-zero positive integers (the context position and the context size) 
196       * a set of variable bindings 
197       * a function library 
198       * the set of namespace declarations in scope for the expression.
199       * 
200       * <p>At this time the exact implementation of this environment is application 
201       * dependent.  Probably a proper interface will be created fairly soon.</p>
202       * 
203       * @param environment The environment object.
204       */
205      public void setEnvironment(Object environment)
206      {
207        // no-op
208      }
209    
210    
211      /**
212       * @return The root node of the Iterator, as specified when it was created.
213       * For non-Iterator NodeSetDTMs, this will be null.
214       */
215      public int getRoot()
216      {
217        if(DTM.NULL == m_root)
218        {
219          if(size() > 0)
220            return item(0);
221          else
222            return DTM.NULL;
223        }
224        else
225          return m_root;
226      }
227      
228      /**
229       * Initialize the context values for this expression
230       * after it is cloned.
231       *
232       * @param context The XPath runtime context for this
233       * transformation.
234       */
235      public void setRoot(int context, Object environment)
236      {
237        // no-op, I guess...  (-sb)
238      }
239    
240      /**
241       * Clone this NodeSetDTM.
242       * At this time, we only expect this to be used with LocPathIterators;
243       * it may not work with other kinds of NodeSetDTMs.
244       *
245       * @return a new NodeSetDTM of the same type, having the same state...
246       * though unless overridden in the subclasses, it may not copy all
247       * the state information.
248       *
249       * @throws CloneNotSupportedException if this subclass of NodeSetDTM
250       * does not support the clone() operation.
251       */
252      public Object clone() throws CloneNotSupportedException
253      {
254    
255        NodeSetDTM clone = (NodeSetDTM) super.clone();
256    
257        return clone;
258      }
259    
260      /**
261       * Get a cloned Iterator, and reset its state to the beginning of the
262       * iteration.
263       *
264       * @return a new NodeSetDTM of the same type, having the same state...
265       * except that the reset() operation has been called.
266       *
267       * @throws CloneNotSupportedException if this subclass of NodeSetDTM
268       * does not support the clone() operation.
269       */
270      public DTMIterator cloneWithReset() throws CloneNotSupportedException
271      {
272    
273        NodeSetDTM clone = (NodeSetDTM) clone();
274    
275        clone.reset();
276    
277        return clone;
278      }
279    
280      /**
281       * Reset the iterator. May have no effect on non-iterator Nodesets.
282       */
283      public void reset()
284      {
285        m_next = 0;
286      }
287    
288      /**
289       *  This attribute determines which node types are presented via the
290       * iterator. The available set of constants is defined in the
291       * <code>DTMFilter</code> interface. For NodeSetDTMs, the mask has been
292       * hardcoded to show all nodes except EntityReference nodes, which have
293       * no equivalent in the XPath data model.
294       *
295       * @return integer used as a bit-array, containing flags defined in
296       * the DOM's DTMFilter class. The value will be 
297       * <code>SHOW_ALL & ~SHOW_ENTITY_REFERENCE</code>, meaning that
298       * only entity references are suppressed.
299       */
300      public int getWhatToShow()
301      {
302        return DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE;
303      }
304    
305      /**
306       * The filter object used to screen nodes. Filters are applied to
307       * further reduce (and restructure) the DTMIterator's view of the
308       * document. In our case, we will be using hardcoded filters built
309       * into our iterators... but getFilter() is part of the DOM's 
310       * DTMIterator interface, so we have to support it.
311       *
312       * @return null, which is slightly misleading. True, there is no
313       * user-written filter object, but in fact we are doing some very
314       * sophisticated custom filtering. A DOM purist might suggest
315       * returning a placeholder object just to indicate that this is
316       * not going to return all nodes selected by whatToShow.
317       */
318      public DTMFilter getFilter()
319      {
320        return null;
321      }
322    
323      /**
324       *  The value of this flag determines whether the children of entity
325       * reference nodes are visible to the iterator. If false, they will be
326       * skipped over.
327       * <br> To produce a view of the document that has entity references
328       * expanded and does not expose the entity reference node itself, use the
329       * whatToShow flags to hide the entity reference node and set
330       * expandEntityReferences to true when creating the iterator. To produce
331       * a view of the document that has entity reference nodes but no entity
332       * expansion, use the whatToShow flags to show the entity reference node
333       * and set expandEntityReferences to false.
334       *
335       * @return true for all iterators based on NodeSetDTM, meaning that the
336       * contents of EntityRefrence nodes may be returned (though whatToShow
337       * says that the EntityReferences themselves are not shown.)
338       */
339      public boolean getExpandEntityReferences()
340      {
341        return true;
342      }
343      
344      /**
345       * Get an instance of a DTM that "owns" a node handle.  Since a node 
346       * iterator may be passed without a DTMManager, this allows the 
347       * caller to easily get the DTM using just the iterator.
348       *
349       * @param nodeHandle the nodeHandle.
350       *
351       * @return a non-null DTM reference.
352       */
353      public DTM getDTM(int nodeHandle)
354      {
355        
356        return m_manager.getDTM(nodeHandle);
357      }
358      
359      /* An instance of the DTMManager. */
360      DTMManager m_manager;
361      
362      /**
363       * Get an instance of the DTMManager.  Since a node 
364       * iterator may be passed without a DTMManager, this allows the 
365       * caller to easily get the DTMManager using just the iterator.
366       *
367       * @return a non-null DTMManager reference.
368       */
369      public DTMManager getDTMManager()
370      {
371        
372        return m_manager;
373      }
374    
375      /**
376       *  Returns the next node in the set and advances the position of the
377       * iterator in the set. After a DTMIterator is created, the first call
378       * to nextNode() returns the first node in the set.
379       * @return  The next <code>Node</code> in the set being iterated over, or
380       *   <code>DTM.NULL</code> if there are no more members in that set.
381       * @throws DOMException
382       *    INVALID_STATE_ERR: Raised if this method is called after the
383       *   <code>detach</code> method was invoked.
384       */
385      public int nextNode()
386      {
387    
388        if ((m_next) < this.size())
389        {
390          int next = this.elementAt(m_next);
391    
392          m_next++;
393    
394          return next;
395        }
396        else
397          return DTM.NULL;
398      }
399    
400      /**
401       *  Returns the previous node in the set and moves the position of the
402       * iterator backwards in the set.
403       * @return  The previous <code>Node</code> in the set being iterated over,
404       *   or<code>DTM.NULL</code> if there are no more members in that set.
405       * @throws DOMException
406       *    INVALID_STATE_ERR: Raised if this method is called after the
407       *   <code>detach</code> method was invoked.
408       * @throws RuntimeException thrown if this NodeSetDTM is not of 
409       * a cached type, and hence doesn't know what the previous node was.
410       */
411      public int previousNode()
412      {
413    
414        if (!m_cacheNodes)
415          throw new RuntimeException(
416            XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_ITERATE, null)); //"This NodeSetDTM can not iterate to a previous node!");
417    
418        if ((m_next - 1) > 0)
419        {
420          m_next--;
421    
422          return this.elementAt(m_next);
423        }
424        else
425          return DTM.NULL;
426      }
427    
428      /**
429       * Detaches the iterator from the set which it iterated over, releasing
430       * any computational resources and placing the iterator in the INVALID
431       * state. After<code>detach</code> has been invoked, calls to
432       * <code>nextNode</code> or<code>previousNode</code> will raise the
433       * exception INVALID_STATE_ERR.
434       * <p>
435       * This operation is a no-op in NodeSetDTM, and will not cause 
436       * INVALID_STATE_ERR to be raised by later operations.
437       * </p>
438       */
439      public void detach(){}
440      
441      /**
442       * Specify if it's OK for detach to release the iterator for reuse.
443       * 
444       * @param allowRelease true if it is OK for detach to release this iterator 
445       * for pooling.
446       */
447      public void allowDetachToRelease(boolean allowRelease)
448      {
449        // no action for right now.
450      }
451    
452    
453      /**
454       * Tells if this NodeSetDTM is "fresh", in other words, if
455       * the first nextNode() that is called will return the
456       * first node in the set.
457       *
458       * @return true if nextNode() would return the first node in the set,
459       * false if it would return a later one.
460       */
461      public boolean isFresh()
462      {
463        return (m_next == 0);
464      }
465    
466      /**
467       * If an index is requested, NodeSetDTM will call this method
468       * to run the iterator to the index.  By default this sets
469       * m_next to the index.  If the index argument is -1, this
470       * signals that the iterator should be run to the end.
471       *
472       * @param index Position to advance (or retreat) to, with
473       * 0 requesting the reset ("fresh") position and -1 (or indeed
474       * any out-of-bounds value) requesting the final position.
475       * @throws RuntimeException thrown if this NodeSetDTM is not
476       * one of the types which supports indexing/counting.
477       */
478      public void runTo(int index)
479      {
480    
481        if (!m_cacheNodes)
482          throw new RuntimeException(
483            XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_INDEX, null)); //"This NodeSetDTM can not do indexing or counting functions!");
484    
485        if ((index >= 0) && (m_next < m_firstFree))
486          m_next = index;
487        else
488          m_next = m_firstFree - 1;
489      }
490    
491      /**
492       * Returns the <code>index</code>th item in the collection. If
493       * <code>index</code> is greater than or equal to the number of nodes in
494       * the list, this returns <code>null</code>.
495       * 
496       * TODO: What happens if index is out of range?
497       * 
498       * @param index Index into the collection.
499       * @return The node at the <code>index</code>th position in the
500       *   <code>NodeList</code>, or <code>null</code> if that is not a valid
501       *   index.
502       */
503      public int item(int index)
504      {
505    
506        runTo(index);
507    
508        return this.elementAt(index);
509      }
510    
511      /**
512       * The number of nodes in the list. The range of valid child node indices is
513       * 0 to <code>length-1</code> inclusive. Note that this operation requires
514       * finding all the matching nodes, which may defeat attempts to defer
515       * that work.
516       *
517       * @return integer indicating how many nodes are represented by this list.
518       */
519      public int getLength()
520      {
521    
522        runTo(-1);
523    
524        return this.size();
525      }
526    
527      /**
528       * Add a node to the NodeSetDTM. Not all types of NodeSetDTMs support this
529       * operation
530       *
531       * @param n Node to be added
532       * @throws RuntimeException thrown if this NodeSetDTM is not of 
533       * a mutable type.
534       */
535      public void addNode(int n)
536      {
537    
538        if (!m_mutable)
539          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
540    
541        this.addElement(n);
542      }
543    
544      /**
545       * Insert a node at a given position.
546       *
547       * @param n Node to be added
548       * @param pos Offset at which the node is to be inserted,
549       * with 0 being the first position.
550       * @throws RuntimeException thrown if this NodeSetDTM is not of 
551       * a mutable type.
552       */
553      public void insertNode(int n, int pos)
554      {
555    
556        if (!m_mutable)
557          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
558    
559        insertElementAt(n, pos);
560      }
561    
562      /**
563       * Remove a node.
564       *
565       * @param n Node to be added
566       * @throws RuntimeException thrown if this NodeSetDTM is not of 
567       * a mutable type.
568       */
569      public void removeNode(int n)
570      {
571    
572        if (!m_mutable)
573          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
574    
575        this.removeElement(n);
576      }
577    
578      // %TBD%
579    //  /**
580    //   * Copy NodeList members into this nodelist, adding in
581    //   * document order.  If a node is null, don't add it.
582    //   *
583    //   * @param nodelist List of nodes which should now be referenced by
584    //   * this NodeSetDTM.
585    //   * @throws RuntimeException thrown if this NodeSetDTM is not of 
586    //   * a mutable type.
587    //   */
588    //  public void addNodes(NodeList nodelist)
589    //  {
590    //
591    //    if (!m_mutable)
592    //      throw new RuntimeException("This NodeSetDTM is not mutable!");
593    //
594    //    if (null != nodelist)  // defensive to fix a bug that Sanjiva reported.
595    //    {
596    //      int nChildren = nodelist.getLength();
597    //
598    //      for (int i = 0; i < nChildren; i++)
599    //      {
600    //        int obj = nodelist.item(i);
601    //
602    //        if (null != obj)
603    //        {
604    //          addElement(obj);
605    //        }
606    //      }
607    //    }
608    //
609    //    // checkDups();
610    //  }
611    
612      // %TBD%
613    //  /**
614    //   * <p>Copy NodeList members into this nodelist, adding in
615    //   * document order.  Only genuine node references will be copied;
616    //   * nulls appearing in the source NodeSetDTM will
617    //   * not be added to this one. </p>
618    //   * 
619    //   * <p> In case you're wondering why this function is needed: NodeSetDTM
620    //   * implements both DTMIterator and NodeList. If this method isn't
621    //   * provided, Java can't decide which of those to use when addNodes()
622    //   * is invoked. Providing the more-explicit match avoids that
623    //   * ambiguity.)</p>
624    //   *
625    //   * @param ns NodeSetDTM whose members should be merged into this NodeSetDTM.
626    //   * @throws RuntimeException thrown if this NodeSetDTM is not of 
627    //   * a mutable type.
628    //   */
629    //  public void addNodes(NodeSetDTM ns)
630    //  {
631    //
632    //    if (!m_mutable)
633    //      throw new RuntimeException("This NodeSetDTM is not mutable!");
634    //
635    //    addNodes((DTMIterator) ns);
636    //  }
637    
638      /**
639       * Copy NodeList members into this nodelist, adding in
640       * document order.  Null references are not added.
641       *
642       * @param iterator DTMIterator which yields the nodes to be added.
643       * @throws RuntimeException thrown if this NodeSetDTM is not of 
644       * a mutable type.
645       */
646      public void addNodes(DTMIterator iterator)
647      {
648    
649        if (!m_mutable)
650          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
651    
652        if (null != iterator)  // defensive to fix a bug that Sanjiva reported.
653        {
654          int obj;
655    
656          while (DTM.NULL != (obj = iterator.nextNode()))
657          {
658            addElement(obj);
659          }
660        }
661    
662        // checkDups();
663      }
664    
665      // %TBD%
666    //  /**
667    //   * Copy NodeList members into this nodelist, adding in
668    //   * document order.  If a node is null, don't add it.
669    //   *
670    //   * @param nodelist List of nodes to be added
671    //   * @param support The XPath runtime context.
672    //   * @throws RuntimeException thrown if this NodeSetDTM is not of 
673    //   * a mutable type.
674    //   */
675    //  public void addNodesInDocOrder(NodeList nodelist, XPathContext support)
676    //  {
677    //
678    //    if (!m_mutable)
679    //      throw new RuntimeException("This NodeSetDTM is not mutable!");
680    //
681    //    int nChildren = nodelist.getLength();
682    //
683    //    for (int i = 0; i < nChildren; i++)
684    //    {
685    //      int node = nodelist.item(i);
686    //
687    //      if (null != node)
688    //      {
689    //        addNodeInDocOrder(node, support);
690    //      }
691    //    }
692    //  }
693    
694      /**
695       * Copy NodeList members into this nodelist, adding in
696       * document order.  If a node is null, don't add it.
697       *
698       * @param iterator DTMIterator which yields the nodes to be added.
699       * @param support The XPath runtime context.
700       * @throws RuntimeException thrown if this NodeSetDTM is not of 
701       * a mutable type.
702       */
703      public void addNodesInDocOrder(DTMIterator iterator, XPathContext support)
704      {
705    
706        if (!m_mutable)
707          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
708    
709        int node;
710    
711        while (DTM.NULL != (node = iterator.nextNode()))
712        {
713          addNodeInDocOrder(node, support);
714        }
715      }
716    
717      // %TBD%
718    //  /**
719    //   * Add the node list to this node set in document order.
720    //   *
721    //   * @param start index.
722    //   * @param end index.
723    //   * @param testIndex index.
724    //   * @param nodelist The nodelist to add.
725    //   * @param support The XPath runtime context.
726    //   *
727    //   * @return false always.
728    //   * @throws RuntimeException thrown if this NodeSetDTM is not of 
729    //   * a mutable type.
730    //   */
731    //  private boolean addNodesInDocOrder(int start, int end, int testIndex,
732    //                                     NodeList nodelist, XPathContext support)
733    //  {
734    //
735    //    if (!m_mutable)
736    //      throw new RuntimeException("This NodeSetDTM is not mutable!");
737    //
738    //    boolean foundit = false;
739    //    int i;
740    //    int node = nodelist.item(testIndex);
741    //
742    //    for (i = end; i >= start; i--)
743    //    {
744    //      int child = elementAt(i);
745    //
746    //      if (child == node)
747    //      {
748    //        i = -2;  // Duplicate, suppress insert
749    //
750    //        break;
751    //      }
752    //
753    //      if (!support.getDOMHelper().isNodeAfter(node, child))
754    //      {
755    //        insertElementAt(node, i + 1);
756    //
757    //        testIndex--;
758    //
759    //        if (testIndex > 0)
760    //        {
761    //          boolean foundPrev = addNodesInDocOrder(0, i, testIndex, nodelist,
762    //                                                 support);
763    //
764    //          if (!foundPrev)
765    //          {
766    //            addNodesInDocOrder(i, size() - 1, testIndex, nodelist, support);
767    //          }
768    //        }
769    //
770    //        break;
771    //      }
772    //    }
773    //
774    //    if (i == -1)
775    //    {
776    //      insertElementAt(node, 0);
777    //    }
778    //
779    //    return foundit;
780    //  }
781    
782      /**
783       * Add the node into a vector of nodes where it should occur in
784       * document order.
785       * @param node The node to be added.
786       * @param test true if we should test for doc order
787       * @param support The XPath runtime context.
788       * @return insertIndex.
789       * @throws RuntimeException thrown if this NodeSetDTM is not of 
790       * a mutable type.
791       */
792      public int addNodeInDocOrder(int node, boolean test, XPathContext support)
793      {
794    
795        if (!m_mutable)
796          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
797    
798        int insertIndex = -1;
799    
800        if (test)
801        {
802    
803          // This needs to do a binary search, but a binary search 
804          // is somewhat tough because the sequence test involves 
805          // two nodes.
806          int size = size(), i;
807    
808          for (i = size - 1; i >= 0; i--)
809          {
810            int child = elementAt(i);
811    
812            if (child == node)
813            {
814              i = -2;  // Duplicate, suppress insert
815    
816              break;
817            }
818    
819            DTM dtm = support.getDTM(node);
820            if (!dtm.isNodeAfter(node, child))
821            {
822              break;
823            }
824          }
825    
826          if (i != -2)
827          {
828            insertIndex = i + 1;
829    
830            insertElementAt(node, insertIndex);
831          }
832        }
833        else
834        {
835          insertIndex = this.size();
836    
837          boolean foundit = false;
838    
839          for (int i = 0; i < insertIndex; i++)
840          {
841            if (i == node)
842            {
843              foundit = true;
844    
845              break;
846            }
847          }
848    
849          if (!foundit)
850            addElement(node);
851        }
852    
853        // checkDups();
854        return insertIndex;
855      }  // end addNodeInDocOrder(Vector v, Object obj)
856    
857      /**
858       * Add the node into a vector of nodes where it should occur in
859       * document order.
860       * @param node The node to be added.
861       * @param support The XPath runtime context.
862       *
863       * @return The index where it was inserted.
864       * @throws RuntimeException thrown if this NodeSetDTM is not of 
865       * a mutable type.
866       */
867      public int addNodeInDocOrder(int node, XPathContext support)
868      {
869    
870        if (!m_mutable)
871          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
872    
873        return addNodeInDocOrder(node, true, support);
874      }  // end addNodeInDocOrder(Vector v, Object obj)
875    
876      /**
877       * Get the length of the list.
878       *
879       * @return The size of this node set.
880       */
881      public int size()
882      {
883        return super.size();
884      }
885    
886      /**
887       * Append a Node onto the vector.
888       *
889       * @param value The node to be added.
890       * @throws RuntimeException thrown if this NodeSetDTM is not of 
891       * a mutable type.
892       */
893      public void addElement(int value)
894      {
895    
896        if (!m_mutable)
897          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
898    
899        super.addElement(value);
900      }
901    
902      /**
903       * Inserts the specified node in this vector at the specified index.
904       * Each component in this vector with an index greater or equal to
905       * the specified index is shifted upward to have an index one greater
906       * than the value it had previously.
907       *
908       * @param value The node to be inserted.
909       * @param at The index where the insert should occur.
910       * @throws RuntimeException thrown if this NodeSetDTM is not of 
911       * a mutable type.
912       */
913      public void insertElementAt(int value, int at)
914      {
915    
916        if (!m_mutable)
917          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
918    
919        super.insertElementAt(value, at);
920      }
921    
922      /**
923       * Append the nodes to the list.
924       *
925       * @param nodes The nodes to be appended to this node set.
926       * @throws RuntimeException thrown if this NodeSetDTM is not of 
927       * a mutable type.
928       */
929      public void appendNodes(NodeVector nodes)
930      {
931    
932        if (!m_mutable)
933          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
934    
935        super.appendNodes(nodes);
936      }
937    
938      /**
939       * Inserts the specified node in this vector at the specified index.
940       * Each component in this vector with an index greater or equal to
941       * the specified index is shifted upward to have an index one greater
942       * than the value it had previously.
943       * @throws RuntimeException thrown if this NodeSetDTM is not of 
944       * a mutable type.
945       */
946      public void removeAllElements()
947      {
948    
949        if (!m_mutable)
950          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
951    
952        super.removeAllElements();
953      }
954    
955      /**
956       * Removes the first occurrence of the argument from this vector.
957       * If the object is found in this vector, each component in the vector
958       * with an index greater or equal to the object's index is shifted
959       * downward to have an index one smaller than the value it had
960       * previously.
961       *
962       * @param s The node to be removed.
963       *
964       * @return True if the node was successfully removed
965       * @throws RuntimeException thrown if this NodeSetDTM is not of 
966       * a mutable type.
967       */
968      public boolean removeElement(int s)
969      {
970    
971        if (!m_mutable)
972          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
973    
974        return super.removeElement(s);
975      }
976    
977      /**
978       * Deletes the component at the specified index. Each component in
979       * this vector with an index greater or equal to the specified
980       * index is shifted downward to have an index one smaller than
981       * the value it had previously.
982       *
983       * @param i The index of the node to be removed.
984       * @throws RuntimeException thrown if this NodeSetDTM is not of 
985       * a mutable type.
986       */
987      public void removeElementAt(int i)
988      {
989    
990        if (!m_mutable)
991          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
992    
993        super.removeElementAt(i);
994      }
995    
996      /**
997       * Sets the component at the specified index of this vector to be the
998       * specified object. The previous component at that position is discarded.
999       *
1000       * The index must be a value greater than or equal to 0 and less
1001       * than the current size of the vector.
1002       *
1003       * @param node  The node to be set.
1004       * @param index The index of the node to be replaced.
1005       * @throws RuntimeException thrown if this NodeSetDTM is not of 
1006       * a mutable type.
1007       */
1008      public void setElementAt(int node, int index)
1009      {
1010    
1011        if (!m_mutable)
1012          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
1013    
1014        super.setElementAt(node, index);
1015      }
1016      
1017      /**
1018       * Same as setElementAt.
1019       *
1020       * @param node  The node to be set.
1021       * @param index The index of the node to be replaced.
1022       * @throws RuntimeException thrown if this NodeSetDTM is not of 
1023       * a mutable type.
1024       */
1025      public void setItem(int node, int index)
1026      {
1027    
1028        if (!m_mutable)
1029          throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_NOT_MUTABLE, null)); //"This NodeSetDTM is not mutable!");
1030    
1031        super.setElementAt(node, index);
1032      }
1033    
1034      /**
1035       * Get the nth element.
1036       *
1037       * @param i The index of the requested node.
1038       *
1039       * @return Node at specified index.
1040       */
1041      public int elementAt(int i)
1042      {
1043    
1044        runTo(i);
1045    
1046        return super.elementAt(i);
1047      }
1048      
1049      /**
1050       * Tell if the table contains the given node.
1051       *
1052       * @param s Node to look for
1053       *
1054       * @return True if the given node was found.
1055       */
1056      public boolean contains(int s)
1057      {
1058    
1059        runTo(-1);
1060    
1061        return super.contains(s);
1062      }
1063    
1064      /**
1065       * Searches for the first occurence of the given argument,
1066       * beginning the search at index, and testing for equality
1067       * using the equals method.
1068       *
1069       * @param elem Node to look for
1070       * @param index Index of where to start the search
1071       * @return the index of the first occurrence of the object
1072       * argument in this vector at position index or later in the
1073       * vector; returns -1 if the object is not found.
1074       */
1075      public int indexOf(int elem, int index)
1076      {
1077    
1078        runTo(-1);
1079    
1080        return super.indexOf(elem, index);
1081      }
1082    
1083      /**
1084       * Searches for the first occurence of the given argument,
1085       * beginning the search at index, and testing for equality
1086       * using the equals method.
1087       *
1088       * @param elem Node to look for 
1089       * @return the index of the first occurrence of the object
1090       * argument in this vector at position index or later in the
1091       * vector; returns -1 if the object is not found.
1092       */
1093      public int indexOf(int elem)
1094      {
1095    
1096        runTo(-1);
1097    
1098        return super.indexOf(elem);
1099      }
1100    
1101      /** If this node is being used as an iterator, the next index that nextNode()
1102       *  will return.  */
1103      transient protected int m_next = 0;
1104    
1105      /**
1106       * Get the current position, which is one less than
1107       * the next nextNode() call will retrieve.  i.e. if
1108       * you call getCurrentPos() and the return is 0, the next
1109       * fetch will take place at index 1.
1110       *
1111       * @return The the current position index.
1112       */
1113      public int getCurrentPos()
1114      {
1115        return m_next;
1116      }
1117    
1118      /**
1119       * Set the current position in the node set.
1120       * @param i Must be a valid index.
1121       * @throws RuntimeException thrown if this NodeSetDTM is not of 
1122       * a cached type, and thus doesn't permit indexed access.
1123       */
1124      public void setCurrentPos(int i)
1125      {
1126    
1127        if (!m_cacheNodes)
1128          throw new RuntimeException(
1129            XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESETDTM_CANNOT_INDEX, null)); //"This NodeSetDTM can not do indexing or counting functions!");
1130    
1131        m_next = i;
1132      }
1133    
1134      /**
1135       * Return the last fetched node.  Needed to support the UnionPathIterator.
1136       *
1137       * @return the last fetched node.
1138       * @throws RuntimeException thrown if this NodeSetDTM is not of 
1139       * a cached type, and thus doesn't permit indexed access.
1140       */
1141      public int getCurrentNode()
1142      {
1143    
1144        if (!m_cacheNodes)
1145          throw new RuntimeException(
1146            "This NodeSetDTM can not do indexing or counting functions!");
1147    
1148        int saved = m_next;
1149        // because nextNode always increments
1150        // But watch out for copy29, where the root iterator didn't
1151        // have nextNode called on it.
1152        int current = (m_next > 0) ? m_next-1 : m_next; 
1153        int n = (current < m_firstFree) ? elementAt(current) : DTM.NULL;
1154        m_next = saved; // HACK: I think this is a bit of a hack.  -sb
1155        return n;
1156      }
1157    
1158      /** True if this list can be mutated.  */
1159      transient protected boolean m_mutable = true;
1160    
1161      /** True if this list is cached.
1162       *  @serial  */
1163      transient protected boolean m_cacheNodes = true;
1164      
1165      /** The root of the iteration, if available. */
1166      protected int m_root = DTM.NULL;
1167    
1168      /**
1169       * Get whether or not this is a cached node set.
1170       *
1171       *
1172       * @return True if this list is cached.
1173       */
1174      public boolean getShouldCacheNodes()
1175      {
1176        return m_cacheNodes;
1177      }
1178    
1179      /**
1180       * If setShouldCacheNodes(true) is called, then nodes will
1181       * be cached.  They are not cached by default. This switch must
1182       * be set before the first call to nextNode is made, to ensure
1183       * that all nodes are cached.
1184       *
1185       * @param b true if this node set should be cached.
1186       * @throws RuntimeException thrown if an attempt is made to
1187       * request caching after we've already begun stepping through the
1188       * nodes in this set.
1189      */
1190      public void setShouldCacheNodes(boolean b)
1191      {
1192    
1193        if (!isFresh())
1194          throw new RuntimeException(
1195            XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CALL_SETSHOULDCACHENODE, null)); //"Can not call setShouldCacheNodes after nextNode has been called!");
1196    
1197        m_cacheNodes = b;
1198        m_mutable = true;
1199      }
1200      
1201      /**
1202       * Tells if this iterator can have nodes added to it or set via 
1203       * the <code>setItem(int node, int index)</code> method.
1204       * 
1205       * @return True if the nodelist can be mutated.
1206       */
1207      public boolean isMutable()
1208      {
1209        return m_mutable;
1210      }
1211      
1212      transient private int m_last = 0;
1213      
1214      public int getLast()
1215      {
1216        return m_last;
1217      }
1218      
1219      public void setLast(int last)
1220      {
1221        m_last = last;
1222      }
1223      
1224      /**
1225       * Returns true if all the nodes in the iteration well be returned in document 
1226       * order.
1227       * 
1228       * @return true as a default.
1229       */
1230      public boolean isDocOrdered()
1231      {
1232        return true;
1233      }
1234      
1235      /**
1236       * Returns the axis being iterated, if it is known.
1237       * 
1238       * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
1239       * types.
1240       */
1241      public int getAxis()
1242      {
1243        return -1;
1244      }
1245      
1246    
1247    }