Home | History | Annotate | Download | only in axes
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: OneStepIterator.java 469314 2006-10-30 23:31:59Z minchau $
     20  */
     21 package org.apache.xpath.axes;
     22 
     23 import org.apache.xml.dtm.DTM;
     24 import org.apache.xml.dtm.DTMAxisIterator;
     25 import org.apache.xml.dtm.DTMFilter;
     26 import org.apache.xml.dtm.DTMIterator;
     27 import org.apache.xpath.Expression;
     28 import org.apache.xpath.XPathContext;
     29 import org.apache.xpath.compiler.Compiler;
     30 import org.apache.xpath.compiler.OpMap;
     31 
     32 /**
     33  * This class implements a general iterator for
     34  * those LocationSteps with only one step, and perhaps a predicate.
     35  * @see org.apache.xpath.axes#LocPathIterator
     36  * @xsl.usage advanced
     37  */
     38 public class OneStepIterator extends ChildTestIterator
     39 {
     40     static final long serialVersionUID = 4623710779664998283L;
     41   /** The traversal axis from where the nodes will be filtered. */
     42   protected int m_axis = -1;
     43 
     44   /** The DTM inner traversal class, that corresponds to the super axis. */
     45   protected DTMAxisIterator m_iterator;
     46 
     47   /**
     48    * Create a OneStepIterator object.
     49    *
     50    * @param compiler A reference to the Compiler that contains the op map.
     51    * @param opPos The position within the op map, which contains the
     52    * location path expression for this itterator.
     53    *
     54    * @throws javax.xml.transform.TransformerException
     55    */
     56   OneStepIterator(Compiler compiler, int opPos, int analysis)
     57           throws javax.xml.transform.TransformerException
     58   {
     59     super(compiler, opPos, analysis);
     60     int firstStepPos = OpMap.getFirstChildPos(opPos);
     61 
     62     m_axis = WalkerFactory.getAxisFromStep(compiler, firstStepPos);
     63 
     64   }
     65 
     66 
     67   /**
     68    * Create a OneStepIterator object.
     69    *
     70    * @param iterator The DTM iterator which this iterator will use.
     71    * @param axis One of Axis.Child, etc., or -1 if the axis is unknown.
     72    *
     73    * @throws javax.xml.transform.TransformerException
     74    */
     75   public OneStepIterator(DTMAxisIterator iterator, int axis)
     76           throws javax.xml.transform.TransformerException
     77   {
     78     super(null);
     79 
     80     m_iterator = iterator;
     81     m_axis = axis;
     82     int whatToShow = DTMFilter.SHOW_ALL;
     83     initNodeTest(whatToShow);
     84   }
     85 
     86   /**
     87    * Initialize the context values for this expression
     88    * after it is cloned.
     89    *
     90    * @param context The XPath runtime context for this
     91    * transformation.
     92    */
     93   public void setRoot(int context, Object environment)
     94   {
     95     super.setRoot(context, environment);
     96     if(m_axis > -1)
     97       m_iterator = m_cdtm.getAxisIterator(m_axis);
     98     m_iterator.setStartNode(m_context);
     99   }
    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 }
    346