1package org.junit.internal.matchers;
2
3import java.lang.reflect.Method;
4
5import org.hamcrest.BaseMatcher;
6
7/**
8 * Convenient base class for Matchers that require a non-null value of a specific type.
9 * This simply implements the null check, checks the type and then casts.
10 *
11 * @author Joe Walnes
12 */
13public 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.getSuperclass()) {
29            for (Method method : c.getDeclaredMethods()) {
30                if (isMatchesSafelyMethod(method)) {
31                    return method.getParameterTypes()[0];
32                }
33            }
34        }
35
36        throw new Error("Cannot determine correct type for matchesSafely() method.");
37    }
38
39    private static boolean isMatchesSafelyMethod(Method method) {
40        return method.getName().equals("matchesSafely")
41            && method.getParameterTypes().length == 1
42            && !method.isSynthetic();
43    }
44
45    protected TypeSafeMatcher(Class<T> expectedType) {
46    	this.expectedType = expectedType;
47    }
48
49    /**
50     * Method made final to prevent accidental override.
51     * If you need to override this, there's no point on extending TypeSafeMatcher.
52     * Instead, extend the {@link BaseMatcher}.
53     */
54    @SuppressWarnings({"unchecked"})
55    public final boolean matches(Object item) {
56        return item != null
57                && expectedType.isInstance(item)
58                && matchesSafely((T) item);
59    }
60}
61