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: Expression.java 468655 2006-10-28 07:12:06Z minchau $
20 */
21package org.apache.xpath;
22
23import javax.xml.transform.ErrorListener;
24import javax.xml.transform.TransformerException;
25
26import org.apache.xalan.res.XSLMessages;
27import org.apache.xml.dtm.DTM;
28import org.apache.xml.dtm.DTMIterator;
29import org.apache.xml.utils.XMLString;
30import org.apache.xpath.objects.XNodeSet;
31import org.apache.xpath.objects.XObject;
32import org.apache.xpath.res.XPATHErrorResources;
33
34import org.xml.sax.ContentHandler;
35
36/**
37 * This abstract class serves as the base for all expression objects.  An
38 * Expression can be executed to return a {@link org.apache.xpath.objects.XObject},
39 * normally has a location within a document or DOM, can send error and warning
40 * events, and normally do not hold state and are meant to be immutable once
41 * construction has completed.  An exception to the immutibility rule is iterators
42 * and walkers, which must be cloned in order to be used -- the original must
43 * still be immutable.
44 */
45public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable
46{
47    static final long serialVersionUID = 565665869777906902L;
48  /**
49   * The location where this expression was built from.  Need for diagnostic
50   *  messages. May be null.
51   *  @serial
52   */
53  private ExpressionNode m_parent;
54
55  /**
56   * Tell if this expression or it's subexpressions can traverse outside
57   * the current subtree.
58   *
59   * @return true if traversal outside the context node's subtree can occur.
60   */
61  public boolean canTraverseOutsideSubtree()
62  {
63    return false;
64  }
65
66//  /**
67//   * Set the location where this expression was built from.
68//   *
69//   *
70//   * @param locator the location where this expression was built from, may be
71//   *                null.
72//   */
73//  public void setSourceLocator(SourceLocator locator)
74//  {
75//    m_slocator = locator;
76//  }
77
78  /**
79   * Execute an expression in the XPath runtime context, and return the
80   * result of the expression.
81   *
82   *
83   * @param xctxt The XPath runtime context.
84   * @param currentNode The currentNode.
85   *
86   * @return The result of the expression in the form of a <code>XObject</code>.
87   *
88   * @throws javax.xml.transform.TransformerException if a runtime exception
89   *         occurs.
90   */
91  public XObject execute(XPathContext xctxt, int currentNode)
92          throws javax.xml.transform.TransformerException
93  {
94
95    // For now, the current node is already pushed.
96    return execute(xctxt);
97  }
98
99  /**
100   * Execute an expression in the XPath runtime context, and return the
101   * result of the expression.
102   *
103   *
104   * @param xctxt The XPath runtime context.
105   * @param currentNode The currentNode.
106   * @param dtm The DTM of the current node.
107   * @param expType The expanded type ID of the current node.
108   *
109   * @return The result of the expression in the form of a <code>XObject</code>.
110   *
111   * @throws javax.xml.transform.TransformerException if a runtime exception
112   *         occurs.
113   */
114  public XObject execute(
115          XPathContext xctxt, int currentNode, DTM dtm, int expType)
116            throws javax.xml.transform.TransformerException
117  {
118
119    // For now, the current node is already pushed.
120    return execute(xctxt);
121  }
122
123  /**
124   * Execute an expression in the XPath runtime context, and return the
125   * result of the expression.
126   *
127   *
128   * @param xctxt The XPath runtime context.
129   *
130   * @return The result of the expression in the form of a <code>XObject</code>.
131   *
132   * @throws javax.xml.transform.TransformerException if a runtime exception
133   *         occurs.
134   */
135  public abstract XObject execute(XPathContext xctxt)
136    throws javax.xml.transform.TransformerException;
137
138  /**
139   * Execute an expression in the XPath runtime context, and return the
140   * result of the expression, but tell that a "safe" object doesn't have
141   * to be returned.  The default implementation just calls execute(xctxt).
142   *
143   *
144   * @param xctxt The XPath runtime context.
145   * @param destructiveOK true if a "safe" object doesn't need to be returned.
146   *
147   * @return The result of the expression in the form of a <code>XObject</code>.
148   *
149   * @throws javax.xml.transform.TransformerException if a runtime exception
150   *         occurs.
151   */
152  public XObject execute(XPathContext xctxt, boolean destructiveOK)
153    throws javax.xml.transform.TransformerException
154  {
155  	return execute(xctxt);
156  }
157
158
159  /**
160   * Evaluate expression to a number.
161   *
162   *
163   * @param xctxt The XPath runtime context.
164   * @return The expression evaluated as a double.
165   *
166   * @throws javax.xml.transform.TransformerException
167   */
168  public double num(XPathContext xctxt)
169          throws javax.xml.transform.TransformerException
170  {
171    return execute(xctxt).num();
172  }
173
174  /**
175   * Evaluate expression to a boolean.
176   *
177   *
178   * @param xctxt The XPath runtime context.
179   * @return false
180   *
181   * @throws javax.xml.transform.TransformerException
182   */
183  public boolean bool(XPathContext xctxt)
184          throws javax.xml.transform.TransformerException
185  {
186    return execute(xctxt).bool();
187  }
188
189  /**
190   * Cast result object to a string.
191   *
192   *
193   * @param xctxt The XPath runtime context.
194   * @return The string this wraps or the empty string if null
195   *
196   * @throws javax.xml.transform.TransformerException
197   */
198  public XMLString xstr(XPathContext xctxt)
199          throws javax.xml.transform.TransformerException
200  {
201    return execute(xctxt).xstr();
202  }
203
204  /**
205   * Tell if the expression is a nodeset expression.  In other words, tell
206   * if you can execute {@link #asNode(XPathContext) asNode} without an exception.
207   * @return true if the expression can be represented as a nodeset.
208   */
209  public boolean isNodesetExpr()
210  {
211    return false;
212  }
213
214  /**
215   * Return the first node out of the nodeset, if this expression is
216   * a nodeset expression.
217   * @param xctxt The XPath runtime context.
218   * @return the first node out of the nodeset, or DTM.NULL.
219   *
220   * @throws javax.xml.transform.TransformerException
221   */
222  public int asNode(XPathContext xctxt)
223          throws javax.xml.transform.TransformerException
224  {
225  	DTMIterator iter = execute(xctxt).iter();
226    return iter.nextNode();
227  }
228
229  /**
230   * Given an select expression and a context, evaluate the XPath
231   * and return the resulting iterator.
232   *
233   * @param xctxt The execution context.
234   * @param contextNode The node that "." expresses.
235   *
236   *
237   * @return A valid DTMIterator.
238   * @throws TransformerException thrown if the active ProblemListener decides
239   * the error condition is severe enough to halt processing.
240   *
241   * @throws javax.xml.transform.TransformerException
242   * @xsl.usage experimental
243   */
244  public DTMIterator asIterator(XPathContext xctxt, int contextNode)
245          throws javax.xml.transform.TransformerException
246  {
247
248    try
249    {
250      xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
251
252      return execute(xctxt).iter();
253    }
254    finally
255    {
256      xctxt.popCurrentNodeAndExpression();
257    }
258  }
259
260  /**
261   * Given an select expression and a context, evaluate the XPath
262   * and return the resulting iterator, but do not clone.
263   *
264   * @param xctxt The execution context.
265   * @param contextNode The node that "." expresses.
266   *
267   *
268   * @return A valid DTMIterator.
269   * @throws TransformerException thrown if the active ProblemListener decides
270   * the error condition is severe enough to halt processing.
271   *
272   * @throws javax.xml.transform.TransformerException
273   * @xsl.usage experimental
274   */
275  public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode)
276          throws javax.xml.transform.TransformerException
277  {
278
279    try
280    {
281      xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
282
283      XNodeSet nodeset = (XNodeSet)execute(xctxt);
284      return nodeset.iterRaw();
285    }
286    finally
287    {
288      xctxt.popCurrentNodeAndExpression();
289    }
290  }
291
292
293  /**
294   * Execute an expression in the XPath runtime context, and return the
295   * result of the expression.
296   *
297   *
298   * @param xctxt The XPath runtime context.
299   * NEEDSDOC @param handler
300   *
301   * @return The result of the expression in the form of a <code>XObject</code>.
302   *
303   * @throws javax.xml.transform.TransformerException if a runtime exception
304   *         occurs.
305   * @throws org.xml.sax.SAXException
306   */
307  public void executeCharsToContentHandler(
308          XPathContext xctxt, ContentHandler handler)
309            throws javax.xml.transform.TransformerException,
310                   org.xml.sax.SAXException
311  {
312
313    XObject obj = execute(xctxt);
314
315    obj.dispatchCharactersEvents(handler);
316    obj.detach();
317  }
318
319  /**
320   * Tell if this expression returns a stable number that will not change during
321   * iterations within the expression.  This is used to determine if a proximity
322   * position predicate can indicate that no more searching has to occur.
323   *
324   *
325   * @return true if the expression represents a stable number.
326   */
327  public boolean isStableNumber()
328  {
329    return false;
330  }
331
332  /**
333   * This function is used to fixup variables from QNames to stack frame
334   * indexes at stylesheet build time.
335   * @param vars List of QNames that correspond to variables.  This list
336   * should be searched backwards for the first qualified name that
337   * corresponds to the variable reference qname.  The position of the
338   * QName in the vector from the start of the vector will be its position
339   * in the stack frame (but variables above the globalsTop value will need
340   * to be offset to the current stack frame).
341   * NEEDSDOC @param globalsSize
342   */
343  public abstract void fixupVariables(java.util.Vector vars, int globalsSize);
344
345  /**
346   * Compare this object with another object and see
347   * if they are equal, include the sub heararchy.
348   *
349   * @param expr Another expression object.
350   * @return true if this objects class and the expr
351   * object's class are the same, and the data contained
352   * within both objects are considered equal.
353   */
354  public abstract boolean deepEquals(Expression expr);
355
356  /**
357   * This is a utility method to tell if the passed in
358   * class is the same class as this.  It is to be used by
359   * the deepEquals method.  I'm bottlenecking it here
360   * because I'm not totally confident that comparing the
361   * class objects is the best way to do this.
362   * @return true of the passed in class is the exact same
363   * class as this class.
364   */
365  protected final boolean isSameClass(Expression expr)
366  {
367  	if(null == expr)
368  	  return false;
369
370  	return (getClass() == expr.getClass());
371  }
372
373  /**
374   * Warn the user of an problem.
375   *
376   * @param xctxt The XPath runtime context.
377   * @param msg An error msgkey that corresponds to one of the conststants found
378   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
379   *            a key for a format string.
380   * @param args An array of arguments represented in the format string, which
381   *             may be null.
382   *
383   * @throws TransformerException if the current ErrorListoner determines to
384   *                              throw an exception.
385   *
386   * @throws javax.xml.transform.TransformerException
387   */
388  public void warn(XPathContext xctxt, String msg, Object[] args)
389          throws javax.xml.transform.TransformerException
390  {
391
392    java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
393
394    if (null != xctxt)
395    {
396      ErrorListener eh = xctxt.getErrorListener();
397
398      // TO DO: Need to get stylesheet Locator from here.
399      eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator()));
400    }
401  }
402
403  /**
404   * Tell the user of an assertion error, and probably throw an
405   * exception.
406   *
407   * @param b  If false, a runtime exception will be thrown.
408   * @param msg The assertion message, which should be informative.
409   *
410   * @throws RuntimeException if the b argument is false.
411   *
412   * @throws javax.xml.transform.TransformerException
413   */
414  public void assertion(boolean b, java.lang.String msg)
415  {
416
417    if (!b)
418    {
419      java.lang.String fMsg = XSLMessages.createXPATHMessage(
420        XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
421        new Object[]{ msg });
422
423      throw new RuntimeException(fMsg);
424    }
425  }
426
427  /**
428   * Tell the user of an error, and probably throw an
429   * exception.
430   *
431   * @param xctxt The XPath runtime context.
432   * @param msg An error msgkey that corresponds to one of the constants found
433   *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
434   *            a key for a format string.
435   * @param args An array of arguments represented in the format string, which
436   *             may be null.
437   *
438   * @throws TransformerException if the current ErrorListoner determines to
439   *                              throw an exception.
440   *
441   * @throws javax.xml.transform.TransformerException
442   */
443  public void error(XPathContext xctxt, String msg, Object[] args)
444          throws javax.xml.transform.TransformerException
445  {
446
447    java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
448
449    if (null != xctxt)
450    {
451      ErrorListener eh = xctxt.getErrorListener();
452      TransformerException te = new TransformerException(fmsg, this);
453
454      eh.fatalError(te);
455    }
456  }
457
458  /**
459   * Get the first non-Expression parent of this node.
460   * @return null or first ancestor that is not an Expression.
461   */
462  public ExpressionNode getExpressionOwner()
463  {
464  	ExpressionNode parent = exprGetParent();
465  	while((null != parent) && (parent instanceof Expression))
466  		parent = parent.exprGetParent();
467  	return parent;
468  }
469
470  //=============== ExpressionNode methods ================
471
472  /** This pair of methods are used to inform the node of its
473    parent. */
474  public void exprSetParent(ExpressionNode n)
475  {
476  	assertion(n != this, "Can not parent an expression to itself!");
477  	m_parent = n;
478  }
479
480  public ExpressionNode exprGetParent()
481  {
482  	return m_parent;
483  }
484
485  /** This method tells the node to add its argument to the node's
486    list of children.  */
487  public void exprAddChild(ExpressionNode n, int i)
488  {
489  	assertion(false, "exprAddChild method not implemented!");
490  }
491
492  /** This method returns a child node.  The children are numbered
493     from zero, left to right. */
494  public ExpressionNode exprGetChild(int i)
495  {
496  	return null;
497  }
498
499  /** Return the number of children the node has. */
500  public int exprGetNumChildren()
501  {
502  	return 0;
503  }
504
505  //=============== SourceLocator methods ================
506
507  /**
508   * Return the public identifier for the current document event.
509   *
510   * <p>The return value is the public identifier of the document
511   * entity or of the external parsed entity in which the markup that
512   * triggered the event appears.</p>
513   *
514   * @return A string containing the public identifier, or
515   *         null if none is available.
516   * @see #getSystemId
517   */
518  public String getPublicId()
519  {
520  	if(null == m_parent)
521  	  return null;
522  	return m_parent.getPublicId();
523  }
524
525  /**
526   * Return the system identifier for the current document event.
527   *
528   * <p>The return value is the system identifier of the document
529   * entity or of the external parsed entity in which the markup that
530   * triggered the event appears.</p>
531   *
532   * <p>If the system identifier is a URL, the parser must resolve it
533   * fully before passing it to the application.</p>
534   *
535   * @return A string containing the system identifier, or null
536   *         if none is available.
537   * @see #getPublicId
538   */
539  public String getSystemId()
540  {
541  	if(null == m_parent)
542  	  return null;
543  	return m_parent.getSystemId();
544  }
545
546  /**
547   * Return the line number where the current document event ends.
548   *
549   * <p><strong>Warning:</strong> The return value from the method
550   * is intended only as an approximation for the sake of error
551   * reporting; it is not intended to provide sufficient information
552   * to edit the character content of the original XML document.</p>
553   *
554   * <p>The return value is an approximation of the line number
555   * in the document entity or external parsed entity where the
556   * markup that triggered the event appears.</p>
557   *
558   * @return The line number, or -1 if none is available.
559   * @see #getColumnNumber
560   */
561  public int getLineNumber()
562  {
563  	if(null == m_parent)
564  	  return 0;
565  	return m_parent.getLineNumber();
566  }
567
568  /**
569   * Return the character position where the current document event ends.
570   *
571   * <p><strong>Warning:</strong> The return value from the method
572   * is intended only as an approximation for the sake of error
573   * reporting; it is not intended to provide sufficient information
574   * to edit the character content of the original XML document.</p>
575   *
576   * <p>The return value is an approximation of the column number
577   * in the document entity or external parsed entity where the
578   * markup that triggered the event appears.</p>
579   *
580   * @return The column number, or -1 if none is available.
581   * @see #getLineNumber
582   */
583  public int getColumnNumber()
584  {
585  	if(null == m_parent)
586  	  return 0;
587  	return m_parent.getColumnNumber();
588  }
589}
590