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: FilterExprWalker.java 469367 2006-10-31 04:41:08Z minchau $
20 */
21package org.apache.xpath.axes;
22
23import org.apache.xml.dtm.Axis;
24import org.apache.xml.dtm.DTM;
25import org.apache.xml.dtm.DTMIterator;
26import org.apache.xpath.Expression;
27import org.apache.xpath.ExpressionOwner;
28import org.apache.xpath.XPathContext;
29import org.apache.xpath.XPathVisitor;
30import org.apache.xpath.compiler.Compiler;
31import org.apache.xpath.compiler.OpCodes;
32import org.apache.xpath.objects.XNodeSet;
33
34/**
35 * Walker for the OP_VARIABLE, or OP_EXTFUNCTION, or OP_FUNCTION, or OP_GROUP,
36 * op codes.
37 * @see <a href="http://www.w3.org/TR/xpath#NT-FilterExpr">XPath FilterExpr descriptions</a>
38 */
39public class FilterExprWalker extends AxesWalker
40{
41    static final long serialVersionUID = 5457182471424488375L;
42
43  /**
44   * Construct a FilterExprWalker using a LocPathIterator.
45   *
46   * @param locPathIterator non-null reference to the parent iterator.
47   */
48  public FilterExprWalker(WalkingIterator locPathIterator)
49  {
50    super(locPathIterator, Axis.FILTEREDLIST);
51  }
52
53  /**
54   * Init a FilterExprWalker.
55   *
56   * @param compiler non-null reference to the Compiler that is constructing.
57   * @param opPos positive opcode position for this step.
58   * @param stepType The type of step.
59   *
60   * @throws javax.xml.transform.TransformerException
61   */
62  public void init(Compiler compiler, int opPos, int stepType)
63          throws javax.xml.transform.TransformerException
64  {
65
66    super.init(compiler, opPos, stepType);
67
68    // Smooth over an anomily in the opcode map...
69    switch (stepType)
70    {
71    case OpCodes.OP_FUNCTION :
72    case OpCodes.OP_EXTFUNCTION :
73    	m_mustHardReset = true;
74    case OpCodes.OP_GROUP :
75    case OpCodes.OP_VARIABLE :
76      m_expr = compiler.compile(opPos);
77      m_expr.exprSetParent(this);
78      //if((OpCodes.OP_FUNCTION == stepType) && (m_expr instanceof org.apache.xalan.templates.FuncKey))
79      if(m_expr instanceof org.apache.xpath.operations.Variable)
80      {
81      	// hack/temp workaround
82      	m_canDetachNodeset = false;
83      }
84      break;
85    default :
86      m_expr = compiler.compile(opPos + 2);
87      m_expr.exprSetParent(this);
88    }
89//    if(m_expr instanceof WalkingIterator)
90//    {
91//      WalkingIterator wi = (WalkingIterator)m_expr;
92//      if(wi.getFirstWalker() instanceof FilterExprWalker)
93//      {
94//      	FilterExprWalker fw = (FilterExprWalker)wi.getFirstWalker();
95//      	if(null == fw.getNextWalker())
96//      	{
97//      		m_expr = fw.m_expr;
98//      		m_expr.exprSetParent(this);
99//      	}
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}
350