mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8354084: Streamline XPath API's extension function control
Reviewed-by: rriggs, naoto
This commit is contained in:
parent
6b553acbaa
commit
cf0db96314
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@ -41,7 +41,7 @@ import javax.xml.transform.TransformerException;
|
||||
* the expression executes, it calls ExtensionsTable#extFunction, and then
|
||||
* converts the result to the appropriate XObject.
|
||||
* @xsl.usage advanced
|
||||
* @LastModified: May 2022
|
||||
* @LastModified: Apr 2025
|
||||
*/
|
||||
public class FuncExtFunction extends Function
|
||||
{
|
||||
@ -186,12 +186,6 @@ public class FuncExtFunction extends Function
|
||||
*/
|
||||
public XObject execute(XPathContext xctxt) throws TransformerException
|
||||
{
|
||||
if (xctxt.isSecureProcessing())
|
||||
throw new javax.xml.transform.TransformerException(
|
||||
XPATHMessages.createXPATHMessage(
|
||||
XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
|
||||
new Object[] {toString()}));
|
||||
|
||||
XObject result;
|
||||
List<XObject> argVec = new ArrayList<>();
|
||||
int nArgs = m_argVec.size();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2013, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@ -37,26 +37,16 @@ import jdk.xml.internal.JdkXmlFeatures;
|
||||
/**
|
||||
*
|
||||
* @author Ramesh Mandava
|
||||
* @LastModified: Nov 2017
|
||||
* @LastModified: Apr 2025
|
||||
*/
|
||||
public class JAXPExtensionsProvider implements ExtensionsProvider {
|
||||
|
||||
private final XPathFunctionResolver resolver;
|
||||
private boolean extensionInvocationDisabled = false;
|
||||
|
||||
public JAXPExtensionsProvider(XPathFunctionResolver resolver) {
|
||||
this.resolver = resolver;
|
||||
this.extensionInvocationDisabled = false;
|
||||
}
|
||||
|
||||
public JAXPExtensionsProvider(XPathFunctionResolver resolver,
|
||||
boolean featureSecureProcessing, JdkXmlFeatures featureManager ) {
|
||||
this.resolver = resolver;
|
||||
if (featureSecureProcessing &&
|
||||
!featureManager.getFeature(JdkXmlFeatures.XmlFeature.ENABLE_EXTENSION_FUNCTION)) {
|
||||
this.extensionInvocationDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the extension function available?
|
||||
@ -111,16 +101,6 @@ public class JAXPExtensionsProvider implements ExtensionsProvider {
|
||||
//Find the XPathFunction corresponding to namespace and funcName
|
||||
javax.xml.namespace.QName myQName = new QName( ns, funcName );
|
||||
|
||||
// JAXP 1.3 spec says When XMLConstants.FEATURE_SECURE_PROCESSING
|
||||
// feature is set then invocation of extension functions need to
|
||||
// throw XPathFunctionException
|
||||
if ( extensionInvocationDisabled ) {
|
||||
String fmsg = XSLMessages.createXPATHMessage(
|
||||
XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
|
||||
new Object[] { myQName.toString() } );
|
||||
throw new XPathFunctionException ( fmsg );
|
||||
}
|
||||
|
||||
// Assuming user is passing all the needed parameters ( including
|
||||
// default values )
|
||||
int arity = argVec.size();
|
||||
@ -167,16 +147,6 @@ public class JAXPExtensionsProvider implements ExtensionsProvider {
|
||||
javax.xml.namespace.QName myQName =
|
||||
new javax.xml.namespace.QName( namespace, functionName );
|
||||
|
||||
// JAXP 1.3 spec says When XMLConstants.FEATURE_SECURE_PROCESSING
|
||||
// feature is set then invocation of extension functions need to
|
||||
// throw XPathFunctionException
|
||||
if ( extensionInvocationDisabled ) {
|
||||
String fmsg = XSLMessages.createXPATHMessage(
|
||||
XPATHErrorResources.ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
|
||||
new Object[] { myQName.toString() } );
|
||||
throw new XPathFunctionException ( fmsg );
|
||||
}
|
||||
|
||||
XPathFunction xpathFunction =
|
||||
resolver.resolveFunction( myQName, arity );
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -54,7 +54,7 @@ import org.xml.sax.SAXException;
|
||||
* This class contains several utility methods used by XPathImpl and
|
||||
* XPathExpressionImpl
|
||||
*
|
||||
* @LastModified: Jan 2022
|
||||
* @LastModified: Apr 2025
|
||||
*/
|
||||
class XPathImplUtil {
|
||||
XPathFunctionResolver functionResolver;
|
||||
@ -85,8 +85,7 @@ class XPathImplUtil {
|
||||
new Object[] {}));
|
||||
}
|
||||
if (functionResolver != null) {
|
||||
JAXPExtensionsProvider jep = new JAXPExtensionsProvider(
|
||||
functionResolver, featureSecureProcessing, featureManager);
|
||||
JAXPExtensionsProvider jep = new JAXPExtensionsProvider(functionResolver);
|
||||
xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext(jep);
|
||||
} else {
|
||||
xpathSupport = new com.sun.org.apache.xpath.internal.XPathContext();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
*/
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
@ -31,7 +31,7 @@ import java.util.ListResourceBundle;
|
||||
* Also you need to update the count of messages(MAX_CODE)or
|
||||
* the count of warnings(MAX_WARNING) [ Information purpose only]
|
||||
* @xsl.usage advanced
|
||||
* @LastModified: Nov 2024
|
||||
* @LastModified: Apr 2025
|
||||
*/
|
||||
public class XPATHErrorResources extends ListResourceBundle
|
||||
{
|
||||
@ -305,7 +305,6 @@ public static final String ER_IGNORABLE_WHITESPACE_NOT_HANDLED =
|
||||
public static final String ER_XPATH_ERROR = "ER_XPATH_ERROR";
|
||||
|
||||
//BEGIN: Keys needed for exception messages of JAXP 1.3 XPath API implementation
|
||||
public static final String ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED = "ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED";
|
||||
public static final String ER_RESOLVE_VARIABLE_RETURNS_NULL = "ER_RESOLVE_VARIABLE_RETURNS_NULL";
|
||||
public static final String ER_NO_XPATH_VARIABLE_RESOLVER = "ER_NO_XPATH_VARIABLE_RESOLVER";
|
||||
public static final String ER_NO_XPATH_FUNCTION_PROVIDER = "ER_NO_XPATH_FUNCTION_PROVIDER";
|
||||
@ -766,11 +765,6 @@ public static final String ER_IGNORABLE_WHITESPACE_NOT_HANDLED =
|
||||
|
||||
//BEGIN: Definitions of error keys used in exception messages of JAXP 1.3 XPath API implementation
|
||||
|
||||
/** Field ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED */
|
||||
|
||||
{ ER_EXTENSION_FUNCTION_CANNOT_BE_INVOKED,
|
||||
"Extension function: ''{0}'' can not be invoked when the XMLConstants.FEATURE_SECURE_PROCESSING feature is set to true."},
|
||||
|
||||
/** Field ER_RESOLVE_VARIABLE_RETURNS_NULL */
|
||||
|
||||
{ ER_RESOLVE_VARIABLE_RETURNS_NULL,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -868,19 +868,19 @@
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td id="ExtFunc">{@systemProperty jdk.xml.enableExtensionFunctions}</td>
|
||||
* <td>Determines if XSLT and XPath extension functions are to be allowed.
|
||||
* <td>Determines whether extension functions in the Transform API are to be allowed.
|
||||
* The extension functions in the XPath API are not affected by this property.
|
||||
* </td>
|
||||
* <td style="text-align:center" rowspan="5">yes</td>
|
||||
* <td style="text-align:center" rowspan="3">Boolean</td>
|
||||
* <td>
|
||||
* true or false. True indicates that extension functions are allowed; False otherwise.
|
||||
* </td>
|
||||
* <td style="text-align:center">true</td>
|
||||
* <td style="text-align:center">false</td>
|
||||
* <td style="text-align:center">false</td>
|
||||
* <td style="text-align:center">Yes</td>
|
||||
* <td style="text-align:center">
|
||||
* <a href="#Transform">Transform</a><br>
|
||||
* <a href="#XPATH">XPath</a>
|
||||
* </td>
|
||||
* <td style="text-align:center"><a href="#Processor">Method 2</a></td>
|
||||
* <td style="text-align:center">8</td>
|
||||
|
||||
@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package xpath;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
import javax.xml.xpath.XPathFactoryConfigurationException;
|
||||
import javax.xml.xpath.XPathFunction;
|
||||
import javax.xml.xpath.XPathFunctionException;
|
||||
import javax.xml.xpath.XPathFunctionResolver;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
|
||||
* @run testng/othervm xpath.SecureProcessingTest
|
||||
* @summary Test when FEATURE_SECURE_PROCESSING is true, calling an external function will cause XPathFunctionException.
|
||||
*/
|
||||
@Test
|
||||
public class SecureProcessingTest {
|
||||
public final void testSecureProcessing() {
|
||||
boolean _isSecureMode = System.getSecurityManager() != null;
|
||||
|
||||
final String XPATH_EXPRESSION = "ext:helloWorld()";
|
||||
|
||||
// the xml source
|
||||
InputStream xmlStream = this.getClass().getResourceAsStream("SecureProcessingTest.xml");
|
||||
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder = null;
|
||||
Document document = null;
|
||||
|
||||
try {
|
||||
documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
document = documentBuilder.parse(xmlStream);
|
||||
} catch (ParserConfigurationException parserConfigurationException) {
|
||||
parserConfigurationException.printStackTrace();
|
||||
Assert.fail(parserConfigurationException.toString());
|
||||
} catch (SAXException saxException) {
|
||||
saxException.printStackTrace();
|
||||
Assert.fail(saxException.toString());
|
||||
} catch (IOException ioException) {
|
||||
ioException.printStackTrace();
|
||||
Assert.fail(ioException.toString());
|
||||
}
|
||||
|
||||
// the XPath
|
||||
XPathFactory xPathFactory = null;
|
||||
XPath xPath = null;
|
||||
String xPathResult = null;
|
||||
|
||||
// SECURE_PROCESSING == false
|
||||
// evaluate an expression with a user defined function with a non-secure
|
||||
// XPath
|
||||
// expect success
|
||||
if (!_isSecureMode) { // jaxp secure feature can not be turned off when
|
||||
// security manager is present
|
||||
try {
|
||||
xPathFactory = xPathFactory.newInstance();
|
||||
xPathFactory.setXPathFunctionResolver(new MyXPathFunctionResolver());
|
||||
xPathFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
|
||||
|
||||
xPath = xPathFactory.newXPath();
|
||||
xPath.setNamespaceContext(new MyNamespaceContext());
|
||||
|
||||
xPathResult = xPath.evaluate(XPATH_EXPRESSION, document);
|
||||
} catch (XPathFactoryConfigurationException xPathFactoryConfigurationException) {
|
||||
xPathFactoryConfigurationException.printStackTrace();
|
||||
Assert.fail(xPathFactoryConfigurationException.toString());
|
||||
} catch (XPathExpressionException xPathExpressionException) {
|
||||
xPathExpressionException.printStackTrace();
|
||||
Assert.fail(xPathExpressionException.toString());
|
||||
}
|
||||
|
||||
// expected success
|
||||
System.out.println("XPath result (SECURE_PROCESSING == false) = \"" + xPathResult + "\"");
|
||||
}
|
||||
// now try with SECURE_PROCESSING == true
|
||||
// evaluate an expression with a user defined function with a secure
|
||||
// XPath
|
||||
// expect Exception
|
||||
boolean securityException = false;
|
||||
try {
|
||||
xPathFactory = xPathFactory.newInstance();
|
||||
xPathFactory.setXPathFunctionResolver(new MyXPathFunctionResolver());
|
||||
xPathFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
|
||||
xPath = xPathFactory.newXPath();
|
||||
xPath.setNamespaceContext(new MyNamespaceContext());
|
||||
|
||||
xPathResult = xPath.evaluate(XPATH_EXPRESSION, document);
|
||||
} catch (XPathFactoryConfigurationException xPathFactoryConfigurationException) {
|
||||
xPathFactoryConfigurationException.printStackTrace();
|
||||
Assert.fail(xPathFactoryConfigurationException.toString());
|
||||
} catch (XPathFunctionException xPathFunctionException) {
|
||||
// expected security exception
|
||||
securityException = true;
|
||||
xPathFunctionException.printStackTrace(System.out);
|
||||
} catch (XPathExpressionException xPathExpressionException) {
|
||||
xPathExpressionException.printStackTrace();
|
||||
Assert.fail(xPathExpressionException.toString());
|
||||
}
|
||||
|
||||
// expected Exception
|
||||
if (!securityException) {
|
||||
Assert.fail("XPath result (SECURE_PROCESSING == true) = \"" + xPathResult + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
private class MyXPathFunctionResolver implements XPathFunctionResolver {
|
||||
|
||||
public XPathFunction resolveFunction(QName functionName, int arity) {
|
||||
|
||||
// not a real ewsolver, always return a default XPathFunction
|
||||
return new MyXPathFunction();
|
||||
}
|
||||
}
|
||||
|
||||
private class MyXPathFunction implements XPathFunction {
|
||||
|
||||
public Object evaluate(List list) throws XPathFunctionException {
|
||||
|
||||
return "Hello World";
|
||||
}
|
||||
}
|
||||
|
||||
private class MyNamespaceContext implements NamespaceContext {
|
||||
|
||||
public String getNamespaceURI(String prefix) {
|
||||
if (prefix == null) {
|
||||
throw new IllegalArgumentException("The prefix cannot be null.");
|
||||
}
|
||||
|
||||
if (prefix.equals("ext")) {
|
||||
return "http://ext.com";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrefix(String namespace) {
|
||||
|
||||
if (namespace == null) {
|
||||
throw new IllegalArgumentException("The namespace uri cannot be null.");
|
||||
}
|
||||
|
||||
if (namespace.equals("http://ext.com")) {
|
||||
return "ext";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator getPrefixes(String namespace) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<helloWorld/>
|
||||
198
test/jaxp/javax/xml/jaxp/unittest/xpath/XPathFunctionTest.java
Normal file
198
test/jaxp/javax/xml/jaxp/unittest/xpath/XPathFunctionTest.java
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package xpath;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.xpath.*;
|
||||
import jaxp.library.JUnitTestUtil;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8354084
|
||||
* @summary Verifies that extensions to XPathFunction.
|
||||
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest /test/lib
|
||||
* @run junit xpath.XPathFunctionTest
|
||||
*/
|
||||
public class XPathFunctionTest {
|
||||
/*
|
||||
* Arguments for XPath Extension Function Test, refer to the test below.
|
||||
*/
|
||||
private static Stream<Arguments> testData() {
|
||||
// expected result when a resolver is set properly
|
||||
String result = "id=2 price=20";
|
||||
return Stream.of(
|
||||
// cases where the result is as expected when a resolver is set
|
||||
Arguments.of(true, false, false, false, false, result, null),
|
||||
Arguments.of(true, true, false, false, false, result, null),
|
||||
Arguments.of(true, true, false, true, false, result, null),
|
||||
Arguments.of(true, true, false, true, true, result, null),
|
||||
Arguments.of(true, true, true, true, true, result, null),
|
||||
// cases XPathExpressionException was thrown before the change even though there's a resolver
|
||||
Arguments.of(true, true, true, false, false, result, null),
|
||||
Arguments.of(true, true, true, true, false, result, null),
|
||||
// XPathExpressionException will continue to be thrown due to missing resolver, though it was
|
||||
// thrown for a different reason (FSP is turned on) before the change
|
||||
Arguments.of(false, false, false, false, false, result, XPathExpressionException.class),
|
||||
Arguments.of(false, true, true, false, false, result, XPathExpressionException.class),
|
||||
Arguments.of(false, true, true, true, false, result, XPathExpressionException.class)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the control over XPath Extension Functions.
|
||||
* @param useResolver indicates whether there is a custom resolver
|
||||
* @param setFSP indicates whether FSP is to be set
|
||||
* @param FSPValue the FSP value
|
||||
* @param setProperty indicates whether the property {@code jdk.xml.enableExtensionFunctions}
|
||||
* is to be set
|
||||
* @param propertyValue the property value
|
||||
* @param expected the expected result
|
||||
* @param expectedType the expected throw type
|
||||
* @throws Exception if the test fails other than the expected Exception, which
|
||||
* would indicate an issue in configuring the test
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("testData")
|
||||
public void test(boolean useResolver, boolean setFSP, boolean FSPValue,
|
||||
boolean setProperty, boolean propertyValue, String expected, Class<Throwable> expectedType) throws Exception {
|
||||
if (expectedType != null) {
|
||||
assertThrows(expectedType, () -> findToy(useResolver, setFSP, FSPValue, setProperty, propertyValue, expectedType));
|
||||
} else {
|
||||
String result = findToy(useResolver, setFSP, FSPValue, setProperty, propertyValue, expectedType);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
public String findToy(boolean useResolver, boolean setFSP, boolean FSPValue,
|
||||
boolean setProperty, boolean propertyValue, Class<Throwable> expectedType)
|
||||
throws Exception {
|
||||
|
||||
Document doc = getDocument(JUnitTestUtil.SRC_DIR + "/XPathFunctionTest.xml");
|
||||
XPathFactory xpf = XPathFactory.newDefaultInstance();
|
||||
if (useResolver) {
|
||||
xpf.setXPathFunctionResolver(new FunctionResolver(doc));
|
||||
}
|
||||
if (setFSP) {
|
||||
xpf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, FSPValue);
|
||||
}
|
||||
if (setProperty) {
|
||||
xpf.setFeature("jdk.xml.enableExtensionFunctions", propertyValue);
|
||||
}
|
||||
|
||||
XPath xPath = xpf.newXPath();
|
||||
XPathExpression exp = xPath.compile("ext:findToy('name', 'Another toy')");
|
||||
Node toyNode = (Node)exp.evaluate(doc, XPathConstants.NODE);
|
||||
String id = "", price = "";
|
||||
if (toyNode != null && toyNode.getNodeType() == Node.ELEMENT_NODE) {
|
||||
Element toyElement = (Element)toyNode;
|
||||
id = toyElement.getAttribute("id");
|
||||
price = toyElement.getElementsByTagName("price").item(0).getTextContent();
|
||||
|
||||
}
|
||||
return "id=" + id + " price=" + price;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DOM Document.
|
||||
* @param xmlFile the XML document to be parsed
|
||||
* @return a DOM Document
|
||||
* @throws Exception if error occurs
|
||||
*/
|
||||
Document getDocument(String xmlFile)
|
||||
throws Exception {
|
||||
try {
|
||||
DocumentBuilder builder = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder();
|
||||
Document out = builder.parse(xmlFile);
|
||||
return out;
|
||||
} catch (Exception e) {
|
||||
// won't happen, parsing a valid file
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// XPathFunctionResolver customized for the FindFunction
|
||||
class FunctionResolver implements XPathFunctionResolver {
|
||||
private final Document doc;
|
||||
|
||||
public FunctionResolver(Document doc) {
|
||||
this.doc = doc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XPathFunction resolveFunction(QName functionName, int arity) {
|
||||
if ("findToy".equals(functionName.getLocalPart()) && arity == 2) {
|
||||
return new FindFunction(doc);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// The Find function
|
||||
class FindFunction implements XPathFunction {
|
||||
private final Document doc;
|
||||
|
||||
public FindFunction(Document doc) {
|
||||
this.doc = doc;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object evaluate(List list) throws XPathFunctionException {
|
||||
if (list == null || list.size() != 2) {
|
||||
throw new XPathFunctionException("FindToy requires two args: name and value");
|
||||
}
|
||||
|
||||
String eleName = (String)list.get(0);
|
||||
String eleValue = (String)list.get(1);
|
||||
NodeList toys = doc.getElementsByTagName("toy");
|
||||
|
||||
for (int i = 0; i<toys.getLength(); i++) {
|
||||
Element toy = (Element)toys.item(i);
|
||||
NodeList children = toy.getElementsByTagName(eleName);
|
||||
|
||||
if (children.getLength() > 0) {
|
||||
String text = children.item(0).getTextContent();
|
||||
if (eleValue.equals(text)) {
|
||||
return toy;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<toys>
|
||||
<toy id="1">
|
||||
<name>Some toy</name>
|
||||
<price>10</price>
|
||||
</toy>
|
||||
<toy id="2">
|
||||
<name>Another toy</name>
|
||||
<price>20</price>
|
||||
</toy>
|
||||
</toys>
|
||||
Loading…
x
Reference in New Issue
Block a user