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