package org.springframework.data.mapping.model;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
import kotlin.reflect.jvm.ReflectJvmMapping;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.util.KotlinReflectionUtils;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.lang.Nullable;
class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEntityInstantiator {
@Override
protected EntityInstantiator doCreateEntityInstantiator(PersistentEntity<?, ?> entity) {
PreferredConstructor<?, ?> constructor = entity.getPersistenceConstructor();
if (KotlinReflectionUtils.isSupportedKotlinClass(entity.getType()) && constructor != null) {
PreferredConstructor<?, ?> defaultConstructor = new DefaultingKotlinConstructorResolver(entity)
.getDefaultConstructor();
if (defaultConstructor != null) {
ObjectInstantiator instantiator = createObjectInstantiator(entity, defaultConstructor);
return new DefaultingKotlinClassInstantiatorAdapter(instantiator, constructor);
}
}
return super.doCreateEntityInstantiator(entity);
}
static class DefaultingKotlinConstructorResolver {
private final @Nullable PreferredConstructor<?, ?> defaultConstructor;
@SuppressWarnings("unchecked")
DefaultingKotlinConstructorResolver(PersistentEntity<?, ?> entity) {
Constructor<?> hit = resolveDefaultConstructor(entity);
PreferredConstructor<?, ?> persistenceConstructor = entity.getPersistenceConstructor();
if (hit != null && persistenceConstructor != null) {
this.defaultConstructor = new PreferredConstructor<>(hit,
persistenceConstructor.getParameters().toArray(new Parameter[0]));
} else {
this.defaultConstructor = null;
}
}
@Nullable
private static Constructor<?> resolveDefaultConstructor(PersistentEntity<?, ?> entity) {
PreferredConstructor<?, ?> persistenceConstructor = entity.getPersistenceConstructor();
if (persistenceConstructor == null) {
return null;
}
Constructor<?> hit = null;
Constructor<?> constructor = persistenceConstructor.getConstructor();
for (Constructor<?> candidate : entity.getType().getDeclaredConstructors()) {
if (!candidate.isSynthetic()) {
continue;
}
int syntheticParameters = KotlinDefaultMask.getMaskCount(constructor.getParameterCount())
+ 1;
if (constructor.getParameterCount() + syntheticParameters != candidate.getParameterCount()) {
continue;
}
java.lang.reflect.Parameter[] constructorParameters = constructor.getParameters();
java.lang.reflect.Parameter[] candidateParameters = candidate.getParameters();
if (!candidateParameters[candidateParameters.length - 1].getType().getName()
.equals("kotlin.jvm.internal.DefaultConstructorMarker")) {
continue;
}
if (parametersMatch(constructorParameters, candidateParameters)) {
hit = candidate;
break;
}
}
return hit;
}
private static boolean parametersMatch(java.lang.reflect.Parameter[] constructorParameters,
java.lang.reflect.Parameter[] candidateParameters) {
return IntStream.range(0, constructorParameters.length)
.allMatch(i -> constructorParameters[i].getType().equals(candidateParameters[i].getType()));
}
@Nullable
PreferredConstructor<?, ?> getDefaultConstructor() {
return defaultConstructor;
}
}
static class DefaultingKotlinClassInstantiatorAdapter implements EntityInstantiator {
private final ObjectInstantiator instantiator;
private final KFunction<?> constructor;
private final List<KParameter> kParameters;
private final Constructor<?> synthetic;
DefaultingKotlinClassInstantiatorAdapter(ObjectInstantiator instantiator, PreferredConstructor<?, ?> constructor) {
KFunction<?> kotlinConstructor = ReflectJvmMapping.getKotlinFunction(constructor.getConstructor());
if (kotlinConstructor == null) {
throw new IllegalArgumentException(
"No corresponding Kotlin constructor found for " + constructor.getConstructor());
}
this.instantiator = instantiator;
this.constructor = kotlinConstructor;
this.kParameters = kotlinConstructor.getParameters();
this.synthetic = constructor.getConstructor();
}
@Override
@SuppressWarnings("unchecked")
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
ParameterValueProvider<P> provider) {
Object[] params = extractInvocationArguments(entity.getPersistenceConstructor(), provider);
try {
return (T) instantiator.newInstance(params);
} catch (Exception e) {
throw new MappingInstantiationException(entity, Arrays.asList(params), e);
}
}
private <P extends PersistentProperty<P>, T> Object[] (
@Nullable PreferredConstructor<? extends T, P> preferredConstructor, ParameterValueProvider<P> provider) {
if (preferredConstructor == null) {
throw new IllegalArgumentException("PreferredConstructor must not be null!");
}
Object[] params = allocateArguments(synthetic.getParameterCount()
+ KotlinDefaultMask.getMaskCount(synthetic.getParameterCount()) + 1);
int userParameterCount = kParameters.size();
List<Parameter<Object, P>> parameters = preferredConstructor.getParameters();
for (int i = 0; i < userParameterCount; i++) {
Parameter<Object, P> parameter = parameters.get(i);
params[i] = provider.getParameterValue(parameter);
}
KotlinDefaultMask defaultMask = KotlinDefaultMask.from(constructor, it -> {
int index = kParameters.indexOf(it);
Parameter<Object, P> parameter = parameters.get(index);
Class<Object> type = parameter.getType().getType();
if (it.isOptional() && params[index] == null) {
if (type.isPrimitive()) {
params[index] = ReflectionUtils.getPrimitiveDefault(type);
}
return false;
}
return true;
});
int[] defaulting = defaultMask.getDefaulting();
for (int i = 0; i < defaulting.length; i++) {
params[userParameterCount + i] = defaulting[i];
}
return params;
}
}
}