Added XPath utilities and updated the javadocs.
authorErik Brakkee <erik@brakkee.org>
Fri, 4 Mar 2011 22:34:04 +0000 (22:34 +0000)
committerErik Brakkee <erik@brakkee.org>
Fri, 4 Mar 2011 22:34:04 +0000 (22:34 +0000)
support/general/src/main/java/org/wamblee/xml/SimpleNamespaceContext.java
support/general/src/main/java/org/wamblee/xml/XPathContext.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/xml/XPathExpression.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/xml/package-info.java
support/general/src/test/java/org/wamblee/xml/XPathExpressionTest.java [new file with mode: 0644]
support/general/src/test/java/org/wamblee/xml/XmlDocumentTest.java
support/general/src/test/resources/org/wamblee/xml/xpathexample.xml [new file with mode: 0644]

index 7e8092a980b7f2197564b4455b61dcc5bbb10d39..d5ff949828f59796d7ac93cf346bbf045d978bb9 100644 (file)
@@ -48,6 +48,10 @@ public class SimpleNamespaceContext implements NamespaceContext {
         defaultNs = null;
         prefixMap = new HashMap<String, String>();
     }
+    
+    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 (file)
index 0000000..42fa99d
--- /dev/null
@@ -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 (file)
index 0000000..fa1e4ed
--- /dev/null
@@ -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);
+        }
+    }
+    
+}
index 2aa6c822d9ed4ba4de0ee24d6f2cc7efca39b4b6..8c4b9c842e58fbdbbdb616a576db649e6aff2ec4 100644 (file)
  * limitations under the License.
  */
 /**
- * Utilities for XML processing.
+ * <p>
+ * 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.  
+ * </p>
+ * 
+ * <p>
+ * 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:
+ * </p>
+ * <pre>
+ * 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());
+ * </pre>
+ * 
+ * <p>
+ * In addition, a URI resolver {@link org.wamblee.xml.ClasspathUriResolver} is provided to allow resolution of 
+ * documents on the classpath. 
+ * </p>
+ * 
+ * <p>
+ * For XPath the following classes are provided: 
+ * </p>
+ * <ul>
+ *   <li> {@link org.wamblee.xml.SimpleNamespaceContext}: An generic implementation of {@link javax.xml.namespace.NamespaceContext}.
+ *   </li>
+ *   <li> {@link org.wamblee.xml.XPathContext}: A factory of <code>XPathExpression</code> objects based on a given namespace
+ *     context. 
+ *   </li>
+ *   <li> {@link org.wamblee.xml.XPathExpression}: The interface for working with XPath. 
+ *   </li>
+ * </ul>
+ * f
+ * <p>
+ * For instance to apply an XPath expression to an XML document: 
+ * </p>
+ * <pre>
+ *      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);
+ * </pre>
+ * <p>
+ * Or, using static imports: 
+ * </p>
+ *  <pre>
+ *      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);
+ * </pre>
  */
 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 (file)
index 0000000..3ff0df0
--- /dev/null
@@ -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());
+    }
+}
index 6e30e5fd3f0fcb90a45b0434789a5afd9874309b..2c5f385b81888f06651e34750a0d77b279d5c66c 100644 (file)
@@ -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 (file)
index 0000000..f11b763
--- /dev/null
@@ -0,0 +1,7 @@
+<n:root xmlns:n="http://example.com/1">
+    <m:x xmlns:m="http://example.com/2">
+        hallo
+    </m:x>
+    <n:elem>11</n:elem>
+    <n:elem>22</n:elem>
+</n:root>
\ No newline at end of file