1 package org.hamcrest.xml;
2 
3 import org.hamcrest.Description;
4 import org.hamcrest.Factory;
5 import org.hamcrest.Matcher;
6 import org.hamcrest.TypeSafeMatcher;
7 import org.w3c.dom.Node;
8 
9 import javax.xml.xpath.XPath;
10 import javax.xml.xpath.XPathConstants;
11 import javax.xml.xpath.XPathExpression;
12 import javax.xml.xpath.XPathExpressionException;
13 import javax.xml.xpath.XPathFactory;
14 
15 /**
16  * Applies a Matcher to a given XML Node in an existing XML Node tree, specified by an XPath expression.
17  *
18  * @author Joe Walnes
19  */
20 public class HasXPath extends TypeSafeMatcher<Node> {
21 
22     private final Matcher<String> valueMatcher;
23     private final XPathExpression compiledXPath;
24     private final String xpathString;
25 
26     /**
27      * @param xPathExpression XPath expression.
28      * @param valueMatcher Matcher to use at given XPath.
29      *                     May be null to specify that the XPath must exist but the value is irrelevant.
30      */
HasXPath(String xPathExpression, Matcher<String> valueMatcher)31     public HasXPath(String xPathExpression, Matcher<String> valueMatcher) {
32         try {
33             XPath xPath = XPathFactory.newInstance().newXPath();
34             compiledXPath = xPath.compile(xPathExpression);
35             this.xpathString = xPathExpression;
36             this.valueMatcher = valueMatcher;
37         } catch (XPathExpressionException e) {
38             throw new IllegalArgumentException("Invalid XPath : " + xPathExpression, e);
39         }
40     }
41 
matchesSafely(Node item)42     public boolean matchesSafely(Node item) {
43         try {
44             String result = (String) compiledXPath.evaluate(item, XPathConstants.STRING);
45             if (result == null) {
46                 return false;
47             } else if (valueMatcher == null) {
48                 return !result.equals("");
49             } else {
50                 return valueMatcher.matches(result);
51             }
52         } catch (XPathExpressionException e) {
53             return false;
54         }
55     }
56 
describeTo(Description description)57     public void describeTo(Description description) {
58         description.appendText("an XML document with XPath ").appendText(xpathString);
59         if (valueMatcher != null) {
60             description.appendText(" ").appendDescriptionOf(valueMatcher);
61         }
62     }
63 
64     @Factory
hasXPath(String xPath, Matcher<String> valueMatcher)65     public static Matcher<Node> hasXPath(String xPath, Matcher<String> valueMatcher) {
66         return new HasXPath(xPath, valueMatcher);
67     }
68 
69     @Factory
hasXPath(String xPath)70     public static Matcher<Node> hasXPath(String xPath) {
71         return hasXPath(xPath, null);
72     }
73 
74 }
75