diff --git a/hamcrest/src/main/java/org/hamcrest/xml/HasXPath.java b/hamcrest/src/main/java/org/hamcrest/xml/HasXPath.java index 30ed081a..0b195a3d 100644 --- a/hamcrest/src/main/java/org/hamcrest/xml/HasXPath.java +++ b/hamcrest/src/main/java/org/hamcrest/xml/HasXPath.java @@ -6,14 +6,18 @@ import org.hamcrest.TypeSafeDiagnosingMatcher; import org.hamcrest.core.IsAnything; import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; import javax.xml.xpath.*; +import java.util.ArrayList; +import java.util.List; + +import static javax.xml.xpath.XPathConstants.NODESET; import static javax.xml.xpath.XPathConstants.STRING; -import static org.hamcrest.Condition.matched; -import static org.hamcrest.Condition.notMatched; +import static org.hamcrest.Condition.*; /** * 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 @Override public boolean matchesSafely(Node item, Description mismatch) { - return evaluated(item, mismatch) - .and(NODE_EXISTS) - .matching(valueMatcher); + if (this.evaluationMode == NODESET) + { + List> match_list = evaluatedList(item, mismatch); + + for (Condition match: match_list) { + if (match.and(NODE_EXISTS).matching(valueMatcher)) { + return true; + } + } + return false; + } + else { + return evaluated(item, mismatch) + .and(NODE_EXISTS) + .matching(valueMatcher); + } } @Override @@ -72,7 +89,7 @@ public void describeTo(Description description) { } private Condition evaluated(Node item, Description mismatch) { - try { + try{ return matched(compiledXPath.evaluate(item, evaluationMode), mismatch); } catch (XPathExpressionException e) { mismatch.appendText(e.getMessage()); @@ -80,6 +97,23 @@ private Condition evaluated(Node item, Description mismatch) { return notMatched(); } + private List> evaluatedList(Node item, Description mismatch) { + List> match_list = new ArrayList<>(); + try { + NodeList list = (NodeList) compiledXPath.evaluate(item, evaluationMode); + Object obj = compiledXPath.evaluate(item, STRING); + for (int i = 0; i < list.getLength(); i++) { + Node node = list.item(i); + match_list.add((matched((Object)node.getTextContent(), mismatch))); + } + return match_list; + } catch (XPathExpressionException e) { + mismatch.appendText(e.getMessage()); + } + match_list.add(notMatched()); + return match_list; + } + private static Condition.Step nodeExists() { return new Condition.Step() { @Override @@ -136,7 +170,7 @@ public static Matcher hasXPath(String xPath, Matcher valueMatcher) * matcher for the value at the specified xpath */ public static Matcher hasXPath(String xPath, NamespaceContext namespaceContext, Matcher valueMatcher) { - return new HasXPath(xPath, namespaceContext, valueMatcher, STRING); + return new HasXPath(xPath, namespaceContext, valueMatcher, NODESET); } /** diff --git a/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java b/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java index a66689dc..52c31b2d 100644 --- a/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java +++ b/hamcrest/src/test/java/org/hamcrest/xml/HasXPathTest.java @@ -75,6 +75,41 @@ public Iterator getPrefixes(String namespaceURI) { assertMatches(hasXPath("//something[@id='b']/cheese"), xml); } + /* + Test whether the multiple elements case works or not + */ + @Test public void + appliesMatcherToXPathInDocumentWithMultipleElement() { + Document multipleBookXml = parse("\n" + + "\n" + + "A-ISBN\n" + + "\n" + + "\n" + + "B-ISBN\n" + + "B-Book>\n" + + "\n" + + "\n" + ); + + assertMatches(hasXPath("/books/book/isbn", equalTo("B-ISBN")), multipleBookXml); + assertMatches(hasXPath("/books/book/isbn", equalTo("A-ISBN")), multipleBookXml); + } + + /* + Test whether the single element case still works or not + */ + @Test public void + appliesMatcherToPathInDocumentWithSingleElement() { + Document multipleBookXml = parse("\n" + + "\n" + + "A-ISBN\n" + + "\n" + + "\n" + ); + + assertMatches(hasXPath("/books/book/isbn", equalTo("A-ISBN")), multipleBookXml); + } + @Test public void matchesEmptyElement() { assertMatches(hasXPath("//emptySomething"), xml);