From 6162029215f7afc5c1554435852df56c1c42db9a Mon Sep 17 00:00:00 2001 From: erik Date: Fri, 4 Mar 2011 22:34:04 +0000 Subject: [PATCH] Added XPath utilities and updated the javadocs. --- .../wamblee/xml/SimpleNamespaceContext.java | 4 + .../java/org/wamblee/xml/XPathContext.java | 59 ++++++++ .../java/org/wamblee/xml/XPathExpression.java | 131 ++++++++++++++++++ .../java/org/wamblee/xml/package-info.java | 62 ++++++++- .../org/wamblee/xml/XPathExpressionTest.java | 82 +++++++++++ .../java/org/wamblee/xml/XmlDocumentTest.java | 1 + .../org/wamblee/xml/xpathexample.xml | 7 + 7 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 support/general/src/main/java/org/wamblee/xml/XPathContext.java create mode 100644 support/general/src/main/java/org/wamblee/xml/XPathExpression.java create mode 100644 support/general/src/test/java/org/wamblee/xml/XPathExpressionTest.java create mode 100644 support/general/src/test/resources/org/wamblee/xml/xpathexample.xml diff --git a/support/general/src/main/java/org/wamblee/xml/SimpleNamespaceContext.java b/support/general/src/main/java/org/wamblee/xml/SimpleNamespaceContext.java index 7e8092a9..d5ff9498 100644 --- a/support/general/src/main/java/org/wamblee/xml/SimpleNamespaceContext.java +++ b/support/general/src/main/java/org/wamblee/xml/SimpleNamespaceContext.java @@ -48,6 +48,10 @@ public class SimpleNamespaceContext implements NamespaceContext { defaultNs = null; prefixMap = new HashMap(); } + + public static SimpleNamespaceContext namespaces() { + return new SimpleNamespaceContext(); + } /** * Constructs a context with a single prefix. diff --git a/support/general/src/main/java/org/wamblee/xml/XPathContext.java b/support/general/src/main/java/org/wamblee/xml/XPathContext.java new file mode 100644 index 00000000..42fa99d4 --- /dev/null +++ b/support/general/src/main/java/org/wamblee/xml/XPathContext.java @@ -0,0 +1,59 @@ +/* + * Copyright 2005-2011 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.wamblee.xml; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; + +/** + * Represents a namespace context and is a factory for creating xpath expressions. + * @author Erik Brakkee + */ +public class XPathContext { + private XPath xpath; + + /** + * Constructs the context. + * @param aContext Namespaces. + */ + public XPathContext(NamespaceContext aContext) { + xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(aContext); + } + + public static XPathContext xpathcontext(NamespaceContext aContext) { + return new XPathContext(aContext); + } + + /** + * Creates the expression. + * @param aExpression XPath expression. + * @return Xpath expression. + * @throws XMLException + */ + public XPathExpression createExpression(String aExpression) throws XMLException { + return new XPathExpression(xpath, aExpression); + } + + /** + * To get access to the lower level API. + * @return Lower level API. + */ + public XPath getXpath() { + return xpath; + } +} diff --git a/support/general/src/main/java/org/wamblee/xml/XPathExpression.java b/support/general/src/main/java/org/wamblee/xml/XPathExpression.java new file mode 100644 index 00000000..fa1e4ede --- /dev/null +++ b/support/general/src/main/java/org/wamblee/xml/XPathExpression.java @@ -0,0 +1,131 @@ +/* + * Copyright 2005-2011 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.wamblee.xml; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * XPath expression. + * + * @author Erik Brakkee + */ +public class XPathExpression { + private String expressionString; + private javax.xml.xpath.XPathExpression expression; + + /** + * Constructs the expression. Users typically use {@link XPathContext#createExpression(String)} instead + * of this constructor. + * + * @param aXpath Xpath interface. + * @param aExpression Expression. + * @throws XMLException In case of problems. + */ + public XPathExpression(XPath aXpath, String aExpression) + throws XMLException { + expressionString = aExpression; + try { + expression = aXpath.compile(aExpression); + } catch (XPathExpressionException e) { + throw new XMLException("Could not compile xpath expression '" + + aExpression + "'", e); + } + } + + /** + * Evaluates the xpath expression to a string. + * @param aDocument Document + * @return Value. + * @throws XMLException In case of problems. + */ + public String stringEval(XMLDocument aDocument) throws XMLException { + return (String)evaluateImpl(aDocument, XPathConstants.STRING); + } + + /** + * Evaluates the xpath expression to a boolean. + * @param aDocument Document + * @return Value. + * @throws XMLException In case of problems. + */ + public boolean booleanEval(XMLDocument aDocument) throws XMLException { + return (Boolean)evaluateImpl(aDocument, XPathConstants.BOOLEAN); + } + + /** + * Evaluates the xpath expression to a node list. + * @param aDocument Document + * @return Value. + * @throws XMLException In case of problems. + */ + public NodeList nodelistEval(XMLDocument aDocument) throws XMLException { + return (NodeList)evaluateImpl(aDocument, XPathConstants.NODESET); + } + + /** + * Evaluates the xpath expression to a Node. + * @param aDocument Document + * @return Value. + * @throws XMLException In case of problems. + */ + public Node nodeEval(XMLDocument aDocument) throws XMLException { + return (Node)evaluateImpl(aDocument, XPathConstants.NODE); + } + + /** + * Evaluates the xpath expression to a number. + * @param aDocument Document + * @return Value. + * @throws XMLException In case of problems. + */ + public Double numberEval(XMLDocument aDocument) throws XMLException { + return (Double)evaluateImpl(aDocument, XPathConstants.NUMBER); + } + + /** + * @return Low-level xpath expression object. + */ + public javax.xml.xpath.XPathExpression getExpression() { + return expression; + } + + /** + * @return XPath expression. + */ + public String getExpressionString() { + return expressionString; + } + + + private Object evaluateImpl(XMLDocument aDocument, QName aResultType) throws XMLException { + try { + return expression.evaluate(aDocument.getDocument(), + aResultType); + } catch (XPathExpressionException e) { + throw new XMLException("Problem evaluating expression '" + + expressionString + "' on document '" + aDocument.print(true) + + "'", e); + } + } + +} diff --git a/support/general/src/main/java/org/wamblee/xml/package-info.java b/support/general/src/main/java/org/wamblee/xml/package-info.java index 2aa6c822..8c4b9c84 100644 --- a/support/general/src/main/java/org/wamblee/xml/package-info.java +++ b/support/general/src/main/java/org/wamblee/xml/package-info.java @@ -14,7 +14,67 @@ * limitations under the License. */ /** - * Utilities for XML processing. + *

+ * Utilities for XML processing. The aim of this package is to simplify the + * common tasks of parsing, validating, and transforming XML files and to + * provide support for XPath. The utlities simply use the standard Java SE APIs + * but are much easier to use. For cases where more advanced functionality is required, + * the classes provide access to the underlying Java SE types. The implementation is + * based on DOM level 3 parsing. + *

+ * + *

+ * Classes {@link org.wamblee.xml.XMLDocument}, {@link org.wamblee.xml.XMLSchema}, and + * {@link org.wamblee.xml.XSLTransformation} provide parsing, validation, and transformation + * of XML files. The {@link org.wamblee.xml.XMLDocument} class provides various static + * methods as entry point for this functionality, simplifying the code a lot when + * static imports are used. The API design uses a fluent interfaces style. For instance, + * to parse, validate, and transform an XML file, one can write: + *

+ *
+ * import static org.wamblee.xml.XMLDocument;
+ * ...
+ * XMLDocument doc = xmldocument(new File("x.xml").toURI()).validate(new File("x.xsd").toURI()).transform(new File("x.xsl").toURI());
+ * 
+ * + *

+ * In addition, a URI resolver {@link org.wamblee.xml.ClasspathUriResolver} is provided to allow resolution of + * documents on the classpath. + *

+ * + *

+ * For XPath the following classes are provided: + *

+ * + * f + *

+ * For instance to apply an XPath expression to an XML document: + *

+ *
+ *      NamespaceContext context = new XPathContext(new SimpleNamespaceContext()
+ *            .addPrefix("n", "http://example.com/1")
+ *            .addPrefix("m", "http://example.com/2"));
+ *      XMLDocument doc = new XMLDocument(new File("xpathexample.xml").toURI());
+ *      String value = context.createExpression("/n:root/m:x").stringEval(doc);
+ * 
+ *

+ * Or, using static imports: + *

+ *
+ *      NamespaceContext context = xpathcontext(namespaces()
+ *            .addPrefix("n", "http://example.com/1")
+ *            .addPrefix("m", "http://example.com/2"));
+ *      XMLDocument doc = xmldocument(new File("xpathexample.xml").toURI());
+ *      String value = context.createExpression("/n:root/m:x").stringEval(doc);
+ * 
*/ package org.wamblee.xml; diff --git a/support/general/src/test/java/org/wamblee/xml/XPathExpressionTest.java b/support/general/src/test/java/org/wamblee/xml/XPathExpressionTest.java new file mode 100644 index 00000000..3ff0df09 --- /dev/null +++ b/support/general/src/test/java/org/wamblee/xml/XPathExpressionTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 2005-2011 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.wamblee.xml; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.junit.Before; +import org.junit.Test; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import static junit.framework.TestCase.*; +import static org.wamblee.xml.XMLDocument.*; +import static org.wamblee.xml.XPathContext.*; +import static org.wamblee.xml.SimpleNamespaceContext.*; + +public class XPathExpressionTest { + + private XPathContext context; + private XMLDocument doc; + + @Before + public void setUp() throws XMLException, URISyntaxException, IOException { + context = xpathcontext(namespaces().addPrefix("n", "http://example.com/1").addPrefix("m", "http://example.com/2")); + doc = xmldocument(getClass().getResource("xpathexample.xml").toURI()); + } + + @Test(expected = XMLException.class) + public void testInvalidXpath() throws XMLException { + context.createExpression("a["); + } + + @Test + public void testStringVal() throws XMLException { + String value = context.createExpression("/n:root/m:x").stringEval(doc); + assertEquals("hallo", value.trim()); + } + + @Test + public void testBooleanVal() throws XMLException { + assertTrue(context.createExpression("/n:root/m:x").booleanEval(doc)); + assertFalse(context.createExpression("unknownelement").booleanEval(doc)); + } + + @Test + public void testNummberVal() throws XMLException { + assertEquals(1.0d, context.createExpression("count(/n:root/m:x)").numberEval(doc)); + assertEquals(11.0d, context.createExpression("//n:elem").numberEval(doc)); + } + + @Test + public void testNodeVal() throws XMLException { + Node node = context.createExpression("/n:root/n:elem").nodeEval(doc); + assertTrue(node instanceof Element); + assertEquals("11", node.getTextContent().trim()); + assertEquals("elem", node.getLocalName()); + assertEquals("http://example.com/1", node.getNamespaceURI()); + } + + @Test + public void testNodeSetVal() throws XMLException { + NodeList nodelist = context.createExpression("/n:root/n:elem").nodelistEval(doc); + assertEquals(2, nodelist.getLength()); + assertEquals("11", nodelist.item(0).getTextContent().trim()); + assertEquals("22", nodelist.item(1).getTextContent().trim()); + } +} diff --git a/support/general/src/test/java/org/wamblee/xml/XmlDocumentTest.java b/support/general/src/test/java/org/wamblee/xml/XmlDocumentTest.java index 6e30e5fd..2c5f385b 100644 --- a/support/general/src/test/java/org/wamblee/xml/XmlDocumentTest.java +++ b/support/general/src/test/java/org/wamblee/xml/XmlDocumentTest.java @@ -17,6 +17,7 @@ package org.wamblee.xml; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; diff --git a/support/general/src/test/resources/org/wamblee/xml/xpathexample.xml b/support/general/src/test/resources/org/wamblee/xml/xpathexample.xml new file mode 100644 index 00000000..f11b763c --- /dev/null +++ b/support/general/src/test/resources/org/wamblee/xml/xpathexample.xml @@ -0,0 +1,7 @@ + + + hallo + + 11 + 22 + \ No newline at end of file -- 2.31.1