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: Expression.java 468655 2006-10-28 07:12:06Z minchau $
020     */
021    package org.apache.xpath;
022    
023    import javax.xml.transform.ErrorListener;
024    import javax.xml.transform.TransformerException;
025    
026    import org.apache.xalan.res.XSLMessages;
027    import org.apache.xml.dtm.DTM;
028    import org.apache.xml.dtm.DTMIterator;
029    import org.apache.xml.utils.XMLString;
030    import org.apache.xpath.objects.XNodeSet;
031    import org.apache.xpath.objects.XObject;
032    import org.apache.xpath.res.XPATHErrorResources;
033    
034    import org.xml.sax.ContentHandler;
035    
036    /**
037     * This abstract class serves as the base for all expression objects.  An
038     * Expression can be executed to return a {@link org.apache.xpath.objects.XObject},
039     * normally has a location within a document or DOM, can send error and warning
040     * events, and normally do not hold state and are meant to be immutable once
041     * construction has completed.  An exception to the immutibility rule is iterators
042     * and walkers, which must be cloned in order to be used -- the original must
043     * still be immutable.
044     */
045    public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable
046    {
047        static final long serialVersionUID = 565665869777906902L;
048      /**
049       * The location where this expression was built from.  Need for diagnostic
050       *  messages. May be null.
051       *  @serial
052       */
053      private ExpressionNode m_parent;
054    
055      /**
056       * Tell if this expression or it's subexpressions can traverse outside
057       * the current subtree.
058       *
059       * @return true if traversal outside the context node's subtree can occur.
060       */
061      public boolean canTraverseOutsideSubtree()
062      {
063        return false;
064      }
065      
066    //  /**
067    //   * Set the location where this expression was built from.
068    //   *
069    //   *
070    //   * @param locator the location where this expression was built from, may be
071    //   *                null.
072    //   */
073    //  public void setSourceLocator(SourceLocator locator)
074    //  {
075    //    m_slocator = locator;
076    //  }
077    
078      /**
079       * Execute an expression in the XPath runtime context, and return the
080       * result of the expression.
081       *
082       *
083       * @param xctxt The XPath runtime context.
084       * @param currentNode The currentNode.
085       *
086       * @return The result of the expression in the form of a <code>XObject</code>.
087       *
088       * @throws javax.xml.transform.TransformerException if a runtime exception
089       *         occurs.
090       */
091      public XObject execute(XPathContext xctxt, int currentNode)
092              throws javax.xml.transform.TransformerException
093      {
094    
095        // For now, the current node is already pushed.
096        return execute(xctxt);
097      }
098    
099      /**
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    }