Skip to content

Commit

Permalink
GenericTypeReflector.mapTypeParameters() now resolves type variables
Browse files Browse the repository at this point in the history
The method GenericTypeReflector.mapTypeParameters() now also tries to resolve
TypeVariable, if these have bounds like the method:

`<T extends Annotation> T getAnnotation()`

Which now returns `Annotation` as return type instead of `Object`.

This fixes #1163 "NodeInfo#getAnnotation returns instance of Cglib-generated that does not actually implement Annotation".
  • Loading branch information
AndreasTu committed Jul 31, 2023
1 parent c47a54e commit a443071
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,40 @@ private static Type mapTypeParameters(Type toMapType, Type typeAndParams) {
return erase(toMapType);
} else {
VarMap varMap = new VarMap();
Type handlingTypeAndParams = typeAndParams;
while(handlingTypeAndParams instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType)handlingTypeAndParams;
Class<?> clazz = (Class<?>)pType.getRawType(); // getRawType should always be Class
varMap.addAll(clazz.getTypeParameters(), pType.getActualTypeArguments());
handlingTypeAndParams = pType.getOwnerType();
}
return varMap.map(toMapType);
}
}
collectVarMapEntriesForTypeVariable(toMapType, varMap);
collectVarMapEntriesForType(typeAndParams, varMap);
return varMap.map(toMapType);
}
}

/**
/**
* This will collect the {@link TypeVariable} bounds of <code>toMapType</code> into the <code>varMap</code>,
* if the <code>toMapType</code> is a {@link TypeVariable} and has exactly one type bound ({@link TypeVariable#getBounds()}.
*
* @param toMapType Type possibly containing type arguments
* @param varMap the {@link VarMap} to save resolve variables into. Caution: This will be modified
*/
private static void collectVarMapEntriesForTypeVariable(Type toMapType, VarMap varMap) {
if (toMapType instanceof TypeVariable) {
final TypeVariable<?> t = (TypeVariable<?>) toMapType;
final Type[] bounds = t.getBounds();
if (bounds.length == 1) {
varMap.add(t, bounds[0]);
}
}
}

private static void collectVarMapEntriesForType(Type typeAndParams, VarMap varMap) {
Type handlingTypeAndParams = typeAndParams;
while (handlingTypeAndParams instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) handlingTypeAndParams;
Class<?> clazz = (Class<?>) pType.getRawType(); // getRawType should always be Class
varMap.addAll(clazz.getTypeParameters(), pType.getActualTypeArguments());
handlingTypeAndParams = pType.getOwnerType();
}
}

/**
* Checks if the given type is a class that is supposed to have type parameters, but doesn't.
* In other words, if it's a really raw type.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@

package org.spockframework.gentyref

import org.spockframework.runtime.model.NodeInfo
import spock.lang.Specification

import java.lang.annotation.Annotation
import java.lang.reflect.AnnotatedElement
import java.lang.reflect.Method
import java.lang.reflect.Type

class GenericTypeReflectorSpec extends Specification {
def "can get exact parameter types for java.lang.Object method"() {
when:
Expand Down Expand Up @@ -51,4 +57,24 @@ class GenericTypeReflectorSpec extends Specification {
notThrown(NullPointerException)
type == Object
}

def "getExactReturnType"(Class<?> clazz, String methodName, Type expectedType) {
given:
def method = clazz.getMethods().find { it.name == methodName }
expect:
clazz && methodName && method != null
GenericTypeReflector.getExactReturnType(method, clazz) == expectedType

where:
clazz | methodName | expectedType
NodeInfo | "getAnnotation" | Annotation
NodeInfo | "getName" | String
NodeInfo | "getReflection" | AnnotatedElement
TestNodeInfo | "getReflection" | Method
NodeInfo | "getParent" | NodeInfo
TestNodeInfo | "getParent" | TestNodeInfo
ArrayList | "get" | Object
}

private abstract class TestNodeInfo extends NodeInfo<TestNodeInfo, Method> {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package org.spockframework.mock

import org.spockframework.runtime.model.SpecInfo
import org.spockframework.util.Nullable
import spock.lang.Specification

import java.lang.annotation.Annotation

class MockSpecInfoAnnotationSpec extends Specification {

def "NodeInfo.getAnnotation() shall return valid Stub for Stub Issue #1163"() {
given:
def mockUtil = new MockUtil()
def spec = Stub(SpecInfo)

when:
def t = spec.getAnnotation(Nullable)

then:
t instanceof Annotation
mockUtil.isMock(t)
}

def "NodeInfo.getAnnotation() shall return null for Mock Issue #1163"() {
given:
def spec = Mock(SpecInfo)

when:
def t = spec.getAnnotation(Nullable)

then:
t == null
}
}

0 comments on commit a443071

Please sign in to comment.