01: package org.junit.matchers;
02:
03: import java.lang.reflect.Method;
04:
05: import org.hamcrest.BaseMatcher;
06:
07: /**
08: * Convenient base class for Matchers that require a non-null value of a specific type.
09: * This simply implements the null check, checks the type and then casts.
10: *
11: * @author Joe Walnes
12: */
13: public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
14:
15: private Class<?> expectedType;
16:
17: /**
18: * Subclasses should implement this. The item will already have been checked for
19: * the specific type and will never be null.
20: */
21: public abstract boolean matchesSafely(T item);
22:
23: protected TypeSafeMatcher() {
24: expectedType = findExpectedType(getClass());
25: }
26:
27: private static Class<?> findExpectedType(Class<?> fromClass) {
28: for (Class<?> c = fromClass; c != Object.class; c = c
29: .getSuperclass()) {
30: for (Method method : c.getDeclaredMethods()) {
31: if (isMatchesSafelyMethod(method)) {
32: return method.getParameterTypes()[0];
33: }
34: }
35: }
36:
37: throw new Error(
38: "Cannot determine correct type for matchesSafely() method.");
39: }
40:
41: private static boolean isMatchesSafelyMethod(Method method) {
42: return method.getName().equals("matchesSafely")
43: && method.getParameterTypes().length == 1
44: && !method.isSynthetic();
45: }
46:
47: protected TypeSafeMatcher(Class<T> expectedType) {
48: this .expectedType = expectedType;
49: }
50:
51: /**
52: * Method made final to prevent accidental override.
53: * If you need to override this, there's no point on extending TypeSafeMatcher.
54: * Instead, extend the {@link BaseMatcher}.
55: */
56: @SuppressWarnings({"unchecked"})
57: public final boolean matches(Object item) {
58: return item != null && expectedType.isInstance(item)
59: && matchesSafely((T) item);
60: }
61: }
|