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: NodeSetDTM.java 468655 2006-10-28 07:12:06Z minchau $
20 */
21package org.apache.xpath;
22
23import org.apache.xalan.res.XSLMessages;
24import org.apache.xml.dtm.DTM;
25import org.apache.xml.dtm.DTMFilter;
26import org.apache.xml.dtm.DTMIterator;
27import org.apache.xml.dtm.DTMManager;
28import org.apache.xml.utils.NodeVector;
29import org.apache.xpath.res.XPATHErrorResources;
30
31import org.w3c.dom.Node;
32import org.w3c.dom.NodeList;
33import org.w3c.dom.traversal.NodeIterator;
34
35
36/**
37 * <p>The NodeSetDTM class can act as either a NodeVector,
38 * NodeList, or NodeIterator.  However, in order for it to
39 * act as a NodeVector or NodeList, it's required that
40 * setShouldCacheNodes(true) be called before the first
41 * nextNode() is called, in order that nodes can be added
42 * as they are fetched.  Derived classes that implement iterators
43 * must override runTo(int index), in order that they may
44 * run the iteration to the given index. </p>
45 *
46 * <p>Note that we directly implement the DOM's NodeIterator
47 * interface. We do not emulate all the behavior of the
48 * standard NodeIterator. In particular, we do not guarantee
49 * to present a "live view" of the document ... but in XSLT,
50 * the source document should never be mutated, so this should
51 * never be an issue.</p>
52 *
53 * <p>Thought: Should NodeSetDTM really implement NodeList and NodeIterator,
54 * or should there be specific subclasses of it which do so? The
55 * advantage of doing it all here is that all NodeSetDTMs will respond
56 * to the same calls; the disadvantage is that some of them may return
57 * less-than-enlightening results when you do so.</p>
58 * @xsl.usage advanced
59 */
60public class NodeSetDTM extends NodeVector
61        implements /* NodeList, NodeIterator, */ DTMIterator,
62        Cloneable
63{
64    static final long serialVersionUID = 7686480133331317070L;
65
66  /**
67   * Create an empty nodelist.
68   */
69  public NodeSetDTM(DTMManager dtmManager)
70  {
71    super();
72    m_manager = dtmManager;
73  }
74
75  /**
76   * Create an empty, using the given block size.
77   *
78   * @param blocksize Size of blocks to allocate
79   * @param dummy pass zero for right now...
80   */
81  public NodeSetDTM(int blocksize, int dummy, DTMManager dtmManager)
82  {
83    super(blocksize);
84    m_manager = dtmManager;
85  }
86
87  // %TBD%
88//  /**
89//   * Create a NodeSetDTM, and copy the members of the
90//   * given nodelist into it.
91//   *
92//   * @param nodelist List of Nodes to be made members of the new set.
93//   */
94//  public NodeSetDTM(NodeList nodelist)
95//  {
96//
97//    super();
98//
99//    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}
1248