diff --git a/core-tiger/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java b/core-tiger/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java index 028ecce0..ed940bfb 100644 --- a/core-tiger/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java +++ b/core-tiger/src/main/java/org/springframework/ws/server/endpoint/adapter/XPathParamAnnotationEndpointAdapter.java @@ -62,6 +62,7 @@ public class XPathParamAnnotationEndpointAdapter extends AbstractMethodEndpointA private Properties namespaces; + /** Sets namespaces used in the XPath expression. Maps prefixes to namespaces. */ public void setNamespaces(Properties namespaces) { this.namespaces = namespaces; } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4bcafb42..603b2bbf 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -6,6 +6,8 @@
namespaces property.
+ *
+ * @author Arjen Poutsma
+ * @see Jaxen
+ */
+public class JaxenXPathTemplate extends AbstractXPathTemplate {
+
+ public boolean evaluateAsBoolean(String expression, Source context) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ return xpath.booleanValueOf(element);
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ public Node evaluateAsNode(String expression, Source context) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ return (Node) xpath.selectSingleNode(element);
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ public List evaluateAsNodeList(String expression, Source context) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ return xpath.selectNodes(element);
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ public double evaluateAsDouble(String expression, Source context) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ return xpath.numberValueOf(element).doubleValue();
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ public String evaluateAsString(String expression, Source context) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ return xpath.stringValueOf(element);
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ public Object evaluateAsObject(String expression, Source context, NodeMapper nodeMapper) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ Node node = (Node) xpath.selectSingleNode(element);
+ if (node != null) {
+ try {
+ return nodeMapper.mapNode(node, 0);
+ }
+ catch (DOMException ex) {
+ throw new XPathException("Mapping resulted in DOMException", ex);
+ }
+ }
+ else {
+ return null;
+ }
+
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ public List evaluate(String expression, Source context, NodeMapper nodeMapper) throws XPathException {
+ try {
+ XPath xpath = createXPath(expression);
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ List nodes = (List) xpath.selectNodes(element);
+ List results = new ArrayList(nodes.size());
+ for (int i = 0; i < nodes.size(); i++) {
+ Node node = (Node) nodes.get(i);
+ try {
+ results.add(nodeMapper.mapNode(node, i));
+ }
+ catch (DOMException ex) {
+ throw new XPathException("Mapping resulted in DOMException", ex);
+ }
+ }
+ return results;
+ }
+ catch (JaxenException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+ private XPath createXPath(String expression) throws JaxenException {
+ XPath xpath = new DOMXPath(expression);
+ if (getNamespaces() != null && !getNamespaces().isEmpty()) {
+ xpath.setNamespaceContext(new SimpleNamespaceContext(getNamespaces()));
+ }
+ return xpath;
+ }
+}
diff --git a/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathExpressionFactory.java b/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathExpressionFactory.java
index 34fbead2..f430628d 100644
--- a/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathExpressionFactory.java
+++ b/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathExpressionFactory.java
@@ -28,7 +28,7 @@ import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
- * JAXP 1.3-specific factory creating XPathExpressions.
+ * JAXP 1.3-specific factory creating {@link XPathExpression} objects.
*
* @author Arjen Poutsma
* @see #createXPathExpression(String)
diff --git a/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathTemplate.java b/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathTemplate.java
new file mode 100644
index 00000000..42356336
--- /dev/null
+++ b/xml/src/main/java/org/springframework/xml/xpath/Jaxp13XPathTemplate.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathFactoryConfigurationException;
+
+import org.springframework.xml.dom.DomUtils;
+import org.springframework.xml.namespace.SimpleNamespaceContext;
+import org.springframework.xml.transform.StaxSource;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * Implementation of {@link XPathOperations} that uses JAXP 1.3. JAXP 1.3 is part of Java SE since 1.5.
+ *
+ * Namespaces can be set using the namespaces property.
+ *
+ * @author Arjen Poutsma
+ * @see #setNamespaces(java.util.Properties)
+ */
+public class Jaxp13XPathTemplate extends AbstractXPathTemplate {
+
+ private XPathFactory xpathFactory;
+
+ public Jaxp13XPathTemplate() {
+ this(XPathFactory.DEFAULT_OBJECT_MODEL_URI);
+ }
+
+ public Jaxp13XPathTemplate(String xpathFactoryUri) {
+ try {
+ xpathFactory = XPathFactory.newInstance(xpathFactoryUri);
+ }
+ catch (XPathFactoryConfigurationException ex) {
+ throw new XPathException("Could not create XPathFactory", ex);
+ }
+ }
+
+ public boolean evaluateAsBoolean(String expression, Source context) throws XPathException {
+ Boolean result = (Boolean) evaluate(expression, context, XPathConstants.BOOLEAN);
+ return result != null && result.booleanValue();
+ }
+
+ public Node evaluateAsNode(String expression, Source context) throws XPathException {
+ return (Node) evaluate(expression, context, XPathConstants.NODE);
+ }
+
+ public List evaluateAsNodeList(String expression, Source context) throws XPathException {
+ NodeList result = (NodeList) evaluate(expression, context, XPathConstants.NODESET);
+ List nodes = new ArrayList(result.getLength());
+ for (int i = 0; i < result.getLength(); i++) {
+ nodes.add(result.item(i));
+ }
+ return nodes;
+ }
+
+ public double evaluateAsDouble(String expression, Source context) throws XPathException {
+ Double result = (Double) evaluate(expression, context, XPathConstants.NUMBER);
+ return result != null ? result.doubleValue() : Double.NaN;
+ }
+
+ public String evaluateAsString(String expression, Source context) throws XPathException {
+ return (String) evaluate(expression, context, XPathConstants.STRING);
+ }
+
+ public Object evaluateAsObject(String expression, Source context, NodeMapper nodeMapper) throws XPathException {
+ Node node = evaluateAsNode(expression, context);
+ if (node != null) {
+ try {
+ return nodeMapper.mapNode(node, 0);
+ }
+ catch (DOMException ex) {
+ throw new XPathException("Mapping resulted in DOMException", ex);
+ }
+ }
+ else {
+ return null;
+ }
+ }
+
+ public List evaluate(String expression, Source context, NodeMapper nodeMapper) throws XPathException {
+ NodeList nodes = (NodeList) evaluate(expression, context, XPathConstants.NODESET);
+ List results = new ArrayList(nodes.getLength());
+ for (int i = 0; i < nodes.getLength(); i++) {
+ try {
+ results.add(nodeMapper.mapNode(nodes.item(i), i));
+ }
+ catch (DOMException ex) {
+ throw new XPathException("Mapping resulted in DOMException", ex);
+ }
+ }
+ return results;
+ }
+
+ private Object evaluate(String expression, Source context, QName returnType) throws XPathException {
+ XPath xpath = xpathFactory.newXPath();
+ if (getNamespaces() != null && !getNamespaces().isEmpty()) {
+ SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
+ namespaceContext.setBindings(getNamespaces());
+ xpath.setNamespaceContext(namespaceContext);
+ }
+ try {
+ if (context instanceof StaxSource) {
+ Element element = DomUtils.getRootElement(context, getTransformerFactory());
+ return xpath.evaluate(expression, element, returnType);
+ }
+ else if (context instanceof SAXSource) {
+ SAXSource saxSource = (SAXSource) context;
+ return xpath.evaluate(expression, saxSource.getInputSource(), returnType);
+ }
+ else if (context instanceof DOMSource) {
+ DOMSource domSource = (DOMSource) context;
+ return xpath.evaluate(expression, domSource.getNode(), returnType);
+ }
+ else if (context instanceof StreamSource) {
+ StreamSource streamSource = (StreamSource) context;
+ InputSource inputSource = null;
+ if (streamSource.getInputStream() != null) {
+ inputSource = new InputSource(streamSource.getInputStream());
+ }
+ else if (streamSource.getReader() != null) {
+ inputSource = new InputSource(streamSource.getReader());
+ }
+ else {
+ throw new IllegalArgumentException("StreamSource contains neither InputStream nor Reader");
+ }
+ return xpath.evaluate(expression, inputSource, returnType);
+ }
+ else {
+ throw new IllegalArgumentException("context type unknown");
+ }
+ }
+ catch (javax.xml.xpath.XPathException ex) {
+ throw new XPathException("Could not evaluate XPath expression [" + expression + "]", ex);
+ }
+ catch (TransformerException ex) {
+ throw new XPathException("Could not transform context to DOM Node", ex);
+ }
+ }
+
+}
diff --git a/xml/src/main/java/org/springframework/xml/xpath/NodeCallbackHandler.java b/xml/src/main/java/org/springframework/xml/xpath/NodeCallbackHandler.java
new file mode 100644
index 00000000..3462486c
--- /dev/null
+++ b/xml/src/main/java/org/springframework/xml/xpath/NodeCallbackHandler.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+
+/**
+ * An interface used by {@link XPathOperations} implementations for processing {@link Node} objects on a per-node basis.
+ * Implementations of this interface perform the actual work of processing nodes, but don't need to worry about
+ * exception handling.
+ *
+ * Consider using a {@link NodeMapper} instead if you need to map exactly result object per node, assembling them in a
+ * List.
+ *
+ * @author Arjen Poutsma
+ * @see XPathOperations#evaluate(String,javax.xml.transform.Source,NodeCallbackHandler)
+ */
+public interface NodeCallbackHandler {
+
+ /**
+ * Processed a single node.
+ *
+ * @param node the node to map
+ * @throws DOMException in case of DOM errors
+ */
+ void processNode(Node node) throws DOMException;
+
+}
diff --git a/xml/src/main/java/org/springframework/xml/xpath/NodeMapper.java b/xml/src/main/java/org/springframework/xml/xpath/NodeMapper.java
new file mode 100644
index 00000000..7cf35b19
--- /dev/null
+++ b/xml/src/main/java/org/springframework/xml/xpath/NodeMapper.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+
+/**
+ * An interface used by {@link XPathOperations} implementations for mapping {@link Node} objects on a per-node basis.
+ * Implementations of this interface perform the actual work of mapping each node to a result object, but don't need to
+ * worry about exception handling.
+ *
+ * @author Arjen Poutsma
+ * @see XPathOperations#evaluate(String,javax.xml.transform.Source,NodeMapper)
+ * @see XPathOperations#evaluateAsObject(String,javax.xml.transform.Source,NodeMapper)
+ */
+public interface NodeMapper {
+
+ /**
+ * Maps a single node to an arbitrary object.
+ *
+ * @param node the node to map
+ * @param nodeNum the number of the current node
+ * @return object for the current node
+ * @throws DOMException in case of DOM errors
+ */
+ Object mapNode(Node node, int nodeNum) throws DOMException;
+
+}
diff --git a/xml/src/main/java/org/springframework/xml/xpath/XPathExpression.java b/xml/src/main/java/org/springframework/xml/xpath/XPathExpression.java
index 4ad7cb73..637b3e39 100644
--- a/xml/src/main/java/org/springframework/xml/xpath/XPathExpression.java
+++ b/xml/src/main/java/org/springframework/xml/xpath/XPathExpression.java
@@ -19,32 +19,46 @@ package org.springframework.xml.xpath;
import org.w3c.dom.Node;
/**
- * Defines the contract for a precompiled XPath expression. Concrete instances can be obtained through the
- * XPathExpressionFactory.
+ * Defines the contract for a precompiled XPath expression. Concrete instances can be obtained through the {@link
+ * XPathExpressionFactory}.
+ *
+ * Implementations of this interface are precompiled, and thus faster, but less flexible, than the XPath expressions
+ * used by {@link XPathOperations} implementations.
*
* @author Arjen Poutsma
- * @see XPathExpressionFactory
*/
public interface XPathExpression {
- /**
- * Evaluates the given expression as a Node. Returns the evaluation of the expression, or
- * null if it is invalid.
- *
- * @param node the starting point
- * @return the result of the evaluation
- */
- Node evaluateAsNode(Node node);
-
/**
* Evaluates the given expression as a boolean. Returns the boolean evaluation of the expression, or
* false if it is invalid.
*
* @param node the starting point
* @return the result of the evaluation
+ * @see XPath specification
*/
boolean evaluateAsBoolean(Node node);
+ /**
+ * Evaluates the given expression as a Node. Returns the evaluation of the expression, or
+ * null if it is invalid.
+ *
+ * @param node the starting point
+ * @return the result of the evaluation
+ * @see XPath specification
+ */
+ Node evaluateAsNode(Node node);
+
+ /**
+ * Evaluates the given expression, and returns all Nodes that conform to it. Returns and empty array if
+ * no result could be found.
+ *
+ * @param node the starting point
+ * @return the Nodes that are selected by the expression
+ * @see XPath specification
+ */
+ Node[] evaluateAsNodes(Node node);
+
/**
* Evaluates the given expression as a number (double). Returns the numeric evaluation of the
* expression, or Double.NaN if it is invalid.
@@ -52,6 +66,7 @@ public interface XPathExpression {
* @param node the starting point
* @return the result of the evaluation
* @see Double#NaN
+ * @see XPath specification
*/
double evaluateAsNumber(Node node);
@@ -61,15 +76,7 @@ public interface XPathExpression {
*
* @param node the starting point
* @return the first Node that is selected by the expression
+ * @see XPath specification
*/
String evaluateAsString(Node node);
-
- /**
- * Evaluates the given expression, and returns all Nodes that conform to it. Returns and empty array if
- * no result could be found.
- *
- * @param node the starting point
- * @return the Nodes that are selected by the expression
- */
- Node[] evaluateAsNodes(Node node);
}
diff --git a/xml/src/main/java/org/springframework/xml/xpath/XPathOperations.java b/xml/src/main/java/org/springframework/xml/xpath/XPathOperations.java
new file mode 100644
index 00000000..2dabf5a9
--- /dev/null
+++ b/xml/src/main/java/org/springframework/xml/xpath/XPathOperations.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import java.util.List;
+import javax.xml.transform.Source;
+
+import org.w3c.dom.Node;
+
+/**
+ * Interface that specifies a basic set of XPath operations, implemented by various XPathTemplates. Contains numerous
+ * evaluation methods,
+ *
+ * The templates that implement this interface do not use precompiled XPath expressions. Consider using the {@link
+ * XPathExpressionFactory} or the {@link XPathExpressionFactoryBean} for optimal performance, but less flexibility.
+ *
+ * @author Arjen Poutsma
+ * @see Jaxp13XPathTemplate
+ * @see JaxenXPathTemplate
+ */
+public interface XPathOperations {
+
+ /**
+ * Evaluates the given expression as a boolean. Returns the boolean evaluation of the expression, or
+ * false if it is invalid.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @return the result of the evaluation
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ boolean evaluateAsBoolean(String expression, Source context) throws XPathException;
+
+ /**
+ * Evaluates the given expression as a {@link Node}. Returns the evaluation of the expression, or null
+ * if it is invalid.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @return the result of the evaluation
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ Node evaluateAsNode(String expression, Source context) throws XPathException;
+
+ /**
+ * Evaluates the given expression as a list of {@link Node} objects. Returns the evaluation of the expression, or an
+ * empty list if no results are found.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @return the result of the evaluation
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ List evaluateAsNodeList(String expression, Source context) throws XPathException;
+
+ /**
+ * Evaluates the given expression as a double. Returns the evaluation of the expression, or {@link
+ * Double#NaN} if it is invalid.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @return the result of the evaluation
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ double evaluateAsDouble(String expression, Source context) throws XPathException;
+
+ /**
+ * Evaluates the given expression as a {@link String}. Returns the evaluation of the expression, or
+ * null if it is invalid.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @return the result of the evaluation
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ String evaluateAsString(String expression, Source context) throws XPathException;
+
+ /**
+ * Evaluates the given expression, mapping a single {@link Node} result to a Java object via a {@link NodeMapper}.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @param nodeMapper object that will map one object per node
+ * @return the single mapped object
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ Object evaluateAsObject(String expression, Source context, NodeMapper nodeMapper) throws XPathException;
+
+ /**
+ * Evaluates the given expression, mapping each result {@link Node} objects to a Java object via a {@link
+ * NodeMapper}.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @param nodeMapper object that will map one object per node
+ * @return the result list, containing mapped objects
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ List evaluate(String expression, Source context, NodeMapper nodeMapper) throws XPathException;
+
+ /**
+ * Evaluates the given expression, handling the result {@link Node} objects on a per-node basis with a {@link
+ * NodeCallbackHandler}.
+ *
+ * @param expression the XPath expression
+ * @param context the context starting point
+ * @param callbackHandler object that will extract results, one row at a time
+ * @throws XPathException in case of XPath errors
+ * @see XPath specification
+ */
+ void evaluate(String expression, Source context, NodeCallbackHandler callbackHandler) throws XPathException;
+}
diff --git a/xml/src/main/java/org/springframework/xml/xpath/package.html b/xml/src/main/java/org/springframework/xml/xpath/package.html
index 3dfd1381..36e7692e 100644
--- a/xml/src/main/java/org/springframework/xml/xpath/package.html
+++ b/xml/src/main/java/org/springframework/xml/xpath/package.html
@@ -1,5 +1,5 @@
-Provides classes for XPath evaluation using JAXP 1.3, Jaxen, and Xalan. Mostly for internal use by the framework.
+Provides XPathTemplate implementations, and various classes for XPath evaluation using JAXP 1.3, Jaxen, and Xalan.
diff --git a/xml/src/test/java/org/springframework/xml/xpath/AbstractXPathTemplateTestCase.java b/xml/src/test/java/org/springframework/xml/xpath/AbstractXPathTemplateTestCase.java
new file mode 100644
index 00000000..dea21574
--- /dev/null
+++ b/xml/src/test/java/org/springframework/xml/xpath/AbstractXPathTemplateTestCase.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+
+import junit.framework.TestCase;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.xml.sax.SaxUtils;
+import org.springframework.xml.transform.ResourceSource;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+public abstract class AbstractXPathTemplateTestCase extends TestCase {
+
+ XPathOperations template;
+
+ private Source namespaces;
+
+ private Source nonamespaces;
+
+ protected final void setUp() throws Exception {
+ template = createTemplate();
+ namespaces = new ResourceSource(new ClassPathResource("namespaces.xml", AbstractXPathTemplateTestCase.class));
+ nonamespaces =
+ new ResourceSource(new ClassPathResource("nonamespaces.xml", AbstractXPathTemplateTestCase.class));
+ }
+
+ protected abstract XPathOperations createTemplate() throws Exception;
+
+ public void testEvaluateAsBoolean() {
+ boolean result = template.evaluateAsBoolean("/root/child/boolean", nonamespaces);
+ assertTrue("Invalid result", result);
+ }
+
+ public void testEvaluateAsBooleanNamespaces() {
+ boolean result = template.evaluateAsBoolean("/prefix1:root/prefix2:child/prefix2:boolean", namespaces);
+ assertTrue("Invalid result", result);
+ }
+
+ public void testEvaluateAsDouble() {
+ double result = template.evaluateAsDouble("/root/child/number", nonamespaces);
+ assertEquals("Invalid result", 42D, result, 0D);
+ }
+
+ public void testEvaluateAsDoubleNamespaces() {
+ double result = template.evaluateAsDouble("/prefix1:root/prefix2:child/prefix2:number", namespaces);
+ assertEquals("Invalid result", 42D, result, 0D);
+ }
+
+ public void testEvaluateAsNode() {
+ Node result = template.evaluateAsNode("/root/child", nonamespaces);
+ assertNotNull("Invalid result", result);
+ assertEquals("Invalid localname", "child", result.getLocalName());
+ }
+
+ public void testEvaluateAsNodeNamespaces() {
+ Node result = template.evaluateAsNode("/prefix1:root/prefix2:child", namespaces);
+ assertNotNull("Invalid result", result);
+ assertEquals("Invalid localname", "child", result.getLocalName());
+ }
+
+ public void testEvaluateAsNodes() {
+ List results = template.evaluateAsNodeList("/root/child/*", nonamespaces);
+ assertNotNull("Invalid result", results);
+ assertEquals("Invalid amount of results", 3, results.size());
+ }
+
+ public void testEvaluateAsNodesNamespaces() {
+ List results = template.evaluateAsNodeList("/prefix1:root/prefix2:child/*", namespaces);
+ assertNotNull("Invalid result", results);
+ assertEquals("Invalid amount of results", 3, results.size());
+ }
+
+ public void testEvaluateAsStringNamespaces() throws IOException, SAXException {
+ String result = template.evaluateAsString("/prefix1:root/prefix2:child/prefix2:text", namespaces);
+ assertEquals("Invalid result", "text", result);
+ }
+
+ public void testEvaluateAsString() throws IOException, SAXException {
+ String result = template.evaluateAsString("/root/child/text", nonamespaces);
+ assertEquals("Invalid result", "text", result);
+ }
+
+ public void testEvaluateDomSource() throws IOException, SAXException, ParserConfigurationException {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ documentBuilderFactory.setNamespaceAware(true);
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ Document document = documentBuilder.parse(SaxUtils.createInputSource(
+ new ClassPathResource("nonamespaces.xml", AbstractXPathTemplateTestCase.class)));
+
+ String result = template.evaluateAsString("/root/child/text", new DOMSource(document));
+ assertEquals("Invalid result", "text", result);
+ }
+
+ public void testEvaluateStreamSource() throws IOException, SAXException, ParserConfigurationException {
+ InputStream in = AbstractXPathTemplateTestCase.class.getResourceAsStream("nonamespaces.xml");
+ String result = template.evaluateAsString("/root/child/text", new StreamSource(in));
+ assertEquals("Invalid result", "text", result);
+ }
+
+ public void testInvalidExpression() {
+ try {
+ template.evaluateAsBoolean("\\", namespaces);
+ fail("No XPathException thrown");
+ }
+ catch (XPathException ex) {
+ // Expected behaviour
+ }
+ }
+
+ public void testEvaluateAsObject() throws Exception {
+ String result = (String) template.evaluateAsObject("/root/child", nonamespaces, new NodeMapper() {
+ public Object mapNode(Node node, int nodeNum) throws DOMException {
+ return node.getLocalName();
+ }
+ });
+ assertNotNull("Invalid result", result);
+ assertEquals("Invalid localname", "child", result);
+ }
+
+ public void testEvaluate() throws Exception {
+ List results = template.evaluate("/root/child/*", nonamespaces, new NodeMapper() {
+ public Object mapNode(Node node, int nodeNum) throws DOMException {
+ return node.getLocalName();
+ }
+ });
+ assertNotNull("Invalid result", results);
+ assertEquals("Invalid amount of results", 3, results.size());
+ assertEquals("Invalid first result", "text", results.get(0));
+ assertEquals("Invalid first result", "number", results.get(1));
+ assertEquals("Invalid first result", "boolean", results.get(2));
+ }
+}
diff --git a/xml/src/test/java/org/springframework/xml/xpath/JaxenXPathTemplateTest.java b/xml/src/test/java/org/springframework/xml/xpath/JaxenXPathTemplateTest.java
new file mode 100644
index 00000000..9b13636a
--- /dev/null
+++ b/xml/src/test/java/org/springframework/xml/xpath/JaxenXPathTemplateTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import java.util.Properties;
+
+public class JaxenXPathTemplateTest extends AbstractXPathTemplateTestCase {
+
+ protected XPathOperations createTemplate() throws Exception {
+ JaxenXPathTemplate template = new JaxenXPathTemplate();
+ Properties namespaces = new Properties();
+ namespaces.setProperty("prefix1", "namespace1");
+ namespaces.setProperty("prefix2", "namespace2");
+ template.setNamespaces(namespaces);
+ return template;
+ }
+
+}
\ No newline at end of file
diff --git a/xml/src/test/java/org/springframework/xml/xpath/Jaxp13XPathTemplateTest.java b/xml/src/test/java/org/springframework/xml/xpath/Jaxp13XPathTemplateTest.java
new file mode 100644
index 00000000..cea2f1b3
--- /dev/null
+++ b/xml/src/test/java/org/springframework/xml/xpath/Jaxp13XPathTemplateTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.xml.xpath;
+
+import java.util.Properties;
+
+public class Jaxp13XPathTemplateTest extends AbstractXPathTemplateTestCase {
+
+ protected XPathOperations createTemplate() throws Exception {
+ Jaxp13XPathTemplate template = new Jaxp13XPathTemplate();
+ Properties namespaces = new Properties();
+ namespaces.setProperty("prefix1", "namespace1");
+ namespaces.setProperty("prefix2", "namespace2");
+ template.setNamespaces(namespaces);
+ return template;
+ }
+
+}
\ No newline at end of file