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  */
21 package org.apache.xpath;
22 
23 import javax.xml.transform.ErrorListener;
24 import javax.xml.transform.TransformerException;
25 
26 import org.apache.xalan.res.XSLMessages;
27 import org.apache.xml.dtm.DTM;
28 import org.apache.xml.dtm.DTMIterator;
29 import org.apache.xml.utils.XMLString;
30 import org.apache.xpath.objects.XNodeSet;
31 import org.apache.xpath.objects.XObject;
32 import org.apache.xpath.res.XPATHErrorResources;
33 
34 import 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  */
45 public 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    */
canTraverseOutsideSubtree()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    */
execute(XPathContext xctxt, int currentNode)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    */
execute( XPathContext xctxt, int currentNode, DTM dtm, int expType)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    */
execute(XPathContext xctxt)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    */
execute(XPathContext xctxt, boolean destructiveOK)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    */
num(XPathContext xctxt)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    */
bool(XPathContext xctxt)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    */
xstr(XPathContext xctxt)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    */
isNodesetExpr()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    */
asNode(XPathContext xctxt)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    */
asIterator(XPathContext xctxt, int contextNode)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    */
asIteratorRaw(XPathContext xctxt, int contextNode)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    */
executeCharsToContentHandler( XPathContext xctxt, ContentHandler handler)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    */
isStableNumber()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    */
fixupVariables(java.util.Vector vars, int globalsSize)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    */
deepEquals(Expression expr)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    */
isSameClass(Expression expr)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    */
warn(XPathContext xctxt, String msg, Object[] args)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    */
assertion(boolean b, java.lang.String msg)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    */
error(XPathContext xctxt, String msg, Object[] args)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    */
getExpressionOwner()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. */
exprSetParent(ExpressionNode n)474   public void exprSetParent(ExpressionNode n)
475   {
476   	assertion(n != this, "Can not parent an expression to itself!");
477   	m_parent = n;
478   }
479 
exprGetParent()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.  */
exprAddChild(ExpressionNode n, int i)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. */
exprGetChild(int i)494   public ExpressionNode exprGetChild(int i)
495   {
496   	return null;
497   }
498 
499   /** Return the number of children the node has. */
exprGetNumChildren()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    */
getPublicId()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    */
getSystemId()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    */
getLineNumber()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    */
getColumnNumber()583   public int getColumnNumber()
584   {
585   	if(null == m_parent)
586   	  return 0;
587   	return m_parent.getColumnNumber();
588   }
589 }
590