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