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: XPath.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xpath;
     22 
     23 import java.io.Serializable;
     24 
     25 import javax.xml.transform.ErrorListener;
     26 import javax.xml.transform.SourceLocator;
     27 import javax.xml.transform.TransformerException;
     28 
     29 import org.apache.xalan.res.XSLMessages;
     30 import org.apache.xml.dtm.DTM;
     31 import org.apache.xml.utils.PrefixResolver;
     32 import org.apache.xml.utils.SAXSourceLocator;
     33 import org.apache.xpath.compiler.Compiler;
     34 import org.apache.xpath.compiler.FunctionTable;
     35 import org.apache.xpath.compiler.XPathParser;
     36 import org.apache.xpath.functions.Function;
     37 import org.apache.xpath.objects.XObject;
     38 import org.apache.xpath.res.XPATHErrorResources;
     39 
     40 /**
     41  * The XPath class wraps an expression object and provides general services
     42  * for execution of that expression.
     43  * @xsl.usage advanced
     44  */
     45 public class XPath implements Serializable, ExpressionOwner
     46 {
     47     static final long serialVersionUID = 3976493477939110553L;
     48 
     49   /** The top of the expression tree.
     50    *  @serial */
     51   private Expression m_mainExp;
     52 
     53   /**
     54    * The function table for xpath build-in functions
     55    */
     56   private transient FunctionTable m_funcTable = null;
     57 
     58   /**
     59    * initial the function table
     60    */
     61   private void initFunctionTable(){
     62   	      m_funcTable = new FunctionTable();
     63   }
     64 
     65   /**
     66    * Get the raw Expression object that this class wraps.
     67    *
     68    *
     69    * @return the raw Expression object, which should not normally be null.
     70    */
     71   public Expression getExpression()
     72   {
     73     return m_mainExp;
     74   }
     75 
     76   /**
     77    * This function is used to fixup variables from QNames to stack frame
     78    * indexes at stylesheet build time.
     79    * @param vars List of QNames that correspond to variables.  This list
     80    * should be searched backwards for the first qualified name that
     81    * corresponds to the variable reference qname.  The position of the
     82    * QName in the vector from the start of the vector will be its position
     83    * in the stack frame (but variables above the globalsTop value will need
     84    * to be offset to the current stack frame).
     85    */
     86   public void fixupVariables(java.util.Vector vars, int globalsSize)
     87   {
     88     m_mainExp.fixupVariables(vars, globalsSize);
     89   }
     90 
     91   /**
     92    * Set the raw expression object for this object.
     93    *
     94    *
     95    * @param exp the raw Expression object, which should not normally be null.
     96    */
     97   public void setExpression(Expression exp)
     98   {
     99   	if(null != m_mainExp)
    100     	exp.exprSetParent(m_mainExp.exprGetParent()); // a bit bogus
    101     m_mainExp = exp;
    102   }
    103 
    104   /**
    105    * Get the SourceLocator on the expression object.
    106    *
    107    *
    108    * @return the SourceLocator on the expression object, which may be null.
    109    */
    110   public SourceLocator getLocator()
    111   {
    112     return m_mainExp;
    113   }
    114 
    115 //  /**
    116 //   * Set the SourceLocator on the expression object.
    117 //   *
    118 //   *
    119 //   * @param l the SourceLocator on the expression object, which may be null.
    120 //   */
    121 //  public void setLocator(SourceLocator l)
    122 //  {
    123 //    // Note potential hazards -- l may not be serializable, or may be changed
    124 //      // after being assigned here.
    125 //    m_mainExp.setSourceLocator(l);
    126 //  }
    127 
    128   /** The pattern string, mainly kept around for diagnostic purposes.
    129    *  @serial  */
    130   String m_patternString;
    131 
    132   /**
    133    * Return the XPath string associated with this object.
    134    *
    135    *
    136    * @return the XPath string associated with this object.
    137    */
    138   public String getPatternString()
    139   {
    140     return m_patternString;
    141   }
    142 
    143   /** Represents a select type expression. */
    144   public static final int SELECT = 0;
    145 
    146   /** Represents a match type expression.  */
    147   public static final int MATCH = 1;
    148 
    149   /**
    150    * Construct an XPath object.
    151    *
    152    * (Needs review -sc) This method initializes an XPathParser/
    153    * Compiler and compiles the expression.
    154    * @param exprString The XPath expression.
    155    * @param locator The location of the expression, may be null.
    156    * @param prefixResolver A prefix resolver to use to resolve prefixes to
    157    *                       namespace URIs.
    158    * @param type one of {@link #SELECT} or {@link #MATCH}.
    159    * @param errorListener The error listener, or null if default should be used.
    160    *
    161    * @throws javax.xml.transform.TransformerException if syntax or other error.
    162    */
    163   public XPath(
    164           String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type,
    165           ErrorListener errorListener)
    166             throws javax.xml.transform.TransformerException
    167   {
    168     initFunctionTable();
    169     if(null == errorListener)
    170       errorListener = new org.apache.xml.utils.DefaultErrorHandler();
    171 
    172     m_patternString = exprString;
    173 
    174     XPathParser parser = new XPathParser(errorListener, locator);
    175     Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
    176 
    177     if (SELECT == type)
    178       parser.initXPath(compiler, exprString, prefixResolver);
    179     else if (MATCH == type)
    180       parser.initMatchPattern(compiler, exprString, prefixResolver);
    181     else
    182       throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, new Object[]{Integer.toString(type)})); //"Can not deal with XPath type: " + type);
    183 
    184     // System.out.println("----------------");
    185     Expression expr = compiler.compile(0);
    186 
    187     // System.out.println("expr: "+expr);
    188     this.setExpression(expr);
    189 
    190     if((null != locator) && locator instanceof ExpressionNode)
    191     {
    192     	expr.exprSetParent((ExpressionNode)locator);
    193     }
    194 
    195   }
    196 
    197   /**
    198    * Construct an XPath object.
    199    *
    200    * (Needs review -sc) This method initializes an XPathParser/
    201    * Compiler and compiles the expression.
    202    * @param exprString The XPath expression.
    203    * @param locator The location of the expression, may be null.
    204    * @param prefixResolver A prefix resolver to use to resolve prefixes to
    205    *                       namespace URIs.
    206    * @param type one of {@link #SELECT} or {@link #MATCH}.
    207    * @param errorListener The error listener, or null if default should be used.
    208    *
    209    * @throws javax.xml.transform.TransformerException if syntax or other error.
    210    */
    211   public XPath(
    212           String exprString, SourceLocator locator,
    213           PrefixResolver prefixResolver, int type,
    214           ErrorListener errorListener, FunctionTable aTable)
    215             throws javax.xml.transform.TransformerException
    216   {
    217     m_funcTable = aTable;
    218     if(null == errorListener)
    219       errorListener = new org.apache.xml.utils.DefaultErrorHandler();
    220 
    221     m_patternString = exprString;
    222 
    223     XPathParser parser = new XPathParser(errorListener, locator);
    224     Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
    225 
    226     if (SELECT == type)
    227       parser.initXPath(compiler, exprString, prefixResolver);
    228     else if (MATCH == type)
    229       parser.initMatchPattern(compiler, exprString, prefixResolver);
    230     else
    231       throw new RuntimeException(XSLMessages.createXPATHMessage(
    232             XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
    233             new Object[]{Integer.toString(type)}));
    234             //"Can not deal with XPath type: " + type);
    235 
    236     // System.out.println("----------------");
    237     Expression expr = compiler.compile(0);
    238 
    239     // System.out.println("expr: "+expr);
    240     this.setExpression(expr);
    241 
    242     if((null != locator) && locator instanceof ExpressionNode)
    243     {
    244     	expr.exprSetParent((ExpressionNode)locator);
    245     }
    246 
    247   }
    248 
    249   /**
    250    * Construct an XPath object.
    251    *
    252    * (Needs review -sc) This method initializes an XPathParser/
    253    * Compiler and compiles the expression.
    254    * @param exprString The XPath expression.
    255    * @param locator The location of the expression, may be null.
    256    * @param prefixResolver A prefix resolver to use to resolve prefixes to
    257    *                       namespace URIs.
    258    * @param type one of {@link #SELECT} or {@link #MATCH}.
    259    *
    260    * @throws javax.xml.transform.TransformerException if syntax or other error.
    261    */
    262   public XPath(
    263           String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
    264             throws javax.xml.transform.TransformerException
    265   {
    266     this(exprString, locator, prefixResolver, type, null);
    267   }
    268 
    269   /**
    270    * Construct an XPath object.
    271    *
    272    * @param expr The Expression object.
    273    *
    274    * @throws javax.xml.transform.TransformerException if syntax or other error.
    275    */
    276   public XPath(Expression expr)
    277   {
    278     this.setExpression(expr);
    279     initFunctionTable();
    280   }
    281 
    282   /**
    283    * Given an expression and a context, evaluate the XPath
    284    * and return the result.
    285    *
    286    * @param xctxt The execution context.
    287    * @param contextNode The node that "." expresses.
    288    * @param namespaceContext The context in which namespaces in the
    289    * XPath are supposed to be expanded.
    290    *
    291    * @return The result of the XPath or null if callbacks are used.
    292    * @throws TransformerException thrown if
    293    * the error condition is severe enough to halt processing.
    294    *
    295    * @throws javax.xml.transform.TransformerException
    296    * @xsl.usage experimental
    297    */
    298   public XObject execute(
    299           XPathContext xctxt, org.w3c.dom.Node contextNode,
    300           PrefixResolver namespaceContext)
    301             throws javax.xml.transform.TransformerException
    302   {
    303     return execute(
    304           xctxt, xctxt.getDTMHandleFromNode(contextNode),
    305           namespaceContext);
    306   }
    307 
    308 
    309   /**
    310    * Given an expression and a context, evaluate the XPath
    311    * and return the result.
    312    *
    313    * @param xctxt The execution context.
    314    * @param contextNode The node that "." expresses.
    315    * @param namespaceContext The context in which namespaces in the
    316    * XPath are supposed to be expanded.
    317    *
    318    * @throws TransformerException thrown if the active ProblemListener decides
    319    * the error condition is severe enough to halt processing.
    320    *
    321    * @throws javax.xml.transform.TransformerException
    322    * @xsl.usage experimental
    323    */
    324   public XObject execute(
    325           XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
    326             throws javax.xml.transform.TransformerException
    327   {
    328 
    329     xctxt.pushNamespaceContext(namespaceContext);
    330 
    331     xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
    332 
    333     XObject xobj = null;
    334 
    335     try
    336     {
    337       xobj = m_mainExp.execute(xctxt);
    338     }
    339     catch (TransformerException te)
    340     {
    341       te.setLocator(this.getLocator());
    342       ErrorListener el = xctxt.getErrorListener();
    343       if(null != el) // defensive, should never happen.
    344       {
    345         el.error(te);
    346       }
    347       else
    348         throw te;
    349     }
    350     catch (Exception e)
    351     {
    352       while (e instanceof org.apache.xml.utils.WrappedRuntimeException)
    353       {
    354         e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
    355       }
    356       // e.printStackTrace();
    357 
    358       String msg = e.getMessage();
    359 
    360       if (msg == null || msg.length() == 0) {
    361            msg = XSLMessages.createXPATHMessage(
    362                XPATHErrorResources.ER_XPATH_ERROR, null);
    363 
    364       }
    365       TransformerException te = new TransformerException(msg,
    366               getLocator(), e);
    367       ErrorListener el = xctxt.getErrorListener();
    368       // te.printStackTrace();
    369       if(null != el) // defensive, should never happen.
    370       {
    371         el.fatalError(te);
    372       }
    373       else
    374         throw te;
    375     }
    376     finally
    377     {
    378       xctxt.popNamespaceContext();
    379 
    380       xctxt.popCurrentNodeAndExpression();
    381     }
    382 
    383     return xobj;
    384   }
    385 
    386   /**
    387    * Given an expression and a context, evaluate the XPath
    388    * and return the result.
    389    *
    390    * @param xctxt The execution context.
    391    * @param contextNode The node that "." expresses.
    392    * @param namespaceContext The context in which namespaces in the
    393    * XPath are supposed to be expanded.
    394    *
    395    * @throws TransformerException thrown if the active ProblemListener decides
    396    * the error condition is severe enough to halt processing.
    397    *
    398    * @throws javax.xml.transform.TransformerException
    399    * @xsl.usage experimental
    400    */
    401   public boolean bool(
    402           XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
    403             throws javax.xml.transform.TransformerException
    404   {
    405 
    406     xctxt.pushNamespaceContext(namespaceContext);
    407 
    408     xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
    409 
    410     try
    411     {
    412       return m_mainExp.bool(xctxt);
    413     }
    414     catch (TransformerException te)
    415     {
    416       te.setLocator(this.getLocator());
    417       ErrorListener el = xctxt.getErrorListener();
    418       if(null != el) // defensive, should never happen.
    419       {
    420         el.error(te);
    421       }
    422       else
    423         throw te;
    424     }
    425     catch (Exception e)
    426     {
    427       while (e instanceof org.apache.xml.utils.WrappedRuntimeException)
    428       {
    429         e = ((org.apache.xml.utils.WrappedRuntimeException) e).getException();
    430       }
    431       // e.printStackTrace();
    432 
    433       String msg = e.getMessage();
    434 
    435       if (msg == null || msg.length() == 0) {
    436            msg = XSLMessages.createXPATHMessage(
    437                XPATHErrorResources.ER_XPATH_ERROR, null);
    438 
    439       }
    440 
    441       TransformerException te = new TransformerException(msg,
    442               getLocator(), e);
    443       ErrorListener el = xctxt.getErrorListener();
    444       // te.printStackTrace();
    445       if(null != el) // defensive, should never happen.
    446       {
    447         el.fatalError(te);
    448       }
    449       else
    450         throw te;
    451     }
    452     finally
    453     {
    454       xctxt.popNamespaceContext();
    455 
    456       xctxt.popCurrentNodeAndExpression();
    457     }
    458 
    459     return false;
    460   }
    461 
    462   /** Set to true to get diagnostic messages about the result of
    463    *  match pattern testing.  */
    464   private static final boolean DEBUG_MATCHES = false;
    465 
    466   /**
    467    * Get the match score of the given node.
    468    *
    469    * @param xctxt XPath runtime context.
    470    * @param context The current source tree context node.
    471    *
    472    * @return score, one of {@link #MATCH_SCORE_NODETEST},
    473    * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER},
    474    * or {@link #MATCH_SCORE_QNAME}.
    475    *
    476    * @throws javax.xml.transform.TransformerException
    477    */
    478   public double getMatchScore(XPathContext xctxt, int context)
    479           throws javax.xml.transform.TransformerException
    480   {
    481 
    482     xctxt.pushCurrentNode(context);
    483     xctxt.pushCurrentExpressionNode(context);
    484 
    485     try
    486     {
    487       XObject score = m_mainExp.execute(xctxt);
    488 
    489       if (DEBUG_MATCHES)
    490       {
    491         DTM dtm = xctxt.getDTM(context);
    492         System.out.println("score: " + score.num() + " for "
    493                            + dtm.getNodeName(context) + " for xpath "
    494                            + this.getPatternString());
    495       }
    496 
    497       return score.num();
    498     }
    499     finally
    500     {
    501       xctxt.popCurrentNode();
    502       xctxt.popCurrentExpressionNode();
    503     }
    504 
    505     // return XPath.MATCH_SCORE_NONE;
    506   }
    507 
    508 
    509   /**
    510    * Warn the user of an problem.
    511    *
    512    * @param xctxt The XPath runtime context.
    513    * @param sourceNode Not used.
    514    * @param msg An error msgkey that corresponds to one of the constants found
    515    *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
    516    *            a key for a format string.
    517    * @param args An array of arguments represented in the format string, which
    518    *             may be null.
    519    *
    520    * @throws TransformerException if the current ErrorListoner determines to
    521    *                              throw an exception.
    522    */
    523   public void warn(
    524           XPathContext xctxt, int sourceNode, String msg, Object[] args)
    525             throws javax.xml.transform.TransformerException
    526   {
    527 
    528     String fmsg = XSLMessages.createXPATHWarning(msg, args);
    529     ErrorListener ehandler = xctxt.getErrorListener();
    530 
    531     if (null != ehandler)
    532     {
    533 
    534       // TO DO: Need to get stylesheet Locator from here.
    535       ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator()));
    536     }
    537   }
    538 
    539   /**
    540    * Tell the user of an assertion error, and probably throw an
    541    * exception.
    542    *
    543    * @param b  If false, a runtime exception will be thrown.
    544    * @param msg The assertion message, which should be informative.
    545    *
    546    * @throws RuntimeException if the b argument is false.
    547    */
    548   public void assertion(boolean b, String msg)
    549   {
    550 
    551     if (!b)
    552     {
    553       String fMsg = XSLMessages.createXPATHMessage(
    554         XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
    555         new Object[]{ msg });
    556 
    557       throw new RuntimeException(fMsg);
    558     }
    559   }
    560 
    561   /**
    562    * Tell the user of an error, and probably throw an
    563    * exception.
    564    *
    565    * @param xctxt The XPath runtime context.
    566    * @param sourceNode Not used.
    567    * @param msg An error msgkey that corresponds to one of the constants found
    568    *            in {@link org.apache.xpath.res.XPATHErrorResources}, which is
    569    *            a key for a format string.
    570    * @param args An array of arguments represented in the format string, which
    571    *             may be null.
    572    *
    573    * @throws TransformerException if the current ErrorListoner determines to
    574    *                              throw an exception.
    575    */
    576   public void error(
    577           XPathContext xctxt, int sourceNode, String msg, Object[] args)
    578             throws javax.xml.transform.TransformerException
    579   {
    580 
    581     String fmsg = XSLMessages.createXPATHMessage(msg, args);
    582     ErrorListener ehandler = xctxt.getErrorListener();
    583 
    584     if (null != ehandler)
    585     {
    586       ehandler.fatalError(new TransformerException(fmsg,
    587                               (SAXSourceLocator)xctxt.getSAXLocator()));
    588     }
    589     else
    590     {
    591       SourceLocator slocator = xctxt.getSAXLocator();
    592       System.out.println(fmsg + "; file " + slocator.getSystemId()
    593                          + "; line " + slocator.getLineNumber() + "; column "
    594                          + slocator.getColumnNumber());
    595     }
    596   }
    597 
    598   /**
    599    * This will traverse the heararchy, calling the visitor for
    600    * each member.  If the called visitor method returns
    601    * false, the subtree should not be called.
    602    *
    603    * @param owner The owner of the visitor, where that path may be
    604    *              rewritten if needed.
    605    * @param visitor The visitor whose appropriate method will be called.
    606    */
    607   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
    608   {
    609   	m_mainExp.callVisitors(this, visitor);
    610   }
    611 
    612   /**
    613    * The match score if no match is made.
    614    * @xsl.usage advanced
    615    */
    616   public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
    617 
    618   /**
    619    * The match score if the pattern has the form
    620    * of a QName optionally preceded by an @ character.
    621    * @xsl.usage advanced
    622    */
    623   public static final double MATCH_SCORE_QNAME = 0.0;
    624 
    625   /**
    626    * The match score if the pattern pattern has the form NCName:*.
    627    * @xsl.usage advanced
    628    */
    629   public static final double MATCH_SCORE_NSWILD = -0.25;
    630 
    631   /**
    632    * The match score if the pattern consists of just a NodeTest.
    633    * @xsl.usage advanced
    634    */
    635   public static final double MATCH_SCORE_NODETEST = -0.5;
    636 
    637   /**
    638    * The match score if the pattern consists of something
    639    * other than just a NodeTest or just a qname.
    640    * @xsl.usage advanced
    641    */
    642   public static final double MATCH_SCORE_OTHER = 0.5;
    643 }
    644