Skip to content

Commit 10895d1

Browse files
author
neildunn
committed
Bug fix for matching empty elements
1 parent c97fdf2 commit 10895d1

File tree

2 files changed

+60
-39
lines changed

2 files changed

+60
-39
lines changed

hamcrest-library/src/main/java/org/hamcrest/xml/HasXPath.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.w3c.dom.Node;
88

99
import javax.xml.namespace.NamespaceContext;
10+
import javax.xml.namespace.QName;
1011
import javax.xml.xpath.XPath;
1112
import javax.xml.xpath.XPathConstants;
1213
import javax.xml.xpath.XPathExpression;
@@ -20,9 +21,10 @@
2021
*/
2122
public class HasXPath extends TypeSafeMatcher<Node> {
2223

23-
private final Matcher<String> valueMatcher;
24+
private final Matcher<?> valueMatcher;
2425
private final XPathExpression compiledXPath;
2526
private final String xpathString;
27+
private final QName evaluationMode;
2628

2729
/**
2830
* @param xPathExpression XPath expression.
@@ -40,27 +42,31 @@ public HasXPath(String xPathExpression, Matcher<String> valueMatcher) {
4042
* May be null to specify that the XPath must exist but the value is irrelevant.
4143
*/
4244
public HasXPath(String xPathExpression, NamespaceContext namespaceContext, Matcher<String> valueMatcher) {
43-
try {
44-
XPath xPath = XPathFactory.newInstance().newXPath();
45-
if (namespaceContext != null)
46-
{
47-
xPath.setNamespaceContext(namespaceContext);
48-
}
49-
compiledXPath = xPath.compile(xPathExpression);
50-
this.xpathString = xPathExpression;
51-
this.valueMatcher = valueMatcher;
52-
} catch (XPathExpressionException e) {
53-
throw new IllegalArgumentException("Invalid XPath : " + xPathExpression, e);
45+
this(xPathExpression, namespaceContext, valueMatcher, XPathConstants.STRING);
46+
}
47+
48+
private HasXPath(String xPathExpression, NamespaceContext namespaceContext, Matcher<?> valueMatcher, QName mode) {
49+
try {
50+
XPath xPath = XPathFactory.newInstance().newXPath();
51+
if (namespaceContext != null) {
52+
xPath.setNamespaceContext(namespaceContext);
5453
}
54+
compiledXPath = xPath.compile(xPathExpression);
55+
this.xpathString = xPathExpression;
56+
this.valueMatcher = valueMatcher;
57+
this.evaluationMode = mode;
58+
} catch (XPathExpressionException e) {
59+
throw new IllegalArgumentException("Invalid XPath : " + xPathExpression, e);
60+
}
5561
}
5662

5763
public boolean matchesSafely(Node item) {
5864
try {
59-
String result = (String) compiledXPath.evaluate(item, XPathConstants.STRING);
65+
Object result = compiledXPath.evaluate(item, evaluationMode);
6066
if (result == null) {
6167
return false;
6268
} else if (valueMatcher == null) {
63-
return !result.equals("");
69+
return true;
6470
} else {
6571
return valueMatcher.matches(result);
6672
}
@@ -75,24 +81,24 @@ public void describeTo(Description description) {
7581
description.appendText(" ").appendDescriptionOf(valueMatcher);
7682
}
7783
}
78-
84+
7985
@Factory
8086
public static Matcher<Node> hasXPath(String xPath, Matcher<String> valueMatcher) {
8187
return hasXPath(xPath, null, valueMatcher);
8288
}
8389

8490
@Factory
8591
public static Matcher<Node> hasXPath(String xPath, NamespaceContext namespaceContext, Matcher<String> valueMatcher) {
86-
return new HasXPath(xPath, namespaceContext, valueMatcher);
92+
return new HasXPath(xPath, namespaceContext, valueMatcher, XPathConstants.STRING);
8793
}
8894

8995
@Factory
9096
public static Matcher<Node> hasXPath(String xPath) {
91-
return hasXPath(xPath, null, null);
97+
return hasXPath(xPath, (NamespaceContext) null);
9298
}
9399

94100
@Factory
95101
public static Matcher<Node> hasXPath(String xPath, NamespaceContext namespaceContext) {
96-
return hasXPath(xPath, namespaceContext, null);
102+
return new HasXPath(xPath, namespaceContext, null, XPathConstants.NODE);
97103
}
98104
}

hamcrest-unit-test/src/main/java/org/hamcrest/xml/HasXPathTest.java

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
public class HasXPathTest extends AbstractMatcherTest {
2323
private Document xml;
24+
private NamespaceContext ns;
2425

2526
protected void setUp() throws Exception {
2627
super.setUp();
@@ -29,8 +30,28 @@ protected void setUp() throws Exception {
2930
+ " <something id='a'><cheese>Edam</cheese></something>\n"
3031
+ " <something id='b'><cheese>Cheddar</cheese></something>\n"
3132
+ " <f:foreignSomething xmlns:f=\"http://cheese.com\" milk=\"camel\">Caravane</f:foreignSomething>\n"
33+
+ " <emptySomething />\n"
34+
+ " <f:emptySomething xmlns:f=\"http://cheese.com\" />"
3235
+ "</root>\n"
3336
);
37+
ns = new NamespaceContext() {
38+
public String getNamespaceURI(String prefix) {
39+
return ("cheese".equals(prefix) ? "http://cheese.com" : null);
40+
}
41+
42+
public String getPrefix(String namespaceURI) {
43+
return ("http://cheese.com".equals(namespaceURI) ? "cheese" : null);
44+
}
45+
46+
public Iterator<String> getPrefixes(String namespaceURI) {
47+
HashSet<String> prefixes = new HashSet<String>();
48+
String prefix = getPrefix(namespaceURI);
49+
if (prefix != null) {
50+
prefixes.add(prefix);
51+
}
52+
return prefixes.iterator();
53+
}
54+
};
3455
}
3556

3657
protected Matcher<?> createMatcher() {
@@ -46,31 +67,25 @@ public void testAppliesMatcherToXPathInDocument() throws Exception {
4667
assertThat(xml, hasXPath("//something[@id='b']/cheese"));
4768
}
4869

70+
public void testMatchesEmptyElement() throws Exception {
71+
assertThat(xml, hasXPath("//emptySomething"));
72+
}
73+
74+
public void testMatchesEmptyElementInNamespace() throws Exception {
75+
assertThat(xml, hasXPath("//cheese:emptySomething", ns));
76+
}
77+
4978
public void testFailsIfNodeIsMissing() throws Exception {
50-
assertThat(xml, not(hasXPath("/root/something[3]/cheese", equalTo("Cheddar"))));
51-
assertThat(xml, not(hasXPath("//something[@id='c']/cheese")));
79+
assertThat(xml, not(hasXPath("/root/something[3]/cheese", ns, equalTo("Cheddar"))));
80+
assertThat(xml, not(hasXPath("//something[@id='c']/cheese", ns)));
5281
}
5382

54-
public void testMatchesWithNamespace() throws Exception {
55-
NamespaceContext ns = new NamespaceContext() {
56-
public String getNamespaceURI(String prefix) {
57-
return ("cheese".equals(prefix) ? "http://cheese.com" : null);
58-
}
59-
60-
public String getPrefix(String namespaceURI) {
61-
return ("http://cheese.com".equals(namespaceURI) ? "cheese" : null);
62-
}
63-
64-
public Iterator<String> getPrefixes(String namespaceURI) {
65-
HashSet<String> prefixes = new HashSet<String>();
66-
String prefix = getPrefix(namespaceURI);
67-
if (prefix != null) {
68-
prefixes.add(prefix);
69-
}
70-
return prefixes.iterator();
71-
}
72-
};
83+
public void testFailsIfNodeIsMissingInNamespace() throws Exception {
84+
assertThat(xml, not(hasXPath("//cheese:foreignSomething", equalTo("Badger"))));
85+
assertThat(xml, not(hasXPath("//cheese:foreignSomething")));
86+
}
7387

88+
public void testMatchesWithNamespace() throws Exception {
7489
assertThat(xml, hasXPath("//cheese:foreignSomething", ns));
7590
assertThat(xml, hasXPath("//cheese:foreignSomething/@milk", ns, equalTo("camel")));
7691
assertThat(xml, hasXPath("//cheese:foreignSomething/text()", ns, equalTo("Caravane")));

0 commit comments

Comments
 (0)