Home | History | Annotate | Download | only in jaxp
      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 // $Id: XPathExpressionImpl.java 524813 2007-04-02 15:52:07Z zongaro $
     19 
     20 package org.apache.xpath.jaxp;
     21 
     22 import org.apache.xpath.*;
     23 import javax.xml.transform.TransformerException;
     24 
     25 import org.apache.xpath.objects.XObject;
     26 import org.apache.xml.utils.PrefixResolver;
     27 import org.apache.xpath.res.XPATHErrorResources;
     28 import org.apache.xalan.res.XSLMessages;
     29 
     30 import javax.xml.namespace.NamespaceContext;
     31 import javax.xml.namespace.QName;
     32 import javax.xml.xpath.XPathExpressionException;
     33 import javax.xml.xpath.XPathConstants;
     34 import javax.xml.xpath.XPathFunctionResolver;
     35 import javax.xml.xpath.XPathVariableResolver;
     36 import javax.xml.xpath.XPathConstants;
     37 
     38 import org.w3c.dom.Node;
     39 import org.w3c.dom.Document;
     40 import org.w3c.dom.DOMImplementation;
     41 import org.w3c.dom.traversal.NodeIterator;
     42 import javax.xml.parsers.DocumentBuilderFactory;
     43 import javax.xml.parsers.DocumentBuilder;
     44 
     45 import org.xml.sax.InputSource;
     46 
     47 /**
     48  * The XPathExpression interface encapsulates a (compiled) XPath expression.
     49  *
     50  * @version $Revision: 524813 $
     51  * @author  Ramesh Mandava
     52  */
     53 public class XPathExpressionImpl  implements javax.xml.xpath.XPathExpression{
     54 
     55     private XPathFunctionResolver functionResolver;
     56     private XPathVariableResolver variableResolver;
     57     private JAXPPrefixResolver prefixResolver;
     58     private org.apache.xpath.XPath xpath;
     59 
     60     // By default Extension Functions are allowed in XPath Expressions. If
     61     // Secure Processing Feature is set on XPathFactory then the invocation of
     62     // extensions function need to throw XPathFunctionException
     63     private boolean featureSecureProcessing = false;
     64 
     65     /** Protected constructor to prevent direct instantiation; use compile()
     66      * from the context.
     67      */
     68     protected XPathExpressionImpl() { };
     69 
     70     protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
     71             JAXPPrefixResolver prefixResolver,
     72             XPathFunctionResolver functionResolver,
     73             XPathVariableResolver variableResolver ) {
     74         this.xpath = xpath;
     75         this.prefixResolver = prefixResolver;
     76         this.functionResolver = functionResolver;
     77         this.variableResolver = variableResolver;
     78         this.featureSecureProcessing = false;
     79     };
     80 
     81     protected XPathExpressionImpl(org.apache.xpath.XPath xpath,
     82             JAXPPrefixResolver prefixResolver,
     83             XPathFunctionResolver functionResolver,
     84             XPathVariableResolver variableResolver,
     85             boolean featureSecureProcessing ) {
     86         this.xpath = xpath;
     87         this.prefixResolver = prefixResolver;
     88         this.functionResolver = functionResolver;
     89         this.variableResolver = variableResolver;
     90         this.featureSecureProcessing = featureSecureProcessing;
     91     };
     92 
     93     public void setXPath (org.apache.xpath.XPath xpath ) {
     94         this.xpath = xpath;
     95     }
     96 
     97     public Object eval(Object item, QName returnType)
     98             throws javax.xml.transform.TransformerException {
     99         XObject resultObject = eval ( item );
    100         return getResultAsType( resultObject, returnType );
    101     }
    102 
    103     private XObject eval ( Object contextItem )
    104             throws javax.xml.transform.TransformerException {
    105         org.apache.xpath.XPathContext xpathSupport = null;
    106 
    107         // Create an XPathContext that doesn't support pushing and popping of
    108         // variable resolution scopes.  Sufficient for simple XPath 1.0
    109         // expressions.
    110         if ( functionResolver != null ) {
    111             JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
    112                     functionResolver, featureSecureProcessing );
    113             xpathSupport = new org.apache.xpath.XPathContext(jep, false);
    114         } else {
    115             xpathSupport = new org.apache.xpath.XPathContext(false);
    116         }
    117 
    118         xpathSupport.setVarStack(new JAXPVariableStack(variableResolver));
    119         XObject xobj = null;
    120 
    121         Node contextNode = (Node)contextItem;
    122         // We always need to have a ContextNode with Xalan XPath implementation
    123         // To allow simple expression evaluation like 1+1 we are setting
    124         // dummy Document as Context Node
    125         if ( contextNode == null ) {
    126               contextNode = getDummyDocument();
    127         }
    128 
    129         xobj = xpath.execute(xpathSupport, contextNode, prefixResolver );
    130         return xobj;
    131     }
    132 
    133 
    134     /**
    135      * <p>Evaluate the compiled XPath expression in the specified context and
    136      *  return the result as the specified type.</p>
    137      *
    138      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
    139      * for context item evaluation,
    140      * variable, function and QName resolution and return type conversion.</p>
    141      *
    142      * <p>If <code>returnType</code> is not one of the types defined
    143      * in {@link XPathConstants},
    144      * then an <code>IllegalArgumentException</code> is thrown.</p>
    145      *
    146      * <p>If a <code>null</code> value is provided for
    147      * <code>item</code>, an empty document will be used for the
    148      * context.
    149      * If <code>returnType</code> is <code>null</code>, then a
    150      * <code>NullPointerException</code> is thrown.</p>
    151      *
    152      * @param item The starting context (node or node list, for example).
    153      * @param returnType The desired return type.
    154      *
    155      * @return The <code>Object</code> that is the result of evaluating the
    156      * expression and converting the result to
    157      *   <code>returnType</code>.
    158      *
    159      * @throws XPathExpressionException If the expression cannot be evaluated.
    160      * @throws IllegalArgumentException If <code>returnType</code> is not one
    161      * of the types defined in {@link XPathConstants}.
    162      * @throws NullPointerException If  <code>returnType</code> is
    163      * <code>null</code>.
    164      */
    165     public Object evaluate(Object item, QName returnType)
    166         throws XPathExpressionException {
    167         //Validating parameters to enforce constraints defined by JAXP spec
    168         if ( returnType == null ) {
    169            //Throwing NullPointerException as defined in spec
    170             String fmsg = XSLMessages.createXPATHMessage(
    171                     XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
    172                     new Object[] {"returnType"} );
    173             throw new NullPointerException( fmsg );
    174         }
    175         // Checking if requested returnType is supported. returnType need to be
    176         // defined in XPathConstants
    177         if ( !isSupported ( returnType ) ) {
    178             String fmsg = XSLMessages.createXPATHMessage(
    179                     XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
    180                     new Object[] { returnType.toString() } );
    181             throw new IllegalArgumentException ( fmsg );
    182         }
    183         try {
    184             return eval( item, returnType);
    185         } catch ( java.lang.NullPointerException npe ) {
    186             // If VariableResolver returns null Or if we get
    187             // NullPointerException at this stage for some other reason
    188             // then we have to reurn XPathException
    189             throw new XPathExpressionException ( npe );
    190         } catch ( javax.xml.transform.TransformerException te ) {
    191             Throwable nestedException = te.getException();
    192             if ( nestedException instanceof javax.xml.xpath.XPathFunctionException ) {
    193                 throw (javax.xml.xpath.XPathFunctionException)nestedException;
    194             } else {
    195                 // For any other exceptions we need to throw
    196                 // XPathExpressionException ( as per spec )
    197                 throw new XPathExpressionException( te);
    198             }
    199         }
    200 
    201     }
    202 
    203     /**
    204      * <p>Evaluate the compiled XPath expression in the specified context and
    205      * return the result as a <code>String</code>.</p>
    206      *
    207      * <p>This method calls {@link #evaluate(Object item, QName returnType)}
    208      * with a <code>returnType</code> of
    209      * {@link XPathConstants#STRING}.</p>
    210      *
    211      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
    212      *  for context item evaluation,
    213      * variable, function and QName resolution and return type conversion.</p>
    214      *
    215      * <p>If a <code>null</code> value is provided for
    216      * <code>item</code>, an empty document will be used for the
    217      * context.
    218      *
    219      * @param item The starting context (node or node list, for example).
    220      *
    221      * @return The <code>String</code> that is the result of evaluating the
    222      * expression and converting the result to a
    223      *   <code>String</code>.
    224      *
    225      * @throws XPathExpressionException If the expression cannot be evaluated.
    226      */
    227     public String evaluate(Object item)
    228         throws XPathExpressionException {
    229         return (String)this.evaluate( item, XPathConstants.STRING );
    230     }
    231 
    232 
    233 
    234     static DocumentBuilderFactory dbf = null;
    235     static DocumentBuilder db = null;
    236     static Document d = null;
    237 
    238     /**
    239      * <p>Evaluate the compiled XPath expression in the context of the
    240      * specified <code>InputSource</code> and return the result as the
    241      *  specified type.</p>
    242      *
    243      * <p>This method builds a data model for the {@link InputSource} and calls
    244      * {@link #evaluate(Object item, QName returnType)} on the resulting
    245      * document object.</p>
    246      *
    247      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
    248      *  for context item evaluation,
    249      * variable, function and QName resolution and return type conversion.</p>
    250      *
    251      * <p>If <code>returnType</code> is not one of the types defined in
    252      * {@link XPathConstants},
    253      * then an <code>IllegalArgumentException</code> is thrown.</p>
    254      *
    255      *<p>If <code>source</code> or <code>returnType</code> is <code>null</code>,
    256      * then a <code>NullPointerException</code> is thrown.</p>
    257      *
    258      * @param source The <code>InputSource</code> of the document to evaluate
    259      * over.
    260      * @param returnType The desired return type.
    261      *
    262      * @return The <code>Object</code> that is the result of evaluating the
    263      * expression and converting the result to
    264      *   <code>returnType</code>.
    265      *
    266      * @throws XPathExpressionException If the expression cannot be evaluated.
    267      * @throws IllegalArgumentException If <code>returnType</code> is not one
    268      * of the types defined in {@link XPathConstants}.
    269      * @throws NullPointerException If  <code>source</code> or
    270      * <code>returnType</code> is <code>null</code>.
    271      */
    272     public Object evaluate(InputSource source, QName returnType)
    273         throws XPathExpressionException {
    274         if ( ( source == null ) || ( returnType == null ) ) {
    275             String fmsg = XSLMessages.createXPATHMessage(
    276                     XPATHErrorResources.ER_SOURCE_RETURN_TYPE_CANNOT_BE_NULL,
    277                     null );
    278             throw new NullPointerException ( fmsg );
    279         }
    280         // Checking if requested returnType is supported. returnType need to be
    281         // defined in XPathConstants
    282         if ( !isSupported ( returnType ) ) {
    283             String fmsg = XSLMessages.createXPATHMessage(
    284                     XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
    285                     new Object[] { returnType.toString() } );
    286             throw new IllegalArgumentException ( fmsg );
    287         }
    288         try {
    289             if ( dbf == null ) {
    290                 dbf = DocumentBuilderFactory.newInstance();
    291                 dbf.setNamespaceAware( true );
    292                 dbf.setValidating( false );
    293             }
    294             db = dbf.newDocumentBuilder();
    295             Document document = db.parse( source );
    296             return eval(  document, returnType );
    297         } catch ( Exception e ) {
    298             throw new XPathExpressionException ( e );
    299         }
    300     }
    301 
    302     /**
    303      * <p>Evaluate the compiled XPath expression in the context of the specified <code>InputSource</code> and return the result as a
    304      * <code>String</code>.</p>
    305      *
    306      * <p>This method calls {@link #evaluate(InputSource source, QName returnType)} with a <code>returnType</code> of
    307      * {@link XPathConstants#STRING}.</p>
    308      *
    309      * <p>See "Evaluation of XPath Expressions" section of JAXP 1.3 spec
    310      * for context item evaluation,
    311      * variable, function and QName resolution and return type conversion.</p>
    312      *
    313      * <p>If <code>source</code> is <code>null</code>, then a <code>NullPointerException</code> is thrown.</p>
    314      *
    315      * @param source The <code>InputSource</code> of the document to evaluate over.
    316      *
    317      * @return The <code>String</code> that is the result of evaluating the expression and converting the result to a
    318      *   <code>String</code>.
    319      *
    320      * @throws XPathExpressionException If the expression cannot be evaluated.
    321      * @throws NullPointerException If  <code>source</code> is <code>null</code>.
    322      */
    323     public String evaluate(InputSource source)
    324         throws XPathExpressionException {
    325         return (String)this.evaluate( source, XPathConstants.STRING );
    326     }
    327 
    328     private boolean isSupported( QName returnType ) {
    329         // XPathConstants.STRING
    330         if ( ( returnType.equals( XPathConstants.STRING ) ) ||
    331              ( returnType.equals( XPathConstants.NUMBER ) ) ||
    332              ( returnType.equals( XPathConstants.BOOLEAN ) ) ||
    333              ( returnType.equals( XPathConstants.NODE ) ) ||
    334              ( returnType.equals( XPathConstants.NODESET ) )  ) {
    335 
    336             return true;
    337         }
    338         return false;
    339      }
    340 
    341      private Object getResultAsType( XObject resultObject, QName returnType )
    342         throws javax.xml.transform.TransformerException {
    343         // XPathConstants.STRING
    344         if ( returnType.equals( XPathConstants.STRING ) ) {
    345             return resultObject.str();
    346         }
    347         // XPathConstants.NUMBER
    348         if ( returnType.equals( XPathConstants.NUMBER ) ) {
    349             return new Double ( resultObject.num());
    350         }
    351         // XPathConstants.BOOLEAN
    352         if ( returnType.equals( XPathConstants.BOOLEAN ) ) {
    353             return new Boolean( resultObject.bool());
    354         }
    355         // XPathConstants.NODESET ---ORdered, UNOrdered???
    356         if ( returnType.equals( XPathConstants.NODESET ) ) {
    357             return resultObject.nodelist();
    358         }
    359         // XPathConstants.NODE
    360         if ( returnType.equals( XPathConstants.NODE ) ) {
    361             NodeIterator ni = resultObject.nodeset();
    362             //Return the first node, or null
    363             return ni.nextNode();
    364         }
    365         // If isSupported check is already done then the execution path
    366         // shouldn't come here. Being defensive
    367         String fmsg = XSLMessages.createXPATHMessage(
    368                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
    369                 new Object[] { returnType.toString()});
    370         throw new IllegalArgumentException ( fmsg );
    371     }
    372 
    373 
    374     private static Document getDummyDocument( ) {
    375         try {
    376             if ( dbf == null ) {
    377                 dbf = DocumentBuilderFactory.newInstance();
    378                 dbf.setNamespaceAware( true );
    379                 dbf.setValidating( false );
    380             }
    381             db = dbf.newDocumentBuilder();
    382 
    383             DOMImplementation dim = db.getDOMImplementation();
    384             d = dim.createDocument("http://java.sun.com/jaxp/xpath",
    385                 "dummyroot", null);
    386             return d;
    387         } catch ( Exception e ) {
    388             e.printStackTrace();
    389         }
    390         return null;
    391     }
    392 
    393 
    394 
    395 
    396 }
    397