KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xpath > NodeSet


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 /*
17  * $Id: NodeSet.java,v 1.21 2004/02/17 04:30:02 minchau Exp $
18  */

19 package org.apache.xpath;
20
21 import org.apache.xalan.res.XSLMessages;
22 import org.apache.xml.utils.DOM2Helper;
23 import org.apache.xpath.axes.ContextNodeList;
24 import org.apache.xpath.res.XPATHErrorResources;
25
26 import org.w3c.dom.DOMException JavaDoc;
27 import org.w3c.dom.Node JavaDoc;
28 import org.w3c.dom.NodeList JavaDoc;
29 import org.w3c.dom.traversal.NodeFilter;
30 import org.w3c.dom.traversal.NodeIterator;
31
32
33 /**
34  * <p>The NodeSet class can act as either a NodeVector,
35  * NodeList, or NodeIterator. However, in order for it to
36  * act as a NodeVector or NodeList, it's required that
37  * setShouldCacheNodes(true) be called before the first
38  * nextNode() is called, in order that nodes can be added
39  * as they are fetched. Derived classes that implement iterators
40  * must override runTo(int index), in order that they may
41  * run the iteration to the given index. </p>
42  *
43  * <p>Note that we directly implement the DOM's NodeIterator
44  * interface. We do not emulate all the behavior of the
45  * standard NodeIterator. In particular, we do not guarantee
46  * to present a "live view" of the document ... but in XSLT,
47  * the source document should never be mutated, so this should
48  * never be an issue.</p>
49  *
50  * <p>Thought: Should NodeSet really implement NodeList and NodeIterator,
51  * or should there be specific subclasses of it which do so? The
52  * advantage of doing it all here is that all NodeSets will respond
53  * to the same calls; the disadvantage is that some of them may return
54  * less-than-enlightening results when you do so.</p>
55  * @xsl.usage advanced
56  */

57 public class NodeSet
58         implements NodeList, NodeIterator, Cloneable JavaDoc, ContextNodeList
59 {
60
61   /**
62    * Create an empty nodelist.
63    */

64   public NodeSet()
65   {
66     m_blocksize = 32;
67     m_mapSize = 0;
68   }
69
70   /**
71    * Create an empty, using the given block size.
72    *
73    * @param blocksize Size of blocks to allocate
74    */

75   public NodeSet(int blocksize)
76   {
77     m_blocksize = blocksize;
78     m_mapSize = 0;
79   }
80
81   /**
82    * Create a NodeSet, and copy the members of the
83    * given nodelist into it.
84    *
85    * @param nodelist List of Nodes to be made members of the new set.
86    */

87   public NodeSet(NodeList nodelist)
88   {
89
90     this(32);
91
92     addNodes(nodelist);
93   }
94
95   /**
96    * Create a NodeSet, and copy the members of the
97    * given NodeSet into it.
98    *
99    * @param nodelist Set of Nodes to be made members of the new set.
100    */

101   public NodeSet(NodeSet nodelist)
102   {
103
104     this(32);
105
106     addNodes((NodeIterator) nodelist);
107   }
108
109   /**
110    * Create a NodeSet, and copy the members of the
111    * given NodeIterator into it.
112    *
113    * @param ni Iterator which yields Nodes to be made members of the new set.
114    */

115   public NodeSet(NodeIterator ni)
116   {
117
118     this(32);
119
120     addNodes(ni);
121   }
122
123   /**
124    * Create a NodeSet which contains the given Node.
125    *
126    * @param node Single node to be added to the new set.
127    */

128   public NodeSet(Node JavaDoc node)
129   {
130
131     this(32);
132
133     addNode(node);
134   }
135
136   /**
137    * @return The root node of the Iterator, as specified when it was created.
138    * For non-Iterator NodeSets, this will be null.
139    */

140   public Node JavaDoc getRoot()
141   {
142     return null;
143   }
144
145   /**
146    * Get a cloned Iterator, and reset its state to the beginning of the
147    * iteration.
148    *
149    * @return a new NodeSet of the same type, having the same state...
150    * except that the reset() operation has been called.
151    *
152    * @throws CloneNotSupportedException if this subclass of NodeSet
153    * does not support the clone() operation.
154    */

155   public NodeIterator cloneWithReset() throws CloneNotSupportedException JavaDoc
156   {
157
158     NodeSet clone = (NodeSet) clone();
159
160     clone.reset();
161
162     return clone;
163   }
164
165   /**
166    * Reset the iterator. May have no effect on non-iterator Nodesets.
167    */

168   public void reset()
169   {
170     m_next = 0;
171   }
172
173   /**
174    * This attribute determines which node types are presented via the
175    * iterator. The available set of constants is defined in the
176    * <code>NodeFilter</code> interface. For NodeSets, the mask has been
177    * hardcoded to show all nodes except EntityReference nodes, which have
178    * no equivalent in the XPath data model.
179    *
180    * @return integer used as a bit-array, containing flags defined in
181    * the DOM's NodeFilter class. The value will be
182    * <code>SHOW_ALL & ~SHOW_ENTITY_REFERENCE</code>, meaning that
183    * only entity references are suppressed.
184    */

185   public int getWhatToShow()
186   {
187     return NodeFilter.SHOW_ALL & ~NodeFilter.SHOW_ENTITY_REFERENCE;
188   }
189
190   /**
191    * The filter object used to screen nodes. Filters are applied to
192    * further reduce (and restructure) the NodeIterator's view of the
193    * document. In our case, we will be using hardcoded filters built
194    * into our iterators... but getFilter() is part of the DOM's
195    * NodeIterator interface, so we have to support it.
196    *
197    * @return null, which is slightly misleading. True, there is no
198    * user-written filter object, but in fact we are doing some very
199    * sophisticated custom filtering. A DOM purist might suggest
200    * returning a placeholder object just to indicate that this is
201    * not going to return all nodes selected by whatToShow.
202    */

203   public NodeFilter getFilter()
204   {
205     return null;
206   }
207
208   /**
209    * The value of this flag determines whether the children of entity
210    * reference nodes are visible to the iterator. If false, they will be
211    * skipped over.
212    * <br> To produce a view of the document that has entity references
213    * expanded and does not expose the entity reference node itself, use the
214    * whatToShow flags to hide the entity reference node and set
215    * expandEntityReferences to true when creating the iterator. To produce
216    * a view of the document that has entity reference nodes but no entity
217    * expansion, use the whatToShow flags to show the entity reference node
218    * and set expandEntityReferences to false.
219    *
220    * @return true for all iterators based on NodeSet, meaning that the
221    * contents of EntityRefrence nodes may be returned (though whatToShow
222    * says that the EntityReferences themselves are not shown.)
223    */

224   public boolean getExpandEntityReferences()
225   {
226     return true;
227   }
228
229   /**
230    * Returns the next node in the set and advances the position of the
231    * iterator in the set. After a NodeIterator is created, the first call
232    * to nextNode() returns the first node in the set.
233    * @return The next <code>Node</code> in the set being iterated over, or
234    * <code>null</code> if there are no more members in that set.
235    * @throws DOMException
236    * INVALID_STATE_ERR: Raised if this method is called after the
237    * <code>detach</code> method was invoked.
238    */

239   public Node JavaDoc nextNode() throws DOMException JavaDoc
240   {
241
242     if ((m_next) < this.size())
243     {
244       Node JavaDoc next = this.elementAt(m_next);
245
246       m_next++;
247
248       return next;
249     }
250     else
251       return null;
252   }
253
254   /**
255    * Returns the previous node in the set and moves the position of the
256    * iterator backwards in the set.
257    * @return The previous <code>Node</code> in the set being iterated over,
258    * or<code>null</code> if there are no more members in that set.
259    * @throws DOMException
260    * INVALID_STATE_ERR: Raised if this method is called after the
261    * <code>detach</code> method was invoked.
262    * @throws RuntimeException thrown if this NodeSet is not of
263    * a cached type, and hence doesn't know what the previous node was.
264    */

265   public Node JavaDoc previousNode() throws DOMException JavaDoc
266   {
267
268     if (!m_cacheNodes)
269       throw new RuntimeException JavaDoc(
270         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_ITERATE, null)); //"This NodeSet can not iterate to a previous node!");
271

272     if ((m_next - 1) > 0)
273     {
274       m_next--;
275
276       return this.elementAt(m_next);
277     }
278     else
279       return null;
280   }
281
282   /**
283    * Detaches the iterator from the set which it iterated over, releasing
284    * any computational resources and placing the iterator in the INVALID
285    * state. After<code>detach</code> has been invoked, calls to
286    * <code>nextNode</code> or<code>previousNode</code> will raise the
287    * exception INVALID_STATE_ERR.
288    * <p>
289    * This operation is a no-op in NodeSet, and will not cause
290    * INVALID_STATE_ERR to be raised by later operations.
291    * </p>
292    */

293   public void detach(){}
294
295   /**
296    * Tells if this NodeSet is "fresh", in other words, if
297    * the first nextNode() that is called will return the
298    * first node in the set.
299    *
300    * @return true if nextNode() would return the first node in the set,
301    * false if it would return a later one.
302    */

303   public boolean isFresh()
304   {
305     return (m_next == 0);
306   }
307
308   /**
309    * If an index is requested, NodeSet will call this method
310    * to run the iterator to the index. By default this sets
311    * m_next to the index. If the index argument is -1, this
312    * signals that the iterator should be run to the end.
313    *
314    * @param index Position to advance (or retreat) to, with
315    * 0 requesting the reset ("fresh") position and -1 (or indeed
316    * any out-of-bounds value) requesting the final position.
317    * @throws RuntimeException thrown if this NodeSet is not
318    * one of the types which supports indexing/counting.
319    */

320   public void runTo(int index)
321   {
322
323     if (!m_cacheNodes)
324       throw new RuntimeException JavaDoc(
325         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_INDEX, null)); //"This NodeSet can not do indexing or counting functions!");
326

327     if ((index >= 0) && (m_next < m_firstFree))
328       m_next = index;
329     else
330       m_next = m_firstFree - 1;
331   }
332
333   /**
334    * Returns the <code>index</code>th item in the collection. If
335    * <code>index</code> is greater than or equal to the number of nodes in
336    * the list, this returns <code>null</code>.
337    *
338    * TODO: What happens if index is out of range?
339    *
340    * @param index Index into the collection.
341    * @return The node at the <code>index</code>th position in the
342    * <code>NodeList</code>, or <code>null</code> if that is not a valid
343    * index.
344    */

345   public Node JavaDoc item(int index)
346   {
347
348     runTo(index);
349
350     return (Node JavaDoc) this.elementAt(index);
351   }
352
353   /**
354    * The number of nodes in the list. The range of valid child node indices is
355    * 0 to <code>length-1</code> inclusive. Note that this operation requires
356    * finding all the matching nodes, which may defeat attempts to defer
357    * that work.
358    *
359    * @return integer indicating how many nodes are represented by this list.
360    */

361   public int getLength()
362   {
363
364     runTo(-1);
365
366     return this.size();
367   }
368
369   /**
370    * Add a node to the NodeSet. Not all types of NodeSets support this
371    * operation
372    *
373    * @param n Node to be added
374    * @throws RuntimeException thrown if this NodeSet is not of
375    * a mutable type.
376    */

377   public void addNode(Node JavaDoc n)
378   {
379
380     if (!m_mutable)
381       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
382

383     this.addElement(n);
384   }
385
386   /**
387    * Insert a node at a given position.
388    *
389    * @param n Node to be added
390    * @param pos Offset at which the node is to be inserted,
391    * with 0 being the first position.
392    * @throws RuntimeException thrown if this NodeSet is not of
393    * a mutable type.
394    */

395   public void insertNode(Node JavaDoc n, int pos)
396   {
397
398     if (!m_mutable)
399       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
400

401     insertElementAt(n, pos);
402   }
403
404   /**
405    * Remove a node.
406    *
407    * @param n Node to be added
408    * @throws RuntimeException thrown if this NodeSet is not of
409    * a mutable type.
410    */

411   public void removeNode(Node JavaDoc n)
412   {
413
414     if (!m_mutable)
415       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
416

417     this.removeElement(n);
418   }
419
420   /**
421    * Copy NodeList members into this nodelist, adding in
422    * document order. If a node is null, don't add it.
423    *
424    * @param nodelist List of nodes which should now be referenced by
425    * this NodeSet.
426    * @throws RuntimeException thrown if this NodeSet is not of
427    * a mutable type.
428    */

429   public void addNodes(NodeList nodelist)
430   {
431
432     if (!m_mutable)
433       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
434

435     if (null != nodelist) // defensive to fix a bug that Sanjiva reported.
436
{
437       int nChildren = nodelist.getLength();
438
439       for (int i = 0; i < nChildren; i++)
440       {
441         Node JavaDoc obj = nodelist.item(i);
442
443         if (null != obj)
444         {
445           addElement(obj);
446         }
447       }
448     }
449
450     // checkDups();
451
}
452
453   /**
454    * <p>Copy NodeList members into this nodelist, adding in
455    * document order. Only genuine node references will be copied;
456    * nulls appearing in the source NodeSet will
457    * not be added to this one. </p>
458    *
459    * <p> In case you're wondering why this function is needed: NodeSet
460    * implements both NodeIterator and NodeList. If this method isn't
461    * provided, Java can't decide which of those to use when addNodes()
462    * is invoked. Providing the more-explicit match avoids that
463    * ambiguity.)</p>
464    *
465    * @param ns NodeSet whose members should be merged into this NodeSet.
466    * @throws RuntimeException thrown if this NodeSet is not of
467    * a mutable type.
468    */

469   public void addNodes(NodeSet ns)
470   {
471
472     if (!m_mutable)
473       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
474

475     addNodes((NodeIterator) ns);
476   }
477
478   /**
479    * Copy NodeList members into this nodelist, adding in
480    * document order. Null references are not added.
481    *
482    * @param iterator NodeIterator which yields the nodes to be added.
483    * @throws RuntimeException thrown if this NodeSet is not of
484    * a mutable type.
485    */

486   public void addNodes(NodeIterator iterator)
487   {
488
489     if (!m_mutable)
490       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
491

492     if (null != iterator) // defensive to fix a bug that Sanjiva reported.
493
{
494       Node JavaDoc obj;
495
496       while (null != (obj = iterator.nextNode()))
497       {
498         addElement(obj);
499       }
500     }
501
502     // checkDups();
503
}
504
505   /**
506    * Copy NodeList members into this nodelist, adding in
507    * document order. If a node is null, don't add it.
508    *
509    * @param nodelist List of nodes to be added
510    * @param support The XPath runtime context.
511    * @throws RuntimeException thrown if this NodeSet is not of
512    * a mutable type.
513    */

514   public void addNodesInDocOrder(NodeList nodelist, XPathContext support)
515   {
516
517     if (!m_mutable)
518       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
519

520     int nChildren = nodelist.getLength();
521
522     for (int i = 0; i < nChildren; i++)
523     {
524       Node JavaDoc node = nodelist.item(i);
525
526       if (null != node)
527       {
528         addNodeInDocOrder(node, support);
529       }
530     }
531   }
532
533   /**
534    * Copy NodeList members into this nodelist, adding in
535    * document order. If a node is null, don't add it.
536    *
537    * @param iterator NodeIterator which yields the nodes to be added.
538    * @param support The XPath runtime context.
539    * @throws RuntimeException thrown if this NodeSet is not of
540    * a mutable type.
541    */

542   public void addNodesInDocOrder(NodeIterator iterator, XPathContext support)
543   {
544
545     if (!m_mutable)
546       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
547

548     Node JavaDoc node;
549
550     while (null != (node = iterator.nextNode()))
551     {
552       addNodeInDocOrder(node, support);
553     }
554   }
555
556   /**
557    * Add the node list to this node set in document order.
558    *
559    * @param start index.
560    * @param end index.
561    * @param testIndex index.
562    * @param nodelist The nodelist to add.
563    * @param support The XPath runtime context.
564    *
565    * @return false always.
566    * @throws RuntimeException thrown if this NodeSet is not of
567    * a mutable type.
568    */

569   private boolean addNodesInDocOrder(int start, int end, int testIndex,
570                                      NodeList nodelist, XPathContext support)
571   {
572
573     if (!m_mutable)
574       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
575

576     boolean foundit = false;
577     int i;
578     Node JavaDoc node = nodelist.item(testIndex);
579
580     for (i = end; i >= start; i--)
581     {
582       Node JavaDoc child = (Node JavaDoc) elementAt(i);
583
584       if (child == node)
585       {
586         i = -2; // Duplicate, suppress insert
587

588         break;
589       }
590
591       if (!DOM2Helper.isNodeAfter(node, child))
592       {
593         insertElementAt(node, i + 1);
594
595         testIndex--;
596
597         if (testIndex > 0)
598         {
599           boolean foundPrev = addNodesInDocOrder(0, i, testIndex, nodelist,
600                                                  support);
601
602           if (!foundPrev)
603           {
604             addNodesInDocOrder(i, size() - 1, testIndex, nodelist, support);
605           }
606         }
607
608         break;
609       }
610     }
611
612     if (i == -1)
613     {
614       insertElementAt(node, 0);
615     }
616
617     return foundit;
618   }
619
620   /**
621    * Add the node into a vector of nodes where it should occur in
622    * document order.
623    * @param v Vector of nodes, presumably containing Nodes
624    * @param obj Node object.
625    *
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 JavaDoc node, boolean test, XPathContext support)
634   {
635
636     if (!m_mutable)
637       throw new RuntimeException JavaDoc(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 JavaDoc child = (Node JavaDoc) 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 v Vector of nodes, presumably containing Nodes
701    * @param obj Node object.
702    *
703    * @param node The node to be added.
704    * @param support The XPath runtime context.
705    *
706    * @return The index where it was inserted.
707    * @throws RuntimeException thrown if this NodeSet is not of
708    * a mutable type.
709    */

710   public int addNodeInDocOrder(Node JavaDoc node, XPathContext support)
711   {
712
713     if (!m_mutable)
714       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
715

716     return addNodeInDocOrder(node, true, support);
717   } // end addNodeInDocOrder(Vector v, Object obj)
718

719
720   /** If this node is being used as an iterator, the next index that nextNode()
721    * will return. */

722   transient protected int m_next = 0;
723
724   /**
725    * Get the current position, which is one less than
726    * the next nextNode() call will retrieve. i.e. if
727    * you call getCurrentPos() and the return is 0, the next
728    * fetch will take place at index 1.
729    *
730    * @return The the current position index.
731    */

732   public int getCurrentPos()
733   {
734     return m_next;
735   }
736
737   /**
738    * Set the current position in the node set.
739    * @param i Must be a valid index.
740    * @throws RuntimeException thrown if this NodeSet is not of
741    * a cached type, and thus doesn't permit indexed access.
742    */

743   public void setCurrentPos(int i)
744   {
745
746     if (!m_cacheNodes)
747       throw new RuntimeException JavaDoc(
748         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_INDEX, null)); //"This NodeSet can not do indexing or counting functions!");
749

750     m_next = i;
751   }
752
753   /**
754    * Return the last fetched node. Needed to support the UnionPathIterator.
755    *
756    * @return the last fetched node.
757    * @throws RuntimeException thrown if this NodeSet is not of
758    * a cached type, and thus doesn't permit indexed access.
759    */

760   public Node JavaDoc getCurrentNode()
761   {
762
763     if (!m_cacheNodes)
764       throw new RuntimeException JavaDoc(
765         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_CANNOT_INDEX, null)); //"This NodeSet can not do indexing or counting functions!");
766

767     int saved = m_next;
768     Node JavaDoc n = (m_next < m_firstFree) ? elementAt(m_next) : null;
769     m_next = saved; // HACK: I think this is a bit of a hack. -sb
770
return n;
771   }
772
773   /** True if this list can be mutated. */
774   transient protected boolean m_mutable = true;
775
776   /** True if this list is cached.
777    * @serial */

778   transient protected boolean m_cacheNodes = true;
779
780   /**
781    * Get whether or not this is a cached node set.
782    *
783    *
784    * @return True if this list is cached.
785    */

786   public boolean getShouldCacheNodes()
787   {
788     return m_cacheNodes;
789   }
790
791   /**
792    * If setShouldCacheNodes(true) is called, then nodes will
793    * be cached. They are not cached by default. This switch must
794    * be set before the first call to nextNode is made, to ensure
795    * that all nodes are cached.
796    *
797    * @param b true if this node set should be cached.
798    * @throws RuntimeException thrown if an attempt is made to
799    * request caching after we've already begun stepping through the
800    * nodes in this set.
801   */

802   public void setShouldCacheNodes(boolean b)
803   {
804
805     if (!isFresh())
806       throw new RuntimeException JavaDoc(
807         XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_CALL_SETSHOULDCACHENODE, null)); //"Can not call setShouldCacheNodes after nextNode has been called!");
808

809     m_cacheNodes = b;
810     m_mutable = true;
811   }
812   
813   
814   transient private int m_last = 0;
815   
816   public int getLast()
817   {
818     return m_last;
819   }
820   
821   public void setLast(int last)
822   {
823     m_last = last;
824   }
825   
826   /** Size of blocks to allocate.
827    * @serial */

828   private int m_blocksize;
829
830   /** Array of nodes this points to.
831    * @serial */

832   Node JavaDoc m_map[];
833
834   /** Number of nodes in this NodeVector.
835    * @serial */

836   protected int m_firstFree = 0;
837
838   /** Size of the array this points to.
839    * @serial */

840   private int m_mapSize; // lazy initialization
841

842   /**
843    * Get a cloned LocPathIterator.
844    *
845    * @return A clone of this
846    *
847    * @throws CloneNotSupportedException
848    */

849   public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc
850   {
851
852     NodeSet clone = (NodeSet) super.clone();
853
854     if ((null != this.m_map) && (this.m_map == clone.m_map))
855     {
856       clone.m_map = new Node JavaDoc[this.m_map.length];
857
858       System.arraycopy(this.m_map, 0, clone.m_map, 0, this.m_map.length);
859     }
860
861     return clone;
862   }
863
864   /**
865    * Get the length of the list.
866    *
867    * @return Number of nodes in this NodeVector
868    */

869   public int size()
870   {
871     return m_firstFree;
872   }
873
874   /**
875    * Append a Node onto the vector.
876    *
877    * @param value Node to add to the vector
878    */

879   public void addElement(Node JavaDoc value)
880   {
881     if (!m_mutable)
882       throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
883

884     if ((m_firstFree + 1) >= m_mapSize)
885     {
886       if (null == m_map)
887       {
888         m_map = new Node JavaDoc[m_blocksize];
889         m_mapSize = m_blocksize;
890       }
891       else
892       {
893         m_mapSize += m_blocksize;
894
895         Node JavaDoc newMap[] = new Node JavaDoc[m_mapSize];
896
897         System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
898
899         m_map = newMap;
900       }
901     }
902
903     m_map[m_firstFree] = value;
904
905     m_firstFree++;
906   }
907
908   /**
909    * Append a Node onto the vector.
910    *
911    * @param value Node to add to the vector
912    */

913   public final void push(Node JavaDoc value)
914   {
915
916     int ff = m_firstFree;
917
918     if ((ff + 1) >= m_mapSize)
919     {
920       if (null == m_map)
921       {
922         m_map = new Node JavaDoc[m_blocksize];
923         m_mapSize = m_blocksize;
924       }
925       else
926       {
927         m_mapSize += m_blocksize;
928
929         Node JavaDoc newMap[] = new Node JavaDoc[m_mapSize];
930
931         System.arraycopy(m_map, 0, newMap, 0, ff + 1);
932
933         m_map = newMap;
934       }
935     }
936
937     m_map[ff] = value;
938
939     ff++;
940
941     m_firstFree = ff;
942   }
943
944   /**
945    * Pop a node from the tail of the vector and return the result.
946    *
947    * @return the node at the tail of the vector
948    */

949   public final Node JavaDoc pop()
950   {
951
952     m_firstFree--;
953
954     Node JavaDoc n = m_map[m_firstFree];
955
956     m_map[m_firstFree] = null;
957
958     return n;
959   }
960
961   /**
962    * Pop a node from the tail of the vector and return the
963    * top of the stack after the pop.
964    *
965    * @return The top of the stack after it's been popped
966    */

967   public final Node JavaDoc popAndTop()
968   {
969
970     m_firstFree--;
971
972     m_map[m_firstFree] = null;
973
974     return (m_firstFree == 0) ? null : m_map[m_firstFree - 1];
975   }
976
977   /**
978    * Pop a node from the tail of the vector.
979    */

980   public final void popQuick()
981   {
982
983     m_firstFree--;
984
985     m_map[m_firstFree] = null;
986   }
987
988   /**
989    * Return the node at the top of the stack without popping the stack.
990    * Special purpose method for TransformerImpl, pushElemTemplateElement.
991    * Performance critical.
992    *
993    * @return Node at the top of the stack or null if stack is empty.
994    */

995   public final Node JavaDoc peepOrNull()
996   {
997     return ((null != m_map) && (m_firstFree > 0))
998            ? m_map[m_firstFree - 1] : null;
999   }
1000
1001  /**
1002   * Push a pair of nodes into the stack.
1003   * Special purpose method for TransformerImpl, pushElemTemplateElement.
1004   * Performance critical.
1005   *
1006   * @param v1 First node to add to vector
1007   * @param v2 Second node to add to vector
1008   */

1009  public final void pushPair(Node JavaDoc v1, Node JavaDoc v2)
1010  {
1011
1012    if (null == m_map)
1013    {
1014      m_map = new Node JavaDoc[m_blocksize];
1015      m_mapSize = m_blocksize;
1016    }
1017    else
1018    {
1019      if ((m_firstFree + 2) >= m_mapSize)
1020      {
1021        m_mapSize += m_blocksize;
1022
1023        Node JavaDoc newMap[] = new Node JavaDoc[m_mapSize];
1024
1025        System.arraycopy(m_map, 0, newMap, 0, m_firstFree);
1026
1027        m_map = newMap;
1028      }
1029    }
1030
1031    m_map[m_firstFree] = v1;
1032    m_map[m_firstFree + 1] = v2;
1033    m_firstFree += 2;
1034  }
1035
1036  /**
1037   * Pop a pair of nodes from the tail of the stack.
1038   * Special purpose method for TransformerImpl, pushElemTemplateElement.
1039   * Performance critical.
1040   */

1041  public final void popPair()
1042  {
1043
1044    m_firstFree -= 2;
1045    m_map[m_firstFree] = null;
1046    m_map[m_firstFree + 1] = null;
1047  }
1048
1049  /**
1050   * Set the tail of the stack to the given node.
1051   * Special purpose method for TransformerImpl, pushElemTemplateElement.
1052   * Performance critical.
1053   *
1054   * @param n Node to set at the tail of vector
1055   */

1056  public final void setTail(Node JavaDoc n)
1057  {
1058    m_map[m_firstFree - 1] = n;
1059  }
1060
1061  /**
1062   * Set the given node one position from the tail.
1063   * Special purpose method for TransformerImpl, pushElemTemplateElement.
1064   * Performance critical.
1065   *
1066   * @param n Node to set
1067   */

1068  public final void setTailSub1(Node JavaDoc n)
1069  {
1070    m_map[m_firstFree - 2] = n;
1071  }
1072
1073  /**
1074   * Return the node at the tail of the vector without popping
1075   * Special purpose method for TransformerImpl, pushElemTemplateElement.
1076   * Performance critical.
1077   *
1078   * @return Node at the tail of the vector
1079   */

1080  public final Node JavaDoc peepTail()
1081  {
1082    return m_map[m_firstFree - 1];
1083  }
1084
1085  /**
1086   * Return the node one position from the tail without popping.
1087   * Special purpose method for TransformerImpl, pushElemTemplateElement.
1088   * Performance critical.
1089   *
1090   * @return Node one away from the tail
1091   */

1092  public final Node JavaDoc peepTailSub1()
1093  {
1094    return m_map[m_firstFree - 2];
1095  }
1096
1097  /**
1098   * Inserts the specified node in this vector at the specified index.
1099   * Each component in this vector with an index greater or equal to
1100   * the specified index is shifted upward to have an index one greater
1101   * than the value it had previously.
1102   *
1103   * @param value Node to insert
1104   * @param at Position where to insert
1105   */

1106  public void insertElementAt(Node JavaDoc value, int at)
1107  {
1108    if (!m_mutable)
1109      throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
1110

1111    if (null == m_map)
1112    {
1113      m_map = new Node JavaDoc[m_blocksize];
1114      m_mapSize = m_blocksize;
1115    }
1116    else if ((m_firstFree + 1) >= m_mapSize)
1117    {
1118      m_mapSize += m_blocksize;
1119
1120      Node JavaDoc newMap[] = new Node JavaDoc[m_mapSize];
1121
1122      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + 1);
1123
1124      m_map = newMap;
1125    }
1126
1127    if (at <= (m_firstFree - 1))
1128    {
1129      System.arraycopy(m_map, at, m_map, at + 1, m_firstFree - at);
1130    }
1131
1132    m_map[at] = value;
1133
1134    m_firstFree++;
1135  }
1136
1137  /**
1138   * Append the nodes to the list.
1139   *
1140   * @param nodes NodeVector to append to this list
1141   */

1142  public void appendNodes(NodeSet nodes)
1143  {
1144
1145    int nNodes = nodes.size();
1146
1147    if (null == m_map)
1148    {
1149      m_mapSize = nNodes + m_blocksize;
1150      m_map = new Node JavaDoc[m_mapSize];
1151    }
1152    else if ((m_firstFree + nNodes) >= m_mapSize)
1153    {
1154      m_mapSize += (nNodes + m_blocksize);
1155
1156      Node JavaDoc newMap[] = new Node JavaDoc[m_mapSize];
1157
1158      System.arraycopy(m_map, 0, newMap, 0, m_firstFree + nNodes);
1159
1160      m_map = newMap;
1161    }
1162
1163    System.arraycopy(nodes.m_map, 0, m_map, m_firstFree, nNodes);
1164
1165    m_firstFree += nNodes;
1166  }
1167
1168  /**
1169   * Inserts the specified node in this vector at the specified index.
1170   * Each component in this vector with an index greater or equal to
1171   * the specified index is shifted upward to have an index one greater
1172   * than the value it had previously.
1173   */

1174  public void removeAllElements()
1175  {
1176
1177    if (null == m_map)
1178      return;
1179
1180    for (int i = 0; i < m_firstFree; i++)
1181    {
1182      m_map[i] = null;
1183    }
1184
1185    m_firstFree = 0;
1186  }
1187
1188  /**
1189   * Removes the first occurrence of the argument from this vector.
1190   * If the object is found in this vector, each component in the vector
1191   * with an index greater or equal to the object's index is shifted
1192   * downward to have an index one smaller than the value it had
1193   * previously.
1194   *
1195   * @param s Node to remove from the list
1196   *
1197   * @return True if the node was successfully removed
1198   */

1199  public boolean removeElement(Node JavaDoc s)
1200  {
1201    if (!m_mutable)
1202      throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
1203

1204    if (null == m_map)
1205      return false;
1206
1207    for (int i = 0; i < m_firstFree; i++)
1208    {
1209      Node JavaDoc node = m_map[i];
1210
1211      if ((null != node) && node.equals(s))
1212      {
1213        if (i < m_firstFree - 1)
1214          System.arraycopy(m_map, i + 1, m_map, i, m_firstFree - i - 1);
1215
1216        m_firstFree--;
1217        m_map[m_firstFree] = null;
1218
1219        return true;
1220      }
1221    }
1222
1223    return false;
1224  }
1225
1226  /**
1227   * Deletes the component at the specified index. Each component in
1228   * this vector with an index greater or equal to the specified
1229   * index is shifted downward to have an index one smaller than
1230   * the value it had previously.
1231   *
1232   * @param i Index of node to remove
1233   */

1234  public void removeElementAt(int i)
1235  {
1236
1237    if (null == m_map)
1238      return;
1239      
1240    if (i >= m_firstFree)
1241      throw new ArrayIndexOutOfBoundsException JavaDoc(i + " >= " + m_firstFree);
1242    else if (i < 0)
1243      throw new ArrayIndexOutOfBoundsException JavaDoc(i);
1244
1245    if (i < m_firstFree - 1)
1246      System.arraycopy(m_map, i + 1, m_map, i, m_firstFree - i - 1);
1247
1248    m_firstFree--;
1249    m_map[m_firstFree] = null;
1250  }
1251
1252  /**
1253   * Sets the component at the specified index of this vector to be the
1254   * specified object. The previous component at that position is discarded.
1255   *
1256   * The index must be a value greater than or equal to 0 and less
1257   * than the current size of the vector.
1258   *
1259   * @param node Node to set
1260   * @param index Index of where to set the node
1261   */

1262  public void setElementAt(Node JavaDoc node, int index)
1263  {
1264    if (!m_mutable)
1265      throw new RuntimeException JavaDoc(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_NODESET_NOT_MUTABLE, null)); //"This NodeSet is not mutable!");
1266

1267    if (null == m_map)
1268    {
1269      m_map = new Node JavaDoc[m_blocksize];
1270      m_mapSize = m_blocksize;
1271    }
1272
1273    m_map[index] = node;
1274  }
1275
1276  /**
1277   * Get the nth element.
1278   *
1279   * @param i Index of node to get
1280   *
1281   * @return Node at specified index
1282   */

1283  public Node JavaDoc elementAt(int i)
1284  {
1285
1286    if (null == m_map)
1287      return null;
1288
1289    return m_map[i];
1290  }
1291
1292  /**
1293   * Tell if the table contains the given node.
1294   *
1295   * @param s Node to look for
1296   *
1297   * @return True if the given node was found.
1298   */

1299  public boolean contains(Node JavaDoc s)
1300  {
1301    runTo(-1);
1302
1303    if (null == m_map)
1304      return false;
1305
1306    for (int i = 0; i < m_firstFree; i++)
1307    {
1308      Node JavaDoc node = m_map[i];
1309
1310      if ((null != node) && node.equals(s))
1311        return true;
1312    }
1313
1314    return false;
1315  }
1316
1317  /**
1318   * Searches for the first occurence of the given argument,
1319   * beginning the search at index, and testing for equality
1320   * using the equals method.
1321   *
1322   * @param elem Node to look for
1323   * @param index Index of where to start the search
1324   * @return the index of the first occurrence of the object
1325   * argument in this vector at position index or later in the
1326   * vector; returns -1 if the object is not found.
1327   */

1328  public int indexOf(Node JavaDoc elem, int index)
1329  {
1330    runTo(-1);
1331
1332    if (null == m_map)
1333      return -1;
1334
1335    for (int i = index; i < m_firstFree; i++)
1336    {
1337      Node JavaDoc node = m_map[i];
1338
1339      if ((null != node) && node.equals(elem))
1340        return i;
1341    }
1342
1343    return -1;
1344  }
1345
1346  /**
1347   * Searches for the first occurence of the given argument,
1348   * beginning the search at index, and testing for equality
1349   * using the equals method.
1350   *
1351   * @param elem Node to look for
1352   * @return the index of the first occurrence of the object
1353   * argument in this vector at position index or later in the
1354   * vector; returns -1 if the object is not found.
1355   */

1356  public int indexOf(Node JavaDoc elem)
1357  {
1358    runTo(-1);
1359
1360    if (null == m_map)
1361      return -1;
1362
1363    for (int i = 0; i < m_firstFree; i++)
1364    {
1365      Node JavaDoc node = m_map[i];
1366
1367      if ((null != node) && node.equals(elem))
1368        return i;
1369    }
1370
1371    return -1;
1372  }
1373
1374}
1375
Popular Tags