1
16 package org.springframework.data.mapping.model;
17
18 import kotlin.reflect.KFunction;
19 import kotlin.reflect.KParameter;
20 import kotlin.reflect.jvm.ReflectJvmMapping;
21
22 import java.lang.reflect.Constructor;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.util.stream.IntStream;
26
27 import org.springframework.data.mapping.PersistentEntity;
28 import org.springframework.data.mapping.PersistentProperty;
29 import org.springframework.data.mapping.PreferredConstructor;
30 import org.springframework.data.mapping.PreferredConstructor.Parameter;
31 import org.springframework.data.util.ReflectionUtils;
32 import org.springframework.lang.Nullable;
33
34
42 class KotlinClassGeneratingEntityInstantiator extends ClassGeneratingEntityInstantiator {
43
44
48 @Override
49 protected EntityInstantiator doCreateEntityInstantiator(PersistentEntity<?, ?> entity) {
50
51 PreferredConstructor<?, ?> constructor = entity.getPersistenceConstructor();
52
53 if (ReflectionUtils.isSupportedKotlinClass(entity.getType()) && constructor != null) {
54
55 PreferredConstructor<?, ?> defaultConstructor = new DefaultingKotlinConstructorResolver(entity)
56 .getDefaultConstructor();
57
58 if (defaultConstructor != null) {
59
60 ObjectInstantiator instantiator = createObjectInstantiator(entity, defaultConstructor);
61
62 return new DefaultingKotlinClassInstantiatorAdapter(instantiator, constructor);
63 }
64 }
65
66 return super.doCreateEntityInstantiator(entity);
67 }
68
69
76 static class DefaultingKotlinConstructorResolver {
77
78 private final @Nullable PreferredConstructor<?, ?> defaultConstructor;
79
80 @SuppressWarnings("unchecked")
81 DefaultingKotlinConstructorResolver(PersistentEntity<?, ?> entity) {
82
83 Constructor<?> hit = resolveDefaultConstructor(entity);
84 PreferredConstructor<?, ?> persistenceConstructor = entity.getPersistenceConstructor();
85
86 if (hit != null && persistenceConstructor != null) {
87 this.defaultConstructor = new PreferredConstructor<>(hit,
88 persistenceConstructor.getParameters().toArray(new Parameter[0]));
89 } else {
90 this.defaultConstructor = null;
91 }
92 }
93
94 @Nullable
95 private static Constructor<?> resolveDefaultConstructor(PersistentEntity<?, ?> entity) {
96
97 PreferredConstructor<?, ?> persistenceConstructor = entity.getPersistenceConstructor();
98
99 if (persistenceConstructor == null) {
100 return null;
101 }
102
103 Constructor<?> hit = null;
104 Constructor<?> constructor = persistenceConstructor.getConstructor();
105
106 for (Constructor<?> candidate : entity.getType().getDeclaredConstructors()) {
107
108
109 if (!candidate.isSynthetic()) {
110 continue;
111 }
112
113
114
115 int syntheticParameters = KotlinDefaultMask.getMaskCount(constructor.getParameterCount())
116 + 1;
117
118 if (constructor.getParameterCount() + syntheticParameters != candidate.getParameterCount()) {
119 continue;
120 }
121
122 java.lang.reflect.Parameter[] constructorParameters = constructor.getParameters();
123 java.lang.reflect.Parameter[] candidateParameters = candidate.getParameters();
124
125 if (!candidateParameters[candidateParameters.length - 1].getType().getName()
126 .equals("kotlin.jvm.internal.DefaultConstructorMarker")) {
127 continue;
128 }
129
130 if (parametersMatch(constructorParameters, candidateParameters)) {
131 hit = candidate;
132 break;
133 }
134 }
135
136 return hit;
137 }
138
139 private static boolean parametersMatch(java.lang.reflect.Parameter[] constructorParameters,
140 java.lang.reflect.Parameter[] candidateParameters) {
141
142 return IntStream.range(0, constructorParameters.length)
143 .allMatch(i -> constructorParameters[i].getType().equals(candidateParameters[i].getType()));
144 }
145
146 @Nullable
147 PreferredConstructor<?, ?> getDefaultConstructor() {
148 return defaultConstructor;
149 }
150 }
151
152
170 static class DefaultingKotlinClassInstantiatorAdapter implements EntityInstantiator {
171
172 private final ObjectInstantiator instantiator;
173 private final KFunction<?> constructor;
174 private final List<KParameter> kParameters;
175 private final Constructor<?> synthetic;
176
177 DefaultingKotlinClassInstantiatorAdapter(ObjectInstantiator instantiator, PreferredConstructor<?, ?> constructor) {
178
179 KFunction<?> kotlinConstructor = ReflectJvmMapping.getKotlinFunction(constructor.getConstructor());
180
181 if (kotlinConstructor == null) {
182 throw new IllegalArgumentException(
183 "No corresponding Kotlin constructor found for " + constructor.getConstructor());
184 }
185
186 this.instantiator = instantiator;
187 this.constructor = kotlinConstructor;
188 this.kParameters = kotlinConstructor.getParameters();
189 this.synthetic = constructor.getConstructor();
190 }
191
192
196 @Override
197 @SuppressWarnings("unchecked")
198 public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
199 ParameterValueProvider<P> provider) {
200
201 Object[] params = extractInvocationArguments(entity.getPersistenceConstructor(), provider);
202
203 try {
204 return (T) instantiator.newInstance(params);
205 } catch (Exception e) {
206 throw new MappingInstantiationException(entity, Arrays.asList(params), e);
207 }
208 }
209
210 private <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
211 @Nullable PreferredConstructor<? extends T, P> preferredConstructor, ParameterValueProvider<P> provider) {
212
213 if (preferredConstructor == null) {
214 throw new IllegalArgumentException("PreferredConstructor must not be null!");
215 }
216
217 Object[] params = allocateArguments(synthetic.getParameterCount()
218 + KotlinDefaultMask.getMaskCount(synthetic.getParameterCount()) + 1);
219 int userParameterCount = kParameters.size();
220
221 List<Parameter<Object, P>> parameters = preferredConstructor.getParameters();
222
223
224 for (int i = 0; i < userParameterCount; i++) {
225
226 Parameter<Object, P> parameter = parameters.get(i);
227 params[i] = provider.getParameterValue(parameter);
228 }
229
230 KotlinDefaultMask defaultMask = KotlinDefaultMask.from(constructor, it -> {
231
232 int index = kParameters.indexOf(it);
233
234 Parameter<Object, P> parameter = parameters.get(index);
235 Class<Object> type = parameter.getType().getType();
236
237 if (it.isOptional() && params[index] == null) {
238 if (type.isPrimitive()) {
239
240
241 params[index] = ReflectionUtils.getPrimitiveDefault(type);
242 }
243 return false;
244 }
245
246 return true;
247 });
248
249 int[] defaulting = defaultMask.getDefaulting();
250
251 for (int i = 0; i < defaulting.length; i++) {
252 params[userParameterCount + i] = defaulting[i];
253 }
254
255 return params;
256 }
257 }
258 }
259