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