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