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: OneStepIterator.java,v 1.15 2004/02/17 04:32:08 minchau Exp $
  18. */
  19. package com.sun.org.apache.xpath.internal.axes;
  20. import com.sun.org.apache.xml.internal.dtm.DTM;
  21. import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  22. import com.sun.org.apache.xml.internal.dtm.DTMFilter;
  23. import com.sun.org.apache.xml.internal.dtm.DTMIterator;
  24. import com.sun.org.apache.xpath.internal.Expression;
  25. import com.sun.org.apache.xpath.internal.XPathContext;
  26. import com.sun.org.apache.xpath.internal.compiler.Compiler;
  27. /**
  28. * This class implements a general iterator for
  29. * those LocationSteps with only one step, and perhaps a predicate.
  30. * @see com.sun.org.apache.xpath.internal.axes#LocPathIterator
  31. * @xsl.usage advanced
  32. */
  33. public class OneStepIterator extends ChildTestIterator
  34. {
  35. /** The traversal axis from where the nodes will be filtered. */
  36. protected int m_axis = -1;
  37. /** The DTM inner traversal class, that corresponds to the super axis. */
  38. protected DTMAxisIterator m_iterator;
  39. /**
  40. * Create a OneStepIterator object.
  41. *
  42. * @param compiler A reference to the Compiler that contains the op map.
  43. * @param opPos The position within the op map, which contains the
  44. * location path expression for this itterator.
  45. *
  46. * @throws javax.xml.transform.TransformerException
  47. */
  48. OneStepIterator(Compiler compiler, int opPos, int analysis)
  49. throws javax.xml.transform.TransformerException
  50. {
  51. super(compiler, opPos, analysis);
  52. int firstStepPos = compiler.getFirstChildPos(opPos);
  53. m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos);
  54. }
  55. /**
  56. * Create a OneStepIterator object.
  57. *
  58. * @param iterator The DTM iterator which this iterator will use.
  59. * @param axis One of Axis.Child, etc., or -1 if the axis is unknown.
  60. *
  61. * @throws javax.xml.transform.TransformerException
  62. */
  63. public OneStepIterator(DTMAxisIterator iterator, int axis)
  64. throws javax.xml.transform.TransformerException
  65. {
  66. super(null);
  67. m_iterator = iterator;
  68. m_axis = axis;
  69. int whatToShow = DTMFilter.SHOW_ALL;
  70. initNodeTest(whatToShow);
  71. }
  72. /**
  73. * Initialize the context values for this expression
  74. * after it is cloned.
  75. *
  76. * @param execContext The XPath runtime context for this
  77. * transformation.
  78. */
  79. public void setRoot(int context, Object environment)
  80. {
  81. super.setRoot(context, environment);
  82. if(m_axis > -1)
  83. m_iterator = m_cdtm.getAxisIterator(m_axis);
  84. m_iterator.setStartNode(m_context);
  85. }
  86. /**
  87. * Detaches the iterator from the set which it iterated over, releasing
  88. * any computational resources and placing the iterator in the INVALID
  89. * state. After<code>detach</code> has been invoked, calls to
  90. * <code>nextNode</code> or<code>previousNode</code> will raise the
  91. * exception INVALID_STATE_ERR.
  92. */
  93. public void detach()
  94. {
  95. if(m_allowDetach)
  96. {
  97. if(m_axis > -1)
  98. m_iterator = null;
  99. // Always call the superclass detach last!
  100. super.detach();
  101. }
  102. }
  103. /**
  104. * Get the next node via getFirstAttribute && getNextAttribute.
  105. */
  106. protected int getNextNode()
  107. {
  108. return m_lastFetched = m_iterator.next();
  109. }
  110. /**
  111. * Get a cloned iterator.
  112. *
  113. * @return A new iterator that can be used without mutating this one.
  114. *
  115. * @throws CloneNotSupportedException
  116. */
  117. public Object clone() throws CloneNotSupportedException
  118. {
  119. // Do not access the location path itterator during this operation!
  120. OneStepIterator clone = (OneStepIterator) super.clone();
  121. if(m_iterator != null)
  122. {
  123. clone.m_iterator = m_iterator.cloneIterator();
  124. }
  125. return clone;
  126. }
  127. /**
  128. * Get a cloned Iterator that is reset to the beginning
  129. * of the query.
  130. *
  131. * @return A cloned NodeIterator set of the start of the query.
  132. *
  133. * @throws CloneNotSupportedException
  134. */
  135. public DTMIterator cloneWithReset() throws CloneNotSupportedException
  136. {
  137. OneStepIterator clone = (OneStepIterator) super.cloneWithReset();
  138. clone.m_iterator = m_iterator;
  139. return clone;
  140. }
  141. /**
  142. * Tells if this is a reverse axes. Overrides AxesWalker#isReverseAxes.
  143. *
  144. * @return true for this class.
  145. */
  146. public boolean isReverseAxes()
  147. {
  148. return m_iterator.isReverse();
  149. }
  150. /**
  151. * Get the current sub-context position. In order to do the
  152. * reverse axes count, for the moment this re-searches the axes
  153. * up to the predicate. An optimization on this is to cache
  154. * the nodes searched, but, for the moment, this case is probably
  155. * rare enough that the added complexity isn't worth it.
  156. *
  157. * @param predicateIndex The predicate index of the proximity position.
  158. *
  159. * @return The pridicate index, or -1.
  160. */
  161. protected int getProximityPosition(int predicateIndex)
  162. {
  163. if(!isReverseAxes())
  164. return super.getProximityPosition(predicateIndex);
  165. // A negative predicate index seems to occur with
  166. // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()]
  167. // -sb
  168. if(predicateIndex < 0)
  169. return -1;
  170. if (m_proximityPositions[predicateIndex] <= 0)
  171. {
  172. XPathContext xctxt = getXPathContext();
  173. try
  174. {
  175. OneStepIterator clone = (OneStepIterator) this.clone();
  176. int root = getRoot();
  177. xctxt.pushCurrentNode(root);
  178. clone.setRoot(root, xctxt);
  179. // clone.setPredicateCount(predicateIndex);
  180. clone.m_predCount = predicateIndex;
  181. // Count 'em all
  182. int count = 1;
  183. int next;
  184. while (DTM.NULL != (next = clone.nextNode()))
  185. {
  186. count++;
  187. }
  188. m_proximityPositions[predicateIndex] += count;
  189. }
  190. catch (CloneNotSupportedException cnse)
  191. {
  192. // can't happen
  193. }
  194. finally
  195. {
  196. xctxt.popCurrentNode();
  197. }
  198. }
  199. return m_proximityPositions[predicateIndex];
  200. }
  201. /**
  202. * The number of nodes in the list. The range of valid child node indices
  203. * is 0 to <code>length-1</code> inclusive.
  204. *
  205. * @return The number of nodes in the list, always greater or equal to zero.
  206. */
  207. public int getLength()
  208. {
  209. if(!isReverseAxes())
  210. return super.getLength();
  211. // Tell if this is being called from within a predicate.
  212. boolean isPredicateTest = (this == m_execContext.getSubContextList());
  213. // And get how many total predicates are part of this step.
  214. int predCount = getPredicateCount();
  215. // If we have already calculated the length, and the current predicate
  216. // is the first predicate, then return the length. We don't cache
  217. // the anything but the length of the list to the first predicate.
  218. if (-1 != m_length && isPredicateTest && m_predicateIndex < 1)
  219. return m_length;
  220. int count = 0;
  221. XPathContext xctxt = getXPathContext();
  222. try
  223. {
  224. OneStepIterator clone = (OneStepIterator) this.cloneWithReset();
  225. int root = getRoot();
  226. xctxt.pushCurrentNode(root);
  227. clone.setRoot(root, xctxt);
  228. clone.m_predCount = m_predicateIndex;
  229. int next;
  230. while (DTM.NULL != (next = clone.nextNode()))
  231. {
  232. count++;
  233. }
  234. }
  235. catch (CloneNotSupportedException cnse)
  236. {
  237. // can't happen
  238. }
  239. finally
  240. {
  241. xctxt.popCurrentNode();
  242. }
  243. if (isPredicateTest && m_predicateIndex < 1)
  244. m_length = count;
  245. return count;
  246. }
  247. /**
  248. * Count backwards one proximity position.
  249. *
  250. * @param i The predicate index.
  251. */
  252. protected void countProximityPosition(int i)
  253. {
  254. if(!isReverseAxes())
  255. super.countProximityPosition(i);
  256. else if (i < m_proximityPositions.length)
  257. m_proximityPositions[i]--;
  258. }
  259. /**
  260. * Reset the iterator.
  261. */
  262. public void reset()
  263. {
  264. super.reset();
  265. if(null != m_iterator)
  266. m_iterator.reset();
  267. }
  268. /**
  269. * Returns the axis being iterated, if it is known.
  270. *
  271. * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
  272. * types.
  273. */
  274. public int getAxis()
  275. {
  276. return m_axis;
  277. }
  278. /**
  279. * @see Expression#deepEquals(Expression)
  280. */
  281. public boolean deepEquals(Expression expr)
  282. {
  283. if(!super.deepEquals(expr))
  284. return false;
  285. if(m_axis != ((OneStepIterator)expr).m_axis)
  286. return false;
  287. return true;
  288. }
  289. }