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 26package com.sun.org.apache.xpath.internal.jaxp; 27 28import com.sun.org.apache.xalan.internal.res.XSLMessages; 29import com.sun.org.apache.xalan.internal.utils.FactoryImpl; 30import com.sun.org.apache.xml.internal.dtm.DTM; 31import com.sun.org.apache.xpath.internal.axes.LocPathIterator; 32import com.sun.org.apache.xpath.internal.objects.XObject; 33import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; 34import java.io.IOException; 35import javax.xml.namespace.QName; 36import javax.xml.parsers.DocumentBuilderFactory; 37import javax.xml.parsers.ParserConfigurationException; 38import javax.xml.transform.TransformerException; 39import javax.xml.xpath.XPathConstants; 40import javax.xml.xpath.XPathEvaluationResult; 41import javax.xml.xpath.XPathExpressionException; 42import javax.xml.xpath.XPathFunctionResolver; 43import javax.xml.xpath.XPathNodes; 44import javax.xml.xpath.XPathVariableResolver; 45import jdk.xml.internal.JdkXmlFeatures; 46import org.w3c.dom.Document; 47import org.w3c.dom.Node; 48import org.w3c.dom.traversal.NodeIterator; 49import org.xml.sax.InputSource; 50import org.xml.sax.SAXException; 51 52/** 53 * This class contains several utility methods used by XPathImpl and 54 * XPathExpressionImpl 55 */ 56class XPathImplUtil { 57 XPathFunctionResolver functionResolver; 58 XPathVariableResolver variableResolver; 59 JAXPPrefixResolver prefixResolver; 60 boolean useServiceMechanism = true; 61 // By default Extension Functions are allowed in XPath Expressions. If 62 // Secure Processing Feature is set on XPathFactory then the invocation of 63 // extensions function need to throw XPathFunctionException 64 boolean featureSecureProcessing = false; 65 JdkXmlFeatures featureManager; 66 67 /** 68 * Evaluate an XPath context using the internal XPath engine 69 * @param contextItem The XPath context 70 * @param xpath The internal XPath engine 71 * @return an XObject 72 * @throws javax.xml.transform.TransformerException If the expression cannot be evaluated. 73 */ 74 XObject eval(Object contextItem, com.sun.org.apache.xpath.internal.XPath xpath) 75 throws javax.xml.transform.TransformerException { 76 com.sun.org.apache.xpath.internal.XPathContext xpathSupport; 77 if (contextItem == null && xpath.getExpression() instanceof LocPathIterator) { 78 // the operation must have no dependency on the context that is null 79 throw new TransformerException(XSLMessages.createXPATHMessage( 80 XPATHErrorResources.ER_CONTEXT_CAN_NOT_BE_NULL, 81 new Object[] {})); 82 } 83 if (functionResolver != null) { 84 JAXPExtensionsProvider jep = new JAXPExtensionsProvider( 85 functionResolver, featureSecureProcessing, featureManager); 86 xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(jep); 87 } else { 88 xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(); 89 } 90 91 xpathSupport.setVarStack(new JAXPVariableStack(variableResolver)); 92 XObject xobj; 93 94 Node contextNode = (Node)contextItem; 95 // We always need to have a ContextNode with Xalan XPath implementation 96 // To allow simple expression evaluation like 1+1 we are setting 97 // dummy Document as Context Node 98 if (contextNode == null) { 99 xobj = xpath.execute(xpathSupport, DTM.NULL, prefixResolver); 100 } else { 101 xobj = xpath.execute(xpathSupport, contextNode, prefixResolver); 102 } 103 104 return xobj; 105 } 106 107 /** 108 * Parse the input source and return a Document. 109 * @param source The {@code InputSource} of the document 110 * @return a DOM Document 111 * @throws XPathExpressionException if there is an error parsing the source. 112 */ 113 Document getDocument(InputSource source) 114 throws XPathExpressionException { 115 requireNonNull(source, "Source"); 116 try { 117 // we'd really like to cache those DocumentBuilders, but we can't because: 118 // 1. thread safety. parsers are not thread-safe, so at least 119 // we need one instance per a thread. 120 // 2. parsers are non-reentrant, so now we are looking at having a 121 // pool of parsers. 122 // 3. then the class loading issue. The look-up procedure of 123 // DocumentBuilderFactory.newInstance() depends on context class loader 124 // and system properties, which may change during the execution of JVM. 125 // 126 // so we really have to create a fresh DocumentBuilder every time we need one 127 // - KK 128 DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(useServiceMechanism); 129 dbf.setNamespaceAware(true); 130 dbf.setValidating(false); 131 return dbf.newDocumentBuilder().parse(source); 132 } catch (ParserConfigurationException | SAXException | IOException e) { 133 throw new XPathExpressionException (e); 134 } 135 } 136 137 /** 138 * Get result depending on the QName type defined in XPathConstants 139 * @param resultObject the result of an evaluation 140 * @param returnType the return type 141 * @return result per the return type 142 * @throws TransformerException if the result can not be converted to 143 * the specified return type. 144 */ 145 Object getResultAsType(XObject resultObject, QName returnType) 146 throws TransformerException { 147 // XPathConstants.STRING 148 if (returnType.equals(XPathConstants.STRING)) { 149 return resultObject.str(); 150 } 151 // XPathConstants.NUMBER 152 if (returnType.equals(XPathConstants.NUMBER)) { 153 return resultObject.num(); 154 } 155 // XPathConstants.BOOLEAN 156 if (returnType.equals(XPathConstants.BOOLEAN)) { 157 return resultObject.bool(); 158 } 159 // XPathConstants.NODESET ---ORdered, UNOrdered??? 160 if (returnType.equals(XPathConstants.NODESET)) { 161 return resultObject.nodelist(); 162 } 163 // XPathConstants.NODE 164 if (returnType.equals(XPathConstants.NODE)) { 165 NodeIterator ni = resultObject.nodeset(); 166 //Return the first node, or null 167 return ni.nextNode(); 168 } 169 // If isSupported check is already done then the execution path 170 // shouldn't come here. Being defensive 171 String fmsg = XSLMessages.createXPATHMessage( 172 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 173 new Object[] { returnType.toString()}); 174 throw new IllegalArgumentException (fmsg); 175 } 176 177 /** 178 * Construct an XPathExpressionResult object based on the result of the 179 * evaluation and cast to the specified class type. 180 * @param <T> The class type 181 * @param resultObject the result of an evaluation 182 * @param type The class type expected to be returned by the XPath expression. 183 * @return an instance of the specified type or null if the XObject returned 184 * an UNKNOWN object type. 185 * @throws TransformerException if there is an error converting the result 186 * to the specified type. It's unlikely to happen. This is mostly needed 187 * by the internal XPath engine. 188 */ 189 <T> T getXPathResult(XObject resultObject, Class<T> type) 190 throws TransformerException { 191 int resultType = resultObject.getType(); 192 193 switch (resultType) { 194 case XObject.CLASS_BOOLEAN : 195 return type.cast(new XPathResultImpl<>(resultObject, Boolean.class)); 196 case XObject.CLASS_NUMBER : 197 return type.cast(new XPathResultImpl<>(resultObject, Double.class)); 198 case XObject.CLASS_STRING : 199 return type.cast(new XPathResultImpl<>(resultObject, String.class)); 200 case XObject.CLASS_NODESET : 201 return type.cast(new XPathResultImpl<>(resultObject, XPathNodes.class)); 202 case XObject.CLASS_RTREEFRAG : //NODE 203 return type.cast(new XPathResultImpl<>(resultObject, Node.class)); 204 } 205 206 return null; 207 } 208 209 /** 210 * Check whether or not the specified type is supported 211 * @param <T> The class type 212 * @param type The type to be checked 213 * @throws IllegalArgumentException if the type is not supported 214 */ 215 <T> void isSupportedClassType(Class<T> type) { 216 requireNonNull(type, "The class type"); 217 if (type.isAssignableFrom(Boolean.class) || 218 type.isAssignableFrom(Double.class) || 219 type.isAssignableFrom(Integer.class) || 220 type.isAssignableFrom(Long.class) || 221 type.isAssignableFrom(String.class) || 222 type.isAssignableFrom(XPathNodes.class) || 223 type.isAssignableFrom(Node.class) || 224 type.isAssignableFrom(XPathEvaluationResult.class)) { 225 return; 226 } 227 String fmsg = XSLMessages.createXPATHMessage( 228 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 229 new Object[] { type.toString() }); 230 throw new IllegalArgumentException (fmsg); 231 } 232 233 /** 234 * Check if the requested returnType is supported. 235 * @param returnType the return type 236 * @throws IllegalArgumentException if the return type is not supported 237 */ 238 void isSupported(QName returnType) { 239 requireNonNull(returnType, "returnType"); 240 if (returnType.equals(XPathConstants.STRING) || 241 returnType.equals(XPathConstants.NUMBER) || 242 returnType.equals(XPathConstants.BOOLEAN) || 243 returnType.equals(XPathConstants.NODE) || 244 returnType.equals(XPathConstants.NODESET)) { 245 return; 246 } 247 String fmsg = XSLMessages.createXPATHMessage( 248 XPATHErrorResources.ER_UNSUPPORTED_RETURN_TYPE, 249 new Object[] { returnType.toString() }); 250 throw new IllegalArgumentException (fmsg); 251 } 252 253 /** 254 * Checks that the specified parameter is not {@code null}. 255 * 256 * @param <T> the type of the reference 257 * @param param the parameter to check for nullity 258 * @param paramName the parameter name 259 * @throws NullPointerException if {@code param} is {@code null} 260 */ 261 <T> void requireNonNull(T param, String paramName) { 262 if (param == null) { 263 String fmsg = XSLMessages.createXPATHMessage( 264 XPATHErrorResources.ER_ARG_CANNOT_BE_NULL, 265 new Object[] {paramName}); 266 throw new NullPointerException (fmsg); 267 } 268 } 269} 270