1 /*
   2  * Copyright (c) 2015, 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.xalan.internal.utils.FeatureManager;
  31 import com.sun.org.apache.xml.internal.dtm.DTM;
  32 import com.sun.org.apache.xpath.internal.objects.XObject;
  33 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  34 import java.io.IOException;
  35 import javax.xml.namespace.QName;
  36 import javax.xml.parsers.DocumentBuilderFactory;
  37 import javax.xml.parsers.ParserConfigurationException;
  38 import javax.xml.transform.TransformerException;
  39 import javax.xml.xpath.XPathConstants;
  40 import javax.xml.xpath.XPathEvaluationResult;
  41 import javax.xml.xpath.XPathExpressionException;
  42 import javax.xml.xpath.XPathFunctionResolver;
  43 import javax.xml.xpath.XPathNodes;
  44 import javax.xml.xpath.XPathVariableResolver;
  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     FeatureManager 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      * Get result depending on the class type defined in XPathExpressionResult
 172      * @param resultObject the result of an evaluation
 173      * @return XPathExpressionResult
 174      * @throws TransformerException if there is an error converting the result
 175      * to the specified type. It's unlikely to happen. This is mostly needed
 176      * by the internal XPath engine.
 177      */
 178     XPathEvaluationResult<?> getXPathResult(XObject resultObject)
 179             throws TransformerException {
 180         int resultType = resultObject.getType();
 181 
 182         switch (resultType) {
 183             case XObject.CLASS_BOOLEAN :
 184                 return new XPathResultImpl<>(resultObject, Boolean.class);
 185             case XObject.CLASS_NUMBER :
 186                 return new XPathResultImpl<>(resultObject, Double.class);
 187             case XObject.CLASS_STRING :
 188                 return new XPathResultImpl<>(resultObject, String.class);
 189             case XObject.CLASS_NODESET :
 190                 return new XPathResultImpl<>(resultObject, XPathNodes.class);
 191             case XObject.CLASS_RTREEFRAG :  //NODE
 192                 return new XPathResultImpl<>(resultObject, Node.class);
 193         }
 194 
 195         return null;
 196     }
 197 
 198     /**
 199      * Check whether or not the specified type is supported
 200      * @param <T> The class type
 201      * @param type The type to be checked
 202      * @throws IllegalArgumentException if the type is not supported
 203      */
 204     <T> void isSupportedClassType(Class<T> type) {
 205         requireNonNull(type, "The class type");
 206         if (type.isAssignableFrom(Boolean.class) ||
 207                 Number.class.isAssignableFrom(type) ||
 208                 type.isAssignableFrom(String.class) ||
 209                 type.isAssignableFrom(XPathNodes.class) ||
 210                 type.isAssignableFrom(Node.class) ||
 211                 type.isAssignableFrom(XPathEvaluationResult.class)) {
 212             return;
 213         }
 214         String fmsg = XSLMessages.createXPATHMessage(
 215                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
 216                 new Object[] { type.toString() });
 217         throw new IllegalArgumentException (fmsg);
 218     }
 219 
 220     /**
 221      * Check if the requested returnType is supported.
 222      * @param returnType the return type
 223      * @throws IllegalArgumentException if the return type is not supported
 224      */
 225     void isSupported(QName returnType) {
 226         requireNonNull(returnType, "returnType");
 227         if (returnType.equals(XPathConstants.STRING) ||
 228                 returnType.equals(XPathConstants.NUMBER) ||
 229                 returnType.equals(XPathConstants.BOOLEAN) ||
 230                 returnType.equals(XPathConstants.NODE) ||
 231                 returnType.equals(XPathConstants.NODESET)) {
 232             return;
 233         }
 234         String fmsg = XSLMessages.createXPATHMessage(
 235                 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE,
 236                 new Object[] { returnType.toString() });
 237         throw new IllegalArgumentException (fmsg);
 238      }
 239 
 240     /**
 241      * Checks that the specified parameter is not {@code null}.
 242      *
 243      * @param <T> the type of the reference
 244      * @param param the parameter to check for nullity
 245      * @param paramName the parameter name
 246      * @throws NullPointerException if {@code param} is {@code null}
 247      */
 248     <T> void requireNonNull(T param, String paramName) {
 249         if (param == null) {
 250             String fmsg = XSLMessages.createXPATHMessage(
 251                     XPATHErrorResources.ER_ARG_CANNOT_BE_NULL,
 252                     new Object[] {paramName});
 253             throw new NullPointerException (fmsg);
 254         }
 255     }
 256 }