Skip to content

Commit 0a3bba0

Browse files
committed
Fixed Matcher.hasXpath against xml of multiple element of same path
1 parent 8522353 commit 0a3bba0

File tree

2 files changed

+75
-6
lines changed

2 files changed

+75
-6
lines changed

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

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
import org.hamcrest.TypeSafeDiagnosingMatcher;
77
import org.hamcrest.core.IsAnything;
88
import org.w3c.dom.Node;
9+
import org.w3c.dom.NodeList;
910

1011
import javax.xml.namespace.NamespaceContext;
1112
import javax.xml.namespace.QName;
1213
import javax.xml.xpath.*;
1314

15+
import java.util.ArrayList;
16+
import java.util.List;
17+
18+
import static javax.xml.xpath.XPathConstants.NODESET;
1419
import static javax.xml.xpath.XPathConstants.STRING;
15-
import static org.hamcrest.Condition.matched;
16-
import static org.hamcrest.Condition.notMatched;
20+
import static org.hamcrest.Condition.*;
1721

1822
/**
1923
* Applies a Matcher to a given XML Node in an existing XML Node tree, specified by an XPath expression.
@@ -58,9 +62,22 @@ private HasXPath(String xPathExpression, NamespaceContext namespaceContext, Matc
5862

5963
@Override
6064
public boolean matchesSafely(Node item, Description mismatch) {
61-
return evaluated(item, mismatch)
62-
.and(NODE_EXISTS)
63-
.matching(valueMatcher);
65+
if (this.evaluationMode == NODESET)
66+
{
67+
List<Condition<Object>> match_list = evaluatedList(item, mismatch);
68+
69+
for (Condition<Object> match: match_list) {
70+
if (match.and(NODE_EXISTS).matching(valueMatcher)) {
71+
return true;
72+
}
73+
}
74+
return false;
75+
}
76+
else {
77+
return evaluated(item, mismatch)
78+
.and(NODE_EXISTS)
79+
.matching(valueMatcher);
80+
}
6481
}
6582

6683
@Override
@@ -80,6 +97,23 @@ private Condition<Object> evaluated(Node item, Description mismatch) {
8097
return notMatched();
8198
}
8299

100+
private List<Condition<Object>> evaluatedList(Node item, Description mismatch) {
101+
List<Condition<Object>> match_list = new ArrayList<>();
102+
try {
103+
NodeList list = (NodeList) compiledXPath.evaluate(item, evaluationMode);
104+
Object obj = compiledXPath.evaluate(item, STRING);
105+
for (int i = 0; i < list.getLength(); i++) {
106+
Node node = list.item(i);
107+
match_list.add((matched((Object)node.getTextContent(), mismatch)));
108+
}
109+
return match_list;
110+
} catch (XPathExpressionException e) {
111+
mismatch.appendText(e.getMessage());
112+
}
113+
match_list.add(notMatched());
114+
return match_list;
115+
}
116+
83117
private static Condition.Step<Object, String> nodeExists() {
84118
return new Condition.Step<Object, String>() {
85119
@Override
@@ -136,7 +170,7 @@ public static Matcher<Node> hasXPath(String xPath, Matcher<String> valueMatcher)
136170
* matcher for the value at the specified xpath
137171
*/
138172
public static Matcher<Node> hasXPath(String xPath, NamespaceContext namespaceContext, Matcher<String> valueMatcher) {
139-
return new HasXPath(xPath, namespaceContext, valueMatcher, STRING);
173+
return new HasXPath(xPath, namespaceContext, valueMatcher, NODESET);
140174
}
141175

142176
/**

hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,41 @@ public Iterator<String> getPrefixes(String namespaceURI) {
7575
assertMatches(hasXPath("//something[@id='b']/cheese"), xml);
7676
}
7777

78+
/*
79+
Test whether the multiple elements case works or not
80+
*/
81+
@Test public void
82+
appliesMatcherToXPathInDocumentWithMultipleElement() {
83+
Document multipleBookXml = parse("<books>\n"
84+
+ "<book>\n"
85+
+ "<isbn>A-ISBN</isbn>\n"
86+
+ "</book>\n"
87+
+ "<book>\n"
88+
+ "<isbn>B-ISBN</isbn>\n"
89+
+ "<name>B-Book></name>\n"
90+
+ "</book>\n"
91+
+ "</books>\n"
92+
);
93+
94+
assertMatches(hasXPath("/books/book/isbn", equalTo("B-ISBN")), multipleBookXml);
95+
assertMatches(hasXPath("/books/book/isbn", equalTo("A-ISBN")), multipleBookXml);
96+
}
97+
98+
/*
99+
Test whether the single element case still works or not
100+
*/
101+
@Test public void
102+
appliesMatcherToPathInDocumentWithSingleElement() {
103+
Document multipleBookXml = parse("<books>\n"
104+
+ "<book>\n"
105+
+ "<isbn>A-ISBN</isbn>\n"
106+
+ "</book>\n"
107+
+ "</books>\n"
108+
);
109+
110+
assertMatches(hasXPath("/books/book/isbn", equalTo("A-ISBN")), multipleBookXml);
111+
}
112+
78113
@Test public void
79114
matchesEmptyElement() {
80115
assertMatches(hasXPath("//emptySomething"), xml);

0 commit comments

Comments
 (0)