DATACMNS-1126 - Add class-generating entity instantiation support for Kotlin.

We now discover Kotlin constructors and apply parameter defaulting to allow construction of immutable value objects. Constructor discovery uses primary constructors by default and considers PersistenceConstructor annotations.

KotlinClassGeneratingEntityInstantiator can instantiate Kotlin classes with default parameters by resolving the synthetic constructor. Null values translate to parameter defaults.

class Person(val firstname: String = "Walter") {
}

class Address(val street: String, val city: String) {

	@PersistenceConstructor
	constructor(street: String = "Unknown", city: String = "Unknown", country: String) : this(street, city)
}

Original pull request: #233.
This commit is contained in:
Mark Paluch
2017-07-26 12:07:20 +02:00
committed by Oliver Gierke
parent e4e677d6b1
commit 22b37800ec
15 changed files with 894 additions and 216 deletions

View File

@@ -53,8 +53,6 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
@Mock PersistentEntity<?, P> entity;
@Mock ParameterValueProvider<P> provider;
@Mock PreferredConstructor<?, P> constructor;
@Mock Parameter<?, P> parameter;
@Test
public void instantiatesSimpleObjectCorrectly() {
@@ -72,11 +70,10 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
this.instance.createInstance(entity, provider);
}
@Test
@Test // DATACMNS-1126
public void instantiatesTypeWithPreferredConstructorUsingParameterValueProvider() {
PreferredConstructor<Foo, P> constructor = new PreferredConstructorDiscoverer<Foo, P>(Foo.class)
.getConstructor();
PreferredConstructor<Foo, P> constructor = PreferredConstructorDiscoverer.discover(Foo.class);
doReturn(Foo.class).when(entity).getType();
doReturn(constructor).when(entity).getPersistenceConstructor();
@@ -150,23 +147,21 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
}
}
@Test // DATACMNS-578
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test // DATACMNS-578, DATACMNS-1126
public void instantiateObjCtorDefault() {
doReturn(ObjCtorDefault.class).when(entity).getType();
doReturn(new PreferredConstructorDiscoverer<>(ObjCtorDefault.class).getConstructor())//
doReturn(PreferredConstructorDiscoverer.discover(ObjCtorDefault.class))//
.when(entity).getPersistenceConstructor();
IntStream.range(0, 2).forEach(i -> assertThat(this.instance.createInstance(entity, provider)).isInstanceOf(ObjCtorDefault.class));
}
@Test // DATACMNS-578
@SuppressWarnings({ "unchecked", "rawtypes" })
@Test // DATACMNS-578, DATACMNS-1126
public void instantiateObjCtorNoArgs() {
doReturn(ObjCtorNoArgs.class).when(entity).getType();
doReturn(new PreferredConstructorDiscoverer<>(ObjCtorNoArgs.class).getConstructor())//
doReturn(PreferredConstructorDiscoverer.discover(ObjCtorNoArgs.class))//
.when(entity).getPersistenceConstructor();
IntStream.range(0, 2).forEach(i -> {
@@ -178,12 +173,11 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
});
}
@Test // DATACMNS-578
@SuppressWarnings("unchecked")
@Test // DATACMNS-578, DATACMNS-1126
public void instantiateObjCtor1ParamString() {
doReturn(ObjCtor1ParamString.class).when(entity).getType();
doReturn(new PreferredConstructorDiscoverer<>(ObjCtor1ParamString.class).getConstructor())//
doReturn(PreferredConstructorDiscoverer.discover(ObjCtor1ParamString.class))//
.when(entity).getPersistenceConstructor();
doReturn("FOO").when(provider).getParameterValue(any());
@@ -197,12 +191,11 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
});
}
@Test // DATACMNS-578
@SuppressWarnings("unchecked")
@Test // DATACMNS-578, DATACMNS-1126
public void instantiateObjCtor2ParamStringString() {
doReturn(ObjCtor2ParamStringString.class).when(entity).getType();
doReturn(new PreferredConstructorDiscoverer<>(ObjCtor2ParamStringString.class).getConstructor())//
doReturn(PreferredConstructorDiscoverer.discover(ObjCtor2ParamStringString.class))//
.when(entity).getPersistenceConstructor();
IntStream.range(0, 2).forEach(i -> {
@@ -218,12 +211,11 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
});
}
@Test // DATACMNS-578
@SuppressWarnings("unchecked")
@Test // DATACMNS-578, DATACMNS-1126
public void instantiateObjectCtor1ParamInt() {
doReturn(ObjectCtor1ParamInt.class).when(entity).getType();
doReturn(new PreferredConstructorDiscoverer<>(ObjectCtor1ParamInt.class).getConstructor())//
doReturn(PreferredConstructorDiscoverer.discover(ObjectCtor1ParamInt.class))//
.when(entity).getPersistenceConstructor();
IntStream.range(0, 2).forEach(i -> {
@@ -237,12 +229,12 @@ public class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProp
});
}
@Test // DATACMNS-578
@Test // DATACMNS-578, DATACMNS-1126
@SuppressWarnings("unchecked")
public void instantiateObjectCtor7ParamsString5IntsString() {
doReturn(ObjectCtor7ParamsString5IntsString.class).when(entity).getType();
doReturn(new PreferredConstructorDiscoverer<>(ObjectCtor7ParamsString5IntsString.class).getConstructor())//
doReturn(PreferredConstructorDiscoverer.discover(ObjectCtor7ParamsString5IntsString.class))//
.when(entity).getPersistenceConstructor();
IntStream.range(0, 2).forEach(i -> {

View File

@@ -51,8 +51,6 @@ public class ReflectionEntityInstantiatorUnitTests<P extends PersistentProperty<
@Mock PersistentEntity<?, P> entity;
@Mock ParameterValueProvider<P> provider;
@Mock PreferredConstructor<?, P> constructor;
@Mock Parameter<?, P> parameter;
@Test
public void instantiatesSimpleObjectCorrectly() {
@@ -68,10 +66,10 @@ public class ReflectionEntityInstantiatorUnitTests<P extends PersistentProperty<
INSTANCE.createInstance(entity, provider);
}
@Test
@Test // DATACMNS-1126
public void instantiatesTypeWithPreferredConstructorUsingParameterValueProvider() {
PreferredConstructor<Foo, P> constructor = new PreferredConstructorDiscoverer<Foo, P>(Foo.class).getConstructor();
PreferredConstructor<Foo, P> constructor = PreferredConstructorDiscoverer.discover(Foo.class);
doReturn(constructor).when(entity).getPersistenceConstructor();

View File

@@ -36,13 +36,10 @@ import org.springframework.data.util.ClassTypeInformation;
*/
public class PreferredConstructorDiscovererUnitTests<P extends PersistentProperty<P>> {
@Test
@Test // DATACMNS-1126
public void findsNoArgConstructorForClassWithoutExplicitConstructor() {
PreferredConstructorDiscoverer<EntityWithoutConstructor, P> discoverer = new PreferredConstructorDiscoverer<>(
EntityWithoutConstructor.class);
assertThat(discoverer.getConstructor()).satisfies(constructor -> {
assertThat(PreferredConstructorDiscoverer.discover(EntityWithoutConstructor.class)).satisfies(constructor -> {
assertThat(constructor).isNotNull();
assertThat(constructor.isNoArgConstructor()).isTrue();
@@ -50,13 +47,10 @@ public class PreferredConstructorDiscovererUnitTests<P extends PersistentPropert
});
}
@Test
@Test // DATACMNS-1126
public void findsNoArgConstructorForClassWithMultipleConstructorsAndNoArgOne() {
PreferredConstructorDiscoverer<ClassWithEmptyConstructor, P> discoverer = new PreferredConstructorDiscoverer<>(
ClassWithEmptyConstructor.class);
assertThat(discoverer.getConstructor()).satisfies(constructor -> {
assertThat(PreferredConstructorDiscoverer.discover(ClassWithEmptyConstructor.class)).satisfies(constructor -> {
assertThat(constructor).isNotNull();
assertThat(constructor.isNoArgConstructor()).isTrue();
@@ -64,22 +58,19 @@ public class PreferredConstructorDiscovererUnitTests<P extends PersistentPropert
});
}
@Test
@Test // DATACMNS-1126
public void doesNotThrowExceptionForMultipleConstructorsAndNoNoArgConstructorWithoutAnnotation() {
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsWithoutEmptyOne, P> discoverer = new PreferredConstructorDiscoverer<>(
ClassWithMultipleConstructorsWithoutEmptyOne.class);
assertThat(discoverer.getConstructor()).isNull();
assertThat(PreferredConstructorDiscoverer.discover(ClassWithMultipleConstructorsWithoutEmptyOne.class)).isNull();
}
@Test
@Test // DATACMNS-1126
@SuppressWarnings({ "unchecked", "rawtypes" })
public void usesConstructorWithAnnotationOverEveryOther() {
PreferredConstructorDiscoverer<ClassWithMultipleConstructorsAndAnnotation, P> discoverer = new PreferredConstructorDiscoverer<>(
ClassWithMultipleConstructorsAndAnnotation.class);
assertThat(discoverer.getConstructor()).satisfies(constructor -> {
assertThat(PreferredConstructorDiscoverer.discover(ClassWithMultipleConstructorsAndAnnotation.class))
.satisfies(constructor -> {
assertThat(constructor).isNotNull();
assertThat(constructor.isNoArgConstructor()).isFalse();
@@ -87,7 +78,7 @@ public class PreferredConstructorDiscovererUnitTests<P extends PersistentPropert
assertThat(constructor.hasParameters()).isTrue();
Iterator<Parameter<Object, P>> parameters = constructor.getParameters().iterator();
Iterator<Parameter<Object, P>> parameters = (Iterator) constructor.getParameters().iterator();
Parameter<?, P> parameter = parameters.next();
assertThat(parameter.getType().getType()).isEqualTo(Long.class);
@@ -95,26 +86,25 @@ public class PreferredConstructorDiscovererUnitTests<P extends PersistentPropert
});
}
@Test // DATACMNS-134
@Test // DATACMNS-134, DATACMNS-1126
public void discoversInnerClassConstructorCorrectly() {
PersistentEntity<Inner, P> entity = new BasicPersistentEntity<>(ClassTypeInformation.from(Inner.class));
PreferredConstructorDiscoverer<Inner, P> discoverer = new PreferredConstructorDiscoverer<>(entity);
assertThat(discoverer.getConstructor()).satisfies(constructor -> {
assertThat(PreferredConstructorDiscoverer.discover(entity)).satisfies(constructor -> {
Parameter<?, P> parameter = constructor.getParameters().iterator().next();
assertThat(constructor.isEnclosingClassParameter(parameter)).isTrue();
});
}
@Test // DATACMNS-1082
@Test // DATACMNS-1082, DATACMNS-1126
public void skipsSyntheticConstructor() {
PersistentEntity<SyntheticConstructor, P> entity = new BasicPersistentEntity<>(ClassTypeInformation.from(SyntheticConstructor.class));
PreferredConstructorDiscoverer<SyntheticConstructor, P> discoverer = new PreferredConstructorDiscoverer<>(entity);
PersistentEntity<SyntheticConstructor, P> entity = new BasicPersistentEntity<>(
ClassTypeInformation.from(SyntheticConstructor.class));
assertThat(discoverer.getConstructor()).satisfies(constructor -> {
assertThat(PreferredConstructorDiscoverer.discover(entity)).satisfies(constructor -> {
PersistenceConstructor annotation = constructor.getConstructor().getAnnotation(PersistenceConstructor.class);
assertThat(annotation).isNotNull();
@@ -124,8 +114,7 @@ public class PreferredConstructorDiscovererUnitTests<P extends PersistentPropert
static class SyntheticConstructor {
@PersistenceConstructor
private SyntheticConstructor(String x) {
}
private SyntheticConstructor(String x) {}
class InnerSynthetic {
// Compiler will generate a synthetic constructor since