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