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