Skip to content

Commit

Permalink
Refine MethodParameter#isOptional Kotlin implementation
Browse files Browse the repository at this point in the history
This commit adds support for Continuation parameter that is now
considered as an optional parameter since it is never provided by
the user.

It also simplifies and optimizes the implementation.

Closes spring-projectsgh-23991
  • Loading branch information
sdeleuze committed Nov 14, 2019
1 parent 89b3a9c commit 3f661dc
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 25 deletions.
Expand Up @@ -26,12 +26,11 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import kotlin.coroutines.Continuation;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
import kotlin.reflect.jvm.ReflectJvmMapping;
Expand Down Expand Up @@ -398,7 +397,7 @@ private MethodParameter nested(int nestingLevel, @Nullable Integer typeIndex) {
* either in the form of Java 8's {@link java.util.Optional}, any variant
* of a parameter-level {@code Nullable} annotation (such as from JSR-305
* or the FindBugs set of annotations), or a language-level nullable type
* declaration in Kotlin.
* declaration or {@code Continuation} parameter in Kotlin.
* @since 4.3
*/
public boolean isOptional() {
Expand Down Expand Up @@ -867,37 +866,39 @@ private static int validateIndex(Executable executable, int parameterIndex) {
private static class KotlinDelegate {

/**
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type
* or an optional parameter (with a default value in the Kotlin declaration).
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type,
* an optional parameter (with a default value in the Kotlin declaration) or a {@code Continuation} parameter
* used in suspending functions.
*/
public static boolean isOptional(MethodParameter param) {
Method method = param.getMethod();
Constructor<?> ctor = param.getConstructor();
int index = param.getParameterIndex();
if (method != null && index == -1) {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
return (function != null && function.getReturnType().isMarkedNullable());
}
else {
KFunction<?> function = null;
Predicate<KParameter> predicate = null;
if (method != null) {
function = ReflectJvmMapping.getKotlinFunction(method);
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind());
}
else if (ctor != null) {
function = ReflectJvmMapping.getKotlinFunction(ctor);
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()) ||
KParameter.Kind.INSTANCE.equals(p.getKind());
KFunction<?> function;
Predicate<KParameter> predicate;
if (method != null) {
if (param.parameterType == Continuation.class) {
return true;
}
if (function != null) {
List<KParameter> parameters = function.getParameters();
KParameter parameter = parameters
.stream()
.filter(predicate)
.collect(Collectors.toList())
.get(index);
return (parameter.getType().isMarkedNullable() || parameter.isOptional());
function = ReflectJvmMapping.getKotlinFunction(method);
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind());
}
else {
function = ReflectJvmMapping.getKotlinFunction(param.getConstructor());
predicate = p -> KParameter.Kind.VALUE.equals(p.getKind()) ||
KParameter.Kind.INSTANCE.equals(p.getKind());
}
if (function != null) {
int i = 0;
for (KParameter kParameter : function.getParameters()) {
if (predicate.test(kParameter)) {
if (index == i++) {
return (kParameter.getType().isMarkedNullable() || kParameter.isOptional());
}
}
}
}
return false;
Expand Down
Expand Up @@ -20,6 +20,7 @@ import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.lang.reflect.Method
import java.lang.reflect.TypeVariable
import kotlin.coroutines.Continuation
import kotlin.reflect.full.declaredFunctions
import kotlin.reflect.jvm.javaMethod

Expand Down Expand Up @@ -101,6 +102,13 @@ class KotlinMethodParameterTests {
assertThat(returnGenericParameterType("suspendFun8")).isEqualTo(Object::class.java)
}

@Test
fun `Continuation parameter is optional`() {
val method = this::class.java.getDeclaredMethod("suspendFun", String::class.java, Continuation::class.java)
assertThat(MethodParameter(method, 0).isOptional).isFalse()
assertThat(MethodParameter(method, 1).isOptional).isTrue()
}

private fun returnParameterType(funName: String) = returnMethodParameter(funName).parameterType
private fun returnGenericParameterType(funName: String) = returnMethodParameter(funName).genericParameterType
private fun returnGenericParameterTypeName(funName: String) = returnGenericParameterType(funName).typeName
Expand Down

0 comments on commit 3f661dc

Please sign in to comment.