Home | History | Annotate | Download | only in xpath
      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    */
     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