Skip to content

Commit 76d0bb8

Browse files
committed
GH-1312 Backporting type discovery fix
1 parent 8527e08 commit 76d0bb8

1 file changed

Lines changed: 120 additions & 119 deletions

File tree

  • spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java

Lines changed: 120 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@
7373
import org.springframework.util.StringUtils;
7474

7575

76-
7776
/**
7877
* Set of utility operations to interrogate function definitions.
7978
*
@@ -95,17 +94,17 @@ private FunctionTypeUtils() {
9594

9695
public static Type functionType(Type input, Type output) {
9796
return ResolvableType.forClassWithGenerics(Function.class,
98-
ResolvableType.forType(input), ResolvableType.forType(output)).getType();
97+
ResolvableType.forType(input), ResolvableType.forType(output)).getType();
9998
}
10099

101100
public static Type consumerType(Type input) {
102101
return ResolvableType.forClassWithGenerics(Consumer.class,
103-
ResolvableType.forType(input)).getType();
102+
ResolvableType.forType(input)).getType();
104103
}
105104

106105
public static Type supplierType(Type output) {
107106
return ResolvableType.forClassWithGenerics(Supplier.class,
108-
ResolvableType.forType(output)).getType();
107+
ResolvableType.forType(output)).getType();
109108
}
110109

111110
/**
@@ -180,7 +179,7 @@ public static Class<?> getRawType(Type type) {
180179
*/
181180
return ObjectUtils.isEmpty(upperbounds) ? Object.class : getRawType(upperbounds[0]);
182181
}
183-
return ResolvableType.forType(type).getRawClass();
182+
return ResolvableType.forType(type).getRawClass() == null ? Object.class : ResolvableType.forType(type).getRawClass();
184183
}
185184

186185
/**
@@ -195,15 +194,15 @@ public static Class<?> getRawType(Type type) {
195194
public static Method discoverFunctionalMethod(Class<?> pojoFunctionClass) {
196195
if (Supplier.class.isAssignableFrom(pojoFunctionClass)) {
197196
return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic()
198-
&& m.getName().equals("get")).findFirst().get();
197+
&& m.getName().equals("get")).findFirst().get();
199198
}
200199
else if (Consumer.class.isAssignableFrom(pojoFunctionClass) || BiConsumer.class.isAssignableFrom(pojoFunctionClass)) {
201200
return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic()
202-
&& m.getName().equals("accept")).findFirst().get();
201+
&& m.getName().equals("accept")).findFirst().get();
203202
}
204203
else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.isAssignableFrom(pojoFunctionClass)) {
205204
return Stream.of(ReflectionUtils.getAllDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic()
206-
&& m.getName().equals("apply")).findFirst().get();
205+
&& m.getName().equals("apply")).findFirst().get();
207206
}
208207

209208
List<Method> methods = new ArrayList<>();
@@ -214,7 +213,7 @@ else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.
214213

215214
}, method ->
216215
!method.getDeclaringClass().isAssignableFrom(Object.class)
217-
&& !method.isSynthetic() && !method.isBridge() && !method.isVarArgs());
216+
&& !method.isSynthetic() && !method.isBridge() && !method.isVarArgs());
218217

219218
if (methods.size() > 1) {
220219
for (Method candidadteMethod : methods) {
@@ -229,6 +228,28 @@ else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.
229228
return CollectionUtils.isEmpty(methods) ? null : methods.get(0);
230229
}
231230

231+
public static Type discoverFunctionTypeFromType(Type functionalType) {
232+
Type typeToReturn = null;
233+
if (Function.class.isAssignableFrom(getRawType(functionalType))) {
234+
ResolvableType functionType = ResolvableType.forType(functionalType).as(Function.class);
235+
typeToReturn = GenericTypeResolver.resolveType(functionType.getType(), getRawType(functionalType));
236+
}
237+
else if (Consumer.class.isAssignableFrom(getRawType(functionalType))) {
238+
ResolvableType functionType = ResolvableType.forType(functionalType).as(Consumer.class);
239+
240+
ResolvableType t = ResolvableType.forClassWithGenerics(getRawType(functionalType), functionType.getGeneric(0));
241+
Type t2 = t.getType();
242+
//Type t = ResolvableType.
243+
244+
typeToReturn = GenericTypeResolver.resolveType(functionType.getType(), functionType.getRawClass());
245+
}
246+
else {
247+
ResolvableType functionType = ResolvableType.forType(functionalType).as(Supplier.class);
248+
typeToReturn = GenericTypeResolver.resolveType(functionType.getType(), getRawType(functionalType));
249+
}
250+
return typeToReturn;
251+
}
252+
232253
public static Type discoverFunctionTypeFromClass(Class<?> functionalClass) {
233254
if (KotlinDetector.isKotlinPresent()) {
234255
if (Function1.class.isAssignableFrom(functionalClass)) {
@@ -249,15 +270,15 @@ else if (Function0.class.isAssignableFrom(functionalClass)) {
249270
}
250271
}
251272
}
252-
ResolvableType functionType = ResolvableType.forClass(functionalClass).as(Function.class);
273+
ResolvableType functionType = ResolvableType.forType(functionalClass).as(Function.class);
253274
typeToReturn = GenericTypeResolver.resolveType(functionType.getType(), functionalClass);
254275
}
255276
else if (Consumer.class.isAssignableFrom(functionalClass)) {
256-
ResolvableType functionType = ResolvableType.forClass(functionalClass).as(Consumer.class);
277+
ResolvableType functionType = ResolvableType.forType(functionalClass).as(Consumer.class);
257278
typeToReturn = GenericTypeResolver.resolveType(functionType.getType(), functionalClass);
258279
}
259280
else if (Supplier.class.isAssignableFrom(functionalClass)) {
260-
ResolvableType functionType = ResolvableType.forClass(functionalClass).as(Supplier.class);
281+
ResolvableType functionType = ResolvableType.forType(functionalClass).as(Supplier.class);
261282
typeToReturn = GenericTypeResolver.resolveType(functionType.getType(), functionalClass);
262283
}
263284
return typeToReturn;
@@ -286,6 +307,7 @@ public static Type discoverFunctionTypeFromFunctionFactoryMethod(Class<?> clazz,
286307
*/
287308
public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) {
288309
return method.getGenericReturnType();
310+
// return discoverFunctionTypeFromClass(method.getReturnType());
289311
}
290312

291313
/**
@@ -299,11 +321,11 @@ public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod)
299321
return null;
300322
}
301323
Assert.isTrue(
302-
functionMethod.getName().equals("apply") ||
324+
functionMethod.getName().equals("apply") ||
303325
functionMethod.getName().equals("accept") ||
304326
functionMethod.getName().equals("get") ||
305327
functionMethod.getName().equals("invoke"),
306-
"Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass());
328+
"Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass());
307329

308330
ResolvableType functionType;
309331
if (functionMethod.getName().equals("apply") || functionMethod.getName().equals("invoke")) {
@@ -376,6 +398,49 @@ public static Type getComponentTypeOfOutputType(Type functionType) {
376398
return getImmediateGenericType(inputType, 0);
377399
}
378400

401+
/**
402+
* Will resolve @{@link ResolvableType} to {@link Type} preserving all the resolved generics.
403+
* @param typeWithGenerics - instance of {@link ResolvableType}.
404+
* @return - {@link Type} representation of the provided {@link ResolvableType}.
405+
*/
406+
public static Type resolveType(ResolvableType typeWithGenerics) {
407+
if (typeWithGenerics.hasResolvableGenerics()) {
408+
ResolvableType[] generics = typeWithGenerics.getGenerics();
409+
List<ResolvableType> resolvedGenerics = new ArrayList<>();
410+
for (int i = 0; i < generics.length; i++) {
411+
ResolvableType genericType = typeWithGenerics.getGenerics()[i];
412+
resolvedGenerics.add(ResolvableType.forType(resolveType(genericType)));
413+
}
414+
return ResolvableType.forClassWithGenerics(typeWithGenerics.getRawClass(),
415+
resolvedGenerics.toArray(new ResolvableType[0])).getType();
416+
}
417+
else {
418+
return typeWithGenerics.resolve();
419+
}
420+
}
421+
422+
public static Type getOutputType(Type functionType) {
423+
assertSupportedTypes(functionType);
424+
if (isConsumer(functionType)) {
425+
logger.debug("Consumer does not have output type, returning null as output type.");
426+
return null;
427+
}
428+
429+
if (KotlinDetector.isKotlinPresent() && Function1.class.isAssignableFrom(getRawType(functionType))) { // Kotlin
430+
return ResolvableType.forType(getImmediateGenericType(functionType, 1)).getType();
431+
}
432+
else {
433+
ResolvableType resolvableFunctionType = isSupplier(functionType)
434+
? ResolvableType.forType(functionType).as(Supplier.class)
435+
: ResolvableType.forType(functionType).as(Function.class);
436+
ResolvableType generics = isSupplier(functionType)
437+
? resolvableFunctionType.getGenerics()[0]
438+
: resolvableFunctionType.getGenerics()[1];
439+
Type outputType = FunctionTypeUtils.resolveType(generics);
440+
return outputType == null || outputType instanceof TypeVariable<?> ? Object.class : outputType;
441+
}
442+
}
443+
379444
/**
380445
* Returns input type of function type that represents Function or Consumer.
381446
* @param functionType the Type of Function or Consumer
@@ -388,35 +453,17 @@ public static Type getInputType(Type functionType) {
388453
return null;
389454
}
390455

391-
ResolvableType resolvableFunctionType = ResolvableType.forType(functionType);
392-
393-
if (FunctionTypeUtils.isFunction(functionType)) {
394-
return extractInputType(resolvableFunctionType.as(Function.class).getGeneric(0));
395-
}
396-
if (FunctionTypeUtils.isConsumer(functionType)) {
397-
return extractInputType(resolvableFunctionType.as(Consumer.class).getGeneric(0));
398-
}
399456
if (KotlinDetector.isKotlinPresent() && Function1.class.isAssignableFrom(getRawType(functionType))) { // Kotlin
400457
return ResolvableType.forType(getImmediateGenericType(functionType, 1)).getType();
401458
}
402-
403-
// no consumer or function
404-
// might be one of the other supported types (as asserted in first line of method)
405-
// for example FunctionRegistration or IntConsumer
406-
407-
// unclear what the contract is in such a case
408-
// maybe returning null here might be "more" correct
409-
return Object.class;
410-
}
411-
412-
private static Type extractInputType(ResolvableType resolvableInputType) {
413-
if (resolvableInputType.getType() instanceof TypeVariable) {
414-
// In case the input type is a type variable (e.g. as in GH-1251) we need to resolve the type
415-
// For the case that the type is unbound Object.class is used
416-
return resolvableInputType.resolve(Object.class);
459+
else {
460+
ResolvableType resolvableFunctionType = isConsumer(functionType)
461+
? ResolvableType.forType(functionType).as(Consumer.class)
462+
: ResolvableType.forType(functionType).as(Function.class);
463+
ResolvableType generics = resolvableFunctionType.getGenerics()[0];
464+
Type inputType = FunctionTypeUtils.resolveType(generics);
465+
return inputType == null || inputType instanceof TypeVariable<?> ? Object.class : inputType;
417466
}
418-
419-
return resolvableInputType.getType();
420467
}
421468

422469
@SuppressWarnings("rawtypes")
@@ -429,7 +476,7 @@ else if (function instanceof FunctionRegistration) {
429476
}
430477
if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { // for Kotlin primarily
431478
FunctionRegistration fr = applicationContext
432-
.getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class);
479+
.getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class);
433480
return fr.getType();
434481
}
435482

@@ -478,52 +525,6 @@ public static String discoverBeanDefinitionNameByQualifier(ListableBeanFactory b
478525
}
479526
return null;
480527
}
481-
482-
public static Type getOutputType(Type functionType) {
483-
assertSupportedTypes(functionType);
484-
if (isConsumer(functionType)) {
485-
logger.debug("Consumer does not have output type, returning null as output type.");
486-
return null;
487-
}
488-
489-
ResolvableType resolvableFunctionType = ResolvableType.forType(functionType);
490-
491-
ResolvableType resolvableOutputType;
492-
if (FunctionTypeUtils.isFunction(functionType)) {
493-
resolvableOutputType = resolvableFunctionType.as(Function.class);
494-
}
495-
else {
496-
if (KotlinDetector.isKotlinPresent() && Function1.class.isAssignableFrom(getRawType(functionType))) { // Kotlin
497-
return ResolvableType.forType(getImmediateGenericType(functionType, 1)).getType();
498-
}
499-
else {
500-
resolvableOutputType = resolvableFunctionType.as(Supplier.class);
501-
}
502-
}
503-
504-
Type outputType;
505-
if (functionType instanceof Class functionTypeClass) {
506-
if (FunctionTypeUtils.isFunction(functionType)) {
507-
ResolvableType genericClass1 = resolvableOutputType.getGeneric(1);
508-
outputType = genericClass1.getType();
509-
outputType = (outputType instanceof TypeVariable) ? Object.class : GenericTypeResolver.resolveType(outputType, functionTypeClass);
510-
}
511-
else {
512-
ResolvableType genericClass0 = resolvableOutputType.getGeneric(0);
513-
outputType = genericClass0.getType();
514-
outputType = (outputType instanceof TypeVariable) ? Object.class : GenericTypeResolver.resolveType(outputType, functionTypeClass);
515-
}
516-
}
517-
else if (functionType instanceof ParameterizedType) {
518-
Type genericType = isSupplier(functionType) ? resolvableOutputType.getGeneric(0).getType() : resolvableOutputType.getGeneric(1).getType();
519-
outputType = GenericTypeResolver.resolveType(genericType, getRawType(functionType));
520-
}
521-
else {
522-
outputType = resolvableOutputType.getType();
523-
}
524-
return outputType instanceof TypeVariable ? Object.class : outputType;
525-
}
526-
527528
public static Type getImmediateGenericType(Type type, int index) {
528529
if (type instanceof ParameterizedType) {
529530
return ((ParameterizedType) type).getActualTypeArguments()[index];
@@ -613,23 +614,23 @@ static Type fromFunctionMethod(Method functionalMethod) {
613614

614615
Type functionType = null;
615616
switch (parameterTypes.length) {
616-
case 0:
617-
functionType = ResolvableType.forClassWithGenerics(Supplier.class,
617+
case 0:
618+
functionType = ResolvableType.forClassWithGenerics(Supplier.class,
618619
ResolvableType.forMethodReturnType(functionalMethod)).getType();
619-
break;
620-
case 1:
621-
if (Void.class.isAssignableFrom(functionalMethod.getReturnType())) {
622-
functionType = ResolvableType.forClassWithGenerics(Consumer.class,
620+
break;
621+
case 1:
622+
if (Void.class.isAssignableFrom(functionalMethod.getReturnType())) {
623+
functionType = ResolvableType.forClassWithGenerics(Consumer.class,
623624
ResolvableType.forMethodParameter(functionalMethod, 0)).getType();
624-
}
625-
else {
626-
functionType = ResolvableType.forClassWithGenerics(Function.class,
625+
}
626+
else {
627+
functionType = ResolvableType.forClassWithGenerics(Function.class,
627628
ResolvableType.forMethodParameter(functionalMethod, 0),
628629
ResolvableType.forMethodReturnType(functionalMethod)).getType();
629-
}
630-
break;
631-
default:
632-
throw new UnsupportedOperationException("Functional method: " + functionalMethod + " is not supported");
630+
}
631+
break;
632+
default:
633+
throw new UnsupportedOperationException("Functional method: " + functionalMethod + " is not supported");
633634
}
634635
return functionType;
635636
}
@@ -652,31 +653,31 @@ private static void assertSupportedTypes(Type type) {
652653
if (type instanceof ParameterizedType) {
653654
type = ((ParameterizedType) type).getRawType();
654655
Assert.isTrue(type instanceof Class<?>, "Must be one of Supplier, Function, Consumer"
655-
+ " or FunctionRegistration. Was " + type);
656+
+ " or FunctionRegistration. Was " + type);
656657
}
657658

658659
Class<?> candidateType = (Class<?>) type;
659660

660661
Assert.isTrue(Supplier.class.isAssignableFrom(candidateType)
661-
|| (KotlinDetector.isKotlinPresent() && (Function0.class.isAssignableFrom(candidateType) || Function1.class.isAssignableFrom(candidateType)))
662-
|| Function.class.isAssignableFrom(candidateType)
663-
|| Consumer.class.isAssignableFrom(candidateType)
664-
|| FunctionRegistration.class.isAssignableFrom(candidateType)
665-
|| IntConsumer.class.isAssignableFrom(candidateType)
666-
|| IntSupplier.class.isAssignableFrom(candidateType)
667-
|| IntFunction.class.isAssignableFrom(candidateType)
668-
|| ToIntFunction.class.isAssignableFrom(candidateType)
669-
|| LongConsumer.class.isAssignableFrom(candidateType)
670-
|| LongSupplier.class.isAssignableFrom(candidateType)
671-
|| LongFunction.class.isAssignableFrom(candidateType)
672-
|| ToLongFunction.class.isAssignableFrom(candidateType)
673-
|| DoubleConsumer.class.isAssignableFrom(candidateType)
674-
|| DoubleSupplier.class.isAssignableFrom(candidateType)
675-
|| DoubleFunction.class.isAssignableFrom(candidateType)
676-
|| ToDoubleFunction.class.isAssignableFrom(candidateType)
677-
|| type.getTypeName().startsWith("org.springframework.context.annotation.ConfigurationClassEnhancer"),
678-
"Must be one of Supplier, Function, Consumer"
679-
+ " or FunctionRegistration. Was " + type);
662+
|| (KotlinDetector.isKotlinPresent() && (Function0.class.isAssignableFrom(candidateType) || Function1.class.isAssignableFrom(candidateType)))
663+
|| Function.class.isAssignableFrom(candidateType)
664+
|| Consumer.class.isAssignableFrom(candidateType)
665+
|| FunctionRegistration.class.isAssignableFrom(candidateType)
666+
|| IntConsumer.class.isAssignableFrom(candidateType)
667+
|| IntSupplier.class.isAssignableFrom(candidateType)
668+
|| IntFunction.class.isAssignableFrom(candidateType)
669+
|| ToIntFunction.class.isAssignableFrom(candidateType)
670+
|| LongConsumer.class.isAssignableFrom(candidateType)
671+
|| LongSupplier.class.isAssignableFrom(candidateType)
672+
|| LongFunction.class.isAssignableFrom(candidateType)
673+
|| ToLongFunction.class.isAssignableFrom(candidateType)
674+
|| DoubleConsumer.class.isAssignableFrom(candidateType)
675+
|| DoubleSupplier.class.isAssignableFrom(candidateType)
676+
|| DoubleFunction.class.isAssignableFrom(candidateType)
677+
|| ToDoubleFunction.class.isAssignableFrom(candidateType)
678+
|| type.getTypeName().startsWith("org.springframework.context.annotation.ConfigurationClassEnhancer"),
679+
"Must be one of Supplier, Function, Consumer"
680+
+ " or FunctionRegistration. Was " + type);
680681
}
681682

682683
private static Type extractReactiveType(Type type) {

0 commit comments

Comments
 (0)