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