1
16 package org.springframework.data.mapping.model;
17
18 import kotlin.jvm.JvmClassMappingKt;
19 import kotlin.reflect.KFunction;
20 import kotlin.reflect.full.KClasses;
21 import kotlin.reflect.jvm.ReflectJvmMapping;
22
23 import java.lang.annotation.Annotation;
24 import java.lang.reflect.Constructor;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28
29 import org.springframework.core.DefaultParameterNameDiscoverer;
30 import org.springframework.core.ParameterNameDiscoverer;
31 import org.springframework.data.annotation.PersistenceConstructor;
32 import org.springframework.data.mapping.PersistentEntity;
33 import org.springframework.data.mapping.PersistentProperty;
34 import org.springframework.data.mapping.PreferredConstructor;
35 import org.springframework.data.mapping.PreferredConstructor.Parameter;
36 import org.springframework.data.util.ClassTypeInformation;
37 import org.springframework.data.util.ReflectionUtils;
38 import org.springframework.data.util.TypeInformation;
39 import org.springframework.lang.Nullable;
40 import org.springframework.util.Assert;
41
42
50 public interface PreferredConstructorDiscoverer<T, P extends PersistentProperty<P>> {
51
52
58 @Nullable
59 static <T, P extends PersistentProperty<P>> PreferredConstructor<T, P> discover(Class<T> type) {
60
61 Assert.notNull(type, "Type must not be null!");
62
63 return Discoverers.findDiscoverer(type)
64 .discover(ClassTypeInformation.from(type), null);
65 }
66
67
73 @Nullable
74 static <T, P extends PersistentProperty<P>> PreferredConstructor<T, P> discover(PersistentEntity<T, P> entity) {
75
76 Assert.notNull(entity, "PersistentEntity must not be null!");
77
78 return Discoverers.findDiscoverer(entity.getType())
79 .discover(entity.getTypeInformation(), entity);
80 }
81
82
91 enum Discoverers {
92
93
96 DEFAULT {
97
98
102 @Nullable
103 @Override
104 <T, P extends PersistentProperty<P>> PreferredConstructor<T, P> discover(TypeInformation<T> type,
105 @Nullable PersistentEntity<T, P> entity) {
106
107 Class<?> rawOwningType = type.getType();
108
109 List<Constructor<?>> candidates = new ArrayList<>();
110 Constructor<?> noArg = null;
111 for (Constructor<?> candidate : rawOwningType.getDeclaredConstructors()) {
112
113
114 if (candidate.isSynthetic()) {
115 continue;
116 }
117
118 if (candidate.isAnnotationPresent(PersistenceConstructor.class)) {
119 return buildPreferredConstructor(candidate, type, entity);
120 }
121
122 if (candidate.getParameterCount() == 0) {
123 noArg = candidate;
124 } else {
125 candidates.add(candidate);
126 }
127 }
128
129 if (noArg != null) {
130 return buildPreferredConstructor(noArg, type, entity);
131 }
132
133 return candidates.size() > 1 || candidates.isEmpty() ? null
134 : buildPreferredConstructor(candidates.iterator().next(), type, entity);
135 }
136 },
137
138
141 KOTLIN {
142
143
147 @Nullable
148 @Override
149 <T, P extends PersistentProperty<P>> PreferredConstructor<T, P> discover(TypeInformation<T> type,
150 @Nullable PersistentEntity<T, P> entity) {
151
152 Class<?> rawOwningType = type.getType();
153
154 return Arrays.stream(rawOwningType.getDeclaredConstructors())
155 .filter(it -> !it.isSynthetic())
156 .filter(it -> it.isAnnotationPresent(PersistenceConstructor.class))
157
158 .map(it -> buildPreferredConstructor(it, type, entity))
159 .findFirst()
160 .orElseGet(() -> {
161
162 KFunction<T> primaryConstructor = KClasses
163 .getPrimaryConstructor(JvmClassMappingKt.getKotlinClass(type.getType()));
164
165 if (primaryConstructor == null) {
166 return DEFAULT.discover(type, entity);
167 }
168
169 Constructor<T> javaConstructor = ReflectJvmMapping.getJavaConstructor(primaryConstructor);
170
171 return javaConstructor != null ? buildPreferredConstructor(javaConstructor, type, entity) : null;
172 });
173 }
174 };
175
176 private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
177
178
184 private static Discoverers findDiscoverer(Class<?> type) {
185 return ReflectionUtils.isSupportedKotlinClass(type) ? KOTLIN : DEFAULT;
186 }
187
188
195 @Nullable
196 abstract <T, P extends PersistentProperty<P>> PreferredConstructor<T, P> discover(TypeInformation<T> type,
197 @Nullable PersistentEntity<T, P> entity);
198
199 @SuppressWarnings({ "unchecked", "rawtypes" })
200 private static <T, P extends PersistentProperty<P>> PreferredConstructor<T, P> buildPreferredConstructor(
201 Constructor<?> constructor, TypeInformation<T> typeInformation, @Nullable PersistentEntity<T, P> entity) {
202
203 if (constructor.getParameterCount() == 0) {
204 return new PreferredConstructor<>((Constructor<T>) constructor);
205 }
206
207 List<TypeInformation<?>> parameterTypes = typeInformation.getParameterTypes(constructor);
208 String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(constructor);
209
210 Parameter<Object, P>[] parameters = new Parameter[parameterTypes.size()];
211 Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
212
213 for (int i = 0; i < parameterTypes.size(); i++) {
214
215 String name = parameterNames == null ? null : parameterNames[i];
216 TypeInformation<?> type = parameterTypes.get(i);
217 Annotation[] annotations = parameterAnnotations[i];
218
219 parameters[i] = new Parameter(name, type, annotations, entity);
220 }
221
222 return new PreferredConstructor<>((Constructor<T>) constructor, parameters);
223 }
224 }
225 }
226