1 /*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * @LastModified: Nov 2017
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
22 package com.sun.org.apache.xpath.internal;
23
24 import com.sun.org.apache.xalan.internal.res.XSLMessages;
25 import com.sun.org.apache.xml.internal.utils.DOM2Helper;
26 import com.sun.org.apache.xpath.internal.axes.ContextNodeList;
27 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
28
29 import org.w3c.dom.DOMException;
30 import org.w3c.dom.Node;
31 import org.w3c.dom.NodeList;
32 import org.w3c.dom.traversal.NodeFilter;
33 import 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 */
60 public 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 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 = 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 = 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 }
--- EOF ---