DATACMNS-1197 - Resolve Kotlin interface properties for nullability inspection.

We now resolve Kotlin interface properties to inspect these for nullability. Kotlin-reflect does not resolve interface property accessors yet so we need to handle this aspect ourselves.

Related ticket: https://youtrack.jetbrains.com/issue/KT-20768.
Original pull request: #254.
This commit is contained in:
Mark Paluch
2017-10-13 16:53:15 +02:00
committed by Oliver Gierke
parent 8ce79c1810
commit 634d8cbbd6
3 changed files with 84 additions and 3 deletions

View File

@@ -15,7 +15,12 @@
*/
package org.springframework.data.util;
import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KCallable;
import kotlin.reflect.KClass;
import kotlin.reflect.KFunction;
import kotlin.reflect.KMutableProperty;
import kotlin.reflect.KProperty;
import kotlin.reflect.KType;
import kotlin.reflect.jvm.ReflectJvmMapping;
import kotlin.reflect.jvm.internal.impl.load.kotlin.header.KotlinClassHeader;
@@ -402,11 +407,37 @@ public class ReflectionUtils {
}
if (isSupportedKotlinClass(parameter.getDeclaringClass())) {
return KotlinReflectionUtils.isNullable(parameter);
}
KFunction<?> kotlinFunction = ReflectJvmMapping.getKotlinFunction(parameter.getMethod());
return !parameter.getParameterType().isPrimitive();
}
/**
* Reflection utility methods specific to Kotlin reflection.
*/
static class KotlinReflectionUtils {
/**
* Returns {@literal} whether the given {@link MethodParameter} is nullable. Its declaring method can reference a
* Kotlin function, property or interface property.
*
* @return {@literal true} if {@link MethodParameter} is nullable.
* @since 2.0.1
*/
static boolean isNullable(MethodParameter parameter) {
Method method = parameter.getMethod();
KFunction<?> kotlinFunction = ReflectJvmMapping.getKotlinFunction(method);
if (kotlinFunction == null) {
throw new IllegalArgumentException(String.format("Cannot resolve %s to a Kotlin function!", parameter));
// Fallback to own lookup because there's no public Kotlin API for that kind of lookup until
// https://youtrack.jetbrains.com/issue/KT-20768 gets resolved.
Optional<? extends KFunction> first = findKFunction(method);
kotlinFunction = first.orElseThrow(
() -> new IllegalArgumentException(String.format("Cannot resolve %s to a Kotlin function!", parameter)));
}
KType type = parameter.getParameterIndex() == -1 ? kotlinFunction.getReturnType()
@@ -415,6 +446,46 @@ public class ReflectionUtils {
return type.isMarkedNullable();
}
return !parameter.getParameterType().isPrimitive();
/**
* Lookup a {@link Method} to a {@link KFunction}.
*
* @param method the JVM {@link Method} to look up.
* @return {@link Optional} wrapping a possibly existing {@link KFunction}.
*/
private static Optional<? extends KFunction> findKFunction(Method method) {
KClass<?> kotlinClass = JvmClassMappingKt.getKotlinClass(method.getDeclaringClass());
return kotlinClass.getMembers() //
.stream() //
.flatMap(KotlinReflectionUtils::toKFunctionStream) //
.filter(it -> {
Method javaMethod = ReflectJvmMapping.getJavaMethod(it);
return javaMethod != null && javaMethod.equals(method);
}) //
.findFirst();
}
private static Stream<? extends KFunction> toKFunctionStream(KCallable<?> it) {
if (it instanceof KMutableProperty<?>) {
KMutableProperty property = (KMutableProperty<?>) it;
return Stream.of(property.getGetter(), property.getSetter());
}
if (it instanceof KProperty<?>) {
KProperty<?> property = (KProperty<?>) it;
return Stream.of(property.getGetter());
}
if (it instanceof KFunction<?>) {
return Stream.of((KFunction<?>) it);
}
return Stream.empty();
}
}
}