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.axes;
  58. import java.util.Vector;
  59. import javax.xml.transform.TransformerException;
  60. import org.apache.xml.dtm.DTM;
  61. import org.apache.xml.dtm.DTMAxisTraverser;
  62. import org.apache.xml.dtm.DTMIterator;
  63. import org.apache.xpath.Expression;
  64. import org.apache.xpath.ExpressionOwner;
  65. import org.apache.xpath.XPathContext;
  66. import org.apache.xpath.XPathVisitor;
  67. import org.apache.xpath.compiler.Compiler;
  68. import org.apache.xpath.patterns.NodeTest;
  69. import org.apache.xpath.res.XPATHErrorResources;
  70. import org.apache.xalan.res.XSLMessages;
  71. /**
  72. * Serves as common interface for axes Walkers, and stores common
  73. * state variables.
  74. */
  75. public class AxesWalker extends PredicatedNodeTest
  76. implements Cloneable, PathComponent, ExpressionOwner
  77. {
  78. /**
  79. * Construct an AxesWalker using a LocPathIterator.
  80. *
  81. * @param locPathIterator non-null reference to the parent iterator.
  82. */
  83. public AxesWalker(LocPathIterator locPathIterator, int axis)
  84. {
  85. super( locPathIterator );
  86. m_axis = axis;
  87. }
  88. public final WalkingIterator wi()
  89. {
  90. return (WalkingIterator)m_lpi;
  91. }
  92. /**
  93. * Initialize an AxesWalker during the parse of the XPath expression.
  94. *
  95. * @param compiler The Compiler object that has information about this
  96. * walker in the op map.
  97. * @param opPos The op code position of this location step.
  98. * @param stepType The type of location step.
  99. *
  100. * @throws javax.xml.transform.TransformerException
  101. */
  102. public void init(Compiler compiler, int opPos, int stepType)
  103. throws javax.xml.transform.TransformerException
  104. {
  105. initPredicateInfo(compiler, opPos);
  106. // int testType = compiler.getOp(nodeTestOpPos);
  107. }
  108. /**
  109. * Get a cloned AxesWalker.
  110. *
  111. * @return A new AxesWalker that can be used without mutating this one.
  112. *
  113. * @throws CloneNotSupportedException
  114. */
  115. public Object clone() throws CloneNotSupportedException
  116. {
  117. // Do not access the location path itterator during this operation!
  118. AxesWalker clone = (AxesWalker) super.clone();
  119. //clone.setCurrentNode(clone.m_root);
  120. // clone.m_isFresh = true;
  121. return clone;
  122. }
  123. /**
  124. * Do a deep clone of this walker, including next and previous walkers.
  125. * If the this AxesWalker is on the clone list, don't clone but
  126. * return the already cloned version.
  127. *
  128. * @param cloneOwner non-null reference to the cloned location path
  129. * iterator to which this clone will be added.
  130. * @param cloneList non-null vector of sources in odd elements, and the
  131. * corresponding clones in even vectors.
  132. *
  133. * @return non-null clone, which may be a new clone, or may be a clone
  134. * contained on the cloneList.
  135. */
  136. AxesWalker cloneDeep(WalkingIterator cloneOwner, Vector cloneList)
  137. throws CloneNotSupportedException
  138. {
  139. AxesWalker clone = findClone(this, cloneList);
  140. if(null != clone)
  141. return clone;
  142. clone = (AxesWalker)this.clone();
  143. clone.setLocPathIterator(cloneOwner);
  144. if(null != cloneList)
  145. {
  146. cloneList.addElement(this);
  147. cloneList.addElement(clone);
  148. }
  149. if(wi().m_lastUsedWalker == this)
  150. cloneOwner.m_lastUsedWalker = clone;
  151. if(null != m_nextWalker)
  152. clone.m_nextWalker = m_nextWalker.cloneDeep(cloneOwner, cloneList);
  153. // If you don't check for the cloneList here, you'll go into an
  154. // recursive infinate loop.
  155. if(null != cloneList)
  156. {
  157. if(null != m_prevWalker)
  158. clone.m_prevWalker = m_prevWalker.cloneDeep(cloneOwner, cloneList);
  159. }
  160. else
  161. {
  162. if(null != m_nextWalker)
  163. clone.m_nextWalker.m_prevWalker = clone;
  164. }
  165. return clone;
  166. }
  167. /**
  168. * Find a clone that corresponds to the key argument.
  169. *
  170. * @param key The original AxesWalker for which there may be a clone.
  171. * @param cloneList vector of sources in odd elements, and the
  172. * corresponding clones in even vectors, may be null.
  173. *
  174. * @return A clone that corresponds to the key, or null if key not found.
  175. */
  176. static AxesWalker findClone(AxesWalker key, Vector cloneList)
  177. {
  178. if(null != cloneList)
  179. {
  180. // First, look for clone on list.
  181. int n = cloneList.size();
  182. for (int i = 0; i < n; i+=2)
  183. {
  184. if(key == cloneList.elementAt(i))
  185. return (AxesWalker)cloneList.elementAt(i+1);
  186. }
  187. }
  188. return null;
  189. }
  190. /**
  191. * Detaches the walker from the set which it iterated over, releasing
  192. * any computational resources and placing the iterator in the INVALID
  193. * state.
  194. */
  195. public void detach()
  196. {
  197. m_currentNode = DTM.NULL;
  198. m_dtm = null;
  199. m_isFresh = true;
  200. m_root = DTM.NULL;
  201. }
  202. //=============== TreeWalker Implementation ===============
  203. /**
  204. * The root node of the TreeWalker, as specified in setRoot(int root).
  205. * Note that this may actually be below the current node.
  206. *
  207. * @return The context node of the step.
  208. */
  209. public int getRoot()
  210. {
  211. return m_root;
  212. }
  213. /**
  214. * Get the analysis bits for this walker, as defined in the WalkerFactory.
  215. * @return One of WalkerFactory#BIT_DESCENDANT, etc.
  216. */
  217. public int getAnalysisBits()
  218. {
  219. int axis = getAxis();
  220. int bit = WalkerFactory.getAnalysisBitFromAxes(axis);
  221. return bit;
  222. }
  223. /**
  224. * Set the root node of the TreeWalker.
  225. * (Not part of the DOM2 TreeWalker interface).
  226. *
  227. * @param root The context node of this step.
  228. */
  229. public void setRoot(int root)
  230. {
  231. // %OPT% Get this directly from the lpi.
  232. XPathContext xctxt = wi().getXPathContext();
  233. m_dtm = xctxt.getDTM(root);
  234. m_traverser = m_dtm.getAxisTraverser(m_axis);
  235. m_isFresh = true;
  236. m_foundLast = false;
  237. m_root = root;
  238. m_currentNode = root;
  239. if (DTM.NULL == root)
  240. {
  241. throw new RuntimeException(
  242. XSLMessages.createXPATHMessage(XPATHErrorResources.ER_SETTING_WALKER_ROOT_TO_NULL, null)); //"\n !!!! Error! Setting the root of a walker to null!!!");
  243. }
  244. resetProximityPositions();
  245. }
  246. /**
  247. * The node at which the TreeWalker is currently positioned.
  248. * <br> The value must not be null. Alterations to the DOM tree may cause
  249. * the current node to no longer be accepted by the TreeWalker's
  250. * associated filter. currentNode may also be explicitly set to any node,
  251. * whether or not it is within the subtree specified by the root node or
  252. * would be accepted by the filter and whatToShow flags. Further
  253. * traversal occurs relative to currentNode even if it is not part of the
  254. * current view by applying the filters in the requested direction (not
  255. * changing currentNode where no traversal is possible).
  256. *
  257. * @return The node at which the TreeWalker is currently positioned, only null
  258. * if setRoot has not yet been called.
  259. */
  260. public final int getCurrentNode()
  261. {
  262. return m_currentNode;
  263. }
  264. /**
  265. * Set the next walker in the location step chain.
  266. *
  267. *
  268. * @param walker Reference to AxesWalker derivative, or may be null.
  269. */
  270. public void setNextWalker(AxesWalker walker)
  271. {
  272. m_nextWalker = walker;
  273. }
  274. /**
  275. * Get the next walker in the location step chain.
  276. *
  277. *
  278. * @return Reference to AxesWalker derivative, or null.
  279. */
  280. public AxesWalker getNextWalker()
  281. {
  282. return m_nextWalker;
  283. }
  284. /**
  285. * Set or clear the previous walker reference in the location step chain.
  286. *
  287. *
  288. * @param walker Reference to previous walker reference in the location
  289. * step chain, or null.
  290. */
  291. public void setPrevWalker(AxesWalker walker)
  292. {
  293. m_prevWalker = walker;
  294. }
  295. /**
  296. * Get the previous walker reference in the location step chain.
  297. *
  298. *
  299. * @return Reference to previous walker reference in the location
  300. * step chain, or null.
  301. */
  302. public AxesWalker getPrevWalker()
  303. {
  304. return m_prevWalker;
  305. }
  306. /**
  307. * This is simply a way to bottle-neck the return of the next node, for
  308. * diagnostic purposes.
  309. *
  310. * @param n Node to return, or null.
  311. *
  312. * @return The argument.
  313. */
  314. private int returnNextNode(int n)
  315. {
  316. return n;
  317. }
  318. /**
  319. * Get the next node in document order on the axes.
  320. *
  321. * @return the next node in document order on the axes, or null.
  322. */
  323. protected int getNextNode()
  324. {
  325. if (m_foundLast)
  326. return DTM.NULL;
  327. if (m_isFresh)
  328. {
  329. m_currentNode = m_traverser.first(m_root);
  330. m_isFresh = false;
  331. }
  332. // I shouldn't have to do this the check for current node, I think.
  333. // numbering\numbering24.xsl fails if I don't do this. I think
  334. // it occurs as the walkers are backing up. -sb
  335. else if(DTM.NULL != m_currentNode)
  336. {
  337. m_currentNode = m_traverser.next(m_root, m_currentNode);
  338. }
  339. if (DTM.NULL == m_currentNode)
  340. this.m_foundLast = true;
  341. return m_currentNode;
  342. }
  343. /**
  344. * Moves the <code>TreeWalker</code> to the next visible node in document
  345. * order relative to the current node, and returns the new node. If the
  346. * current node has no next node, or if the search for nextNode attempts
  347. * to step upward from the TreeWalker's root node, returns
  348. * <code>null</code> , and retains the current node.
  349. * @return The new node, or <code>null</code> if the current node has no
  350. * next node in the TreeWalker's logical view.
  351. */
  352. public int nextNode()
  353. {
  354. int nextNode = DTM.NULL;
  355. AxesWalker walker = wi().getLastUsedWalker();
  356. while (true)
  357. {
  358. if (null == walker)
  359. break;
  360. nextNode = walker.getNextNode();
  361. if (DTM.NULL == nextNode)
  362. {
  363. walker = walker.m_prevWalker;
  364. }
  365. else
  366. {
  367. if (walker.acceptNode(nextNode) != DTMIterator.FILTER_ACCEPT)
  368. {
  369. continue;
  370. }
  371. if (null == walker.m_nextWalker)
  372. {
  373. wi().setLastUsedWalker(walker);
  374. // return walker.returnNextNode(nextNode);
  375. break;
  376. }
  377. else
  378. {
  379. AxesWalker prev = walker;
  380. walker = walker.m_nextWalker;
  381. walker.setRoot(nextNode);
  382. walker.m_prevWalker = prev;
  383. continue;
  384. }
  385. } // if(null != nextNode)
  386. } // while(null != walker)
  387. return nextNode;
  388. }
  389. //============= End TreeWalker Implementation =============
  390. /**
  391. * Get the index of the last node that can be itterated to.
  392. *
  393. *
  394. * @param xctxt XPath runtime context.
  395. *
  396. * @return the index of the last node that can be itterated to.
  397. */
  398. public int getLastPos(XPathContext xctxt)
  399. {
  400. int pos = getProximityPosition();
  401. AxesWalker walker;
  402. try
  403. {
  404. walker = (AxesWalker) clone();
  405. }
  406. catch (CloneNotSupportedException cnse)
  407. {
  408. return -1;
  409. }
  410. walker.setPredicateCount(walker.getPredicateCount() - 1);
  411. walker.setNextWalker(null);
  412. walker.setPrevWalker(null);
  413. WalkingIterator lpi = wi();
  414. AxesWalker savedWalker = lpi.getLastUsedWalker();
  415. try
  416. {
  417. lpi.setLastUsedWalker(walker);
  418. int next;
  419. while (DTM.NULL != (next = walker.nextNode()))
  420. {
  421. pos++;
  422. }
  423. // TODO: Should probably save this in the iterator.
  424. }
  425. finally
  426. {
  427. lpi.setLastUsedWalker(savedWalker);
  428. }
  429. // System.out.println("pos: "+pos);
  430. return pos;
  431. }
  432. //============= State Data =============
  433. /**
  434. * The DTM for the root. This can not be used, or must be changed,
  435. * for the filter walker, or any walker that can have nodes
  436. * from multiple documents.
  437. * Never, ever, access this value without going through getDTM(int node).
  438. */
  439. private DTM m_dtm;
  440. /**
  441. * Set the DTM for this walker.
  442. *
  443. * @param dtm Non-null reference to a DTM.
  444. */
  445. public void setDefaultDTM(DTM dtm)
  446. {
  447. m_dtm = dtm;
  448. }
  449. /**
  450. * Get the DTM for this walker.
  451. *
  452. * @return Non-null reference to a DTM.
  453. */
  454. public DTM getDTM(int node)
  455. {
  456. //
  457. return wi().getXPathContext().getDTM(node);
  458. }
  459. /**
  460. * Returns true if all the nodes in the iteration well be returned in document
  461. * order.
  462. * Warning: This can only be called after setRoot has been called!
  463. *
  464. * @return true as a default.
  465. */
  466. public boolean isDocOrdered()
  467. {
  468. return true;
  469. }
  470. /**
  471. * Returns the axis being iterated, if it is known.
  472. *
  473. * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
  474. * types.
  475. */
  476. public int getAxis()
  477. {
  478. return m_axis;
  479. }
  480. /**
  481. * This will traverse the heararchy, calling the visitor for
  482. * each member. If the called visitor method returns
  483. * false, the subtree should not be called.
  484. *
  485. * @param owner The owner of the visitor, where that path may be
  486. * rewritten if needed.
  487. * @param visitor The visitor whose appropriate method will be called.
  488. */
  489. public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
  490. {
  491. if(visitor.visitStep(owner, this))
  492. {
  493. callPredicateVisitors(visitor);
  494. if(null != m_nextWalker)
  495. {
  496. m_nextWalker.callVisitors(this, visitor);
  497. }
  498. }
  499. }
  500. /**
  501. * @see ExpressionOwner#getExpression()
  502. */
  503. public Expression getExpression()
  504. {
  505. return m_nextWalker;
  506. }
  507. /**
  508. * @see ExpressionOwner#setExpression(Expression)
  509. */
  510. public void setExpression(Expression exp)
  511. {
  512. exp.exprSetParent(this);
  513. m_nextWalker = (AxesWalker)exp;
  514. }
  515. /**
  516. * @see Expression#deepEquals(Expression)
  517. */
  518. public boolean deepEquals(Expression expr)
  519. {
  520. if (!super.deepEquals(expr))
  521. return false;
  522. AxesWalker walker = (AxesWalker)expr;
  523. if(this.m_axis != walker.m_axis)
  524. return false;
  525. return true;
  526. }
  527. /**
  528. * The root node of the TreeWalker, as specified when it was created.
  529. */
  530. transient int m_root = DTM.NULL;
  531. /**
  532. * The node at which the TreeWalker is currently positioned.
  533. */
  534. private transient int m_currentNode = DTM.NULL;
  535. /** True if an itteration has not begun. */
  536. transient boolean m_isFresh;
  537. /** The next walker in the location step chain.
  538. * @serial */
  539. protected AxesWalker m_nextWalker;
  540. /** The previous walker in the location step chain, or null.
  541. * @serial */
  542. AxesWalker m_prevWalker;
  543. /** The traversal axis from where the nodes will be filtered. */
  544. protected int m_axis = -1;
  545. /** The DTM inner traversal class, that corresponds to the super axis. */
  546. protected DTMAxisTraverser m_traverser;
  547. }