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