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: DescendantIterator.java 469314 2006-10-30 23:31:59Z minchau $
020     */
021    package org.apache.xpath.axes;
022    
023    import org.apache.xml.dtm.Axis;
024    import org.apache.xml.dtm.DTM;
025    import org.apache.xml.dtm.DTMAxisTraverser;
026    import org.apache.xml.dtm.DTMFilter;
027    import org.apache.xml.dtm.DTMIterator;
028    import org.apache.xpath.Expression;
029    import org.apache.xpath.XPathContext;
030    import org.apache.xpath.compiler.Compiler;
031    import org.apache.xpath.compiler.OpCodes;
032    import org.apache.xpath.compiler.OpMap;
033    import org.apache.xpath.patterns.NodeTest;
034    
035    /**
036     * This class implements an optimized iterator for
037     * descendant, descendant-or-self, or "//foo" patterns.
038     * @see org.apache.xpath.axes.LocPathIterator
039     * @xsl.usage advanced
040     */
041    public class DescendantIterator extends LocPathIterator
042    {
043        static final long serialVersionUID = -1190338607743976938L;
044      /**
045       * Create a DescendantIterator object.
046       *
047       * @param compiler A reference to the Compiler that contains the op map.
048       * @param opPos The position within the op map, which contains the
049       * location path expression for this itterator.
050       *
051       * @throws javax.xml.transform.TransformerException
052       */
053      DescendantIterator(Compiler compiler, int opPos, int analysis)
054              throws javax.xml.transform.TransformerException
055      {
056    
057        super(compiler, opPos, analysis, false);
058    
059        int firstStepPos = OpMap.getFirstChildPos(opPos);
060        int stepType = compiler.getOp(firstStepPos);
061    
062        boolean orSelf = (OpCodes.FROM_DESCENDANTS_OR_SELF == stepType);
063        boolean fromRoot = false;
064        if (OpCodes.FROM_SELF == stepType)
065        {
066          orSelf = true;
067          // firstStepPos += 8;
068        }
069        else if(OpCodes.FROM_ROOT == stepType)
070        {
071          fromRoot = true;
072          // Ugly code... will go away when AST work is done.
073          int nextStepPos = compiler.getNextStepPos(firstStepPos);
074          if(compiler.getOp(nextStepPos) == OpCodes.FROM_DESCENDANTS_OR_SELF)
075            orSelf = true;
076          // firstStepPos += 8;
077        }
078        
079        // Find the position of the last step.
080        int nextStepPos = firstStepPos;
081        while(true)
082        {
083          nextStepPos = compiler.getNextStepPos(nextStepPos);
084          if(nextStepPos > 0)
085          {
086            int stepOp = compiler.getOp(nextStepPos);
087            if(OpCodes.ENDOP != stepOp)
088              firstStepPos = nextStepPos;
089            else
090              break;
091          }
092          else
093            break;
094          
095        }
096        
097        // Fix for http://nagoya.apache.org/bugzilla/show_bug.cgi?id=1336
098        if((analysis & WalkerFactory.BIT_CHILD) != 0)
099          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    }