{
+
+ /**
+ * Creates a matcher that matches if the examined {@link CharSequence} matches the specified
+ * regular expression.
+ *
+ * For example:
+ * assertThat("myStringOfNote", pattern("[0-9]+"))
+ *
+ * @param regex the regular expression that the returned matcher will use to match any examined {@link CharSequence}
+ */
+ @Factory
+ public static Matcher pattern(String regex) {
+ return pattern(Pattern.compile(regex));
+ }
+
+ /**
+ * Creates a matcher that matches if the examined {@link CharSequence} matches the specified
+ * {@link java.util.regex.Pattern}.
+ *
+ * For example:
+ * assertThat("myStringOfNote", Pattern.compile("[0-9]+"))
+ *
+ * @param pattern the pattern that the returned matcher will use to match any examined {@link CharSequence}
+ */
+ @Factory
+ public static Matcher pattern(Pattern pattern) {
+ return new PatternMatcher(pattern);
+ }
+
+ public static Matcher pattern (final String pattern, final int group, final Matcher super String> matcher) {
+ return new TypeSafeMatcher() {
+ @Override
+ protected boolean matchesSafely(CharSequence item) {
+ java.util.regex.Matcher match = Pattern.compile(pattern).matcher(item);
+ match.find();
+ return matcher.matches(match.group(group));
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Group ")
+ .appendValue(group)
+ .appendText(" in pattern ")
+ .appendValue(pattern)
+ .appendDescriptionOf(matcher);
+ }
+
+ @Override
+ protected void describeMismatchSafely(CharSequence item, Description mismatchDescription) {
+ java.util.regex.Matcher match = Pattern.compile(pattern).matcher(item);
+ match.find();
+ mismatchDescription.appendText(match.group(group));
+ }
+ };
+ }
+
+ private final Pattern pattern;
+
+ public PatternMatcher(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ @Override
+ public boolean matchesSafely(CharSequence item) {
+ return pattern.matcher(item).matches();
+ }
+
+ @Override
+ public void describeMismatchSafely(CharSequence item, org.hamcrest.Description mismatchDescription) {
+ mismatchDescription.appendText("was \"").appendText(String.valueOf(item)).appendText("\"");
+ }
+
+ @Override
+ public void describeTo(org.hamcrest.Description description) {
+ description.appendText("a string with pattern \"").appendText(String.valueOf(pattern)).appendText("\"");
+ }
+}
diff --git a/src/test/java/com/lyncode/test/matchers/xml/XPathMatchers.java b/src/test/java/com/lyncode/test/matchers/xml/XPathMatchers.java
new file mode 100644
index 00000000..f1a85994
--- /dev/null
+++ b/src/test/java/com/lyncode/test/matchers/xml/XPathMatchers.java
@@ -0,0 +1,136 @@
+package com.lyncode.test.matchers.xml;
+
+import com.lyncode.builder.MapBuilder;
+import org.dom4j.*;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class XPathMatchers {
+ private static final String DEFAULT_PREFIX = "defprefix";
+
+ private static String replaceXpath(String xpath) {
+ int offset = 0;
+ String newXpath = "";
+ Pattern pattern = Pattern.compile("/[^/]+");
+ java.util.regex.Matcher matcher = pattern.matcher(xpath);
+ while (matcher.find()) {
+ if (matcher.start() > offset) newXpath += xpath.substring(offset, matcher.start());
+ if (!matcher.group().contains(":") && !matcher.group().startsWith("/@"))
+ newXpath += "/" + DEFAULT_PREFIX + ":" + matcher.group().substring(1);
+ else
+ newXpath += matcher.group();
+ offset = matcher.end() + 1;
+ }
+
+ return newXpath;
+ }
+
+ public static XPathValueMatcher xPath(String expression, Matcher value) {
+ return new XPathValueMatcher(expression, value);
+ }
+
+ public static XPathValueMatcher xPath(String expression, Matcher value, String defaultNamespace) {
+ return xPath(replaceXpath(expression), value, new MapBuilder().withPair(DEFAULT_PREFIX, defaultNamespace));
+ }
+
+ public static XPathValueMatcher xPath(String expression, Matcher value, MapBuilder namespaces) {
+ return new XPathValueMatcher(expression, value, namespaces);
+ }
+
+ public static XPathMatcher hasXPath(String expression) {
+ return new XPathMatcher(expression);
+ }
+
+ public static XPathMatcher hasXPath(String expression, String defaultNamespace) {
+ return new XPathMatcher(replaceXpath(expression), new MapBuilder().withPair(DEFAULT_PREFIX, defaultNamespace));
+ }
+
+ public static XPathMatcher hasXPath(String expression, MapBuilder namespaces) {
+ return new XPathMatcher(expression, namespaces);
+ }
+
+ private static class XPathMatcher extends BaseMatcher {
+ private String expression;
+ private MapBuilder contexts;
+
+ public XPathMatcher(String expression) {
+ this.expression = expression;
+ }
+
+ public XPathMatcher(String expression, MapBuilder namespaces) {
+ this.expression = expression;
+ this.contexts = namespaces;
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof String) {
+ String input = (String) o;
+ try {
+ Document document = DocumentHelper.parseText(input);
+ XPath xPath = document.createXPath(this.expression);
+ if (this.contexts != null) {
+ xPath.setNamespaceURIs(this.contexts.build());
+ }
+ List list = xPath.selectNodes(document);
+ boolean contains = !list.isEmpty();
+ return contains;
+ } catch (Exception e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("XML with XPath ").appendValue(this.expression);
+ }
+ }
+
+ private static class XPathValueMatcher extends BaseMatcher {
+ private MapBuilder namespaces;
+ private Matcher value;
+ private String expression;
+
+ public XPathValueMatcher(String expression, Matcher value) {
+ this.value = value;
+ this.expression = expression;
+ }
+
+ public XPathValueMatcher(String expression, Matcher value, MapBuilder namespaces) {
+ this.value = value;
+ this.expression = expression;
+ this.namespaces = namespaces;
+ }
+
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof String) {
+ String input = (String) o;
+ try {
+ Document document = DocumentHelper.parseText(input);
+ XPath xPath = document.createXPath(this.expression);
+ if (this.namespaces != null)
+ xPath.setNamespaceURIs(this.namespaces.build());
+ String evaluatedValue = xPath.valueOf(document);
+ return !xPath.selectNodes(document).isEmpty() && value.matches(evaluatedValue);
+ } catch (DocumentException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ System.err.println("Incorrect input");
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("XPath ").appendValue(this.expression).appendText(" must resolve to ");
+ value.describeTo(description);
+ }
+ }
+}
\ No newline at end of file