diff --git a/pom.xml b/pom.xml index 2b83a3eb..50b54ee2 100644 --- a/pom.xml +++ b/pom.xml @@ -229,7 +229,7 @@ org.apache.logging.log4j - log4j-api + log4j-core 2.20.0 @@ -237,6 +237,11 @@ commons-codec 1.16.0 + + org.apache.commons + commons-collections4 + 4.1 + net.sf.saxon Saxon-HE @@ -280,22 +285,9 @@ 2.13.0 - com.lyncode - builder-commons - 1.0.2 - - - com.lyncode - test-support - 1.0.3 - test - - - - dom4j - dom4j - - + com.google.guava + guava + 32.0.0-jre org.dom4j diff --git a/src/main/java/com/lyncode/xoai/builders/Builder.java b/src/main/java/com/lyncode/xoai/builders/Builder.java new file mode 100644 index 00000000..e3a5451f --- /dev/null +++ b/src/main/java/com/lyncode/xoai/builders/Builder.java @@ -0,0 +1,5 @@ +package com.lyncode.builder; + +public interface Builder { + T build(); +} \ No newline at end of file diff --git a/src/main/java/com/lyncode/xoai/builders/DateBuilder.java b/src/main/java/com/lyncode/xoai/builders/DateBuilder.java new file mode 100644 index 00000000..aebb8960 --- /dev/null +++ b/src/main/java/com/lyncode/xoai/builders/DateBuilder.java @@ -0,0 +1,46 @@ +package com.lyncode.builder; + +import java.util.Calendar; +import java.util.Date; + +public class DateBuilder implements Builder { + public static final int MIN_MILLISECONDS = 0; + public static final int MAX_MILLISECONDS = 999; + private Calendar calendar = Calendar.getInstance(); + + public DateBuilder() { + } + + public DateBuilder(Date time) { + calendar.setTime(time); + } + + public DateBuilder addDays(int days) { + calendar.add(Calendar.DAY_OF_YEAR, days); + return this; + } + + public DateBuilder subtractDays(int days) { + calendar.add(Calendar.DAY_OF_YEAR, days * -1); + return this; + } + + public DateBuilder setMilliseconds(int milliseconds) { + calendar.set(Calendar.MILLISECOND, milliseconds); + return this; + } + + public DateBuilder setMinMilliseconds() { + calendar.set(Calendar.MILLISECOND, MIN_MILLISECONDS); + return this; + } + + public DateBuilder setMaxMilliseconds() { + calendar.set(Calendar.MILLISECOND, MAX_MILLISECONDS); + return this; + } + + public Date build() { + return calendar.getTime(); + } +} \ No newline at end of file diff --git a/src/main/java/com/lyncode/xoai/builders/ListBuilder.java b/src/main/java/com/lyncode/xoai/builders/ListBuilder.java new file mode 100644 index 00000000..f5aa880c --- /dev/null +++ b/src/main/java/com/lyncode/xoai/builders/ListBuilder.java @@ -0,0 +1,37 @@ +package com.lyncode.builder; + +import com.google.common.base.Function; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static com.google.common.collect.Collections2.transform; +import static com.google.common.collect.Lists.newArrayList; + +public class ListBuilder implements Builder> { + private List list; + + public ListBuilder() { + list = new ArrayList(); + } + + public ListBuilder add(Collection list) { + this.list.addAll(list); + return this; + } + + public ListBuilder add(T... list) { + for (T t : list) + this.list.add(t); + return this; + } + + public List build() { + return list; + } + + public List build(Function transformer) { + return newArrayList(transform(list, transformer)); + } +} \ No newline at end of file diff --git a/src/main/java/com/lyncode/xoai/builders/MapBuilder.java b/src/main/java/com/lyncode/xoai/builders/MapBuilder.java new file mode 100644 index 00000000..82a286ea --- /dev/null +++ b/src/main/java/com/lyncode/xoai/builders/MapBuilder.java @@ -0,0 +1,17 @@ +package com.lyncode.builder; + +import java.util.Map; +import java.util.TreeMap; + +public class MapBuilder implements Builder> { + private Map map = new TreeMap(); + + public MapBuilder withPair(K key, V value) { + map.put(key, value); + return this; + } + + public Map build() { + return this.map; + } +} \ No newline at end of file diff --git a/src/test/java/com/lyncode/test/matchers/string/PatternMatcher.java b/src/test/java/com/lyncode/test/matchers/string/PatternMatcher.java new file mode 100644 index 00000000..f17e6951 --- /dev/null +++ b/src/test/java/com/lyncode/test/matchers/string/PatternMatcher.java @@ -0,0 +1,90 @@ +package com.lyncode.test.matchers.string; + +import org.hamcrest.Description; +import org.hamcrest.Factory; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import java.util.regex.Pattern; + +/** + * Tests if the argument is a {@link CharSequence} that matches a regular expression. + */ +public class PatternMatcher extends TypeSafeMatcher { + + /** + * 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 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