1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.org.apache.xpath.internal.jaxp;
  27 
  28 import com.sun.org.apache.xalan.internal.res.XSLMessages;
  29 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  30 import com.sun.org.apache.xml.internal.dtm.DTM;
  31 import com.sun.org.apache.xpath.internal.objects.XObject;
  32 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  33 import java.io.IOException;
  34 import javax.xml.namespace.QName;
  35 import javax.xml.parsers.DocumentBuilderFactory;
  36 import javax.xml.parsers.ParserConfigurationException;
  37 import javax.xml.transform.TransformerException;
  38 import javax.xml.xpath.XPathConstants;
  39 import javax.xml.xpath.XPathEvaluationResult;
  40 import javax.xml.xpath.XPathExpressionException;
  41 import javax.xml.xpath.XPathFunctionResolver;
  42 import javax.xml.xpath.XPathNodes;
  43 import javax.xml.xpath.XPathVariableResolver;
  44 import jdk.xml.internal.JdkXmlFeatures;
  45 import org.w3c.dom.Document;
  46 import org.w3c.dom.Node;
  47 import org.w3c.dom.traversal.NodeIterator;
  48 import org.xml.sax.InputSource;
  49 import org.xml.sax.SAXException;
  50 
  51 /**
  52  * This class contains several utility methods used by XPathImpl and
  53  * XPathExpressionImpl
  54  */
  55 class XPathImplUtil {
  56     XPathFunctionResolver functionResolver;
  57     XPathVariableResolver variableResolver;
  58     JAXPPrefixResolver prefixResolver;
  59     boolean useServiceMechanism = true;
  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     boolean featureSecureProcessing = false;
  64     JdkXmlFeatures featureManager;
  65 
  66     /**
  67      * Evaluate an XPath context using the internal XPath engine
  68      * @param contextItem The XPath context
  69      * @param xpath The internal XPath engine
  70      * @return an XObject
  71      * @throws javax.xml.transform.TransformerException If the expression cannot be evaluated.
  72      */
  73     XObject eval(Object contextItem, com.sun.org.apache.xpath.internal.XPath xpath)
  74             throws javax.xml.transform.TransformerException {
  75         com.sun.org.apache.xpath.internal.XPathContext xpathSupport;
  76         if (functionResolver != null) {
  77             JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
  78                     functionResolver, featureSecureProcessing, featureManager);
  79             xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(jep);
  80         } else {
  81             xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext();
  82         }
  83 
  84         xpathSupport.setVarStack(new JAXPVariableStack(variableResolver));
  85         XObject xobj;
  86 
  87         Node contextNode = (Node)contextItem;
  88         // We always need to have a ContextNode with Xalan XPath implementation
  89         // To allow simple expression evaluation like 1+1 we are setting
  90         // dummy Document as Context Node
  91         if (contextNode == null) {
  92             xobj = xpath.execute(xpathSupport, DTM.NULL, prefixResolver);
  93         } else {
  94             xobj = xpath.execute(xpathSupport, contextNode, prefixResolver);
  95         }
  96 
  97         return xobj;
  98     }
  99 
 100     /**
 101      * Parse the input source and return a Document.
 102      * @param source The {@code InputSource} of the document
 103      * @return a DOM Document
 104      * @throws XPathExpressionException if there is an error parsing the source.
 105      */
 106     Document getDocument(InputSource source)
 107         throws XPathExpressionException {
 108         requireNonNull(source, "Source");
 109         try {
 110             // we'd really like to cache those DocumentBuilders, but we can't because:
 111             // 1. thread safety. parsers are not thread-safe, so at least
 112             //    we need one instance per a thread.
 113             // 2. parsers are non-reentrant, so now we are looking at having a
 114             // pool of parsers.
 115             // 3. then the class loading issue. The look-up procedure of
 116             //    DocumentBuilderFactory.newInstance() depends on context class loader
 117             //    and system properties, which may change during the execution of JVM.
 118             //
 119             // so we really have to create a fresh DocumentBuilder every time we need one
 120             // - KK
 121             DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(useServiceMechanism);
 122             dbf.setNamespaceAware(true);
 123             dbf.setValidating(false);
 124             return dbf.newDocumentBuilder().parse(source);
 125         } catch (ParserConfigurationException | SAXException | IOException e) {
 126             throw new XPathExpressionException (e);
 127         }
 128     }
 129 
 130     /**
 131      * Get result depending on the QName type defined in XPathConstants
 132      * @param resultObject the result of an evaluation
 133      * @param returnType the return type
 134      * @return result per the return type
 135      * @throws TransformerException if the result can not be converted to
 136      * the specified return type.
 137      */
 138     Object getResultAsType(XObject resultObject, QName returnType)
 139         throws TransformerException {
 140         // XPathConstants.STRING
 141         if (returnType.equals(XPathConstants.STRING)) {
 142             return resultObject.str();
 143         }
 144         // XPathConstants.NUMBER
 145         if (returnType.equals(XPathConstants.NUMBER)) {
 146             return resultObject.num();
 147         }
 148         // XPathConstants.BOOLEAN
 149         if (returnType.equals(XPathConstants.BOOLEAN)) {
 150             return resultObject.bool();
 151         }
 152         // XPathConstants.NODESET ---ORdered, UNOrdered???
 153         if (returnType.equals(XPathConstants.NODESET)) {
 154             return resultObject.nodelist();
 155         }
 156         // XPathConstants.NODE
 157         if (returnType.equals(XPathConstants.NODE)) {
 158             NodeIterator ni = resultObject.nodeset();
 159             //Return the first node, or null
 160             return ni.nextNode();
 161         }
 162         // If isSupported check is already done then the execution path
 163         // shouldn't come here. Being defensive
 164         String fmsg = XSLMessages.createXPATHMessage(
 165                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
 166                 new Object[] { returnType.toString()});
 167         throw new IllegalArgumentException (fmsg);
 168     }
 169 
 170     /**
 171      * Construct an XPathExpressionResult object based on the result of the
 172      * evaluation and cast to the specified class type.
 173      * @param <T> The class type
 174      * @param resultObject the result of an evaluation
 175      * @param type The class type expected to be returned by the XPath expression.
 176      * @return an instance of the specified type or null if the XObject returned
 177      * an UNKNOWN object type.
 178      * @throws TransformerException if there is an error converting the result
 179      * to the specified type. It's unlikely to happen. This is mostly needed
 180      * by the internal XPath engine.
 181      */
 182     <T> T getXPathResult(XObject resultObject, Class<T> type)
 183             throws TransformerException {
 184         int resultType = resultObject.getType();
 185 
 186         switch (resultType) {
 187             case XObject.CLASS_BOOLEAN :
 188                 return type.cast(new XPathResultImpl<>(resultObject, Boolean.class));
 189             case XObject.CLASS_NUMBER :
 190                 return type.cast(new XPathResultImpl<>(resultObject, Double.class));
 191             case XObject.CLASS_STRING :
 192                 return type.cast(new XPathResultImpl<>(resultObject, String.class));
 193             case XObject.CLASS_NODESET :
 194                 return type.cast(new XPathResultImpl<>(resultObject, XPathNodes.class));
 195             case XObject.CLASS_RTREEFRAG :  //NODE
 196                 return type.cast(new XPathResultImpl<>(resultObject, Node.class));
 197         }
 198 
 199         return null;
 200     }
 201 
 202     /**
 203      * Check whether or not the specified type is supported
 204      * @param <T> The class type
 205      * @param type The type to be checked
 206      * @throws IllegalArgumentException if the type is not supported
 207      */
 208     <T> void isSupportedClassType(Class<T> type) {
 209         requireNonNull(type, "The class type");
 210         if (type.isAssignableFrom(Boolean.class) ||
 211                 type.isAssignableFrom(Double.class) ||
 212                 type.isAssignableFrom(Integer.class) ||
 213                 type.isAssignableFrom(Long.class) ||
 214                 type.isAssignableFrom(String.class) ||
 215                 type.isAssignableFrom(XPathNodes.class) ||
 216                 type.isAssignableFrom(Node.class) ||
 217                 type.isAssignableFrom(XPathEvaluationResult.class)) {
 218             return;
 219         }
 220         String fmsg = XSLMessages.createXPATHMessage(
 221                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
 222                 new Object[] { type.toString() });
 223         throw new IllegalArgumentException (fmsg);
 224     }
 225 
 226     /**
 227      * Check if the requested returnType is supported.
 228      * @param returnType the return type
 229      * @throws IllegalArgumentException if the return type is not supported
 230      */
 231     void isSupported(QName returnType) {
 232         requireNonNull(returnType, "returnType");
 233         if (returnType.equals(XPathConstants.STRING) ||
 234                 returnType.equals(XPathConstants.NUMBER) ||
 235                 returnType.equals(XPathConstants.BOOLEAN) ||
 236                 returnType.equals(XPathConstants.NODE) ||
 237                 returnType.equals(XPathConstants.NODESET)) {
 238             return;
 239         }
 240         String fmsg = XSLMessages.createXPATHMessage(
 241                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
 242                 new Object[] { returnType.toString() });
 243         throw new IllegalArgumentException (fmsg);
 244      }
 245 
 246     /**
 247      * Checks that the specified parameter is not {@code null}.
 248      *
 249      * @param <T> the type of the reference
 250      * @param param the parameter to check for nullity
 251      * @param paramName the parameter name
 252      * @throws NullPointerException if {@code param} is {@code null}
 253      */
 254     <T> void requireNonNull(T param, String paramName) {
 255         if (param == null) {
 256             String fmsg = XSLMessages.createXPATHMessage(
 257                     XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
 258                     new Object[] {paramName});
 259             throw new NullPointerException (fmsg);
 260         }
 261     }
 262 }