Removed the Xalan XPathExpression, since Xalan *is* JAXP 1.3. Added NodeMapper functionality to XPathExpression.

This commit is contained in:
Arjen Poutsma
2007-04-18 22:49:47 +00:00
parent e1fd757336
commit 3b1841fb44
11 changed files with 175 additions and 272 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.xml.xpath;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -23,6 +24,7 @@ import org.jaxen.JaxenException;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.XPath;
import org.jaxen.dom.DOMXPath;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
/**
@@ -71,9 +73,7 @@ abstract class JaxenXPathExpressionFactory {
}
}
/**
* Jaxen implementation of the <code>XPathExpression</code> interface.
*/
/** Jaxen implementation of the <code>XPathExpression</code> interface. */
private static class JaxenXpathExpression implements XPathExpression {
private XPath xpath;
@@ -118,10 +118,49 @@ abstract class JaxenXPathExpressionFactory {
}
}
public Node[] evaluateAsNodes(Node node) {
public List evaluateAsNodeList(Node node) {
try {
List result = xpath.selectNodes(node);
return (Node[]) result.toArray(new Node[result.size()]);
return xpath.selectNodes(node);
}
catch (JaxenException ex) {
throw new XPathException("Could not evaluate XPath expression [" + xpath + "] :" + ex.getMessage(), ex);
}
}
public Object evaluateAsObject(Node context, NodeMapper nodeMapper) throws XPathException {
try {
Node result = (Node) xpath.selectSingleNode(context);
if (result != null) {
try {
return nodeMapper.mapNode(result, 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 [" + xpath + "] :" + ex.getMessage(), ex);
}
}
public List evaluate(Node context, NodeMapper nodeMapper) throws XPathException {
try {
List nodes = xpath.selectNodes(context);
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 [" + xpath + "] :" + ex.getMessage(), ex);

View File

@@ -16,6 +16,8 @@
package org.springframework.xml.xpath;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
@@ -24,6 +26,7 @@ import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.springframework.xml.namespace.SimpleNamespaceContext;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -96,9 +99,9 @@ abstract class Jaxp13XPathExpressionFactory {
return (String) evaluate(node, XPathConstants.STRING);
}
public Node[] evaluateAsNodes(Node node) {
public List evaluateAsNodeList(Node node) {
NodeList nodeList = (NodeList) evaluate(node, XPathConstants.NODESET);
return toNodeArray(nodeList);
return toNodeList(nodeList);
}
private Object evaluate(Node node, QName returnType) {
@@ -113,10 +116,10 @@ abstract class Jaxp13XPathExpressionFactory {
}
}
private Node[] toNodeArray(NodeList nodeList) {
Node[] result = new Node[nodeList.getLength()];
private List toNodeList(NodeList nodeList) {
List result = new ArrayList(nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++) {
result[i] = nodeList.item(i);
result.add(nodeList.item(i));
}
return result;
}
@@ -134,6 +137,35 @@ abstract class Jaxp13XPathExpressionFactory {
public Node evaluateAsNode(Node node) {
return (Node) evaluate(node, XPathConstants.NODE);
}
public Object evaluateAsObject(Node node, NodeMapper nodeMapper) throws XPathException {
Node result = (Node) evaluate(node, XPathConstants.NODE);
if (result != null) {
try {
return nodeMapper.mapNode(result, 0);
}
catch (DOMException ex) {
throw new XPathException("Mapping resulted in DOMException", ex);
}
}
else {
return null;
}
}
public List evaluate(Node node, NodeMapper nodeMapper) throws XPathException {
NodeList nodes = (NodeList) evaluate(node, 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;
}
}
}

View File

@@ -27,6 +27,8 @@ import org.w3c.dom.Node;
* @author Arjen Poutsma
* @see XPathOperations#evaluate(String,javax.xml.transform.Source,NodeMapper)
* @see XPathOperations#evaluateAsObject(String,javax.xml.transform.Source,NodeMapper)
* @see XPathExpression#evaluate(org.w3c.dom.Node,NodeMapper)
* @see XPathExpression#evaluateAsObject(org.w3c.dom.Node,NodeMapper)
*/
public interface NodeMapper {

View File

@@ -16,6 +16,8 @@
package org.springframework.xml.xpath;
import java.util.List;
import org.w3c.dom.Node;
/**
@@ -35,48 +37,74 @@ public interface XPathExpression {
*
* @param node the starting point
* @return the result of the evaluation
* @throws XPathException in case of XPath errors
* @see <a href="http://www.w3.org/TR/xpath#booleans">XPath specification</a>
*/
boolean evaluateAsBoolean(Node node);
boolean evaluateAsBoolean(Node node) throws XPathException;
/**
* Evaluates the given expression as a <code>Node</code>. Returns the evaluation of the expression, or
* <code>null</code> if it is invalid.
* Evaluates the given expression as a {@link Node}. Returns the evaluation of the expression, or <code>null</code>
* if it is invalid.
*
* @param node the starting point
* @return the result of the evaluation
* @throws XPathException in case of XPath errors
* @see <a href="http://www.w3.org/TR/xpath#node-sets">XPath specification</a>
*/
Node evaluateAsNode(Node node);
Node evaluateAsNode(Node node) throws XPathException;
/**
* Evaluates the given expression, and returns all <code>Node</code>s that conform to it. Returns and empty array if
* Evaluates the given expression, and returns all {@link Node} objects that conform to it. Returns an empty list if
* no result could be found.
*
* @param node the starting point
* @return the <code>Node</code>s that are selected by the expression
* @return a list of <code>Node</code>s that are selected by the expression
* @throws XPathException in case of XPath errors
* @see <a href="http://www.w3.org/TR/xpath#node-sets">XPath specification</a>
*/
Node[] evaluateAsNodes(Node node);
List evaluateAsNodeList(Node node) throws XPathException;
/**
* Evaluates the given expression as a number (<code>double</code>). Returns the numeric evaluation of the
* expression, or <code>Double.NaN</code> if it is invalid.
* expression, or {@link Double#NaN} if it is invalid.
*
* @param node the starting point
* @return the result of the evaluation
* @see Double#NaN
* @throws XPathException in case of XPath errors
* @see <a href="http://www.w3.org/TR/xpath#numbers">XPath specification</a>
*/
double evaluateAsNumber(Node node);
double evaluateAsNumber(Node node) throws XPathException;
/**
* Evaluates the given expression, and returns the first <code>Node</code> that conforms to it. Returns
* <code>null</code> if no result could be found.
* Evaluates the given expression as a String. Returns <code>null</code> if no result could be found.
*
* @param node the starting point
* @return the first <code>Node</code> that is selected by the expression
* @return the result of the evaluation
* @throws XPathException in case of XPath errors
* @see <a href="http://www.w3.org/TR/xpath#strings">XPath specification</a>
*/
String evaluateAsString(Node node);
String evaluateAsString(Node node) throws XPathException;
/**
* Evaluates the given expression, mapping a single {@link Node} result to a Java object via a {@link NodeMapper}.
*
* @param node the 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 <a href="http://www.w3.org/TR/xpath#node-sets">XPath specification</a>
*/
Object evaluateAsObject(Node node, NodeMapper nodeMapper) throws XPathException;
/**
* Evaluates the given expression, mapping each result {@link Node} objects to a Java object via a {@link
* NodeMapper}.
*
* @param node the 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 <a href="http://www.w3.org/TR/xpath#node-sets">XPath specification</a>
*/
List evaluate(Node node, NodeMapper nodeMapper) throws XPathException;
}

View File

@@ -26,11 +26,11 @@ import org.springframework.util.ClassUtils;
import org.springframework.xml.JaxpVersion;
/**
* Factory for compiled <code>XPathExpression</code>s, being aware of JAXP 1.3+ XPath functionality, Jaxen, and Xalan.
* Mainly for internal use of the framework.
* Factory for compiled <code>XPathExpression</code>s, being aware of JAXP 1.3+ XPath functionality, and Jaxen. Mainly
* for internal use of the framework.
* <p/>
* The goal of this class is to avoid runtime dependencies a specific XPath engine, simply using the best XPath
* implementation that is available. Prefers JAXP 1.3+ XPath implementations to Jaxen, and falls back to Xalan.
* implementation that is available. Prefers JAXP 1.3+ XPath implementations to Jaxen.
*
* @author Arjen Poutsma
* @see XPathExpression
@@ -39,16 +39,12 @@ public abstract class XPathExpressionFactory {
private static final Log logger = LogFactory.getLog(XPathExpressionFactory.class);
private static final String XALAN_XPATH_CLASS_NAME = "org.apache.xpath.XPath";
private static boolean xalanXPathAvailable;
private static final String JAXEN_CLASS_NAME = "org.jaxen.XPath";
private static boolean jaxenAvailable;
static {
// Check whether JAXP 1.3, Resin, Jaxen, or Xalan are available
// Check whether JAXP 1.3, or Jaxen are available
if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) {
logger.info("JAXP 1.3 available");
}
@@ -60,14 +56,6 @@ public abstract class XPathExpressionFactory {
catch (ClassNotFoundException ex) {
jaxenAvailable = false;
}
try {
ClassUtils.forName(XALAN_XPATH_CLASS_NAME);
xalanXPathAvailable = true;
logger.info("Xalan available");
}
catch (ClassNotFoundException ex) {
xalanXPathAvailable = false;
}
}
/**
@@ -75,7 +63,7 @@ public abstract class XPathExpressionFactory {
*
* @param expression the XPath expression
* @return the compiled XPath expression
* @throws IllegalStateException if neither JAXP 1.3+, Jaxen, or Xalan are available
* @throws IllegalStateException if neither JAXP 1.3+, or Jaxen are available
* @throws XPathParseException if the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression)
@@ -90,7 +78,7 @@ public abstract class XPathExpressionFactory {
* @param expression the XPath expression
* @param namespaces a map that binds string prefixes to string namespaces
* @return the compiled XPath expression
* @throws IllegalStateException if neither JAXP 1.3+, Jaxen, or Xalan are available
* @throws IllegalStateException if neither JAXP 1.3+, or Jaxen are available
* @throws XPathParseException if the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression, Map namespaces)
@@ -104,13 +92,9 @@ public abstract class XPathExpressionFactory {
logger.debug("Creating [org.jaxen.XPath]");
return JaxenXPathExpressionFactory.createXPathExpression(expression, namespaces);
}
else if (xalanXPathAvailable) {
logger.debug("Creating [org.apache.xpath.XPath]");
return XalanXPathExpressionFactory.createXPathExpression(expression, namespaces);
}
else {
throw new IllegalStateException(
"Could not create XPathExpression: could not locate JAXP 1.3, Resin, Xalan on the class path");
"Could not create XPathExpression: could not locate JAXP 1.3, or Jaxen on the class path");
}
}

View File

@@ -26,7 +26,7 @@ import org.springframework.util.Assert;
* Spring {@link FactoryBean} for {@link XPathExpression} object. Facilitates injection of XPath expressions into
* endpoint beans.
* <p/>
* Uses {@link XPathExpressionFactory} underneath, so support is provided for JAXP 1.3, Jaxen, and Xalan XPaths.
* Uses {@link XPathExpressionFactory} underneath, so support is provided for JAXP 1.3, and Jaxen XPaths.
*
* @author Arjen Poutsma
* @see #setExpression(String)

View File

@@ -1,173 +0,0 @@
/*
* Copyright 2006 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.Map;
import javax.xml.transform.TransformerException;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XObject;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.NodeIterator;
/**
* Xalan-specific factory creating <code>XPathExpression</code>s.
*
* @author Arjen Poutsma
* @see #createXPathExpression(String)
*/
abstract class XalanXPathExpressionFactory {
/**
* Creates a Xalan <code>XPathExpression</code> from the given string expression.
*
* @param expression the XPath expression
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
static XPathExpression createXPathExpression(String expression) {
try {
XPath xpath = new XPath(expression, null, null, XPath.SELECT);
return new XalanXPathExpression(xpath);
}
catch (TransformerException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Creates a Xalan <code>XPathExpression</code> from the given string expression and namespaces.
*
* @param expression the XPath expression
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression, Map namespaces) {
try {
PrefixResolver prefixResolver = new SimplePrefixResolver(namespaces);
XPath xpath = new XPath(expression, null, prefixResolver, XPath.SELECT);
return new XalanXPathExpression(xpath);
}
catch (TransformerException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Xalan implementation of the <code>XPathExpression</code> interface.
*/
private static class XalanXPathExpression implements XPathExpression {
private XPath xpath;
private XPathContext context = new XPathContext();
private XalanXPathExpression(XPath xpath) {
this.xpath = xpath;
}
public Node evaluateAsNode(Node node) {
try {
XObject result = xpath.execute(context, node, null);
NodeIterator iterator = result.nodeset();
return iterator.nextNode();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public boolean evaluateAsBoolean(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return result.bool();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public double evaluateAsNumber(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return result.num();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public String evaluateAsString(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return result.str();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public Node[] evaluateAsNodes(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return toNodeArray(result.nodelist());
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
private Node[] toNodeArray(NodeList nodeList) {
Node[] result = new Node[nodeList.getLength()];
for (int i = 0; i < nodeList.getLength(); i++) {
result[i] = nodeList.item(i);
}
return result;
}
}
private static class SimplePrefixResolver implements PrefixResolver {
private Map namespaces;
private SimplePrefixResolver(Map namespaces) {
this.namespaces = namespaces;
}
public String getNamespaceForPrefix(String prefix) {
return (String) namespaces.get(prefix);
}
public String getNamespaceForPrefix(String prefix, Node node) {
return (String) namespaces.get(prefix);
}
public String getBaseIdentifier() {
return null;
}
public boolean handlesNullPrefixes() {
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
<html>
<body>
Provides XPathTemplate implementations, and various classes for XPath evaluation using JAXP 1.3, Jaxen, and Xalan.
Provides XPathTemplate implementations, and various classes for XPath evaluation using JAXP 1.3, and Jaxen.
</body>
</html>