diff options
Diffstat (limited to 'hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java')
-rw-r--r-- | hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java | 81 |
1 files changed, 54 insertions, 27 deletions
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); + } + } } |