Skip to content

Add support for Kotlin Value Classes #2866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed
Prev Previous commit
Next Next commit
Refine wrapping rules for copy and constructor usage.
  • Loading branch information
mp911de committed Jun 27, 2023
commit 8a6ba2ed56ac9f36c43e96a45dfb25af44a69318
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin}</version>
</dependency>

<!-- RxJava -->

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ private static <T> Map<KParameter, Object> getCallArgs(KCallable<?> callable, Pe
if (parameter.getKind() == Kind.VALUE && parameter.getName() != null
&& parameter.getName().equals(property.getName())) {

args.put(parameter, KotlinValueUtils.getValueHierarchy(parameter).wrap(value));
args.put(parameter, KotlinValueUtils.getCopyValueHierarchy(parameter).wrap(value));
}
}
return args;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ public void setProperty(PersistentProperty<?> property, @Nullable Object value)
* @param property the persistent property to inspect.
* @return a wrapper function to wrap a value class component into the hierarchy of value classes or
* {@link Function#identity()} if wrapping is not necessary.
* @see KotlinValueUtils#getValueHierarchy(Class)
* @see KotlinValueUtils#getCopyValueHierarchy(KParameter)
*/
static Function<Object, Object> getWrapper(PersistentProperty<?> property) {

Expand All @@ -1506,7 +1506,7 @@ static Function<Object, Object> getWrapper(PersistentProperty<?> property) {
.filter(kf -> kf.getName().equals(property.getName())) //
.findFirst();

ValueBoxing vh = kParameter.map(KotlinValueUtils::getValueHierarchy).orElse(null);
ValueBoxing vh = kParameter.map(KotlinValueUtils::getCopyValueHierarchy).orElse(null);
KotlinCopyByProperty kotlinCopyByProperty = copyMethod.forProperty(property).get();
Method copy = copyMethod.getSyntheticCopyMethod();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
*/
package org.springframework.data.mapping.model;

import kotlin.jvm.JvmClassMappingKt;
import kotlin.reflect.KCallable;
import kotlin.reflect.KClass;
import kotlin.reflect.KProperty;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
Expand All @@ -24,6 +29,7 @@
import java.util.List;

import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.data.annotation.PersistenceCreator;
Expand Down Expand Up @@ -55,7 +61,8 @@ class InstanceCreatorMetadataDiscoverer {
* @return
*/
@Nullable
public static <T, P extends PersistentProperty<P>> InstanceCreatorMetadata<P> discover(PersistentEntity<T, P> entity) {
public static <T, P extends PersistentProperty<P>> InstanceCreatorMetadata<P> discover(
PersistentEntity<T, P> entity) {

Constructor<?>[] declaredConstructors = entity.getType().getDeclaredConstructors();
Method[] declaredMethods = entity.getType().getDeclaredMethods();
Expand All @@ -78,6 +85,34 @@ public static <T, P extends PersistentProperty<P>> InstanceCreatorMetadata<P> di
}
}

if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(entity.getType())) {

KClass<?> kClass = JvmClassMappingKt.getKotlinClass(entity.getType());
// We use box-impl as factory for classes.
if (kClass.isValue()) {

String propertyName = "";
for (KCallable<?> member : kClass.getMembers()) {
if (member instanceof KProperty<?>) {
propertyName = member.getName();
break;
}
}

for (Method declaredMethod : entity.getType().getDeclaredMethods()) {
if (declaredMethod.getName().equals("box-impl") && declaredMethod.isSynthetic()
&& declaredMethod.getParameterCount() == 1) {

Annotation[][] parameterAnnotations = declaredMethod.getParameterAnnotations();
List<TypeInformation<?>> types = entity.getTypeInformation().getParameterTypes(declaredMethod);

return new FactoryMethod<>(declaredMethod,
new Parameter<>(propertyName, (TypeInformation) types.get(0), parameterAnnotations[0], entity));
}
}
}
}

return PreferredConstructorDiscoverer.discover(entity);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public KotlinInstantiationDelegate(PreferredConstructor<?, ?> preferredConstruct

for (KParameter kParameter : kParameters) {

ValueBoxing valueBoxing = KotlinValueUtils.getValueHierarchy(kParameter);
ValueBoxing valueBoxing = KotlinValueUtils.getConstructorValueHierarchy(kParameter);
wrappers.add(valueBoxing::wrap);
}
}
Expand Down Expand Up @@ -242,9 +242,9 @@ static boolean parametersMatch(java.lang.reflect.Parameter constructorParameter,
}

// candidate can be also a wrapper
Class<?> componentOrWrapperType = KotlinValueUtils.getConstructorValueHierarchy(candidateParameter.getType())
.getActualType();

Class<?> componentType = KotlinValueUtils.getValueHierarchy(candidateParameter.getType()).getActualType();

return constructorParameter.getType().equals(componentType);
return constructorParameter.getType().equals(componentOrWrapperType);
}
}
Loading