From 0db0a2f55562b96ed82eb8db9c6587bd46f4e907 Mon Sep 17 00:00:00 2001 From: Tsypov Dmitriy Date: Wed, 19 May 2021 20:37:31 +0700 Subject: [PATCH] GH-699 Fixed a bug where Kotlin Lambda was incorrectly converted to a consumer function Fixed consumer type Fixed tests Added javadoc @author tag Resolves #699 Resolves #700 --- ...tlinLambdaToFunctionAutoConfiguration.java | 46 +++++++++++++++---- ...onCatalogAutoConfigurationKotlinTests.java | 6 +-- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java index 88f0a5ab0..4f5289f55 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java @@ -22,6 +22,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import kotlin.Unit; import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function2; @@ -57,6 +58,7 @@ import org.springframework.util.ObjectUtils; * * @author Oleg Zhurakousky * @author Adrien Poupard + * @author Dmitriy Tsypov * @since 2.0 */ @Configuration @@ -102,7 +104,7 @@ public class KotlinLambdaToFunctionAutoConfiguration { @SuppressWarnings({ "unchecked", "rawtypes" }) - public static final class KotlinFunctionWrapper implements Function, Supplier, Consumer, + public static final class KotlinFunctionWrapper implements Function, Supplier, Consumer, Function0, Function1, Function2, Function3, Function4, FactoryBean, @@ -164,7 +166,7 @@ public class KotlinLambdaToFunctionAutoConfiguration { } @Override - public void accept(Object[] input) { + public void accept(Object input) { this.apply(input); } @@ -182,10 +184,13 @@ public class KotlinLambdaToFunctionAutoConfiguration { FunctionRegistration registration = new FunctionRegistration<>(this, name); Type[] types = ((ParameterizedType) functionType).getActualTypeArguments(); - if (functionType.getTypeName().contains("Function0")) { + if (isValidKotlinSupplier(functionType)) { functionType = ResolvableType.forClassWithGenerics(Supplier.class, ResolvableType.forType(types[0])) .getType(); - + } + else if (isValidKotlinConsumer(functionType, types)) { + functionType = ResolvableType.forClassWithGenerics(Consumer.class, ResolvableType.forType(types[0])) + .getType(); } else if (isValidKotlinFunction(functionType, types)) { functionType = ResolvableType.forClassWithGenerics(Function.class, ResolvableType.forType(types[0]), @@ -221,20 +226,45 @@ public class KotlinLambdaToFunctionAutoConfiguration { return registration; } + private boolean isValidKotlinSupplier(Type functionType) { + return isTypeRepresentedByClass(functionType, Function0.class); + } + + private boolean isValidKotlinConsumer(Type functionType, Type[] type) { + return isTypeRepresentedByClass(functionType, Function1.class) && + type.length == 2 && + !CoroutinesUtils.isContinuationType(type[0]) && + isTypeRepresentedByClass(type[1], Unit.class); + } + private boolean isValidKotlinFunction(Type functionType, Type[] type) { - return functionType.getTypeName().contains(Function1.class.getName()) && type.length == 2 && !CoroutinesUtils.isContinuationType(type[0]); + return isTypeRepresentedByClass(functionType, Function1.class) && + type.length == 2 && + !CoroutinesUtils.isContinuationType(type[0]) && + !isTypeRepresentedByClass(type[1], Unit.class); } private boolean isValidKotlinSuspendSupplier(Type functionType, Type[] type) { - return functionType.getTypeName().contains(Function1.class.getName()) && type.length == 2 && CoroutinesUtils.isContinuationFlowType(type[0]); + return isTypeRepresentedByClass(functionType, Function1.class) && + type.length == 2 && + CoroutinesUtils.isContinuationFlowType(type[0]); } private boolean isValidKotlinSuspendConsumer(Type functionType, Type[] type) { - return functionType.getTypeName().contains(Function2.class.getName()) && type.length == 3 && CoroutinesUtils.isFlowType(type[0]) && CoroutinesUtils.isContinuationUnitType(type[1]); + return isTypeRepresentedByClass(functionType, Function2.class) && + type.length == 3 && + CoroutinesUtils.isFlowType(type[0]) && + CoroutinesUtils.isContinuationUnitType(type[1]); } private boolean isValidKotlinSuspendFunction(Type functionType, Type[] type) { - return functionType.getTypeName().contains(Function2.class.getName()) && type.length == 3 && CoroutinesUtils.isContinuationFlowType(type[1]); + return isTypeRepresentedByClass(functionType, Function2.class) && + type.length == 3 && + CoroutinesUtils.isContinuationFlowType(type[1]); + } + + private boolean isTypeRepresentedByClass(Type type, Class clazz) { + return type.getTypeName().contains(clazz.getName()); } @Override diff --git a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java index dd715c8ff..d8afa544e 100644 --- a/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java +++ b/spring-cloud-function-kotlin/src/test/java/org/springframework/cloud/function/kotlin/ContextFunctionCatalogAutoConfigurationKotlinTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.kotlin; import java.lang.reflect.ParameterizedType; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -67,10 +68,9 @@ public class ContextFunctionCatalogAutoConfigurationKotlinTests { function = this.context.getBean("kotlinConsumer"); functionType = (ParameterizedType) FunctionTypeUtils.discoverFunctionType(function, "kotlinConsumer", this.context); - assertThat(functionType.getRawType().getTypeName()).isEqualTo(Function.class.getName()); - assertThat(functionType.getActualTypeArguments().length).isEqualTo(2); + assertThat(functionType.getRawType().getTypeName()).isEqualTo(Consumer.class.getName()); + assertThat(functionType.getActualTypeArguments().length).isEqualTo(1); assertThat(functionType.getActualTypeArguments()[0].getTypeName()).isEqualTo(String.class.getName()); - assertThat(functionType.getActualTypeArguments()[1].getTypeName()).isEqualTo("kotlin.Unit"); function = this.context.getBean("kotlinSupplier"); functionType = (ParameterizedType) FunctionTypeUtils.discoverFunctionType(function, "kotlinSupplier", this.context);