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