aboutsummaryrefslogtreecommitdiff
path: root/hamcrest-core/src/main/java/org/hamcrest/internal/ReflectiveTypeFinder.java
diff options
context:
space:
mode:
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.java70
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&lt;T&gt;</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&lt;String&gt;</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(&lt;T&gt;, 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];
+ }
+}