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 }