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: FilterExprWalker.java 469367 2006-10-31 04:41:08Z 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.DTMIterator;
026    import org.apache.xpath.Expression;
027    import org.apache.xpath.ExpressionOwner;
028    import org.apache.xpath.XPathContext;
029    import org.apache.xpath.XPathVisitor;
030    import org.apache.xpath.compiler.Compiler;
031    import org.apache.xpath.compiler.OpCodes;
032    import org.apache.xpath.objects.XNodeSet;
033    
034    /**
035     * Walker for the OP_VARIABLE, or OP_EXTFUNCTION, or OP_FUNCTION, or OP_GROUP,
036     * op codes.
037     * @see <a href="http://www.w3.org/TR/xpath#NT-FilterExpr">XPath FilterExpr descriptions</a>
038     */
039    public class FilterExprWalker extends AxesWalker
040    {
041        static final long serialVersionUID = 5457182471424488375L;
042    
043      /**
044       * Construct a FilterExprWalker using a LocPathIterator.
045       *
046       * @param locPathIterator non-null reference to the parent iterator.
047       */
048      public FilterExprWalker(WalkingIterator locPathIterator)
049      {
050        super(locPathIterator, Axis.FILTEREDLIST);
051      }
052    
053      /**
054       * Init a FilterExprWalker.
055       *
056       * @param compiler non-null reference to the Compiler that is constructing.
057       * @param opPos positive opcode position for this step.
058       * @param stepType The type of step.
059       *
060       * @throws javax.xml.transform.TransformerException
061       */
062      public void init(Compiler compiler, int opPos, int stepType)
063              throws javax.xml.transform.TransformerException
064      {
065    
066        super.init(compiler, opPos, stepType);
067    
068        // Smooth over an anomily in the opcode map...
069        switch (stepType)
070        {
071        case OpCodes.OP_FUNCTION :
072        case OpCodes.OP_EXTFUNCTION :
073            m_mustHardReset = true;
074        case OpCodes.OP_GROUP :
075        case OpCodes.OP_VARIABLE :
076          m_expr = compiler.compile(opPos);
077          m_expr.exprSetParent(this);
078          //if((OpCodes.OP_FUNCTION == stepType) && (m_expr instanceof org.apache.xalan.templates.FuncKey))
079          if(m_expr instanceof org.apache.xpath.operations.Variable)
080          {
081            // hack/temp workaround
082            m_canDetachNodeset = false;
083          }
084          break;
085        default :
086          m_expr = compiler.compile(opPos + 2);
087          m_expr.exprSetParent(this);
088        }
089    //    if(m_expr instanceof WalkingIterator)
090    //    {
091    //      WalkingIterator wi = (WalkingIterator)m_expr;
092    //      if(wi.getFirstWalker() instanceof FilterExprWalker)
093    //      {
094    //              FilterExprWalker fw = (FilterExprWalker)wi.getFirstWalker();
095    //              if(null == fw.getNextWalker())
096    //              {
097    //                      m_expr = fw.m_expr;
098    //                      m_expr.exprSetParent(this);
099    //              }
100    //      }
101    //                      
102    //    }
103      }
104      
105      /**
106       * Detaches the walker from the set which it iterated over, releasing
107       * any computational resources and placing the iterator in the INVALID
108       * state.
109       */
110      public void detach()
111      {  
112            super.detach();
113            if (m_canDetachNodeset)
114            {
115              m_exprObj.detach();
116            }
117            m_exprObj = null;
118      }
119    
120      /**
121       *  Set the root node of the TreeWalker.
122       *
123       * @param root non-null reference to the root, or starting point of 
124       *        the query.
125       */
126      public void setRoot(int root)
127      {
128    
129        super.setRoot(root);
130    
131            m_exprObj = FilterExprIteratorSimple.executeFilterExpr(root, 
132                              m_lpi.getXPathContext(), m_lpi.getPrefixResolver(), 
133                              m_lpi.getIsTopLevel(), m_lpi.m_stackFrame, m_expr);
134    
135      }
136    
137      /**
138       * Get a cloned FilterExprWalker.
139       *
140       * @return A new FilterExprWalker that can be used without mutating this one.
141       *
142       * @throws CloneNotSupportedException
143       */
144      public Object clone() throws CloneNotSupportedException
145      {
146    
147        FilterExprWalker clone = (FilterExprWalker) super.clone();
148    
149        if (null != m_exprObj)
150          clone.m_exprObj = (XNodeSet) m_exprObj.clone();
151    
152        return clone;
153      }
154      
155      /**
156       * This method needs to override AxesWalker.acceptNode because FilterExprWalkers
157       * don't need to, and shouldn't, do a node test.
158       * @param n  The node to check to see if it passes the filter or not.
159       * @return  a constant to determine whether the node is accepted,
160       *   rejected, or skipped, as defined  above .
161       */
162      public short acceptNode(int n)
163      {
164    
165        try
166        {
167          if (getPredicateCount() > 0)
168          {
169            countProximityPosition(0);
170    
171            if (!executePredicates(n, m_lpi.getXPathContext()))
172              return DTMIterator.FILTER_SKIP;
173          }
174    
175          return DTMIterator.FILTER_ACCEPT;
176        }
177        catch (javax.xml.transform.TransformerException se)
178        {
179          throw new RuntimeException(se.getMessage());
180        }
181      }
182    
183      /**
184       *  Moves the <code>TreeWalker</code> to the next visible node in document
185       * order relative to the current node, and returns the new node. If the
186       * current node has no next node,  or if the search for nextNode attempts
187       * to step upward from the TreeWalker's root node, returns
188       * <code>null</code> , and retains the current node.
189       * @return  The new node, or <code>null</code> if the current node has no
190       *   next node  in the TreeWalker's logical view.
191       */
192      public int getNextNode()
193      {
194    
195        if (null != m_exprObj)
196        {
197           int next = m_exprObj.nextNode();
198           return next;
199        }
200        else
201          return DTM.NULL;
202      }
203      
204      /**
205       * Get the index of the last node that can be itterated to.
206       *
207       *
208       * @param xctxt XPath runtime context.
209       *
210       * @return the index of the last node that can be itterated to.
211       */
212      public int getLastPos(XPathContext xctxt)
213      {
214        return m_exprObj.getLength();
215      }
216      
217      /** The contained expression. Should be non-null.
218       *  @serial   */
219      private Expression m_expr;
220    
221      /** The result of executing m_expr.  Needs to be deep cloned on clone op.  */
222      transient private XNodeSet m_exprObj;
223      
224      private boolean m_mustHardReset = false;
225      private boolean m_canDetachNodeset = true;
226    
227      /**
228       * This function is used to fixup variables from QNames to stack frame 
229       * indexes at stylesheet build time.
230       * @param vars List of QNames that correspond to variables.  This list 
231       * should be searched backwards for the first qualified name that 
232       * corresponds to the variable reference qname.  The position of the 
233       * QName in the vector from the start of the vector will be its position 
234       * in the stack frame (but variables above the globalsTop value will need 
235       * to be offset to the current stack frame).
236       */
237      public void fixupVariables(java.util.Vector vars, int globalsSize)
238      {
239        super.fixupVariables(vars, globalsSize);
240        m_expr.fixupVariables(vars, globalsSize);
241      }
242      
243      /**
244       * Get the inner contained expression of this filter.
245       */
246      public Expression getInnerExpression()
247      {
248            return m_expr;
249      }
250      
251      /**
252       * Set the inner contained expression of this filter.
253       */
254      public void setInnerExpression(Expression expr)
255      {
256            expr.exprSetParent(this);
257            m_expr = expr;
258      }
259    
260      
261      /** 
262       * Get the analysis bits for this walker, as defined in the WalkerFactory.
263       * @return One of WalkerFactory#BIT_DESCENDANT, etc.
264       */
265      public int getAnalysisBits()
266      {
267          if (null != m_expr && m_expr instanceof PathComponent)
268          {
269            return ((PathComponent) m_expr).getAnalysisBits();
270          }
271          return WalkerFactory.BIT_FILTER;
272      }
273      
274      /**
275       * Returns true if all the nodes in the iteration well be returned in document 
276       * order.
277       * Warning: This can only be called after setRoot has been called!
278       * 
279       * @return true as a default.
280       */
281      public boolean isDocOrdered()
282      {
283        return m_exprObj.isDocOrdered();
284      }
285      
286      /**
287       * Returns the axis being iterated, if it is known.
288       * 
289       * @return Axis.CHILD, etc., or -1 if the axis is not known or is of multiple 
290       * types.
291       */
292      public int getAxis()
293      {
294        return m_exprObj.getAxis();
295      }
296      
297      class filterExprOwner implements ExpressionOwner
298      {
299          /**
300         * @see ExpressionOwner#getExpression()
301         */
302        public Expression getExpression()
303        {
304          return m_expr;
305        }
306    
307        /**
308         * @see ExpressionOwner#setExpression(Expression)
309         */
310        public void setExpression(Expression exp)
311        {
312            exp.exprSetParent(FilterExprWalker.this);
313            m_expr = exp;
314        }
315      }
316      
317            /**
318             * This will traverse the heararchy, calling the visitor for 
319             * each member.  If the called visitor method returns 
320             * false, the subtree should not be called.
321             * 
322             * @param visitor The visitor whose appropriate method will be called.
323             */
324            public void callPredicateVisitors(XPathVisitor visitor)
325            {
326              m_expr.callVisitors(new filterExprOwner(), visitor);
327              
328              super.callPredicateVisitors(visitor);
329            } 
330    
331    
332        /**
333         * @see Expression#deepEquals(Expression)
334         */
335        public boolean deepEquals(Expression expr)
336        {
337          if (!super.deepEquals(expr))
338                    return false;
339    
340          FilterExprWalker walker = (FilterExprWalker)expr;
341          if(!m_expr.deepEquals(walker.m_expr))
342            return false;
343    
344          return true;
345        }
346    
347            
348    
349    }