1
16 package org.springframework.data.mapping.model;
17
18 import lombok.AccessLevel;
19 import lombok.Getter;
20
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.util.Collections;
25 import java.util.Map;
26 import java.util.Optional;
27
28 import org.springframework.data.annotation.Reference;
29 import org.springframework.data.mapping.Association;
30 import org.springframework.data.mapping.PersistentEntity;
31 import org.springframework.data.mapping.PersistentProperty;
32 import org.springframework.data.util.Lazy;
33 import org.springframework.data.util.ReflectionUtils;
34 import org.springframework.data.util.TypeInformation;
35 import org.springframework.lang.Nullable;
36 import org.springframework.util.Assert;
37
38
46 public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>> implements PersistentProperty<P> {
47
48 private static final Field CAUSE_FIELD;
49
50 static {
51 CAUSE_FIELD = ReflectionUtils.findRequiredField(Throwable.class, "cause");
52 }
53
54 private final String name;
55 private final TypeInformation<?> information;
56 private final Class<?> rawType;
57 private final Lazy<Association<P>> association;
58 private final @Getter PersistentEntity<?, P> owner;
59
60 @SuppressWarnings("null")
61 private final @Getter(value = AccessLevel.PROTECTED, onMethod = @__(@SuppressWarnings("null"))) Property property;
62 private final Lazy<Integer> hashCode;
63 private final Lazy<Boolean> usePropertyAccess;
64 private final Lazy<Optional<? extends TypeInformation<?>>> entityTypeInformation;
65
66 private final @Getter(onMethod = @__(@Nullable)) Method getter;
67 private final @Getter(onMethod = @__(@Nullable)) Method setter;
68 private final @Getter(onMethod = @__(@Nullable)) Field field;
69 private final @Getter(onMethod = @__(@Nullable)) Method wither;
70 private final boolean immutable;
71
72 public AbstractPersistentProperty(Property property, PersistentEntity<?, P> owner,
73 SimpleTypeHolder simpleTypeHolder) {
74
75 Assert.notNull(simpleTypeHolder, "SimpleTypeHolder must not be null!");
76 Assert.notNull(owner, "Owner entity must not be null!");
77
78 this.name = property.getName();
79 this.information = owner.getTypeInformation().getRequiredProperty(getName());
80 this.rawType = this.information.getType();
81 this.property = property;
82 this.association = Lazy.of(() -> isAssociation() ? createAssociation() : null);
83 this.owner = owner;
84
85 this.hashCode = Lazy.of(property::hashCode);
86 this.usePropertyAccess = Lazy.of(() -> owner.getType().isInterface() || CAUSE_FIELD.equals(getField()));
87
88 this.entityTypeInformation = Lazy.of(() -> Optional.ofNullable(information.getActualType())
89 .filter(it -> !simpleTypeHolder.isSimpleType(it.getType()))
90 .filter(it -> !it.isCollectionLike())
91 .filter(it -> !it.isMap()));
92
93 this.getter = property.getGetter().orElse(null);
94 this.setter = property.getSetter().orElse(null);
95 this.field = property.getField().orElse(null);
96 this.wither = property.getWither().orElse(null);
97
98 if (setter == null && (field == null || Modifier.isFinal(field.getModifiers()))) {
99 this.immutable = true;
100 } else {
101 this.immutable = false;
102 }
103 }
104
105 protected abstract Association<P> createAssociation();
106
107
111 @Override
112 public String getName() {
113 return name;
114 }
115
116
120 @Override
121 public Class<?> getType() {
122 return information.getType();
123 }
124
125
129 @Override
130 public Class<?> getRawType() {
131 return this.rawType;
132 }
133
134
138 @Override
139 public TypeInformation<?> getTypeInformation() {
140 return information;
141 }
142
143
147 @Override
148 public Iterable<? extends TypeInformation<?>> getPersistentEntityTypes() {
149
150 if (!isEntity()) {
151 return Collections.emptySet();
152 }
153
154 return entityTypeInformation.get()
155 .map(Collections::singleton)
156 .orElseGet(Collections::emptySet);
157 }
158
159
163 @Override
164 @Nullable
165 public String getSpelExpression() {
166 return null;
167 }
168
169
173 @Override
174 public boolean isTransient() {
175 return false;
176 }
177
178
182 @Override
183 public boolean isWritable() {
184 return !isTransient();
185 }
186
187
191 @Override
192 public boolean isImmutable() {
193 return immutable;
194 }
195
196
200 @Override
201 public boolean isAssociation() {
202 return isAnnotationPresent(Reference.class);
203 }
204
205
209 @Nullable
210 @Override
211 public Association<P> getAssociation() {
212 return association.orElse(null);
213 }
214
215
219 @Override
220 public boolean isCollectionLike() {
221 return information.isCollectionLike();
222 }
223
224
228 @Override
229 public boolean isMap() {
230 return Map.class.isAssignableFrom(getType());
231 }
232
233
237 @Override
238 public boolean isArray() {
239 return getType().isArray();
240 }
241
242
246 @Override
247 public boolean isEntity() {
248 return !isTransient() && entityTypeInformation.get().isPresent();
249 }
250
251
255 @Nullable
256 @Override
257 public Class<?> getComponentType() {
258 return isMap() || isCollectionLike() ? information.getRequiredComponentType().getType() : null;
259 }
260
261
265 @Nullable
266 @Override
267 public Class<?> getMapValueType() {
268
269 if (isMap()) {
270
271 TypeInformation<?> mapValueType = information.getMapValueType();
272 if (mapValueType != null) {
273 return mapValueType.getType();
274 }
275 }
276
277 return null;
278 }
279
280
284 @Override
285 public Class<?> getActualType() {
286 return information.getRequiredActualType().getType();
287 }
288
289
293 public boolean usePropertyAccess() {
294 return usePropertyAccess.get();
295 }
296
297
301 @Override
302 public boolean equals(@Nullable Object obj) {
303
304 if (this == obj) {
305 return true;
306 }
307
308 if (!(obj instanceof AbstractPersistentProperty)) {
309 return false;
310 }
311
312 AbstractPersistentProperty<?> that = (AbstractPersistentProperty<?>) obj;
313
314 return this.property.equals(that.property);
315 }
316
317
321 @Override
322 public int hashCode() {
323 return this.hashCode.get();
324 }
325
326
330 @Override
331 public String toString() {
332 return property.toString();
333 }
334 }
335