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