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    }