Browse Source

Default generic type arguments when resolving `KType` from a `Class`.

We now fill up missing KTypeProjection arguments with star because the Kotlin Reflection.typeOf resolution fails if arguments are not provided.

Closes #3041
Original pull request: #3048
pull/3053/head
Christoph Strobl 2 years ago committed by Mark Paluch
parent
commit
9d73853244
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 26
      src/main/java/org/springframework/data/mapping/model/KotlinValueUtils.java
  2. 30
      src/test/kotlin/org/springframework/data/mapping/model/KotlinClassGeneratingEntityInstantiatorUnitTests.kt

26
src/main/java/org/springframework/data/mapping/model/KotlinValueUtils.java

@ -25,10 +25,12 @@ import kotlin.reflect.KParameter; @@ -25,10 +25,12 @@ import kotlin.reflect.KParameter;
import kotlin.reflect.KProperty;
import kotlin.reflect.KType;
import kotlin.reflect.KTypeParameter;
import kotlin.reflect.KTypeProjection;
import kotlin.reflect.jvm.ReflectJvmMapping;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -39,6 +41,7 @@ import org.springframework.util.Assert; @@ -39,6 +41,7 @@ import org.springframework.util.Assert;
* Utilities for Kotlin Value class support.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 3.2
*/
class KotlinValueUtils {
@ -72,7 +75,28 @@ class KotlinValueUtils { @@ -72,7 +75,28 @@ class KotlinValueUtils {
public static ValueBoxing getConstructorValueHierarchy(Class<?> cls) {
KClass<?> kotlinClass = JvmClassMappingKt.getKotlinClass(cls);
return new ValueBoxing(BoxingRules.CONSTRUCTOR, Reflection.typeOf(kotlinClass), kotlinClass, false);
KType kType = extractKType(kotlinClass);
return new ValueBoxing(BoxingRules.CONSTRUCTOR, kType, kotlinClass, false);
}
/**
* Get the {@link KType} for a given {@link KClass} and potentially fill missing generic type arguments with
* {@link KTypeProjection#star} to prevent Kotlin internal checks to fail.
*
* @param kotlinClass
* @return
*/
private static KType extractKType(KClass<?> kotlinClass) {
return kotlinClass.getTypeParameters().isEmpty() ? Reflection.typeOf(kotlinClass)
: Reflection.typeOf(JvmClassMappingKt.getJavaClass(kotlinClass), stubKTypeProjections(kotlinClass));
}
private static KTypeProjection[] stubKTypeProjections(KClass<?> kotlinClass) {
KTypeProjection[] kTypeProjections = new KTypeProjection[kotlinClass.getTypeParameters().size()];
Arrays.fill(kTypeProjections, KTypeProjection.star);
return kTypeProjections;
}
/**

30
src/test/kotlin/org/springframework/data/mapping/model/KotlinClassGeneratingEntityInstantiatorUnitTests.kt

@ -21,8 +21,11 @@ import org.assertj.core.api.Assertions.assertThat @@ -21,8 +21,11 @@ import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.Test
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.annotation.Persistent
import org.springframework.data.mapping.PersistentEntity
import org.springframework.data.mapping.context.SamplePersistentProperty
import org.springframework.data.mapping.model.KotlinValueUtils.BoxingRules
import kotlin.jvm.internal.Reflection
import kotlin.reflect.KClass
/**
@ -149,6 +152,28 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests { @@ -149,6 +152,28 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests {
assertThat(instance.aBool).isTrue()
}
@Test // GH-3041
fun `should pick preferred constructor if multiple with same argument count are present`() {
val entity =
mockk<PersistentEntity<WithConstructorsHavingSameParameterCount, SamplePersistentProperty>>()
val constructor =
PreferredConstructorDiscoverer.discover<WithConstructorsHavingSameParameterCount, SamplePersistentProperty>(
WithConstructorsHavingSameParameterCount::class.java
)
every { provider.getParameterValue<Any>(any()) }.returns(1L).andThen(null)
every { entity.instanceCreatorMetadata } returns constructor
every { entity.type } returns constructor!!.constructor.declaringClass
every { entity.typeInformation } returns mockk()
val instance: WithConstructorsHavingSameParameterCount =
KotlinClassGeneratingEntityInstantiator().createInstance(entity, provider)
assertThat(instance.id).isEqualTo(1L)
assertThat(instance.notes).isEmpty();
}
@Test // DATACMNS-1338
fun `should create instance using @PersistenceConstructor`() {
@ -271,6 +296,11 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests { @@ -271,6 +296,11 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests {
val aFloat: Float = 0.0f, val aDouble: Double = 0.0, val aChar: Char = 'a', val aBool: Boolean = true
)
data class WithConstructorsHavingSameParameterCount @PersistenceConstructor constructor(val id: Long?, val notes: Map<String, String> = emptyMap()) {
constructor(notes: Map<String, String>, additionalNotes: Map<String, String> = emptyMap()) : this(null, notes + additionalNotes)
}
data class ContactWithPersistenceConstructor(val firstname: String, val lastname: String) {
@PersistenceConstructor

Loading…
Cancel
Save