diff options
Diffstat (limited to 'hamcrest-core/src/main/java/org/hamcrest')
41 files changed, 2018 insertions, 648 deletions
diff --git a/hamcrest-core/src/main/java/org/hamcrest/BaseDescription.java b/hamcrest-core/src/main/java/org/hamcrest/BaseDescription.java index 4c98e5f..6e62e69 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/BaseDescription.java +++ b/hamcrest-core/src/main/java/org/hamcrest/BaseDescription.java @@ -1,127 +1,144 @@ -package org.hamcrest;
-
-import static java.lang.String.valueOf;
-
-import java.util.Arrays;
-import java.util.Iterator;
-
-import org.hamcrest.internal.ArrayIterator;
-import org.hamcrest.internal.SelfDescribingValueIterator;
-
-/**
- * A {@link Description} that is stored as a string.
- */
-public abstract class BaseDescription implements Description {
- public Description appendText(String text) {
- append(text);
- return this;
- }
-
- public Description appendDescriptionOf(SelfDescribing value) {
- value.describeTo(this);
- return this;
- }
-
- public Description appendValue(Object value) {
- if (value == null) {
- append("null");
- } else if (value instanceof String) {
- toJavaSyntax((String) value);
- } else if (value instanceof Character) {
- append('"');
- toJavaSyntax((Character) value);
- append('"');
- } else if (value instanceof Short) {
- append('<');
- append(valueOf(value));
- append("s>");
- } else if (value instanceof Long) {
- append('<');
- append(valueOf(value));
- append("L>");
- } else if (value instanceof Float) {
- append('<');
- append(valueOf(value));
- append("F>");
- } else if (value.getClass().isArray()) {
- appendValueList("[",", ","]", new ArrayIterator(value));
- } else {
- append('<');
- append(valueOf(value));
- append('>');
- }
- return this;
- }
-
- public <T> Description appendValueList(String start, String separator, String end, T... values) {
- return appendValueList(start, separator, end, Arrays.asList(values));
- }
-
- public <T> Description appendValueList(String start, String separator, String end, Iterable<T> values) {
- return appendValueList(start, separator, end, values.iterator());
- }
-
- private <T> Description appendValueList(String start, String separator, String end, Iterator<T> values) {
- return appendList(start, separator, end, new SelfDescribingValueIterator<T>(values));
- }
-
- public Description appendList(String start, String separator, String end, Iterable<? extends SelfDescribing> values) {
- return appendList(start, separator, end, values.iterator());
- }
-
- private Description appendList(String start, String separator, String end, Iterator<? extends SelfDescribing> i) {
- boolean separate = false;
-
- append(start);
- while (i.hasNext()) {
- if (separate) append(separator);
- appendDescriptionOf(i.next());
- separate = true;
- }
- append(end);
-
- return this;
- }
-
-
- /** Append the String <var>str</var> to the description.
- * The default implementation passes every character to {@link #append(char)}.
- * Override in subclasses to provide an efficient implementation.
- */
- protected void append(String str) {
- for (int i = 0; i < str.length(); i++) {
- append(str.charAt(i));
- }
- }
-
- /** Append the char <var>c</var> to the description.
- */
- protected abstract void append(char c);
-
- private void toJavaSyntax(String unformatted) {
- append('"');
- for (int i = 0; i < unformatted.length(); i++) {
- toJavaSyntax(unformatted.charAt(i));
- }
- append('"');
- }
-
- private void toJavaSyntax(char ch) {
- switch (ch) {
- case '"':
- append("\\\"");
- break;
- case '\n':
- append("\\n");
- break;
- case '\r':
- append("\\r");
- break;
- case '\t':
- append("\\t");
- break;
- default:
- append(ch);
- }
- }
-}
+package org.hamcrest; + +import org.hamcrest.internal.ArrayIterator; +import org.hamcrest.internal.SelfDescribingValueIterator; + +import java.util.Arrays; +import java.util.Iterator; + +import static java.lang.String.valueOf; + +/** + * A {@link Description} that is stored as a string. + */ +public abstract class BaseDescription implements Description { + + @Override + public Description appendText(String text) { + append(text); + return this; + } + + @Override + public Description appendDescriptionOf(SelfDescribing value) { + value.describeTo(this); + return this; + } + + @Override + public Description appendValue(Object value) { + if (value == null) { + append("null"); + } else if (value instanceof String) { + toJavaSyntax((String) value); + } else if (value instanceof Character) { + append('"'); + toJavaSyntax((Character) value); + append('"'); + } else if (value instanceof Short) { + append('<'); + append(descriptionOf(value)); + append("s>"); + } else if (value instanceof Long) { + append('<'); + append(descriptionOf(value)); + append("L>"); + } else if (value instanceof Float) { + append('<'); + append(descriptionOf(value)); + append("F>"); + } else if (value.getClass().isArray()) { + appendValueList("[",", ","]", new ArrayIterator(value)); + } else { + append('<'); + append(descriptionOf(value)); + append('>'); + } + return this; + } + + private String descriptionOf(Object value) { + try { + return valueOf(value); + } + catch (Exception e) { + return value.getClass().getName() + "@" + Integer.toHexString(value.hashCode()); + } + } + + @Override + public <T> Description appendValueList(String start, String separator, String end, T... values) { + return appendValueList(start, separator, end, Arrays.asList(values)); + } + + @Override + public <T> Description appendValueList(String start, String separator, String end, Iterable<T> values) { + return appendValueList(start, separator, end, values.iterator()); + } + + private <T> Description appendValueList(String start, String separator, String end, Iterator<T> values) { + return appendList(start, separator, end, new SelfDescribingValueIterator<T>(values)); + } + + @Override + public Description appendList(String start, String separator, String end, Iterable<? extends SelfDescribing> values) { + return appendList(start, separator, end, values.iterator()); + } + + private Description appendList(String start, String separator, String end, Iterator<? extends SelfDescribing> i) { + boolean separate = false; + + append(start); + while (i.hasNext()) { + if (separate) append(separator); + appendDescriptionOf(i.next()); + separate = true; + } + append(end); + + return this; + } + + /** + * Append the String <var>str</var> to the description. + * The default implementation passes every character to {@link #append(char)}. + * Override in subclasses to provide an efficient implementation. + */ + protected void append(String str) { + for (int i = 0; i < str.length(); i++) { + append(str.charAt(i)); + } + } + + /** + * Append the char <var>c</var> to the description. + */ + protected abstract void append(char c); + + private void toJavaSyntax(String unformatted) { + append('"'); + for (int i = 0; i < unformatted.length(); i++) { + toJavaSyntax(unformatted.charAt(i)); + } + append('"'); + } + + private void toJavaSyntax(char ch) { + switch (ch) { + case '"': + append("\\\""); + break; + case '\n': + append("\\n"); + break; + case '\r': + append("\\r"); + break; + case '\t': + append("\\t"); + break; + default: + append(ch); + } + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/BaseMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/BaseMatcher.java index 3fdd6f7..484c101 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/BaseMatcher.java +++ b/hamcrest-core/src/main/java/org/hamcrest/BaseMatcher.java @@ -1,5 +1,3 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest; /** @@ -12,11 +10,18 @@ public abstract class BaseMatcher<T> implements Matcher<T> { /** * @see Matcher#_dont_implement_Matcher___instead_extend_BaseMatcher_() */ + @Override + @Deprecated public final void _dont_implement_Matcher___instead_extend_BaseMatcher_() { // See Matcher interface for an explanation of this method. } @Override + public void describeMismatch(Object item, Description description) { + description.appendText("was ").appendValue(item); + } + + @Override public String toString() { return StringDescription.toString(this); } diff --git a/hamcrest-core/src/main/java/org/hamcrest/Condition.java b/hamcrest-core/src/main/java/org/hamcrest/Condition.java new file mode 100644 index 0000000..02ce09e --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/Condition.java @@ -0,0 +1,69 @@ +package org.hamcrest; + +/** + * A Condition implements part of a multi-step match. We sometimes need to write matchers + * that have a sequence of steps, where each step depends on the result of the previous + * step and we can stop processing as soon as a step fails. These classes provide + * infrastructure for writing such a sequence. + * + * Based on https://github.com/npryce/maybe-java + * @author Steve Freeman 2012 http://www.hamcrest.com + */ + +public abstract class Condition<T> { + public static final NotMatched<Object> NOT_MATCHED = new NotMatched<Object>(); + + public interface Step<I, O> { + Condition<O> apply(I value, Description mismatch); + } + + private Condition() { } + + public abstract boolean matching(Matcher<T> match, String message); + public abstract <U> Condition<U> and(Step<? super T, U> mapping); + + public final boolean matching(Matcher<T> match) { return matching(match, ""); } + public final <U> Condition<U> then(Step<? super T, U> mapping) { return and(mapping); } + + @SuppressWarnings("unchecked") + public static <T> Condition<T> notMatched() { + return (Condition<T>) NOT_MATCHED; + } + + public static <T> Condition<T> matched(final T theValue, final Description mismatch) { + return new Matched<T>(theValue, mismatch); + } + + private static final class Matched<T> extends Condition<T> { + private final T theValue; + private final Description mismatch; + + private Matched(T theValue, Description mismatch) { + this.theValue = theValue; + this.mismatch = mismatch; + } + + @Override + public boolean matching(Matcher<T> matcher, String message) { + if (matcher.matches(theValue)) { + return true; + } + mismatch.appendText(message); + matcher.describeMismatch(theValue, mismatch); + return false; + } + + @Override + public <U> Condition<U> and(Step<? super T, U> next) { + return next.apply(theValue, mismatch); + } + } + + private static final class NotMatched<T> extends Condition<T> { + @Override public boolean matching(Matcher<T> match, String message) { return false; } + + @Override public <U> Condition<U> and(Step<? super T, U> mapping) { + return notMatched(); + } + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/CoreMatchers.java b/hamcrest-core/src/main/java/org/hamcrest/CoreMatchers.java index 400a491..799a26a 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/CoreMatchers.java +++ b/hamcrest-core/src/main/java/org/hamcrest/CoreMatchers.java @@ -1,24 +1,114 @@ -// Generated source. package org.hamcrest; +@SuppressWarnings("UnusedDeclaration") public class CoreMatchers { /** - * Decorates another Matcher, retaining the behavior but allowing tests - * to be slightly more expressive. + * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> + */ + public static <T> org.hamcrest.Matcher<T> allOf(java.lang.Iterable<org.hamcrest.Matcher<? super T>> matchers) { + return org.hamcrest.core.AllOf.<T>allOf(matchers); + } + + /** + * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> + */ + @SafeVarargs + public static <T> org.hamcrest.Matcher<T> allOf(org.hamcrest.Matcher<? super T>... matchers) { + return org.hamcrest.core.AllOf.<T>allOf(matchers); + } + + + /** + * Creates a matcher that matches if the examined object matches <b>ANY</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", anyOf(startsWith("foo"), containsString("Val")))</pre> + */ + public static <T> org.hamcrest.core.AnyOf<T> anyOf(java.lang.Iterable<org.hamcrest.Matcher<? super T>> matchers) { + return org.hamcrest.core.AnyOf.<T>anyOf(matchers); + } + + /** + * Creates a matcher that matches if the examined object matches <b>ANY</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", anyOf(startsWith("foo"), containsString("Val")))</pre> + */ + @SafeVarargs + public static <T> org.hamcrest.core.AnyOf<T> anyOf(org.hamcrest.Matcher<? super T>... matchers) { + return org.hamcrest.core.AnyOf.<T>anyOf(matchers); + } + + /** + * Creates a matcher that matches when both of the specified matchers match the examined object. + * For example: + * <pre>assertThat("fab", both(containsString("a")).and(containsString("b")))</pre> + */ + public static <LHS> org.hamcrest.core.CombinableMatcher.CombinableBothMatcher<LHS> both(org.hamcrest.Matcher<? super LHS> matcher) { + return org.hamcrest.core.CombinableMatcher.both(matcher); + } + + /** + * Creates a matcher that matches when either of the specified matchers match the examined object. + * For example: + * <pre>assertThat("fan", either(containsString("a")).or(containsString("b")))</pre> + */ + public static <LHS> org.hamcrest.core.CombinableMatcher.CombinableEitherMatcher<LHS> either(org.hamcrest.Matcher<? super LHS> matcher) { + return org.hamcrest.core.CombinableMatcher.either(matcher); + } + + /** + * Wraps an existing matcher, overriding its description with that specified. All other functions are + * delegated to the decorated matcher, including its mismatch description. + * For example: + * <pre>describedAs("a big decimal equal to %0", equalTo(myBigDecimal), myBigDecimal.toPlainString())</pre> + * + * @param description + * the new description for the wrapped matcher + * @param matcher + * the matcher to wrap + * @param values + * optional values to insert into the tokenised description + */ + public static <T> org.hamcrest.Matcher<T> describedAs(java.lang.String description, org.hamcrest.Matcher<T> matcher, java.lang.Object... values) { + return org.hamcrest.core.DescribedAs.describedAs(description, matcher, values); + } + + /** + * Creates a matcher for {@link Iterable}s that only matches when a single pass over the + * examined {@link Iterable} yields items that are all matched by the specified + * <code>itemMatcher</code>. + * For example: + * <pre>assertThat(Arrays.asList("bar", "baz"), everyItem(startsWith("ba")))</pre> * - * eg. assertThat(cheese, equalTo(smelly)) - * vs assertThat(cheese, is(equalTo(smelly))) + * @param itemMatcher + * the matcher to apply to every item provided by the examined {@link Iterable} + */ + public static <U> org.hamcrest.Matcher<java.lang.Iterable<? extends U>> everyItem(org.hamcrest.Matcher<U> itemMatcher) { + return org.hamcrest.core.Every.everyItem(itemMatcher); + } + + /** + * Decorates another Matcher, retaining its behaviour, but allowing tests + * to be slightly more expressive. + * For example: + * <pre>assertThat(cheese, is(equalTo(smelly)))</pre> + * instead of: + * <pre>assertThat(cheese, equalTo(smelly))</pre> */ public static <T> org.hamcrest.Matcher<T> is(org.hamcrest.Matcher<T> matcher) { return org.hamcrest.core.Is.is(matcher); } /** - * This is a shortcut to the frequently used is(equalTo(x)). - * - * eg. assertThat(cheese, is(equalTo(smelly))) - * vs assertThat(cheese, is(smelly)) + * A shortcut to the frequently used <code>is(equalTo(x))</code>. + * For example: + * <pre>assertThat(cheese, is(smelly))</pre> + * instead of: + * <pre>assertThat(cheese, is(equalTo(smelly)))</pre> */ public static <T> org.hamcrest.Matcher<T> is(T value) { return org.hamcrest.core.Is.is(value); @@ -41,131 +131,334 @@ public class CoreMatchers { } /** - * Inverts the rule. + * A shortcut to the frequently used <code>is(instanceOf(SomeClass.class))</code>. + * For example: + * <pre>assertThat(cheese, isA(Cheddar.class))</pre> + * instead of: + * <pre>assertThat(cheese, is(instanceOf(Cheddar.class)))</pre> */ - public static <T> org.hamcrest.Matcher<T> not(org.hamcrest.Matcher<T> matcher) { - return org.hamcrest.core.IsNot.not(matcher); + public static <T> org.hamcrest.Matcher<T> isA(java.lang.Class<T> type) { + return org.hamcrest.core.Is.isA(type); } /** - * This is a shortcut to the frequently used not(equalTo(x)). + * Creates a matcher that always matches, regardless of the examined object. + */ + public static org.hamcrest.Matcher<java.lang.Object> anything() { + return org.hamcrest.core.IsAnything.anything(); + } + + /** + * Creates a matcher that always matches, regardless of the examined object, but describes + * itself with the specified {@link String}. * - * eg. assertThat(cheese, is(not(equalTo(smelly)))) - * vs assertThat(cheese, is(not(smelly))) + * @param description + * a meaningful {@link String} used when describing itself */ - public static <T> org.hamcrest.Matcher<T> not(T value) { - return org.hamcrest.core.IsNot.not(value); + public static org.hamcrest.Matcher<java.lang.Object> anything(java.lang.String description) { + return org.hamcrest.core.IsAnything.anything(description); } /** - * Is the value equal to another value, as tested by the - * {@link java.lang.Object#equals} invokedMethod? + * Creates a matcher for {@link Iterable}s that only matches when a single pass over the + * examined {@link Iterable} yields at least one item that is matched by the specified + * <code>itemMatcher</code>. Whilst matching, the traversal of the examined {@link Iterable} + * will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar"), hasItem(startsWith("ba")))</pre> + * + * @param itemMatcher + * the matcher to apply to items provided by the examined {@link Iterable} */ - public static <T> org.hamcrest.Matcher<T> equalTo(T operand) { - return org.hamcrest.core.IsEqual.equalTo(operand); + public static <T> org.hamcrest.Matcher<java.lang.Iterable<? super T>> hasItem(org.hamcrest.Matcher<? super T> itemMatcher) { + return org.hamcrest.core.IsCollectionContaining.<T>hasItem(itemMatcher); } /** - * Is the value an instance of a particular type? + * Creates a matcher for {@link Iterable}s that only matches when a single pass over the + * examined {@link Iterable} yields at least one item that is equal to the specified + * <code>item</code>. Whilst matching, the traversal of the examined {@link Iterable} + * will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar"), hasItem("bar"))</pre> + * + * @param item + * the item to compare against the items provided by the examined {@link Iterable} */ - public static org.hamcrest.Matcher<java.lang.Object> instanceOf(java.lang.Class<?> type) { - return org.hamcrest.core.IsInstanceOf.instanceOf(type); + public static <T> org.hamcrest.Matcher<java.lang.Iterable<? super T>> hasItem(T item) { + return org.hamcrest.core.IsCollectionContaining.<T>hasItem(item); } /** - * Evaluates to true only if ALL of the passed in matchers evaluate to true. + * Creates a matcher for {@link Iterable}s that matches when consecutive passes over the + * examined {@link Iterable} yield at least one item that is matched by the corresponding + * matcher from the specified <code>itemMatchers</code>. Whilst matching, each traversal of + * the examined {@link Iterable} will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems(endsWith("z"), endsWith("o")))</pre> + * + * @param itemMatchers + * the matchers to apply to items provided by the examined {@link Iterable} */ - public static <T> org.hamcrest.Matcher<T> allOf(org.hamcrest.Matcher<? extends T>... matchers) { - return org.hamcrest.core.AllOf.<T>allOf(matchers); + @SafeVarargs + public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(org.hamcrest.Matcher<? super T>... itemMatchers) { + return org.hamcrest.core.IsCollectionContaining.<T>hasItems(itemMatchers); } /** - * Evaluates to true only if ALL of the passed in matchers evaluate to true. + * Creates a matcher for {@link Iterable}s that matches when consecutive passes over the + * examined {@link Iterable} yield at least one item that is equal to the corresponding + * item from the specified <code>items</code>. Whilst matching, each traversal of the + * examined {@link Iterable} will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems("baz", "foo"))</pre> + * + * @param items + * the items to compare against the items provided by the examined {@link Iterable} */ - public static <T> org.hamcrest.Matcher<T> allOf(java.lang.Iterable<org.hamcrest.Matcher<? extends T>> matchers) { - return org.hamcrest.core.AllOf.allOf(matchers); + @SafeVarargs + public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(T... items) { + return org.hamcrest.core.IsCollectionContaining.<T>hasItems(items); } /** - * Evaluates to true if ANY of the passed in matchers evaluate to true. + * Creates a matcher that matches when the examined object is logically equal to the specified + * <code>operand</code>, as determined by calling the {@link java.lang.Object#equals} method on + * the <b>examined</b> object. + * + * <p>If the specified operand is <code>null</code> then the created matcher will only match if + * the examined object's <code>equals</code> method returns <code>true</code> when passed a + * <code>null</code> (which would be a violation of the <code>equals</code> contract), unless the + * examined object itself is <code>null</code>, in which case the matcher will return a positive + * match.</p> + * + * <p>The created matcher provides a special behaviour when examining <code>Array</code>s, whereby + * it will match if both the operand and the examined object are arrays of the same length and + * contain items that are equal to each other (according to the above rules) <b>in the same + * indexes</b>.</p> + * For example: + * <pre> + * assertThat("foo", equalTo("foo")); + * assertThat(new String[] {"foo", "bar"}, equalTo(new String[] {"foo", "bar"})); + * </pre> */ - public static <T> org.hamcrest.Matcher<T> anyOf(org.hamcrest.Matcher<? extends T>... matchers) { - return org.hamcrest.core.AnyOf.<T>anyOf(matchers); + public static <T> org.hamcrest.Matcher<T> equalTo(T operand) { + return org.hamcrest.core.IsEqual.equalTo(operand); } /** - * Evaluates to true if ANY of the passed in matchers evaluate to true. + * Creates an {@link org.hamcrest.core.IsEqual} matcher that does not enforce the values being + * compared to be of the same static type. */ - public static <T> org.hamcrest.Matcher<T> anyOf(java.lang.Iterable<org.hamcrest.Matcher<? extends T>> matchers) { - return org.hamcrest.core.AnyOf.anyOf(matchers); + public static org.hamcrest.Matcher<java.lang.Object> equalToObject(java.lang.Object operand) { + return org.hamcrest.core.IsEqual.equalToObject(operand); } /** - * Creates a new instance of IsSame + * Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>, + * as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the + * the examined object. * - * @param object The predicate evaluates to true only when the argument is - * this object. + * <p>The created matcher forces a relationship between specified type and the examined object, and should be + * used when it is necessary to make generics conform, for example in the JMock clause + * <code>with(any(Thing.class))</code></p> + * For example: + * <pre>assertThat(new Canoe(), instanceOf(Canoe.class));</pre> */ - public static <T> org.hamcrest.Matcher<T> sameInstance(T object) { - return org.hamcrest.core.IsSame.sameInstance(object); + public static <T> org.hamcrest.Matcher<T> any(java.lang.Class<T> type) { + return org.hamcrest.core.IsInstanceOf.any(type); } /** - * This matcher always evaluates to true. + * Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>, + * as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the + * the examined object. + * + * <p>The created matcher assumes no relationship between specified type and the examined object.</p> + * For example: + * <pre>assertThat(new Canoe(), instanceOf(Paddlable.class));</pre> */ - public static <T> org.hamcrest.Matcher<T> anything() { - return org.hamcrest.core.IsAnything.anything(); + public static <T> org.hamcrest.Matcher<T> instanceOf(java.lang.Class<?> type) { + return org.hamcrest.core.IsInstanceOf.instanceOf(type); } /** - * This matcher always evaluates to true. + * Creates a matcher that wraps an existing matcher, but inverts the logic by which + * it will match. + * For example: + * <pre>assertThat(cheese, is(not(equalTo(smelly))))</pre> * - * @param description A meaningful string used when describing itself. + * @param matcher + * the matcher whose sense should be inverted */ - public static <T> org.hamcrest.Matcher<T> anything(java.lang.String description) { - return org.hamcrest.core.IsAnything.anything(description); + public static <T> org.hamcrest.Matcher<T> not(org.hamcrest.Matcher<T> matcher) { + return org.hamcrest.core.IsNot.not(matcher); } /** - * This matcher always evaluates to true. With type inference. + * A shortcut to the frequently used <code>not(equalTo(x))</code>. + * For example: + * <pre>assertThat(cheese, is(not(smelly)))</pre> + * instead of: + * <pre>assertThat(cheese, is(not(equalTo(smelly))))</pre> + * + * @param value + * the value that any examined object should <b>not</b> equal */ - public static <T> org.hamcrest.Matcher<T> any(java.lang.Class<T> type) { - return org.hamcrest.core.IsAnything.any(type); + public static <T> org.hamcrest.Matcher<T> not(T value) { + return org.hamcrest.core.IsNot.not(value); } /** - * Matches if value is null. + * A shortcut to the frequently used <code>not(nullValue())</code>. + * For example: + * <pre>assertThat(cheese, is(notNullValue()))</pre> + * instead of: + * <pre>assertThat(cheese, is(not(nullValue())))</pre> */ - public static <T> org.hamcrest.Matcher<T> nullValue() { + public static org.hamcrest.Matcher<java.lang.Object> notNullValue() { + return org.hamcrest.core.IsNull.notNullValue(); + } + + /** + * A shortcut to the frequently used <code>not(nullValue(X.class)). Accepts a + * single dummy argument to facilitate type inference.</code>. + * For example: + * <pre>assertThat(cheese, is(notNullValue(X.class)))</pre> + * instead of: + * <pre>assertThat(cheese, is(not(nullValue(X.class))))</pre> + * + * @param type + * dummy parameter used to infer the generic type of the returned matcher + */ + public static <T> org.hamcrest.Matcher<T> notNullValue(java.lang.Class<T> type) { + return org.hamcrest.core.IsNull.notNullValue(type); + } + + /** + * Creates a matcher that matches if examined object is <code>null</code>. + * For example: + * <pre>assertThat(cheese, is(nullValue())</pre> + */ + public static org.hamcrest.Matcher<java.lang.Object> nullValue() { return org.hamcrest.core.IsNull.nullValue(); } /** - * Matches if value is null. With type inference. + * Creates a matcher that matches if examined object is <code>null</code>. Accepts a + * single dummy argument to facilitate type inference. + * For example: + * <pre>assertThat(cheese, is(nullValue(Cheese.class))</pre> + * + * @param type + * dummy parameter used to infer the generic type of the returned matcher */ public static <T> org.hamcrest.Matcher<T> nullValue(java.lang.Class<T> type) { return org.hamcrest.core.IsNull.nullValue(type); } /** - * Matches if value is not null. + * Creates a matcher that matches only when the examined object is the same instance as + * the specified target object. + * + * @param target + * the target instance against which others should be assessed */ - public static <T> org.hamcrest.Matcher<T> notNullValue() { - return org.hamcrest.core.IsNull.notNullValue(); + public static <T> org.hamcrest.Matcher<T> sameInstance(T target) { + return org.hamcrest.core.IsSame.sameInstance(target); } /** - * Matches if value is not null. With type inference. + * Creates a matcher that matches only when the examined object is the same instance as + * the specified target object. + * + * @param target + * the target instance against which others should be assessed */ - public static <T> org.hamcrest.Matcher<T> notNullValue(java.lang.Class<T> type) { - return org.hamcrest.core.IsNull.notNullValue(type); + public static <T> org.hamcrest.Matcher<T> theInstance(T target) { + return org.hamcrest.core.IsSame.theInstance(target); } /** - * Wraps an existing matcher and overrides the description when it fails. + * Creates a matcher that matches if the examined {@link String} contains the specified + * {@link String} anywhere. + * For example: + * <pre>assertThat("myStringOfNote", containsString("ring"))</pre> + * + * @param substring + * the substring that the returned matcher will expect to find within any examined string */ - public static <T> org.hamcrest.Matcher<T> describedAs(java.lang.String description, org.hamcrest.Matcher<T> matcher, java.lang.Object... values) { - return org.hamcrest.core.DescribedAs.describedAs(description, matcher, values); + public static org.hamcrest.Matcher<java.lang.String> containsString(java.lang.String substring) { + return org.hamcrest.core.StringContains.containsString(substring); + } + + /** + * Creates a matcher that matches if the examined {@link String} contains the specified + * {@link String} anywhere, ignoring case. + * For example: + * <pre>assertThat("myStringOfNote", containsString("ring"))</pre> + * + * @param substring + * the substring that the returned matcher will expect to find within any examined string + */ + public static org.hamcrest.Matcher<java.lang.String> containsStringIgnoringCase(java.lang.String substring) { + return org.hamcrest.core.StringContains.containsStringIgnoringCase(substring); + } + + /** + * <p> + * Creates a matcher that matches if the examined {@link String} starts with the specified + * {@link String}. + * </p> + * For example: + * <pre>assertThat("myStringOfNote", startsWith("my"))</pre> + * + * @param prefix + * the substring that the returned matcher will expect at the start of any examined string + */ + public static org.hamcrest.Matcher<java.lang.String> startsWith(java.lang.String prefix) { + return org.hamcrest.core.StringStartsWith.startsWith(prefix); + } + + /** + * <p> + * Creates a matcher that matches if the examined {@link String} starts with the specified + * {@link String}, ignoring case + * </p> + * For example: + * <pre>assertThat("myStringOfNote", startsWith("my"))</pre> + * + * @param prefix + * the substring that the returned matcher will expect at the start of any examined string + */ + public static org.hamcrest.Matcher<java.lang.String> startsWithIgnoringCase(java.lang.String prefix) { + return org.hamcrest.core.StringStartsWith.startsWithIgnoringCase(prefix); + } + + /** + * Creates a matcher that matches if the examined {@link String} ends with the specified + * {@link String}. + * For example: + * <pre>assertThat("myStringOfNote", endsWith("Note"))</pre> + * + * @param suffix + * the substring that the returned matcher will expect at the end of any examined string + */ + public static org.hamcrest.Matcher<java.lang.String> endsWith(java.lang.String suffix) { + return org.hamcrest.core.StringEndsWith.endsWith(suffix); + } + + /** + * Creates a matcher that matches if the examined {@link String} ends with the specified + * {@link String}, ignoring case. + * For example: + * <pre>assertThat("myStringOfNote", endsWith("Note"))</pre> + * + * @param suffix + * the substring that the returned matcher will expect at the end of any examined string + */ + public static org.hamcrest.Matcher<java.lang.String> endsWithIgnoringCase(java.lang.String suffix) { + return org.hamcrest.core.StringEndsWith.endsWithIgnoringCase(suffix); } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/CustomMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/CustomMatcher.java new file mode 100644 index 0000000..036a764 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/CustomMatcher.java @@ -0,0 +1,37 @@ +package org.hamcrest; + +/** + * Utility class for writing one off matchers. + * For example: + * <pre> + * Matcher<String> aNonEmptyString = new CustomMatcher<String>("a non empty string") { + * public boolean matches(Object object) { + * return ((object instanceof String) && !((String) object).isEmpty(); + * } + * }; + * </pre> + * <p> + * This class is designed for scenarios where an anonymous inner class + * matcher makes sense. It should not be used by API designers implementing + * matchers. + * + * @author Neil Dunn + * @see CustomTypeSafeMatcher for a type safe variant of this class that you probably + * want to use. + * @param <T> The type of object being matched. + */ +public abstract class CustomMatcher<T> extends BaseMatcher<T> { + private final String fixedDescription; + + public CustomMatcher(String description) { + if (description == null) { + throw new IllegalArgumentException("Description should be non null!"); + } + this.fixedDescription = description; + } + + @Override + public final void describeTo(Description description) { + description.appendText(fixedDescription); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/CustomTypeSafeMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/CustomTypeSafeMatcher.java new file mode 100644 index 0000000..7c5c46c --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/CustomTypeSafeMatcher.java @@ -0,0 +1,39 @@ +package org.hamcrest; + + +/** + * Utility class for writing one off matchers. + * For example: + * <pre> + * Matcher<String> aNonEmptyString = new CustomTypeSafeMatcher<String>("a non empty string") { + * public boolean matchesSafely(String string) { + * return !string.isEmpty(); + * } + * public void describeMismatchSafely(String string, Description mismatchDescription) { + * mismatchDescription.appendText("was empty"); + * } + * }; + * </pre> + * This is a variant of {@link CustomMatcher} that first type checks + * the argument being matched. By the time {@link TypeSafeMatcher#matchesSafely} is + * is called the argument is guaranteed to be non-null and of the correct + * type. + * + * @author Neil Dunn + * @param <T> The type of object being matched + */ +public abstract class CustomTypeSafeMatcher<T> extends TypeSafeMatcher<T> { + private final String fixedDescription; + + public CustomTypeSafeMatcher(String description) { + if (description == null) { + throw new IllegalArgumentException("Description must be non null!"); + } + this.fixedDescription = description; + } + + @Override + public final void describeTo(Description description) { + description.appendText(fixedDescription); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/Description.java b/hamcrest-core/src/main/java/org/hamcrest/Description.java index 36ddeda..73bfa38 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/Description.java +++ b/hamcrest-core/src/main/java/org/hamcrest/Description.java @@ -1,44 +1,89 @@ -package org.hamcrest;
-
-/**
- * A description of a Matcher. A Matcher will describe itself to a description
- * which can later be used for reporting.
- *
- * @see Matcher#describeTo(Description)
- */
-public interface Description {
-
- /**
- * Appends some plain text to the description.
- */
- Description appendText(String text);
-
- /**
- * Appends the description of a {@link SelfDescribing} value to this description.
- */
- Description appendDescriptionOf(SelfDescribing value);
-
- /**
- * Appends an arbitary value to the description.
- */
- Description appendValue(Object value);
-
- /**
- * Appends a list of values to the description.
- */
- <T> Description appendValueList(String start, String separator, String end,
- T... values);
-
- /**
- * Appends a list of values to the description.
- */
- <T> Description appendValueList(String start, String separator, String end,
- Iterable<T> values);
-
- /**
- * Appends a list of {@link org.hamcrest.SelfDescribing} objects
- * to the description.
- */
- Description appendList(String start, String separator, String end,
- Iterable<? extends SelfDescribing> values);
-}
+package org.hamcrest; + +/** + * A description of a Matcher. A Matcher will describe itself to a description + * which can later be used for reporting. + * + * @see Matcher#describeTo(Description) + */ +public interface Description { + /** + * A description that consumes input but does nothing. + */ + static final Description NONE = new NullDescription(); + + /** + * Appends some plain text to the description. + */ + Description appendText(String text); + + /** + * Appends the description of a {@link SelfDescribing} value to this description. + */ + Description appendDescriptionOf(SelfDescribing value); + + /** + * Appends an arbitrary value to the description. + */ + Description appendValue(Object value); + + /** + * Appends a list of values to the description. + */ + <T> Description appendValueList(String start, String separator, String end, + T... values); + + /** + * Appends a list of values to the description. + */ + <T> Description appendValueList(String start, String separator, String end, + Iterable<T> values); + + /** + * Appends a list of {@link org.hamcrest.SelfDescribing} objects + * to the description. + */ + Description appendList(String start, String separator, String end, + Iterable<? extends SelfDescribing> values); + + + public static final class NullDescription implements Description { + @Override + public Description appendDescriptionOf(SelfDescribing value) { + return this; + } + + @Override + public Description appendList(String start, String separator, + String end, Iterable<? extends SelfDescribing> values) { + return this; + } + + @Override + public Description appendText(String text) { + return this; + } + + @Override + public Description appendValue(Object value) { + return this; + } + + @Override + public <T> Description appendValueList(String start, String separator, + String end, T... values) { + return this; + } + + @Override + public <T> Description appendValueList(String start, String separator, + String end, Iterable<T> values) { + return this; + } + + @Override + public String toString() { + return ""; + } + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/DiagnosingMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/DiagnosingMatcher.java new file mode 100644 index 0000000..f87de2d --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/DiagnosingMatcher.java @@ -0,0 +1,21 @@ +package org.hamcrest; + +/** + * TODO(ngd): Document. + * + * @param <T> + */ +public abstract class DiagnosingMatcher<T> extends BaseMatcher<T> { + + @Override + public final boolean matches(Object item) { + return matches(item, Description.NONE); + } + + @Override + public final void describeMismatch(Object item, Description mismatchDescription) { + matches(item, mismatchDescription); + } + + protected abstract boolean matches(Object item, Description mismatchDescription); +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/Factory.java b/hamcrest-core/src/main/java/org/hamcrest/Factory.java index a8bf5f9..99a5132 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/Factory.java +++ b/hamcrest-core/src/main/java/org/hamcrest/Factory.java @@ -1,17 +1,20 @@ package org.hamcrest; -import static java.lang.annotation.ElementType.METHOD; import java.lang.annotation.Retention; -import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Target; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + /** * Marks a Hamcrest static factory method so tools recognise them. * A factory method is an equivalent to a named constructor. - * + * + * @deprecated The code generator is no longer maintained. Write classes of syntactic sugar by hand. * @author Joe Walnes */ @Retention(RUNTIME) @Target({METHOD}) +@Deprecated public @interface Factory { } diff --git a/hamcrest-core/src/main/java/org/hamcrest/FeatureMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/FeatureMatcher.java new file mode 100644 index 0000000..385cf99 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/FeatureMatcher.java @@ -0,0 +1,54 @@ +package org.hamcrest; + +import org.hamcrest.internal.ReflectiveTypeFinder; + +/** + * Supporting class for matching a feature of an object. Implement <code>featureValueOf()</code> + * in a subclass to pull out the feature to be matched against. + * + * @param <T> The type of the object to be matched + * @param <U> The type of the feature to be matched + */ +public abstract class FeatureMatcher<T, U> extends TypeSafeDiagnosingMatcher<T> { + private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("featureValueOf", 1, 0); + private final Matcher<? super U> subMatcher; + private final String featureDescription; + private final String featureName; + + /** + * Constructor + * @param subMatcher The matcher to apply to the feature + * @param featureDescription Descriptive text to use in describeTo + * @param featureName Identifying text for mismatch message + */ + public FeatureMatcher(Matcher<? super U> subMatcher, String featureDescription, String featureName) { + super(TYPE_FINDER); + this.subMatcher = subMatcher; + this.featureDescription = featureDescription; + this.featureName = featureName; + } + + /** + * Implement this to extract the interesting feature. + * @param actual the target object + * @return the feature to be matched + */ + protected abstract U featureValueOf(T actual); + + @Override + protected boolean matchesSafely(T actual, Description mismatch) { + final U featureValue = featureValueOf(actual); + if (!subMatcher.matches(featureValue)) { + mismatch.appendText(featureName).appendText(" "); + subMatcher.describeMismatch(featureValue, mismatch); + return false; + } + return true; + } + + @Override + public final void describeTo(Description description) { + description.appendText(featureDescription).appendText(" ") + .appendDescriptionOf(subMatcher); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/Matcher.java b/hamcrest-core/src/main/java/org/hamcrest/Matcher.java index fd10207..3ac1a53 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/Matcher.java +++ b/hamcrest-core/src/main/java/org/hamcrest/Matcher.java @@ -1,32 +1,31 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest; /** + * <p> * A matcher over acceptable values. * A matcher is able to describe itself to give feedback when it fails. - * <p/> + * </p> + * <p> * Matcher implementations should <b>NOT directly implement this interface</b>. * Instead, <b>extend</b> the {@link BaseMatcher} abstract class, * which will ensure that the Matcher API can grow to support * new features and remain compatible with all Matcher implementations. - * <p/> - * For easy access to common Matcher implementations, use the static factory - * methods in {@link CoreMatchers}. + * </p> + * <p> + * N.B. Well designed matchers should be immutable. + * </p> * - * @see CoreMatchers * @see BaseMatcher */ -@SuppressWarnings({"unused", "UnusedDeclaration"}) public interface Matcher<T> extends SelfDescribing { /** * Evaluates the matcher for argument <var>item</var>. - * <p/> + * * This method matches against Object, instead of the generic type T. This is * because the caller of the Matcher does not know at runtime what the type is * (because of type erasure with Java generics). It is down to the implementations - * to check the correct type. + * to check the correct type. * * @param item the object against which the matcher is evaluated. * @return <code>true</code> if <var>item</var> matches, otherwise <code>false</code>. @@ -34,6 +33,19 @@ public interface Matcher<T> extends SelfDescribing { * @see BaseMatcher */ boolean matches(Object item); + + /** + * Generate a description of why the matcher has not accepted the item. + * The description will be part of a larger description of why a matching + * failed, so it should be concise. + * This method assumes that <code>matches(item)</code> is false, but + * will not check this. + * + * @param item The item that the Matcher has rejected. + * @param mismatchDescription + * The description to be built or appended to. + */ + void describeMismatch(Object item, Description mismatchDescription); /** * This method simply acts a friendly reminder not to implement Matcher directly and @@ -42,6 +54,8 @@ public interface Matcher<T> extends SelfDescribing { * * @see Matcher for reasons why. * @see BaseMatcher + * @deprecated to make */ + @Deprecated void _dont_implement_Matcher___instead_extend_BaseMatcher_(); } diff --git a/hamcrest-core/src/main/java/org/hamcrest/MatcherAssert.java b/hamcrest-core/src/main/java/org/hamcrest/MatcherAssert.java index 3eb234a..049e1df 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/MatcherAssert.java +++ b/hamcrest-core/src/main/java/org/hamcrest/MatcherAssert.java @@ -1,24 +1,27 @@ -/* Copyright (c) 2000-2006 hamcrest.org
- */
-package org.hamcrest;
-
-
-public class MatcherAssert {
- public static <T> void assertThat(T actual, Matcher<T> matcher) {
- assertThat("", actual, matcher);
- }
-
- public static <T> void assertThat(String reason, T actual, Matcher<T> matcher) {
- if (!matcher.matches(actual)) {
- Description description = new StringDescription();
- description.appendText(reason)
- .appendText("\nExpected: ")
- .appendDescriptionOf(matcher)
- .appendText("\n got: ")
- .appendValue(actual)
- .appendText("\n");
-
- throw new java.lang.AssertionError(description.toString());
- }
- }
-}
+package org.hamcrest; + + +public class MatcherAssert { + public static <T> void assertThat(T actual, Matcher<? super T> matcher) { + assertThat("", actual, matcher); + } + + public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher) { + if (!matcher.matches(actual)) { + Description description = new StringDescription(); + description.appendText(reason) + .appendText("\nExpected: ") + .appendDescriptionOf(matcher) + .appendText("\n but: "); + matcher.describeMismatch(actual, description); + + throw new AssertionError(description.toString()); + } + } + + public static void assertThat(String reason, boolean assertion) { + if (!assertion) { + throw new AssertionError(reason); + } + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/SelfDescribing.java b/hamcrest-core/src/main/java/org/hamcrest/SelfDescribing.java index cd53070..06b361d 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/SelfDescribing.java +++ b/hamcrest-core/src/main/java/org/hamcrest/SelfDescribing.java @@ -12,5 +12,5 @@ public interface SelfDescribing { * @param description * The description to be built or appended to. */ - void describeTo(Description description); + void describeTo(Description description); }
\ No newline at end of file diff --git a/hamcrest-core/src/main/java/org/hamcrest/StringDescription.java b/hamcrest-core/src/main/java/org/hamcrest/StringDescription.java index 66709ee..813c178 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/StringDescription.java +++ b/hamcrest-core/src/main/java/org/hamcrest/StringDescription.java @@ -1,60 +1,63 @@ -package org.hamcrest;
-
-import java.io.IOException;
-
-/**
- * A {@link Description} that is stored as a string.
- */
-public class StringDescription extends BaseDescription {
- private final Appendable out;
-
- public StringDescription() {
- this(new StringBuilder());
- }
-
- public StringDescription(Appendable out) {
- this.out = out;
- }
-
- /**
- * Return the description of a {@link SelfDescribing} object as a String.
- *
- * @param selfDescribing
- * The object to be described.
- * @return
- * The description of the object.
- */
- public static String toString(SelfDescribing value) {
- return new StringDescription().appendDescriptionOf(value).toString();
- }
-
- /**
- * Alias for {@link #toString(SelfDescribing)}.
- */
- public static String asString(SelfDescribing selfDescribing) {
- return toString(selfDescribing);
- }
-
- protected void append(String str) {
- try {
- out.append(str);
- } catch (IOException e) {
- throw new RuntimeException("Could not write description", e);
- }
- }
-
- protected void append(char c) {
- try {
- out.append(c);
- } catch (IOException e) {
- throw new RuntimeException("Could not write description", e);
- }
- }
-
- /**
- * Returns the description as a string.
- */
- public String toString() {
- return out.toString();
- }
-}
+package org.hamcrest; + +import java.io.IOException; + +/** + * A {@link Description} that is stored as a string. + */ +public class StringDescription extends BaseDescription { + private final Appendable out; + + public StringDescription() { + this(new StringBuilder()); + } + + public StringDescription(Appendable out) { + this.out = out; + } + + /** + * Return the description of a {@link SelfDescribing} object as a String. + * + * @param selfDescribing + * The object to be described. + * @return + * The description of the object. + */ + public static String toString(SelfDescribing selfDescribing) { + return new StringDescription().appendDescriptionOf(selfDescribing).toString(); + } + + /** + * Alias for {@link #toString(SelfDescribing)}. + */ + public static String asString(SelfDescribing selfDescribing) { + return toString(selfDescribing); + } + + @Override + protected void append(String str) { + try { + out.append(str); + } catch (IOException e) { + throw new RuntimeException("Could not write description", e); + } + } + + @Override + protected void append(char c) { + try { + out.append(c); + } catch (IOException e) { + throw new RuntimeException("Could not write description", e); + } + } + + /** + * Returns the description as a string. + */ + @Override + public String toString() { + return out.toString(); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/TypeSafeDiagnosingMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/TypeSafeDiagnosingMatcher.java new file mode 100644 index 0000000..c204120 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/TypeSafeDiagnosingMatcher.java @@ -0,0 +1,69 @@ +package org.hamcrest; + +import org.hamcrest.internal.ReflectiveTypeFinder; + + +/** + * Convenient base class for Matchers that require a non-null value of a specific type + * and that will report why the received value has been rejected. + * This implements the null check, checks the type and then casts. + * To use, implement <pre>matchesSafely()</pre>. + * + * @param <T> + * @author Neil Dunn + * @author Nat Pryce + * @author Steve Freeman + */ +public abstract class TypeSafeDiagnosingMatcher<T> extends BaseMatcher<T> { + private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("matchesSafely", 2, 0); + private final Class<?> expectedType; + + /** + * Subclasses should implement this. The item will already have been checked + * for the specific type and will never be null. + */ + protected abstract boolean matchesSafely(T item, Description mismatchDescription); + + /** + * Use this constructor if the subclass that implements <code>matchesSafely</code> + * is <em>not</em> the class that binds <T> to a type. + * @param expectedType The expectedType of the actual value. + */ + protected TypeSafeDiagnosingMatcher(Class<?> expectedType) { + this.expectedType = expectedType; + } + + /** + * Use this constructor if the subclass that implements <code>matchesSafely</code> + * is <em>not</em> the class that binds <T> to a type. + * @param typeFinder A type finder to extract the type + */ + protected TypeSafeDiagnosingMatcher(ReflectiveTypeFinder typeFinder) { + this.expectedType = typeFinder.findExpectedType(getClass()); + } + + /** + * The default constructor for simple sub types + */ + protected TypeSafeDiagnosingMatcher() { + this(TYPE_FINDER); + } + + @Override + @SuppressWarnings("unchecked") + public final boolean matches(Object item) { + return item != null + && expectedType.isInstance(item) + && matchesSafely((T) item, new Description.NullDescription()); + } + + @SuppressWarnings("unchecked") + @Override + public final void describeMismatch(Object item, Description mismatchDescription) { + if (item == null || !expectedType.isInstance(item)) { + super.describeMismatch(item, mismatchDescription); + } else { + matchesSafely((T) item, mismatchDescription); + } + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java index 7f18fd3..08dfce8 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java +++ b/hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java @@ -1,58 +1,85 @@ package org.hamcrest; -import java.lang.reflect.Method; +import org.hamcrest.internal.ReflectiveTypeFinder; /** * Convenient base class for Matchers that require a non-null value of a specific type. * This simply implements the null check, checks the type and then casts. * * @author Joe Walnes + * @author Steve Freeman + * @author Nat Pryce */ public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> { - - private Class expectedType; + private static final ReflectiveTypeFinder TYPE_FINDER = new ReflectiveTypeFinder("matchesSafely", 1, 0); + + final private Class<?> expectedType; /** - * Subclasses should implement this. The item will already have been checked for - * the specific type and will never be null. + * The default constructor for simple sub types */ - public abstract boolean matchesSafely(T item); - protected TypeSafeMatcher() { - expectedType = findExpectedType(getClass()); + this(TYPE_FINDER); } - - private static Class<?> findExpectedType(Class<?> fromClass) { - for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) { - for (Method method : c.getDeclaredMethods()) { - if (isMatchesSafelyMethod(method)) { - return method.getParameterTypes()[0]; - } - } - } - - throw new Error("Cannot determine correct type for matchesSafely() method."); + + /** + * Use this constructor if the subclass that implements <code>matchesSafely</code> + * is <em>not</em> the class that binds <T> to a type. + * @param expectedType The expectedType of the actual value. + */ + protected TypeSafeMatcher(Class<?> expectedType) { + this.expectedType = expectedType; } - private static boolean isMatchesSafelyMethod(Method method) { - return method.getName().equals("matchesSafely") - && method.getParameterTypes().length == 1 - && !method.isSynthetic(); + /** + * Use this constructor if the subclass that implements <code>matchesSafely</code> + * is <em>not</em> the class that binds <T> to a type. + * @param typeFinder A type finder to extract the type + */ + protected TypeSafeMatcher(ReflectiveTypeFinder typeFinder) { + this.expectedType = typeFinder.findExpectedType(getClass()); } + + /** + * Subclasses should implement this. The item will already have been checked for + * the specific type and will never be null. + */ + protected abstract boolean matchesSafely(T item); - protected TypeSafeMatcher(Class<T> expectedType) { - this.expectedType = expectedType; + /** + * Subclasses should override this. The item will already have been checked for + * the specific type and will never be null. + */ + protected void describeMismatchSafely(T item, Description mismatchDescription) { + super.describeMismatch(item, mismatchDescription); } - + /** - * Method made final to prevent accidental override. + * Methods made final to prevent accidental override. * If you need to override this, there's no point on extending TypeSafeMatcher. * Instead, extend the {@link BaseMatcher}. */ + @Override @SuppressWarnings({"unchecked"}) public final boolean matches(Object item) { return item != null && expectedType.isInstance(item) && matchesSafely((T) item); } + + @SuppressWarnings("unchecked") + @Override + final public void describeMismatch(Object item, Description description) { + if (item == null) { + super.describeMismatch(null, description); + } else if (! expectedType.isInstance(item)) { + description.appendText("was a ") + .appendText(item.getClass().getName()) + .appendText(" (") + .appendValue(item) + .appendText(")"); + } else { + describeMismatchSafely((T)item, description); + } + } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/AllOf.java b/hamcrest-core/src/main/java/org/hamcrest/core/AllOf.java index f619a7d..2cbe2e3 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/AllOf.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/AllOf.java @@ -1,51 +1,57 @@ package org.hamcrest.core; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Matcher; import org.hamcrest.Description; -import org.hamcrest.Factory; +import org.hamcrest.DiagnosingMatcher; +import org.hamcrest.Matcher; import java.util.Arrays; +import java.util.List; /** - * Calculates the logical conjunction of two matchers. Evaluation is - * shortcut, so that the second matcher is not called if the first - * matcher returns <code>false</code>. + * Calculates the logical conjunction of multiple matchers. Evaluation is shortcut, so + * subsequent matchers are not called if an earlier matcher returns <code>false</code>. */ -public class AllOf<T> extends BaseMatcher<T> { - private final Iterable<Matcher<? extends T>> matchers; +public class AllOf<T> extends DiagnosingMatcher<T> { - public AllOf(Iterable<Matcher<? extends T>> matchers) { + private final Iterable<Matcher<? super T>> matchers; + + public AllOf(Iterable<Matcher<? super T>> matchers) { this.matchers = matchers; } - public boolean matches(Object o) { - for (Matcher<? extends T> matcher : matchers) { + @Override + public boolean matches(Object o, Description mismatch) { + for (Matcher<? super T> matcher : matchers) { if (!matcher.matches(o)) { - return false; + mismatch.appendDescriptionOf(matcher).appendText(" "); + matcher.describeMismatch(o, mismatch); + return false; } } return true; } + @Override public void describeTo(Description description) { - description.appendList("(", " and ", ")", matchers); + description.appendList("(", " " + "and" + " ", ")", matchers); } /** - * Evaluates to true only if ALL of the passed in matchers evaluate to true. + * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ - @Factory - public static <T> Matcher<T> allOf(Matcher<? extends T>... matchers) { - return allOf(Arrays.asList(matchers)); + public static <T> Matcher<T> allOf(Iterable<Matcher<? super T>> matchers) { + return new AllOf<>(matchers); } /** - * Evaluates to true only if ALL of the passed in matchers evaluate to true. + * Creates a matcher that matches if the examined object matches <b>ALL</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", allOf(startsWith("my"), containsString("Val")))</pre> */ - @Factory - public static <T> Matcher<T> allOf(Iterable<Matcher<? extends T>> matchers) { - return new AllOf<T>(matchers); + @SafeVarargs + public static <T> Matcher<T> allOf(Matcher<? super T>... matchers) { + return allOf((List) Arrays.asList(matchers)); } - } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/AnyOf.java b/hamcrest-core/src/main/java/org/hamcrest/core/AnyOf.java index e7e9181..106a473 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/AnyOf.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/AnyOf.java @@ -1,51 +1,47 @@ package org.hamcrest.core; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Matcher; import org.hamcrest.Description; -import org.hamcrest.Factory; +import org.hamcrest.Matcher; import java.util.Arrays; +import java.util.List; /** - * Calculates the logical disjunction of two matchers. Evaluation is - * shortcut, so that the second matcher is not called if the first - * matcher returns <code>true</code>. + * Calculates the logical disjunction of multiple matchers. Evaluation is shortcut, so + * subsequent matchers are not called if an earlier matcher returns <code>true</code>. */ -public class AnyOf<T> extends BaseMatcher<T> { +public class AnyOf<T> extends ShortcutCombination<T> { - private final Iterable<Matcher<? extends T>> matchers; - - public AnyOf(Iterable<Matcher<? extends T>> matchers) { - this.matchers = matchers; + public AnyOf(Iterable<Matcher<? super T>> matchers) { + super(matchers); } + @Override public boolean matches(Object o) { - for (Matcher<? extends T> matcher : matchers) { - if (matcher.matches(o)) { - return true; - } - } - return false; + return matches(o, true); } + @Override public void describeTo(Description description) { - description.appendList("(", " or ", ")", matchers); + describeTo(description, "or"); } /** - * Evaluates to true if ANY of the passed in matchers evaluate to true. + * Creates a matcher that matches if the examined object matches <b>ANY</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", anyOf(startsWith("foo"), containsString("Val")))</pre> */ - @Factory - public static <T> Matcher<T> anyOf(Matcher<? extends T>... matchers) { - return anyOf(Arrays.asList(matchers)); + public static <T> AnyOf<T> anyOf(Iterable<Matcher<? super T>> matchers) { + return new AnyOf<>(matchers); } - + /** - * Evaluates to true if ANY of the passed in matchers evaluate to true. + * Creates a matcher that matches if the examined object matches <b>ANY</b> of the specified matchers. + * For example: + * <pre>assertThat("myValue", anyOf(startsWith("foo"), containsString("Val")))</pre> */ - @Factory - public static <T> Matcher<T> anyOf(Iterable<Matcher<? extends T>> matchers) { - return new AnyOf<T>(matchers); + @SafeVarargs + public static <T> AnyOf<T> anyOf(Matcher<? super T>... matchers) { + return anyOf((List) Arrays.asList(matchers)); } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/CombinableMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/core/CombinableMatcher.java new file mode 100644 index 0000000..2414bbb --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/CombinableMatcher.java @@ -0,0 +1,82 @@ +package org.hamcrest.core; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.util.ArrayList; + +public class CombinableMatcher<T> extends TypeSafeDiagnosingMatcher<T> { + private final Matcher<? super T> matcher; + + public CombinableMatcher(Matcher<? super T> matcher) { + this.matcher = matcher; + } + + @Override + protected boolean matchesSafely(T item, Description mismatch) { + if (!matcher.matches(item)) { + matcher.describeMismatch(item, mismatch); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendDescriptionOf(matcher); + } + + public CombinableMatcher<T> and(Matcher<? super T> other) { + return new CombinableMatcher<T>(new AllOf<T>(templatedListWith(other))); + } + + public CombinableMatcher<T> or(Matcher<? super T> other) { + return new CombinableMatcher<T>(new AnyOf<T>(templatedListWith(other))); + } + + private ArrayList<Matcher<? super T>> templatedListWith(Matcher<? super T> other) { + ArrayList<Matcher<? super T>> matchers = new ArrayList<Matcher<? super T>>(); + matchers.add(matcher); + matchers.add(other); + return matchers; + } + + /** + * Creates a matcher that matches when both of the specified matchers match the examined object. + * For example: + * <pre>assertThat("fab", both(containsString("a")).and(containsString("b")))</pre> + */ + public static <LHS> CombinableBothMatcher<LHS> both(Matcher<? super LHS> matcher) { + return new CombinableBothMatcher<LHS>(matcher); + } + + public static final class CombinableBothMatcher<X> { + private final Matcher<? super X> first; + public CombinableBothMatcher(Matcher<? super X> matcher) { + this.first = matcher; + } + public CombinableMatcher<X> and(Matcher<? super X> other) { + return new CombinableMatcher<X>(first).and(other); + } + } + + /** + * Creates a matcher that matches when either of the specified matchers match the examined object. + * For example: + * <pre>assertThat("fan", either(containsString("a")).or(containsString("b")))</pre> + */ + public static <LHS> CombinableEitherMatcher<LHS> either(Matcher<? super LHS> matcher) { + return new CombinableEitherMatcher<LHS>(matcher); + } + + public static final class CombinableEitherMatcher<X> { + private final Matcher<? super X> first; + public CombinableEitherMatcher(Matcher<? super X> matcher) { + this.first = matcher; + } + public CombinableMatcher<X> or(Matcher<? super X> other) { + return new CombinableMatcher<X>(first).or(other); + } + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/DescribedAs.java b/hamcrest-core/src/main/java/org/hamcrest/core/DescribedAs.java index 7b8c151..2387609 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/DescribedAs.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/DescribedAs.java @@ -1,55 +1,69 @@ -/* Copyright (c) 2000-2006 hamcrest.org
- */
-package org.hamcrest.core;
-
-import java.util.regex.Pattern;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.Factory;
-import org.hamcrest.BaseMatcher;
-
-/**
- * Provides a custom description to another matcher.
- */
-public class DescribedAs<T> extends BaseMatcher<T> {
- private final String descriptionTemplate;
- private final Matcher<T> matcher;
- private final Object[] values;
-
- private final static Pattern ARG_PATTERN = Pattern.compile("%([0-9]+)");
-
- public DescribedAs(String descriptionTemplate, Matcher<T> matcher, Object[] values) {
- this.descriptionTemplate = descriptionTemplate;
- this.matcher = matcher;
- this.values = values.clone();
- }
-
- public boolean matches(Object o) {
- return matcher.matches(o);
- }
-
- public void describeTo(Description description) {
- java.util.regex.Matcher arg = ARG_PATTERN.matcher(descriptionTemplate);
-
- int textStart = 0;
- while (arg.find()) {
- description.appendText(descriptionTemplate.substring(textStart, arg.start()));
- int argIndex = Integer.parseInt(arg.group(1));
- description.appendValue(values[argIndex]);
- textStart = arg.end();
- }
-
- if (textStart < descriptionTemplate.length()) {
- description.appendText(descriptionTemplate.substring(textStart));
- }
- }
-
- /**
- * Wraps an existing matcher and overrides the description when it fails.
- */
- @Factory
- public static <T> Matcher<T> describedAs(String description, Matcher<T> matcher, Object... values) {
- return new DescribedAs<T>(description, matcher, values);
- }
-}
+package org.hamcrest.core; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import java.util.regex.Pattern; + +import static java.lang.Integer.parseInt; + +/** + * Provides a custom description to another matcher. + */ +public class DescribedAs<T> extends BaseMatcher<T> { + private final String descriptionTemplate; + private final Matcher<T> matcher; + private final Object[] values; + + private final static Pattern ARG_PATTERN = Pattern.compile("%([0-9]+)"); + + public DescribedAs(String descriptionTemplate, Matcher<T> matcher, Object[] values) { + this.descriptionTemplate = descriptionTemplate; + this.matcher = matcher; + this.values = values.clone(); + } + + @Override + public boolean matches(Object o) { + return matcher.matches(o); + } + + @Override + public void describeTo(Description description) { + java.util.regex.Matcher arg = ARG_PATTERN.matcher(descriptionTemplate); + + int textStart = 0; + while (arg.find()) { + description.appendText(descriptionTemplate.substring(textStart, arg.start())); + description.appendValue(values[parseInt(arg.group(1))]); + textStart = arg.end(); + } + + if (textStart < descriptionTemplate.length()) { + description.appendText(descriptionTemplate.substring(textStart)); + } + } + + @Override + public void describeMismatch(Object item, Description description) { + matcher.describeMismatch(item, description); + } + + /** + * Wraps an existing matcher, overriding its description with that specified. All other functions are + * delegated to the decorated matcher, including its mismatch description. + * For example: + * <pre>describedAs("a big decimal equal to %0", equalTo(myBigDecimal), myBigDecimal.toPlainString())</pre> + * + * @param description + * the new description for the wrapped matcher + * @param matcher + * the matcher to wrap + * @param values + * optional values to insert into the tokenised description + */ + public static <T> Matcher<T> describedAs(String description, Matcher<T> matcher, Object... values) { + return new DescribedAs<T>(description, matcher, values); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/Every.java b/hamcrest-core/src/main/java/org/hamcrest/core/Every.java new file mode 100644 index 0000000..757b7b4 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/Every.java @@ -0,0 +1,44 @@ +package org.hamcrest.core; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +public class Every<T> extends TypeSafeDiagnosingMatcher<Iterable<? extends T>> { + private final Matcher<? super T> matcher; + + public Every(Matcher<? super T> matcher) { + this.matcher= matcher; + } + + @Override + public boolean matchesSafely(Iterable<? extends T> collection, Description mismatchDescription) { + for (T t : collection) { + if (!matcher.matches(t)) { + mismatchDescription.appendText("an item "); + matcher.describeMismatch(t, mismatchDescription); + return false; + } + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("every item is ").appendDescriptionOf(matcher); + } + + /** + * Creates a matcher for {@link Iterable}s that only matches when a single pass over the + * examined {@link Iterable} yields items that are all matched by the specified + * <code>itemMatcher</code>. + * For example: + * <pre>assertThat(Arrays.asList("bar", "baz"), everyItem(startsWith("ba")))</pre> + * + * @param itemMatcher + * the matcher to apply to every item provided by the examined {@link Iterable} + */ + public static <U> Matcher<Iterable<? extends U>> everyItem(final Matcher<U> itemMatcher) { + return new Every<U>(itemMatcher); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/Is.java b/hamcrest-core/src/main/java/org/hamcrest/core/Is.java index f9152e9..ec22238 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/Is.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/Is.java @@ -1,68 +1,76 @@ package org.hamcrest.core; -import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.hamcrest.core.IsEqual.equalTo; -import org.hamcrest.Factory; -import org.hamcrest.Matcher; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsInstanceOf.instanceOf; /** - * Decorates another Matcher, retaining the behavior but allowing tests + * Decorates another Matcher, retaining the behaviour but allowing tests * to be slightly more expressive. * - * eg. assertThat(cheese, equalTo(smelly)) - * vs assertThat(cheese, is(equalTo(smelly))) + * For example: assertThat(cheese, equalTo(smelly)) + * vs. assertThat(cheese, is(equalTo(smelly))) */ public class Is<T> extends BaseMatcher<T> { - private final Matcher<T> matcher; public Is(Matcher<T> matcher) { this.matcher = matcher; } + @Override public boolean matches(Object arg) { return matcher.matches(arg); } + @Override public void describeTo(Description description) { description.appendText("is ").appendDescriptionOf(matcher); } - + + @Override + public void describeMismatch(Object item, Description mismatchDescription) { + matcher.describeMismatch(item, mismatchDescription); + } + /** - * Decorates another Matcher, retaining the behavior but allowing tests + * Decorates another Matcher, retaining its behaviour, but allowing tests * to be slightly more expressive. - * - * eg. assertThat(cheese, equalTo(smelly)) - * vs assertThat(cheese, is(equalTo(smelly))) + * For example: + * <pre>assertThat(cheese, is(equalTo(smelly)))</pre> + * instead of: + * <pre>assertThat(cheese, equalTo(smelly))</pre> + * */ - @Factory public static <T> Matcher<T> is(Matcher<T> matcher) { return new Is<T>(matcher); } /** - * This is a shortcut to the frequently used is(equalTo(x)). - * - * eg. assertThat(cheese, is(equalTo(smelly))) - * vs assertThat(cheese, is(smelly)) + * A shortcut to the frequently used <code>is(equalTo(x))</code>. + * For example: + * <pre>assertThat(cheese, is(smelly))</pre> + * instead of: + * <pre>assertThat(cheese, is(equalTo(smelly)))</pre> + * */ - @Factory public static <T> Matcher<T> is(T value) { return is(equalTo(value)); } /** - * This is a shortcut to the frequently used is(instanceOf(SomeClass.class)). - * - * eg. assertThat(cheese, is(instanceOf(Cheddar.class))) - * vs assertThat(cheese, is(Cheddar.class)) + * A shortcut to the frequently used <code>is(instanceOf(SomeClass.class))</code>. + * For example: + * <pre>assertThat(cheese, isA(Cheddar.class))</pre> + * instead of: + * <pre>assertThat(cheese, is(instanceOf(Cheddar.class)))</pre> + * */ - @Factory - public static Matcher<Object> is(Class<?> type) { - return is(instanceOf(type)); + public static <T> Matcher<T> isA(Class<T> type) { + final Matcher<T> typeMatcher = instanceOf(type); + return is(typeMatcher); } - } - diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsAnything.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsAnything.java index c5ca49d..4c71a9b 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/IsAnything.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsAnything.java @@ -1,11 +1,8 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest.core; +import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.hamcrest.Factory; -import org.hamcrest.BaseMatcher; /** @@ -13,47 +10,41 @@ import org.hamcrest.BaseMatcher; */ public class IsAnything<T> extends BaseMatcher<T> { - private final String description; + private final String message; public IsAnything() { this("ANYTHING"); } - public IsAnything(String description) { - this.description = description; + public IsAnything(String message) { + this.message = message; } + @Override public boolean matches(Object o) { return true; } + @Override public void describeTo(Description description) { - description.appendText(this.description); + description.appendText(message); } /** - * This matcher always evaluates to true. + * Creates a matcher that always matches, regardless of the examined object. */ - @Factory - public static <T> Matcher<T> anything() { - return new IsAnything<T>(); + public static Matcher<Object> anything() { + return new IsAnything<Object>(); } /** - * This matcher always evaluates to true. + * Creates a matcher that always matches, regardless of the examined object, but describes + * itself with the specified {@link String}. * - * @param description A meaningful string used when describing itself. - */ - @Factory - public static <T> Matcher<T> anything(String description) { - return new IsAnything<T>(description); - } - - /** - * This matcher always evaluates to true. With type inference. + * @param description + * a meaningful {@link String} used when describing itself */ - @Factory - public static <T> Matcher<T> any(@SuppressWarnings("unused")Class<T> type) { - return new IsAnything<T>(); + public static Matcher<Object> anything(String description) { + return new IsAnything<Object>(description); } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsCollectionContaining.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsCollectionContaining.java new file mode 100644 index 0000000..c55853d --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsCollectionContaining.java @@ -0,0 +1,133 @@ +package org.hamcrest.core; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.IsEqual.equalTo; + +public class IsCollectionContaining<T> extends TypeSafeDiagnosingMatcher<Iterable<? super T>> { + private final Matcher<? super T> elementMatcher; + + public IsCollectionContaining(Matcher<? super T> elementMatcher) { + this.elementMatcher = elementMatcher; + } + + @Override + protected boolean matchesSafely(Iterable<? super T> collection, Description mismatchDescription) { + if (isEmpty(collection)) { + mismatchDescription.appendText("was empty"); + return false; + } + + for (Object item : collection) { + if (elementMatcher.matches(item)) { + return true; + } + } + + mismatchDescription.appendText("mismatches were: ["); + boolean isPastFirst = false; + for (Object item : collection) { + if (isPastFirst) { + mismatchDescription.appendText(", "); + } + elementMatcher.describeMismatch(item, mismatchDescription); + isPastFirst = true; + } + mismatchDescription.appendText("]"); + return false; + } + + private boolean isEmpty(Iterable<? super T> iterable) { + return ! iterable.iterator().hasNext(); + } + + @Override + public void describeTo(Description description) { + description + .appendText("a collection containing ") + .appendDescriptionOf(elementMatcher); + } + + + /** + * Creates a matcher for {@link Iterable}s that only matches when a single pass over the + * examined {@link Iterable} yields at least one item that is matched by the specified + * <code>itemMatcher</code>. Whilst matching, the traversal of the examined {@link Iterable} + * will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar"), hasItem(startsWith("ba")))</pre> + * + * @param itemMatcher + * the matcher to apply to items provided by the examined {@link Iterable} + */ + public static <T> Matcher<Iterable<? super T>> hasItem(Matcher<? super T> itemMatcher) { + return new IsCollectionContaining<>(itemMatcher); + } + + /** + * Creates a matcher for {@link Iterable}s that only matches when a single pass over the + * examined {@link Iterable} yields at least one item that is equal to the specified + * <code>item</code>. Whilst matching, the traversal of the examined {@link Iterable} + * will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar"), hasItem("bar"))</pre> + * + * @param item + * the item to compare against the items provided by the examined {@link Iterable} + */ + public static <T> Matcher<Iterable<? super T>> hasItem(T item) { + // Doesn't forward to hasItem() method so compiler can sort out generics. + return new IsCollectionContaining<>(equalTo(item)); + } + + /** + * Creates a matcher for {@link Iterable}s that matches when consecutive passes over the + * examined {@link Iterable} yield at least one item that is matched by the corresponding + * matcher from the specified <code>itemMatchers</code>. Whilst matching, each traversal of + * the examined {@link Iterable} will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems(endsWith("z"), endsWith("o")))</pre> + * + * @param itemMatchers + * the matchers to apply to items provided by the examined {@link Iterable} + */ + @SafeVarargs + public static <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... itemMatchers) { + List<Matcher<? super Iterable<T>>> all = new ArrayList<>(itemMatchers.length); + + for (Matcher<? super T> elementMatcher : itemMatchers) { + // Doesn't forward to hasItem() method so compiler can sort out generics. + all.add(new IsCollectionContaining<>(elementMatcher)); + } + + return allOf(all); + } + + /** + * Creates a matcher for {@link Iterable}s that matches when consecutive passes over the + * examined {@link Iterable} yield at least one item that is equal to the corresponding + * item from the specified <code>items</code>. Whilst matching, each traversal of the + * examined {@link Iterable} will stop as soon as a matching item is found. + * For example: + * <pre>assertThat(Arrays.asList("foo", "bar", "baz"), hasItems("baz", "foo"))</pre> + * + * @param items + * the items to compare against the items provided by the examined {@link Iterable} + */ + @SafeVarargs + public static <T> Matcher<Iterable<T>> hasItems(T... items) { + List<Matcher<? super Iterable<T>>> all = new ArrayList<>(items.length); + for (T item : items) { + all.add(hasItem(item)); + } + + return allOf(all); + } + +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsEqual.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsEqual.java index b9f17c5..860e85e 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/IsEqual.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsEqual.java @@ -1,11 +1,8 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest.core; +import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.hamcrest.Factory; -import org.hamcrest.BaseMatcher; import java.lang.reflect.Array; @@ -15,42 +12,47 @@ import java.lang.reflect.Array; * {@link java.lang.Object#equals} invokedMethod? */ public class IsEqual<T> extends BaseMatcher<T> { - private final Object object; + private final Object expectedValue; public IsEqual(T equalArg) { - object = equalArg; + expectedValue = equalArg; } - public boolean matches(Object arg) { - return areEqual(object, arg); + @Override + public boolean matches(Object actualValue) { + return areEqual(actualValue, expectedValue); } + @Override public void describeTo(Description description) { - description.appendValue(object); + description.appendValue(expectedValue); } - private static boolean areEqual(Object o1, Object o2) { - if (o1 == null || o2 == null) { - return o1 == null && o2 == null; - } else if (isArray(o1)) { - return isArray(o2) && areArraysEqual(o1, o2); - } else { - return o1.equals(o2); + private static boolean areEqual(Object actual, Object expected) { + if (actual == null) { + return expected == null; + } + + if (expected != null && isArray(actual)) { + return isArray(expected) && areArraysEqual(actual, expected); } + + return actual.equals(expected); } - private static boolean areArraysEqual(Object o1, Object o2) { - return areArrayLengthsEqual(o1, o2) - && areArrayElementsEqual(o1, o2); + private static boolean areArraysEqual(Object actualArray, Object expectedArray) { + return areArrayLengthsEqual(actualArray, expectedArray) && areArrayElementsEqual(actualArray, expectedArray); } - private static boolean areArrayLengthsEqual(Object o1, Object o2) { - return Array.getLength(o1) == Array.getLength(o2); + private static boolean areArrayLengthsEqual(Object actualArray, Object expectedArray) { + return Array.getLength(actualArray) == Array.getLength(expectedArray); } - private static boolean areArrayElementsEqual(Object o1, Object o2) { - for (int i = 0; i < Array.getLength(o1); i++) { - if (!areEqual(Array.get(o1, i), Array.get(o2, i))) return false; + private static boolean areArrayElementsEqual(Object actualArray, Object expectedArray) { + for (int i = 0; i < Array.getLength(actualArray); i++) { + if (!areEqual(Array.get(actualArray, i), Array.get(expectedArray, i))) { + return false; + } } return true; } @@ -60,12 +62,36 @@ public class IsEqual<T> extends BaseMatcher<T> { } /** - * Is the value equal to another value, as tested by the - * {@link java.lang.Object#equals} invokedMethod? + * Creates a matcher that matches when the examined object is logically equal to the specified + * <code>operand</code>, as determined by calling the {@link java.lang.Object#equals} method on + * the <b>examined</b> object. + * + * <p>If the specified operand is <code>null</code> then the created matcher will only match if + * the examined object's <code>equals</code> method returns <code>true</code> when passed a + * <code>null</code> (which would be a violation of the <code>equals</code> contract), unless the + * examined object itself is <code>null</code>, in which case the matcher will return a positive + * match.</p> + * + * <p>The created matcher provides a special behaviour when examining <code>Array</code>s, whereby + * it will match if both the operand and the examined object are arrays of the same length and + * contain items that are equal to each other (according to the above rules) <b>in the same + * indexes</b>.</p> + * For example: + * <pre> + * assertThat("foo", equalTo("foo")); + * assertThat(new String[] {"foo", "bar"}, equalTo(new String[] {"foo", "bar"})); + * </pre> + * */ - @Factory public static <T> Matcher<T> equalTo(T operand) { return new IsEqual<T>(operand); } - + + /** + * Creates an {@link org.hamcrest.core.IsEqual} matcher that does not enforce the values being + * compared to be of the same static type. + */ + public static Matcher<Object> equalToObject(Object operand) { + return new IsEqual<Object>(operand); + } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsInstanceOf.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsInstanceOf.java index df20824..5a508c9 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/IsInstanceOf.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsInstanceOf.java @@ -1,44 +1,91 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest.core; import org.hamcrest.Description; +import org.hamcrest.DiagnosingMatcher; import org.hamcrest.Matcher; -import org.hamcrest.Factory; -import org.hamcrest.BaseMatcher; /** * Tests whether the value is an instance of a class. + * Classes of basic types will be converted to the relevant "Object" classes */ -public class IsInstanceOf extends BaseMatcher<Object> { - private final Class<?> theClass; +public class IsInstanceOf extends DiagnosingMatcher<Object> { + private final Class<?> expectedClass; + private final Class<?> matchableClass; /** * Creates a new instance of IsInstanceOf * - * @param theClass The predicate evaluates to true for instances of this class + * @param expectedClass The predicate evaluates to true for instances of this class * or one of its subclasses. */ - public IsInstanceOf(Class<?> theClass) { - this.theClass = theClass; + public IsInstanceOf(Class<?> expectedClass) { + this.expectedClass = expectedClass; + this.matchableClass = matchableClass(expectedClass); + } + + private static Class<?> matchableClass(Class<?> expectedClass) { + if (boolean.class.equals(expectedClass)) return Boolean.class; + if (byte.class.equals(expectedClass)) return Byte.class; + if (char.class.equals(expectedClass)) return Character.class; + if (double.class.equals(expectedClass)) return Double.class; + if (float.class.equals(expectedClass)) return Float.class; + if (int.class.equals(expectedClass)) return Integer.class; + if (long.class.equals(expectedClass)) return Long.class; + if (short.class.equals(expectedClass)) return Short.class; + return expectedClass; } - public boolean matches(Object item) { - return theClass.isInstance(item); + @Override + protected boolean matches(Object item, Description mismatch) { + if (null == item) { + mismatch.appendText("null"); + return false; + } + + if (!matchableClass.isInstance(item)) { + mismatch.appendValue(item).appendText(" is a " + item.getClass().getName()); + return false; + } + + return true; } + @Override public void describeTo(Description description) { - description.appendText("an instance of ") - .appendText(theClass.getName()); + description.appendText("an instance of ").appendText(expectedClass.getName()); } /** - * Is the value an instance of a particular type? + * Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>, + * as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the + * the examined object. + * + * <p>The created matcher assumes no relationship between specified type and the examined object.</p> + * For example: + * <pre>assertThat(new Canoe(), instanceOf(Paddlable.class));</pre> + * + */ + @SuppressWarnings("unchecked") + public static <T> Matcher<T> instanceOf(Class<?> type) { + return (Matcher<T>) new IsInstanceOf(type); + } + + /** + * Creates a matcher that matches when the examined object is an instance of the specified <code>type</code>, + * as determined by calling the {@link java.lang.Class#isInstance(Object)} method on that type, passing the + * the examined object. + * + * <p>The created matcher forces a relationship between specified type and the examined object, and should be + * used when it is necessary to make generics conform, for example in the JMock clause + * <code>with(any(Thing.class))</code></p> + * For example: + * <pre>assertThat(new Canoe(), instanceOf(Canoe.class));</pre> + * */ - @Factory - public static Matcher<Object> instanceOf(Class<?> type) { - return new IsInstanceOf(type); + @SuppressWarnings("unchecked") + public static <T> Matcher<T> any(Class<T> type) { + return (Matcher<T>) new IsInstanceOf(type); } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsNot.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsNot.java index cb6946c..d5cf9c0 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/IsNot.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsNot.java @@ -1,49 +1,57 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest.core; -import static org.hamcrest.core.IsEqual.equalTo; +import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.hamcrest.Factory; -import org.hamcrest.BaseMatcher; + +import static org.hamcrest.core.IsEqual.equalTo; /** * Calculates the logical negation of a matcher. */ -public class IsNot<T> extends BaseMatcher<T> { +public class IsNot<T> extends BaseMatcher<T> { private final Matcher<T> matcher; public IsNot(Matcher<T> matcher) { this.matcher = matcher; } + @Override public boolean matches(Object arg) { return !matcher.matches(arg); } + @Override public void describeTo(Description description) { description.appendText("not ").appendDescriptionOf(matcher); } + /** - * Inverts the rule. + * Creates a matcher that wraps an existing matcher, but inverts the logic by which + * it will match. + * For example: + * <pre>assertThat(cheese, is(not(equalTo(smelly))))</pre> + * + * @param matcher + * the matcher whose sense should be inverted */ - @Factory public static <T> Matcher<T> not(Matcher<T> matcher) { return new IsNot<T>(matcher); } /** - * This is a shortcut to the frequently used not(equalTo(x)). - * - * eg. assertThat(cheese, is(not(equalTo(smelly)))) - * vs assertThat(cheese, is(not(smelly))) + * A shortcut to the frequently used <code>not(equalTo(x))</code>. + * For example: + * <pre>assertThat(cheese, is(not(smelly)))</pre> + * instead of: + * <pre>assertThat(cheese, is(not(equalTo(smelly))))</pre> + * + * @param value + * the value that any examined object should <b>not</b> equal */ - @Factory public static <T> Matcher<T> not(T value) { return not(equalTo(value)); } - } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsNull.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsNull.java index 737dcf2..9ebf080 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/IsNull.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsNull.java @@ -1,55 +1,74 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest.core; -import static org.hamcrest.core.IsNot.not; +import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.hamcrest.Factory; -import org.hamcrest.BaseMatcher; + +import static org.hamcrest.core.IsNot.not; /** * Is the value null? */ public class IsNull<T> extends BaseMatcher<T> { + @Override public boolean matches(Object o) { return o == null; } + @Override public void describeTo(Description description) { description.appendText("null"); } /** - * Matches if value is null. + * Creates a matcher that matches if examined object is <code>null</code>. + * For example: + * <pre>assertThat(cheese, is(nullValue())</pre> + * */ - @Factory - public static <T> Matcher<T> nullValue() { - return new IsNull<T>(); + public static Matcher<Object> nullValue() { + return new IsNull<Object>(); } /** - * Matches if value is not null. + * A shortcut to the frequently used <code>not(nullValue())</code>. + * For example: + * <pre>assertThat(cheese, is(notNullValue()))</pre> + * instead of: + * <pre>assertThat(cheese, is(not(nullValue())))</pre> + * */ - @Factory - public static <T> Matcher<T> notNullValue() { - return not(IsNull.<T>nullValue()); + public static Matcher<Object> notNullValue() { + return not(nullValue()); } /** - * Matches if value is null. With type inference. + * Creates a matcher that matches if examined object is <code>null</code>. Accepts a + * single dummy argument to facilitate type inference. + * For example: + * <pre>assertThat(cheese, is(nullValue(Cheese.class))</pre> + * + * @param type + * dummy parameter used to infer the generic type of the returned matcher */ - @Factory - public static <T> Matcher<T> nullValue(@SuppressWarnings("unused") Class<T> type) { - return nullValue(); + public static <T> Matcher<T> nullValue(Class<T> type) { + return new IsNull<T>(); } /** - * Matches if value is not null. With type inference. + * A shortcut to the frequently used <code>not(nullValue(X.class)). Accepts a + * single dummy argument to facilitate type inference.</code>. + * For example: + * <pre>assertThat(cheese, is(notNullValue(X.class)))</pre> + * instead of: + * <pre>assertThat(cheese, is(not(nullValue(X.class))))</pre> + * + * @param type + * dummy parameter used to infer the generic type of the returned matcher + * */ - @Factory - public static <T> Matcher<T> notNullValue(@SuppressWarnings("unused") Class<T> type) { - return notNullValue(); + public static <T> Matcher<T> notNullValue(Class<T> type) { + return not(nullValue(type)); } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/IsSame.java b/hamcrest-core/src/main/java/org/hamcrest/core/IsSame.java index b3ad77e..cbc3971 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/core/IsSame.java +++ b/hamcrest-core/src/main/java/org/hamcrest/core/IsSame.java @@ -1,11 +1,8 @@ -/* Copyright (c) 2000-2006 hamcrest.org - */ package org.hamcrest.core; +import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.hamcrest.Factory; -import org.hamcrest.BaseMatcher; /** @@ -13,28 +10,42 @@ import org.hamcrest.BaseMatcher; */ public class IsSame<T> extends BaseMatcher<T> { private final T object; - + public IsSame(T object) { this.object = object; } + @Override public boolean matches(Object arg) { return arg == object; } + @Override public void describeTo(Description description) { - description.appendText("same(") .appendValue(object) .appendText(")"); + description.appendText("sameInstance(") + .appendValue(object) + .appendText(")"); } - + /** - * Creates a new instance of IsSame + * Creates a matcher that matches only when the examined object is the same instance as + * the specified target object. * - * @param object The predicate evaluates to true only when the argument is - * this object. + * @param target + * the target instance against which others should be assessed */ - @Factory - public static <T> Matcher<T> sameInstance(T object) { - return new IsSame<T>(object); + public static <T> Matcher<T> sameInstance(T target) { + return new IsSame<T>(target); + } + + /** + * Creates a matcher that matches only when the examined object is the same instance as + * the specified target object. + * + * @param target + * the target instance against which others should be assessed + */ + public static <T> Matcher<T> theInstance(T target) { + return new IsSame<T>(target); } - } diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/ShortcutCombination.java b/hamcrest-core/src/main/java/org/hamcrest/core/ShortcutCombination.java new file mode 100644 index 0000000..30b33af --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/ShortcutCombination.java @@ -0,0 +1,33 @@ +package org.hamcrest.core; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +abstract class ShortcutCombination<T> extends BaseMatcher<T> { + + private final Iterable<Matcher<? super T>> matchers; + + public ShortcutCombination(Iterable<Matcher<? super T>> matchers) { + this.matchers = matchers; + } + + @Override + public abstract boolean matches(Object o); + + @Override + public abstract void describeTo(Description description); + + protected boolean matches(Object o, boolean shortcut) { + for (Matcher<? super T> matcher : matchers) { + if (matcher.matches(o) == shortcut) { + return shortcut; + } + } + return !shortcut; + } + + public void describeTo(Description description, String operator) { + description.appendList("(", " " + operator + " ", ")", matchers); + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/StringContains.java b/hamcrest-core/src/main/java/org/hamcrest/core/StringContains.java new file mode 100644 index 0000000..9e0a4ab --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/StringContains.java @@ -0,0 +1,46 @@ +package org.hamcrest.core; + +import org.hamcrest.Matcher; + +/** + * Tests if the argument is a string that contains a substring. + */ +public class StringContains extends SubstringMatcher { + public StringContains(boolean ignoringCase, String substring) { + super("containing", ignoringCase, substring); + } + + @Override + protected boolean evalSubstringOf(String s) { + return converted(s).contains(converted(substring)); + } + + /** + * Creates a matcher that matches if the examined {@link String} contains the specified + * {@link String} anywhere. + * For example: + * <pre>assertThat("myStringOfNote", containsString("ring"))</pre> + * + * @param substring + * the substring that the returned matcher will expect to find within any examined string + * + */ + public static Matcher<String> containsString(String substring) { + return new StringContains(false, substring); + } + + /** + * Creates a matcher that matches if the examined {@link String} contains the specified + * {@link String} anywhere, ignoring case. + * For example: + * <pre>assertThat("myStringOfNote", containsString("ring"))</pre> + * + * @param substring + * the substring that the returned matcher will expect to find within any examined string + * + */ + public static Matcher<String> containsStringIgnoringCase(String substring) { + return new StringContains(true, substring); + } + +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/StringEndsWith.java b/hamcrest-core/src/main/java/org/hamcrest/core/StringEndsWith.java new file mode 100644 index 0000000..6a6d1a0 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/StringEndsWith.java @@ -0,0 +1,42 @@ +package org.hamcrest.core; + +import org.hamcrest.Matcher; + +/** + * Tests if the argument is a string that contains a substring. + */ +public class StringEndsWith extends SubstringMatcher { + public StringEndsWith(boolean ignoringCase, String substring) { super("ending with", ignoringCase, substring); } + + @Override + protected boolean evalSubstringOf(String s) { + return converted(s).endsWith(converted(substring)); + } + + /** + * Creates a matcher that matches if the examined {@link String} ends with the specified + * {@link String}. + * For example: + * <pre>assertThat("myStringOfNote", endsWith("Note"))</pre> + * + * @param suffix + * the substring that the returned matcher will expect at the end of any examined string + */ + public static Matcher<String> endsWith(String suffix) { + return new StringEndsWith(false, suffix); + } + + /** + * Creates a matcher that matches if the examined {@link String} ends with the specified + * {@link String}, ignoring case. + * For example: + * <pre>assertThat("myStringOfNote", endsWith("Note"))</pre> + * + * @param suffix + * the substring that the returned matcher will expect at the end of any examined string + */ + public static Matcher<String> endsWithIgnoringCase(String suffix) { + return new StringEndsWith(true, suffix); + } + +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/StringStartsWith.java b/hamcrest-core/src/main/java/org/hamcrest/core/StringStartsWith.java new file mode 100644 index 0000000..fe7b990 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/StringStartsWith.java @@ -0,0 +1,40 @@ +package org.hamcrest.core; + +import org.hamcrest.Matcher; + +/** + * Tests if the argument is a string that contains a substring. + */ +public class StringStartsWith extends SubstringMatcher { + public StringStartsWith(boolean ignoringCase, String substring) { super("starting with", ignoringCase, substring); } + + @Override + protected boolean evalSubstringOf(String s) { return converted(s).startsWith(converted(substring)); } + + /** + * <p> + * Creates a matcher that matches if the examined {@link String} starts with the specified + * {@link String}. + * </p> + * For example: + * <pre>assertThat("myStringOfNote", startsWith("my"))</pre> + * + * @param prefix + * the substring that the returned matcher will expect at the start of any examined string + */ + public static Matcher<String> startsWith(String prefix) { return new StringStartsWith(false, prefix); } + + /** + * <p> + * Creates a matcher that matches if the examined {@link String} starts with the specified + * {@link String}, ignoring case + * </p> + * For example: + * <pre>assertThat("myStringOfNote", startsWith("my"))</pre> + * + * @param prefix + * the substring that the returned matcher will expect at the start of any examined string + */ + public static Matcher<String> startsWithIgnoringCase(String prefix) { return new StringStartsWith(true, prefix); } + +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/core/SubstringMatcher.java b/hamcrest-core/src/main/java/org/hamcrest/core/SubstringMatcher.java new file mode 100644 index 0000000..85c6657 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/core/SubstringMatcher.java @@ -0,0 +1,44 @@ +package org.hamcrest.core; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; + +public abstract class SubstringMatcher extends TypeSafeMatcher<String> { + + // TODO: Replace String with CharSequence to allow for easy interoperability between + // String, StringBuffer, StringBuilder, CharBuffer, etc (joe). + + private final String relationship; + private final boolean ignoringCase; + protected final String substring; + + protected SubstringMatcher(String relationship, boolean ignoringCase, String substring) { + this.relationship = relationship; + this.ignoringCase = ignoringCase; + this.substring = substring; + } + + @Override + public boolean matchesSafely(String item) { + return evalSubstringOf(ignoringCase ? item.toLowerCase() :item); + } + @Override + public void describeMismatchSafely(String item, Description mismatchDescription) { + mismatchDescription.appendText("was \"").appendText(item).appendText("\""); + } + + @Override + public void describeTo(Description description) { + description.appendText("a string ") + .appendText(relationship) + .appendText(" ") + .appendValue(substring); + if (ignoringCase) { + description.appendText(" ignoring case"); + } + } + + protected String converted(String arg) { return ignoringCase ? arg.toLowerCase() : arg; } + protected abstract boolean evalSubstringOf(String string); + +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/ArrayIterator.java b/hamcrest-core/src/main/java/org/hamcrest/internal/ArrayIterator.java index 093cdba..03e4c43 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/internal/ArrayIterator.java +++ b/hamcrest-core/src/main/java/org/hamcrest/internal/ArrayIterator.java @@ -4,25 +4,28 @@ import java.lang.reflect.Array; import java.util.Iterator; public class ArrayIterator implements Iterator<Object> { - private final Object array; - private int currentIndex = 0; - - public ArrayIterator(Object array) { - if (!array.getClass().isArray()) { - throw new IllegalArgumentException("not an array"); - } - this.array = array; - } - - public boolean hasNext() { - return currentIndex < Array.getLength(array); - } + private final Object array; + private int currentIndex = 0; + + public ArrayIterator(Object array) { + if (!array.getClass().isArray()) { + throw new IllegalArgumentException("not an array"); + } + this.array = array; + } + + @Override + public boolean hasNext() { + return currentIndex < Array.getLength(array); + } - public Object next() { - return Array.get(array, currentIndex++); - } - - public void remove() { - throw new UnsupportedOperationException("cannot remove items from an array"); - } + @Override + public Object next() { + return Array.get(array, currentIndex++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("cannot remove items from an array"); + } } diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/NullSafety.java b/hamcrest-core/src/main/java/org/hamcrest/internal/NullSafety.java new file mode 100644 index 0000000..9310abf --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/internal/NullSafety.java @@ -0,0 +1,18 @@ +package org.hamcrest.internal; + +import org.hamcrest.Matcher; +import org.hamcrest.core.IsNull; + +import java.util.ArrayList; +import java.util.List; + +public class NullSafety { + @SuppressWarnings("unchecked") + public static <E> List<Matcher<? super E>> nullSafe(Matcher<? super E>[] itemMatchers) { + final List<Matcher<? super E>> matchers = new ArrayList<Matcher<? super E>>(itemMatchers.length); + for (final Matcher<? super E> itemMatcher : itemMatchers) { + matchers.add((Matcher<? super E>) (itemMatcher == null ? IsNull.nullValue() : itemMatcher)); + } + return matchers; + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java b/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java new file mode 100644 index 0000000..ada74d6 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java @@ -0,0 +1,70 @@ +/** + * The TypeSafe classes, and their descendants, need a mechanism to find out what type has been used as a parameter + * for the concrete matcher. Unfortunately, this type is lost during type erasure so we need to use reflection + * to get it back, by picking out the type of a known parameter to a known method. + * The catch is that, with bridging methods, this type is only visible in the class that actually implements + * the expected method, so the ReflectiveTypeFinder needs to be applied to that class or a subtype. + * + * For example, the abstract <code>TypeSafeDiagnosingMatcher<T></code> defines an abstract method + * <pre>protected abstract boolean matchesSafely(T item, Description mismatchDescription);</pre> + * By default it uses <code>new ReflectiveTypeFinder("matchesSafely", 2, 0); </code> to find the + * parameterised type. If we create a <code>TypeSafeDiagnosingMatcher<String></code>, the type + * finder will return <code>String.class</code>. + * + * A <code>FeatureMatcher</code> is an abstract subclass of <code>TypeSafeDiagnosingMatcher</code>. + * Although it has a templated implementation of <code>matchesSafely(<T>, Description);</code>, the + * actual run-time signature of this is <code>matchesSafely(Object, Description);</code>. Instead, + * we must find the type by reflecting on the concrete implementation of + * <pre>protected abstract U featureValueOf(T actual);</pre> + * a method which is declared in <code>FeatureMatcher</code>. + * + * In short, use this to extract a type from a method in the leaf class of a templated class hierarchy. + * + * @author Steve Freeman + * @author Nat Pryce + */ +package org.hamcrest.internal; + +import java.lang.reflect.Method; + +public class ReflectiveTypeFinder { + private final String methodName; + private final int expectedNumberOfParameters; + private final int typedParameter; + + public ReflectiveTypeFinder(String methodName, int expectedNumberOfParameters, int typedParameter) { + this.methodName = methodName; + this.expectedNumberOfParameters = expectedNumberOfParameters; + this.typedParameter = typedParameter; + } + + public Class<?> findExpectedType(Class<?> fromClass) { + for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) { + for (Method method : c.getDeclaredMethods()) { + if (canObtainExpectedTypeFrom(method)) { + return expectedTypeFrom(method); + } + } + } + throw new Error("Cannot determine correct type for " + methodName + "() method."); + } + + /** + * @param method The method to examine. + * @return true if this method references the relevant type + */ + protected boolean canObtainExpectedTypeFrom(Method method) { + return method.getName().equals(methodName) + && method.getParameterTypes().length == expectedNumberOfParameters + && !method.isSynthetic(); + } + + + /** + * @param method The method from which to extract + * @return The type we're looking for + */ + protected Class<?> expectedTypeFrom(Method method) { + return method.getParameterTypes()[typedParameter]; + } +} diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValue.java b/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValue.java index 0634527..6537018 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValue.java +++ b/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValue.java @@ -10,6 +10,7 @@ public class SelfDescribingValue<T> implements SelfDescribing { this.value = value; } + @Override public void describeTo(Description description) { description.appendValue(value); } diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValueIterator.java b/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValueIterator.java index 58bedf6..bc8f8f4 100644 --- a/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValueIterator.java +++ b/hamcrest-core/src/main/java/org/hamcrest/internal/SelfDescribingValueIterator.java @@ -1,9 +1,9 @@ package org.hamcrest.internal; -import java.util.Iterator; - import org.hamcrest.SelfDescribing; +import java.util.Iterator; + public class SelfDescribingValueIterator<T> implements Iterator<SelfDescribing> { private Iterator<T> values; @@ -11,14 +11,17 @@ public class SelfDescribingValueIterator<T> implements Iterator<SelfDescribing> this.values = values; } + @Override public boolean hasNext() { return values.hasNext(); } + @Override public SelfDescribing next() { return new SelfDescribingValue<T>(values.next()); } + @Override public void remove() { values.remove(); } diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/package.html b/hamcrest-core/src/main/java/org/hamcrest/internal/package.html deleted file mode 100644 index 1c9bf9d..0000000 --- a/hamcrest-core/src/main/java/org/hamcrest/internal/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body> - {@hide} -</body> -</html> diff --git a/hamcrest-core/src/main/java/org/hamcrest/package.html b/hamcrest-core/src/main/java/org/hamcrest/package.html deleted file mode 100644 index 143c704..0000000 --- a/hamcrest-core/src/main/java/org/hamcrest/package.html +++ /dev/null @@ -1,9 +0,0 @@ -<html> -<head> -</head> -<body> - <p>The stable API defining Matcher and its associated interfaces and classes. - Hamcrest sub-projects define their convenience classes in the org.hamcrest package. - </p> -</body> -</html> |