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