001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: OneStepIterator.java 469314 2006-10-30 23:31:59Z minchau $
020 */
021 package org.apache.xpath.axes;
022
023 import org.apache.xml.dtm.DTM;
024 import org.apache.xml.dtm.DTMAxisIterator;
025 import org.apache.xml.dtm.DTMFilter;
026 import org.apache.xml.dtm.DTMIterator;
027 import org.apache.xpath.Expression;
028 import org.apache.xpath.XPathContext;
029 import org.apache.xpath.compiler.Compiler;
030 import org.apache.xpath.compiler.OpMap;
031
032 /**
033 * This class implements a general iterator for
034 * those LocationSteps with only one step, and perhaps a predicate.
035 * @see org.apache.xpath.axes#LocPathIterator
036 * @xsl.usage advanced
037 */
038 public class OneStepIterator extends ChildTestIterator
039 {
040 static final long serialVersionUID = 4623710779664998283L;
041 /** The traversal axis from where the nodes will be filtered. */
042 protected int m_axis = -1;
043
044 /** The DTM inner traversal class, that corresponds to the super axis. */
045 protected DTMAxisIterator m_iterator;
046
047 /**
048 * Create a OneStepIterator object.
049 *
050 * @param compiler A reference to the Compiler that contains the op map.
051 * @param opPos The position within the op map, which contains the
052 * location path expression for this itterator.
053 *
054 * @throws javax.xml.transform.TransformerException
055 */
056 OneStepIterator(Compiler compiler, int opPos, int analysis)
057 throws javax.xml.transform.TransformerException
058 {
059 super(compiler, opPos, analysis);
060 int firstStepPos = OpMap.getFirstChildPos(opPos);
061
062 m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos);
063
064 }
065
066
067 /**
068 * Create a OneStepIterator object.
069 *
070 * @param iterator The DTM iterator which this iterator will use.
071 * @param axis One of Axis.Child, etc., or -1 if the axis is unknown.
072 *
073 * @throws javax.xml.transform.TransformerException
074 */
075 public OneStepIterator(DTMAxisIterator iterator, int axis)
076 throws javax.xml.transform.TransformerException
077 {
078 super(null);
079
080 m_iterator = iterator;
081 m_axis = axis;
082 int whatToShow = DTMFilter.SHOW_ALL;
083 initNodeTest(whatToShow);
084 }
085
086 /**
087 * Initialize the context values for this expression
088 * after it is cloned.
089 *
090 * @param context The XPath runtime context for this
091 * transformation.
092 */
093 public void setRoot(int context, Object environment)
094 {
095 super.setRoot(context, environment);
096 if(m_axis > -1)
097 m_iterator = m_cdtm.getAxisIterator(m_axis);
098 m_iterator.setStartNode(m_context);
099 }
100
101 /**
102 * Detaches the iterator from the set which it iterated over, releasing
103 * any computational resources and placing the iterator in the INVALID
104 * state. After<code>detach</code> has been invoked, calls to
105 * <code>nextNode</code> or<code>previousNode</code> will raise the
106 * exception INVALID_STATE_ERR.
107 */
108 public void detach()
109 {
110 if(m_allowDetach)
111 {
112 if(m_axis > -1)
113 m_iterator = null;
114
115 // Always call the superclass detach last!
116 super.detach();
117 }
118 }
119
120 /**
121 * Get the next node via getFirstAttribute && getNextAttribute.
122 */
123 protected int getNextNode()
124 {
125 return m_lastFetched = m_iterator.next();
126 }
127
128 /**
129 * Get a cloned iterator.
130 *
131 * @return A new iterator that can be used without mutating this one.
132 *
133 * @throws CloneNotSupportedException
134 */
135 public Object clone() throws CloneNotSupportedException
136 {
137 // Do not access the location path itterator during this operation!
138
139 OneStepIterator clone = (OneStepIterator) super.clone();
140
141 if(m_iterator != null)
142 {
143 clone.m_iterator = m_iterator.cloneIterator();
144 }
145 return clone;
146 }
147
148 /**
149 * Get a cloned Iterator that is reset to the beginning
150 * of the query.
151 *
152 * @return A cloned NodeIterator set of the start of the query.
153 *
154 * @throws CloneNotSupportedException
155 */
156 public DTMIterator cloneWithReset() throws CloneNotSupportedException
157 {
158
159 OneStepIterator clone = (OneStepIterator) super.cloneWithReset();
160 clone.m_iterator = m_iterator;
161
162 return clone;
163 }
164
165
166
167 /**
168 * Tells if this is a reverse axes. Overrides AxesWalker#isReverseAxes.
169 *
170 * @return true for this class.
171 */
172 public boolean isReverseAxes()
173 {
174 return m_iterator.isReverse();
175 }
176
177 /**
178 * Get the current sub-context position. In order to do the
179 * reverse axes count, for the moment this re-searches the axes
180 * up to the predicate. An optimization on this is to cache
181 * the nodes searched, but, for the moment, this case is probably
182 * rare enough that the added complexity isn't worth it.
183 *
184 * @param predicateIndex The predicate index of the proximity position.
185 *
186 * @return The pridicate index, or -1.
187 */
188 protected int getProximityPosition(int predicateIndex)
189 {
190 if(!isReverseAxes())
191 return super.getProximityPosition(predicateIndex);
192
193 // A negative predicate index seems to occur with
194 // (preceding-sibling::*|following-sibling::*)/ancestor::*[position()]/*[position()]
195 // -sb
196 if(predicateIndex < 0)
197 return -1;
198
199 if (m_proximityPositions[predicateIndex] <= 0)
200 {
201 XPathContext xctxt = getXPathContext();
202 try
203 {
204 OneStepIterator clone = (OneStepIterator) this.clone();
205
206 int root = getRoot();
207 xctxt.pushCurrentNode(root);
208 clone.setRoot(root, xctxt);
209
210 // clone.setPredicateCount(predicateIndex);
211 clone.m_predCount = predicateIndex;
212
213 // Count 'em all
214 int count = 1;
215 int next;
216
217 while (DTM.NULL != (next = clone.nextNode()))
218 {
219 count++;
220 }
221
222 m_proximityPositions[predicateIndex] += count;
223 }
224 catch (CloneNotSupportedException cnse)
225 {
226
227 // can't happen
228 }
229 finally
230 {
231 xctxt.popCurrentNode();
232 }
233 }
234
235 return m_proximityPositions[predicateIndex];
236 }
237
238 /**
239 * The number of nodes in the list. The range of valid child node indices
240 * is 0 to <code>length-1</code> inclusive.
241 *
242 * @return The number of nodes in the list, always greater or equal to zero.
243 */
244 public int getLength()
245 {
246 if(!isReverseAxes())
247 return super.getLength();
248
249 // Tell if this is being called from within a predicate.
250 boolean isPredicateTest = (this == m_execContext.getSubContextList());
251
252 // And get how many total predicates are part of this step.
253 int predCount = getPredicateCount();
254
255 // If we have already calculated the length, and the current predicate
256 // is the first predicate, then return the length. We don't cache
257 // the anything but the length of the list to the first predicate.
258 if (-1 != m_length && isPredicateTest && m_predicateIndex < 1)
259 return m_length;
260
261 int count = 0;
262
263 XPathContext xctxt = getXPathContext();
264 try
265 {
266 OneStepIterator clone = (OneStepIterator) this.cloneWithReset();
267
268 int root = getRoot();
269 xctxt.pushCurrentNode(root);
270 clone.setRoot(root, xctxt);
271
272 clone.m_predCount = m_predicateIndex;
273
274 int next;
275
276 while (DTM.NULL != (next = clone.nextNode()))
277 {
278 count++;
279 }
280 }
281 catch (CloneNotSupportedException cnse)
282 {
283 // can't happen
284 }
285 finally
286 {
287 xctxt.popCurrentNode();
288 }
289 if (isPredicateTest && m_predicateIndex < 1)
290 m_length = count;
291
292 return count;
293 }
294
295 /**
296 * Count backwards one proximity position.
297 *
298 * @param i The predicate index.
299 */
300 protected void countProximityPosition(int i)
301 {
302 if(!isReverseAxes())
303 super.countProximityPosition(i);
304 else if (i < m_proximityPositions.length)
305 m_proximityPositions[i]--;
306 }
307
308 /**
309 * Reset the iterator.
310 */
311 public void reset()
312 {
313
314 super.reset();
315 if(null != m_iterator)
316 m_iterator.reset();
317 }
318
319 /**
320 * Returns the axis being iterated, if it is known.
321 *
322 * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
323 * types.
324 */
325 public int getAxis()
326 {
327 return m_axis;
328 }
329
330 /**
331 * @see Expression#deepEquals(Expression)
332 */
333 public boolean deepEquals(Expression expr)
334 {
335 if(!super.deepEquals(expr))
336 return false;
337
338 if(m_axis != ((OneStepIterator)expr).m_axis)
339 return false;
340
341 return true;
342 }
343
344
345 }