| /** |
| * The TypeSafe classes, and their descendants, need a mechanism to find out what type has been used as a parameter |
| * for the concrete matcher. Unfortunately, this type is lost during type erasure so we need to use reflection |
| * to get it back, by picking out the type of a known parameter to a known method. |
| * The catch is that, with bridging methods, this type is only visible in the class that actually implements |
| * the expected method, so the ReflectiveTypeFinder needs to be applied to that class or a subtype. |
| * |
| * For example, the abstract <code>TypeSafeDiagnosingMatcher<T></code> defines an abstract method |
| * <pre>protected abstract boolean matchesSafely(T item, Description mismatchDescription);</pre> |
| * By default it uses <code>new ReflectiveTypeFinder("matchesSafely", 2, 0); </code> to find the |
| * parameterised type. If we create a <code>TypeSafeDiagnosingMatcher<String></code>, the type |
| * finder will return <code>String.class</code>. |
| * |
| * A <code>FeatureMatcher</code> is an abstract subclass of <code>TypeSafeDiagnosingMatcher</code>. |
| * Although it has a templated implementation of <code>matchesSafely(<T>, Description);</code>, the |
| * actual run-time signature of this is <code>matchesSafely(Object, Description);</code>. Instead, |
| * we must find the type by reflecting on the concrete implementation of |
| * <pre>protected abstract U featureValueOf(T actual);</pre> |
| * a method which is declared in <code>FeatureMatcher</code>. |
| * |
| * In short, use this to extract a type from a method in the leaf class of a templated class hierarchy. |
| * |
| * @author Steve Freeman |
| * @author Nat Pryce |
| */ |
| package org.hamcrest.internal; |
| |
| import java.lang.reflect.Method; |
| |
| public class ReflectiveTypeFinder { |
| private final String methodName; |
| private final int expectedNumberOfParameters; |
| private final int typedParameter; |
| |
| public ReflectiveTypeFinder(String methodName, int expectedNumberOfParameters, int typedParameter) { |
| this.methodName = methodName; |
| this.expectedNumberOfParameters = expectedNumberOfParameters; |
| this.typedParameter = typedParameter; |
| } |
| |
| public Class<?> findExpectedType(Class<?> fromClass) { |
| for (Class<?> c = fromClass; c != Object.class; c = c.getSuperclass()) { |
| for (Method method : c.getDeclaredMethods()) { |
| if (canObtainExpectedTypeFrom(method)) { |
| return expectedTypeFrom(method); |
| } |
| } |
| } |
| throw new Error("Cannot determine correct type for " + methodName + "() method."); |
| } |
| |
| /** |
| * @param method The method to examine. |
| * @return true if this method references the relevant type |
| */ |
| protected boolean canObtainExpectedTypeFrom(Method method) { |
| return method.getName().equals(methodName) |
| && method.getParameterTypes().length == expectedNumberOfParameters |
| && !method.isSynthetic(); |
| } |
| |
| |
| /** |
| * @param method The method from which to extract |
| * @return The type we're looking for |
| */ |
| protected Class<?> expectedTypeFrom(Method method) { |
| return method.getParameterTypes()[typedParameter]; |
| } |
| } |