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