diff options
Diffstat (limited to 'hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java')
-rw-r--r-- | hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java b/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java new file mode 100644 index 0000000..ada74d6 --- /dev/null +++ b/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java @@ -0,0 +1,70 @@ +/** + * 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]; + } +} |