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