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: DescendantIterator.java 469314 2006-10-30 23:31:59Z minchau $
     20  */
     21 package org.apache.xpath.axes;
     22 
     23 import org.apache.xml.dtm.Axis;
     24 import org.apache.xml.dtm.DTM;
     25 import org.apache.xml.dtm.DTMAxisTraverser;
     26 import org.apache.xml.dtm.DTMFilter;
     27 import org.apache.xml.dtm.DTMIterator;
     28 import org.apache.xpath.Expression;
     29 import org.apache.xpath.XPathContext;
     30 import org.apache.xpath.compiler.Compiler;
     31 import org.apache.xpath.compiler.OpCodes;
     32 import org.apache.xpath.compiler.OpMap;
     33 import org.apache.xpath.patterns.NodeTest;
     34 
     35 /**
     36  * This class implements an optimized iterator for
     37  * descendant, descendant-or-self, or "//foo" patterns.
     38  * @see org.apache.xpath.axes.LocPathIterator
     39  * @xsl.usage advanced
     40  */
     41 public class DescendantIterator extends LocPathIterator
     42 {
     43     static final long serialVersionUID = -1190338607743976938L;
     44   /**
     45    * Create a DescendantIterator object.
     46    *
     47    * @param compiler A reference to the Compiler that contains the op map.
     48    * @param opPos The position within the op map, which contains the
     49    * location path expression for this itterator.
     50    *
     51    * @throws javax.xml.transform.TransformerException
     52    */
     53   DescendantIterator(Compiler compiler, int opPos, int analysis)
     54           throws javax.xml.transform.TransformerException
     55   {
     56 
     57     super(compiler, opPos, analysis, false);
     58 
     59     int firstStepPos = OpMap.getFirstChildPos(opPos);
     60     int stepType = compiler.getOp(firstStepPos);
     61 
     62     boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType);
     63     boolean fromRoot = false;
     64     if (OpCodes.FROM_SELF == stepType)
     65     {
     66       orSelf = true;
     67       // firstStepPos += 8;
     68     }
     69     else if(OpCodes.FROM_ROOT == stepType)
     70     {
     71       fromRoot = true;
     72       // Ugly code... will go away when AST work is done.
     73       int nextStepPos = compiler.getNextStepPos(firstStepPos);
     74       if(compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF)
     75         orSelf = true;
     76       // firstStepPos += 8;
     77     }
     78 
     79     // Find the position of the last step.
     80     int nextStepPos = firstStepPos;
     81     while(true)
     82     {
     83       nextStepPos = compiler.getNextStepPos(nextStepPos);
     84       if(nextStepPos > 0)
     85       {
     86         int stepOp = compiler.getOp(nextStepPos);
     87         if(OpCodes.ENDOP != stepOp)
     88           firstStepPos = nextStepPos;
     89         else
     90           break;
     91       }
     92       else
     93         break;
     94 
     95     }
     96 
     97     // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336
     98     if((analysis & WalkerFactory.BIT_CHILD) != 0)
     99       orSelf = false;
    100 
    101     if(fromRoot)
    102     {
    103       if(orSelf)
    104         m_axis = Axis.DESCENDANTSORSELFFROMROOT;
    105       else
    106         m_axis = Axis.DESCENDANTSFROMROOT;
    107     }
    108     else if(orSelf)
    109       m_axis = Axis.DESCENDANTORSELF;
    110     else
    111       m_axis = Axis.DESCENDANT;
    112 
    113     int whatToShow = compiler.getWhatToShow(firstStepPos);
    114 
    115     if ((0 == (whatToShow
    116                & (DTMFilter.SHOW_ATTRIBUTE | DTMFilter.SHOW_ELEMENT
    117                   | DTMFilter.SHOW_PROCESSING_INSTRUCTION))) ||
    118                    (whatToShow == DTMFilter.SHOW_ALL))
    119       initNodeTest(whatToShow);
    120     else
    121     {
    122       initNodeTest(whatToShow, compiler.getStepNS(firstStepPos),
    123                               compiler.getStepLocalName(firstStepPos));
    124     }
    125     initPredicateInfo(compiler, firstStepPos);
    126   }
    127 
    128   /**
    129    * Create a DescendantIterator object.
    130    *
    131    */
    132   public DescendantIterator()
    133   {
    134     super(null);
    135     m_axis = Axis.DESCENDANTSORSELFFROMROOT;
    136     int whatToShow = DTMFilter.SHOW_ALL;
    137     initNodeTest(whatToShow);
    138   }
    139 
    140 
    141   /**
    142    *  Get a cloned Iterator that is reset to the beginning
    143    *  of the query.
    144    *
    145    *  @return A cloned NodeIterator set of the start of the query.
    146    *
    147    *  @throws CloneNotSupportedException
    148    */
    149   public DTMIterator cloneWithReset() throws CloneNotSupportedException
    150   {
    151 
    152     DescendantIterator clone = (DescendantIterator) super.cloneWithReset();
    153     clone.m_traverser = m_traverser;
    154 
    155     clone.resetProximityPositions();
    156 
    157     return clone;
    158   }
    159 
    160   /**
    161    *  Returns the next node in the set and advances the position of the
    162    * iterator in the set. After a NodeIterator is created, the first call
    163    * to nextNode() returns the first node in the set.
    164    *
    165    * @return  The next <code>Node</code> in the set being iterated over, or
    166    *   <code>null</code> if there are no more members in that set.
    167    *
    168    * @throws DOMException
    169    *    INVALID_STATE_ERR: Raised if this method is called after the
    170    *   <code>detach</code> method was invoked.
    171    */
    172   public int nextNode()
    173   {
    174    	if(m_foundLast)
    175   		return DTM.NULL;
    176 
    177     if(DTM.NULL == m_lastFetched)
    178     {
    179       resetProximityPositions();
    180     }
    181 
    182     int next;
    183 
    184     org.apache.xpath.VariableStack vars;
    185     int savedStart;
    186     if (-1 != m_stackFrame)
    187     {
    188       vars = m_execContext.getVarStack();
    189 
    190       // These three statements need to be combined into one operation.
    191       savedStart = vars.getStackFrame();
    192 
    193       vars.setStackFrame(m_stackFrame);
    194     }
    195     else
    196     {
    197       // Yuck.  Just to shut up the compiler!
    198       vars = null;
    199       savedStart = 0;
    200     }
    201 
    202     try
    203     {
    204       do
    205       {
    206         if(0 == m_extendedTypeID)
    207         {
    208           next = m_lastFetched = (DTM.NULL == m_lastFetched)
    209                        ? m_traverser.first(m_context)
    210                        : m_traverser.next(m_context, m_lastFetched);
    211         }
    212         else
    213         {
    214           next = m_lastFetched = (DTM.NULL == m_lastFetched)
    215                        ? m_traverser.first(m_context, m_extendedTypeID)
    216                        : m_traverser.next(m_context, m_lastFetched,
    217                                           m_extendedTypeID);
    218         }
    219 
    220         if (DTM.NULL != next)
    221         {
    222           if(DTMIterator.FILTER_ACCEPT == acceptNode(next))
    223             break;
    224           else
    225             continue;
    226         }
    227         else
    228           break;
    229       }
    230       while (next != DTM.NULL);
    231 
    232       if (DTM.NULL != next)
    233       {
    234       	m_pos++;
    235         return next;
    236       }
    237       else
    238       {
    239         m_foundLast = true;
    240 
    241         return DTM.NULL;
    242       }
    243     }
    244     finally
    245     {
    246       if (-1 != m_stackFrame)
    247       {
    248         // These two statements need to be combined into one operation.
    249         vars.setStackFrame(savedStart);
    250       }
    251     }
    252   }
    253 
    254   /**
    255    * Initialize the context values for this expression
    256    * after it is cloned.
    257    *
    258    * @param context The XPath runtime context for this
    259    * transformation.
    260    */
    261   public void setRoot(int context, Object environment)
    262   {
    263     super.setRoot(context, environment);
    264     m_traverser = m_cdtm.getAxisTraverser(m_axis);
    265 
    266     String localName = getLocalName();
    267     String namespace = getNamespace();
    268     int what = m_whatToShow;
    269     // System.out.println("what: ");
    270     // NodeTest.debugWhatToShow(what);
    271     if(DTMFilter.SHOW_ALL == what
    272        || NodeTest.WILD.equals(localName)
    273        || NodeTest.WILD.equals(namespace))
    274     {
    275       m_extendedTypeID = 0;
    276     }
    277     else
    278     {
    279       int type = getNodeTypeTest(what);
    280       m_extendedTypeID = m_cdtm.getExpandedTypeID(namespace, localName, type);
    281     }
    282 
    283   }
    284 
    285   /**
    286    * Return the first node out of the nodeset, if this expression is
    287    * a nodeset expression.  This is the default implementation for
    288    * nodesets.
    289    * <p>WARNING: Do not mutate this class from this function!</p>
    290    * @param xctxt The XPath runtime context.
    291    * @return the first node out of the nodeset, or DTM.NULL.
    292    */
    293   public int asNode(XPathContext xctxt)
    294     throws javax.xml.transform.TransformerException
    295   {
    296     if(getPredicateCount() > 0)
    297       return super.asNode(xctxt);
    298 
    299     int current = xctxt.getCurrentNode();
    300 
    301     DTM dtm = xctxt.getDTM(current);
    302     DTMAxisTraverser traverser = dtm.getAxisTraverser(m_axis);
    303 
    304     String localName = getLocalName();
    305     String namespace = getNamespace();
    306     int what = m_whatToShow;
    307 
    308     // System.out.print(" (DescendantIterator) ");
    309 
    310     // System.out.println("what: ");
    311     // NodeTest.debugWhatToShow(what);
    312     if(DTMFilter.SHOW_ALL == what
    313        || localName == NodeTest.WILD
    314        || namespace == NodeTest.WILD)
    315     {
    316       return traverser.first(current);
    317     }
    318     else
    319     {
    320       int type = getNodeTypeTest(what);
    321       int extendedType = dtm.getExpandedTypeID(namespace, localName, type);
    322       return traverser.first(current, extendedType);
    323     }
    324   }
    325 
    326   /**
    327    *  Detaches the iterator from the set which it iterated over, releasing
    328    * any computational resources and placing the iterator in the INVALID
    329    * state. After<code>detach</code> has been invoked, calls to
    330    * <code>nextNode</code> or<code>previousNode</code> will raise the
    331    * exception INVALID_STATE_ERR.
    332    */
    333   public void detach()
    334   {
    335     if (m_allowDetach) {
    336       m_traverser = null;
    337       m_extendedTypeID = 0;
    338 
    339       // Always call the superclass detach last!
    340       super.detach();
    341     }
    342   }
    343 
    344   /**
    345    * Returns the axis being iterated, if it is known.
    346    *
    347    * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple
    348    * types.
    349    */
    350   public int getAxis()
    351   {
    352     return m_axis;
    353   }
    354 
    355 
    356   /** The traverser to use to navigate over the descendants. */
    357   transient protected DTMAxisTraverser m_traverser;
    358 
    359   /** The axis that we are traversing. */
    360   protected int m_axis;
    361 
    362   /** The extended type ID, not set until setRoot. */
    363   protected int m_extendedTypeID;
    364 
    365   /**
    366    * @see Expression#deepEquals(Expression)
    367    */
    368   public boolean deepEquals(Expression expr)
    369   {
    370   	if(!super.deepEquals(expr))
    371   		return false;
    372 
    373   	if(m_axis != ((DescendantIterator)expr).m_axis)
    374   		return false;
    375 
    376   	return true;
    377   }
    378 
    379 
    380 }
    381