1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 2002 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.axes;
  58. import java.io.Serializable;
  59. import java.util.Vector;
  60. import javax.xml.transform.TransformerException;
  61. import org.apache.xml.dtm.DTM;
  62. import org.apache.xml.dtm.DTMIterator;
  63. import org.apache.xml.dtm.DTMManager;
  64. import org.apache.xml.dtm.DTMFilter;
  65. import org.apache.xml.utils.NodeVector;
  66. import org.apache.xpath.Expression;
  67. import org.apache.xpath.NodeSetDTM;
  68. import org.apache.xpath.XPathContext;
  69. import org.apache.xpath.objects.XObject;
  70. /**
  71. * This class is the dynamic wrapper for a Xalan DTMIterator instance, and
  72. * provides random access capabilities.
  73. */
  74. public class NodeSequence extends XObject
  75. implements DTMIterator, Cloneable, PathComponent
  76. {
  77. /** The index of the last node in the iteration. */
  78. protected int m_last = -1;
  79. /**
  80. * The index of the next node to be fetched. Useful if this
  81. * is a cached iterator, and is being used as random access
  82. * NodeList.
  83. */
  84. protected int m_next = 0;
  85. /**
  86. * If this iterator needs to cache nodes that are fetched, they
  87. * are stored in the Vector in the generic object.
  88. */
  89. protected NodeVector getVector()
  90. {
  91. return (NodeVector)m_obj;
  92. }
  93. /**
  94. * Set the vector where nodes will be stored.
  95. */
  96. protected void SetVector(NodeVector v)
  97. {
  98. m_obj = v;
  99. }
  100. /**
  101. * If this iterator needs to cache nodes that are fetched, they
  102. * are stored here.
  103. */
  104. public boolean hasCache()
  105. {
  106. return (m_obj != null);
  107. }
  108. /**
  109. * The functional iterator that fetches nodes.
  110. */
  111. protected DTMIterator m_iter;
  112. /**
  113. * Set the functional iterator that fetches nodes.
  114. * @param iter The iterator that is to be contained.
  115. */
  116. public final void setIter(DTMIterator iter)
  117. {
  118. m_iter = iter;
  119. }
  120. /**
  121. * Get the functional iterator that fetches nodes.
  122. * @return The contained iterator.
  123. */
  124. public final DTMIterator getContainedIter()
  125. {
  126. return m_iter;
  127. }
  128. /**
  129. * The DTMManager to use if we're using a NodeVector only.
  130. * We may well want to do away with this, and store it in the NodeVector.
  131. */
  132. protected DTMManager m_dtmMgr;
  133. // ==== Constructors ====
  134. /**
  135. * Create a new NodeSequence from a (already cloned) iterator.
  136. *
  137. * @param iter Cloned (not static) DTMIterator.
  138. * @param context The initial context node.
  139. * @param xctxt The execution context.
  140. * @param shouldCacheNodes True if this sequence can random access.
  141. */
  142. public NodeSequence(DTMIterator iter, int context, XPathContext xctxt, boolean shouldCacheNodes)
  143. {
  144. setIter(iter);
  145. setRoot(context, xctxt);
  146. setShouldCacheNodes(shouldCacheNodes);
  147. }
  148. /**
  149. * Create a new NodeSequence from a (already cloned) iterator.
  150. *
  151. * @param iter Cloned (not static) DTMIterator.
  152. */
  153. public NodeSequence(Object nodeVector)
  154. {
  155. super(nodeVector);
  156. if(null != nodeVector)
  157. {
  158. assertion(nodeVector instanceof NodeVector,
  159. "Must have a NodeVector as the object for NodeSequence!");
  160. if(nodeVector instanceof DTMIterator)
  161. {
  162. setIter((DTMIterator)nodeVector);
  163. m_last = ((DTMIterator)nodeVector).getLength();
  164. }
  165. }
  166. }
  167. /**
  168. * Construct an empty XNodeSet object. This is used to create a mutable
  169. * nodeset to which random nodes may be added.
  170. */
  171. public NodeSequence(DTMManager dtmMgr)
  172. {
  173. super(new NodeVector());
  174. m_last = 0;
  175. m_dtmMgr = dtmMgr;
  176. }
  177. /**
  178. * Create a new NodeSequence in an invalid (null) state.
  179. */
  180. public NodeSequence()
  181. {
  182. }
  183. /**
  184. * @see DTMIterator#getDTM(int)
  185. */
  186. public DTM getDTM(int nodeHandle)
  187. {
  188. DTMManager mgr = getDTMManager();
  189. if(null != mgr)
  190. return getDTMManager().getDTM(nodeHandle);
  191. else
  192. {
  193. assertion(false, "Can not get a DTM Unless a DTMManager has been set!");
  194. return null;
  195. }
  196. }
  197. /**
  198. * @see DTMIterator#getDTMManager()
  199. */
  200. public DTMManager getDTMManager()
  201. {
  202. return m_dtmMgr;
  203. }
  204. /**
  205. * @see DTMIterator#getRoot()
  206. */
  207. public int getRoot()
  208. {
  209. if(null != m_iter)
  210. return m_iter.getRoot();
  211. else
  212. {
  213. // NodeSetDTM will call this, and so it's not a good thing to throw
  214. // an assertion here.
  215. // assertion(false, "Can not get the root from a non-iterated NodeSequence!");
  216. return DTM.NULL;
  217. }
  218. }
  219. /**
  220. * @see DTMIterator#setRoot(int, Object)
  221. */
  222. public void setRoot(int nodeHandle, Object environment)
  223. {
  224. if(null != m_iter)
  225. {
  226. XPathContext xctxt = (XPathContext)environment;
  227. m_dtmMgr = xctxt.getDTMManager();
  228. m_iter.setRoot(nodeHandle, environment);
  229. if(!m_iter.isDocOrdered())
  230. {
  231. if(!hasCache())
  232. setShouldCacheNodes(true);
  233. runTo(-1);
  234. m_next=0;
  235. }
  236. }
  237. else
  238. assertion(false, "Can not setRoot on a non-iterated NodeSequence!");
  239. }
  240. /**
  241. * @see DTMIterator#reset()
  242. */
  243. public void reset()
  244. {
  245. m_next = 0;
  246. // not resetting the iterator on purpose!!!
  247. }
  248. /**
  249. * @see DTMIterator#getWhatToShow()
  250. */
  251. public int getWhatToShow()
  252. {
  253. return hasCache() ? (DTMFilter.SHOW_ALL & ~DTMFilter.SHOW_ENTITY_REFERENCE)
  254. : m_iter.getWhatToShow();
  255. }
  256. /**
  257. * @see DTMIterator#getExpandEntityReferences()
  258. */
  259. public boolean getExpandEntityReferences()
  260. {
  261. if(null != m_iter)
  262. return m_iter.getExpandEntityReferences();
  263. else
  264. return true;
  265. }
  266. /**
  267. * @see DTMIterator#nextNode()
  268. */
  269. public int nextNode()
  270. {
  271. // If the cache is on, and the node has already been found, then
  272. // just return from the list.
  273. NodeVector vec = getVector();
  274. if (null != vec)
  275. {
  276. if(m_next < vec.size())
  277. {
  278. int next = vec.elementAt(m_next);
  279. m_next++;
  280. return next;
  281. }
  282. else if((-1 != m_last) || (null == m_iter))
  283. {
  284. m_next++;
  285. return DTM.NULL;
  286. }
  287. }
  288. if (null == m_iter)
  289. return DTM.NULL;
  290. int next = m_iter.nextNode();
  291. if(DTM.NULL != next)
  292. {
  293. if(hasCache())
  294. {
  295. if(m_iter.isDocOrdered())
  296. {
  297. getVector().addElement(next);
  298. m_next++;
  299. }
  300. else
  301. {
  302. int insertIndex = addNodeInDocOrder(next);
  303. if(insertIndex >= 0)
  304. m_next++;
  305. }
  306. }
  307. else
  308. m_next++;
  309. }
  310. else
  311. {
  312. m_last = m_next;
  313. m_next++;
  314. }
  315. return next;
  316. }
  317. /**
  318. * @see DTMIterator#previousNode()
  319. */
  320. public int previousNode()
  321. {
  322. if(hasCache())
  323. {
  324. if(m_next <= 0)
  325. return DTM.NULL;
  326. else
  327. {
  328. m_next--;
  329. return item(m_next);
  330. }
  331. }
  332. else
  333. {
  334. int n = m_iter.previousNode();
  335. m_next = m_iter.getCurrentPos();
  336. return m_next;
  337. }
  338. }
  339. /**
  340. * @see DTMIterator#detach()
  341. */
  342. public void detach()
  343. {
  344. if(null != m_iter)
  345. m_iter.detach();
  346. super.detach();
  347. }
  348. /**
  349. * Calling this with a value of false will cause the nodeset
  350. * to be cached.
  351. * @see DTMIterator#allowDetachToRelease(boolean)
  352. */
  353. public void allowDetachToRelease(boolean allowRelease)
  354. {
  355. if((false == allowRelease) && !hasCache())
  356. {
  357. setShouldCacheNodes(true);
  358. }
  359. if(null != m_iter)
  360. m_iter.allowDetachToRelease(allowRelease);
  361. super.allowDetachToRelease(allowRelease);
  362. }
  363. /**
  364. * @see DTMIterator#getCurrentNode()
  365. */
  366. public int getCurrentNode()
  367. {
  368. if(hasCache())
  369. {
  370. int currentIndex = m_next-1;
  371. NodeVector vec = getVector();
  372. if((currentIndex >= 0) && (currentIndex < vec.size()))
  373. return vec.elementAt(currentIndex);
  374. else
  375. return DTM.NULL;
  376. }
  377. if(null != m_iter)
  378. {
  379. return m_iter.getCurrentNode();
  380. }
  381. else
  382. return DTM.NULL;
  383. }
  384. /**
  385. * @see DTMIterator#isFresh()
  386. */
  387. public boolean isFresh()
  388. {
  389. return (0 == m_next);
  390. }
  391. /**
  392. * @see DTMIterator#setShouldCacheNodes(boolean)
  393. */
  394. public void setShouldCacheNodes(boolean b)
  395. {
  396. if (b)
  397. {
  398. if(!hasCache())
  399. {
  400. SetVector(new NodeVector());
  401. }
  402. // else
  403. // getVector().RemoveAllNoClear(); // Is this good?
  404. }
  405. else
  406. SetVector(null);
  407. }
  408. /**
  409. * @see DTMIterator#isMutable()
  410. */
  411. public boolean isMutable()
  412. {
  413. return hasCache(); // though may be surprising if it also has an iterator!
  414. }
  415. /**
  416. * @see DTMIterator#getCurrentPos()
  417. */
  418. public int getCurrentPos()
  419. {
  420. return m_next;
  421. }
  422. /**
  423. * @see DTMIterator#runTo(int)
  424. */
  425. public void runTo(int index)
  426. {
  427. int n;
  428. if (-1 == index)
  429. {
  430. int pos = m_next;
  431. while (DTM.NULL != (n = nextNode()));
  432. m_next = pos;
  433. }
  434. else if(m_next == index)
  435. {
  436. return;
  437. }
  438. else if(hasCache() && m_next < getVector().size())
  439. {
  440. m_next = index;
  441. }
  442. else if((null == getVector()) && (index < m_next))
  443. {
  444. while ((m_next >= index) && DTM.NULL != (n = previousNode()));
  445. }
  446. else
  447. {
  448. while ((m_next < index) && DTM.NULL != (n = nextNode()));
  449. }
  450. }
  451. /**
  452. * @see DTMIterator#setCurrentPos(int)
  453. */
  454. public void setCurrentPos(int i)
  455. {
  456. runTo(i);
  457. }
  458. /**
  459. * @see DTMIterator#item(int)
  460. */
  461. public int item(int index)
  462. {
  463. setCurrentPos(index);
  464. int n = nextNode();
  465. m_next = index;
  466. return n;
  467. }
  468. /**
  469. * @see DTMIterator#setItem(int, int)
  470. */
  471. public void setItem(int node, int index)
  472. {
  473. NodeVector vec = getVector();
  474. if(null != vec)
  475. {
  476. vec.setElementAt(node, index);
  477. m_last = vec.size();
  478. }
  479. else
  480. m_iter.setItem(node, index);
  481. }
  482. /**
  483. * @see DTMIterator#getLength()
  484. */
  485. public int getLength()
  486. {
  487. if(hasCache())
  488. {
  489. // If this NodeSequence wraps a mutable nodeset, then
  490. // m_last will not reflect the size of the nodeset if
  491. // it has been mutated...
  492. if (m_iter instanceof NodeSetDTM)
  493. {
  494. return m_iter.getLength();
  495. }
  496. if(-1 == m_last)
  497. {
  498. int pos = m_next;
  499. runTo(-1);
  500. m_next = pos;
  501. }
  502. return m_last;
  503. }
  504. else
  505. {
  506. return (-1 == m_last) ? (m_last = m_iter.getLength()) : m_last;
  507. }
  508. }
  509. /**
  510. * Note: Not a deep clone.
  511. * @see DTMIterator#cloneWithReset()
  512. */
  513. public DTMIterator cloneWithReset() throws CloneNotSupportedException
  514. {
  515. NodeSequence seq = (NodeSequence)super.clone();
  516. seq.m_next = 0;
  517. return seq;
  518. }
  519. /**
  520. * Get a clone of this iterator, but don't reset the iteration in the
  521. * process, so that it may be used from the current position.
  522. * Note: Not a deep clone.
  523. *
  524. * @return A clone of this object.
  525. *
  526. * @throws CloneNotSupportedException
  527. */
  528. public Object clone() throws CloneNotSupportedException
  529. {
  530. return super.clone();
  531. }
  532. /**
  533. * @see DTMIterator#isDocOrdered()
  534. */
  535. public boolean isDocOrdered()
  536. {
  537. if(null != m_iter)
  538. return m_iter.isDocOrdered();
  539. else
  540. return true; // can't be sure?
  541. }
  542. /**
  543. * @see DTMIterator#getAxis()
  544. */
  545. public int getAxis()
  546. {
  547. if(null != m_iter)
  548. return m_iter.getAxis();
  549. else
  550. {
  551. assertion(false, "Can not getAxis from a non-iterated node sequence!");
  552. return 0;
  553. }
  554. }
  555. /**
  556. * @see PathComponent#getAnalysisBits()
  557. */
  558. public int getAnalysisBits()
  559. {
  560. if((null != m_iter) && (m_iter instanceof PathComponent))
  561. return ((PathComponent)m_iter).getAnalysisBits();
  562. else
  563. return 0;
  564. }
  565. /**
  566. * @see Expression#fixupVariables(Vector, int)
  567. */
  568. public void fixupVariables(Vector vars, int globalsSize)
  569. {
  570. super.fixupVariables(vars, globalsSize);
  571. }
  572. /**
  573. * Add the node into a vector of nodes where it should occur in
  574. * document order.
  575. * @param v Vector of nodes, presumably containing Nodes
  576. * @param obj Node object.
  577. *
  578. * @param node The node to be added.
  579. * @param test true if we should test for doc order
  580. * @param support The XPath runtime context.
  581. * @return insertIndex.
  582. * @throws RuntimeException thrown if this NodeSetDTM is not of
  583. * a mutable type.
  584. */
  585. protected int addNodeInDocOrder(int node)
  586. {
  587. assertion(hasCache(), "addNodeInDocOrder must be done on a mutable sequence!");
  588. int insertIndex = -1;
  589. NodeVector vec = getVector();
  590. // This needs to do a binary search, but a binary search
  591. // is somewhat tough because the sequence test involves
  592. // two nodes.
  593. int size = vec.size(), i;
  594. for (i = size - 1; i >= 0; i--)
  595. {
  596. int child = vec.elementAt(i);
  597. if (child == node)
  598. {
  599. i = -2; // Duplicate, suppress insert
  600. break;
  601. }
  602. DTM dtm = m_dtmMgr.getDTM(node);
  603. if (!dtm.isNodeAfter(node, child))
  604. {
  605. break;
  606. }
  607. }
  608. if (i != -2)
  609. {
  610. insertIndex = i + 1;
  611. vec.insertElementAt(node, insertIndex);
  612. }
  613. // checkDups();
  614. return insertIndex;
  615. } // end addNodeInDocOrder(Vector v, Object obj)
  616. }