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