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 }