aboutsummaryrefslogtreecommitdiff
path: root/hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java')
-rw-r--r--hamcrest-core/src/main/java/org/hamcrest/TypeSafeMatcher.java81
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 &lt;T&gt; 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 &lt;T&gt; 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);
+ }
+ }
}