1 /*
2  * Copyright 2011-2020 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

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 /**
39  * Simple implementation of {@link PersistentProperty}.
40  *
41  * @author Jon Brisbin
42  * @author Oliver Gierke
43  * @author Christoph Strobl
44  * @author Mark Paluch
45  */

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     /*
108      * (non-Javadoc)
109      * @see org.springframework.data.mapping.PersistentProperty#getName()
110      */

111     @Override
112     public String getName() {
113         return name;
114     }
115
116     /*
117      * (non-Javadoc)
118      * @see org.springframework.data.mapping.PersistentProperty#getType()
119      */

120     @Override
121     public Class<?> getType() {
122         return information.getType();
123     }
124
125     /*
126      * (non-Javadoc)
127      * @see org.springframework.data.mapping.PersistentProperty#getRawType()
128      */

129     @Override
130     public Class<?> getRawType() {
131         return this.rawType;
132     }
133
134     /*
135      * (non-Javadoc)
136      * @see org.springframework.data.mapping.PersistentProperty#getTypeInformation()
137      */

138     @Override
139     public TypeInformation<?> getTypeInformation() {
140         return information;
141     }
142
143     /*
144      * (non-Javadoc)
145      * @see org.springframework.data.mapping.PersistentProperty#getPersistentEntityTypes()
146      */

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     /*
160      * (non-Javadoc)
161      * @see org.springframework.data.mapping.PersistentProperty#getSpelExpression()
162      */

163     @Override
164     @Nullable
165     public String getSpelExpression() {
166         return null;
167     }
168
169     /*
170      * (non-Javadoc)
171      * @see org.springframework.data.mapping.PersistentProperty#isTransient()
172      */

173     @Override
174     public boolean isTransient() {
175         return false;
176     }
177
178     /*
179      * (non-Javadoc)
180      * @see org.springframework.data.mapping.PersistentProperty#isWritable()
181      */

182     @Override
183     public boolean isWritable() {
184         return !isTransient();
185     }
186
187     /*
188      * (non-Javadoc)
189      * @see org.springframework.data.mapping.PersistentProperty#isImmutable()
190      */

191     @Override
192     public boolean isImmutable() {
193         return immutable;
194     }
195
196     /*
197      * (non-Javadoc)
198      * @see org.springframework.data.mapping.PersistentProperty#isAssociation()
199      */

200     @Override
201     public boolean isAssociation() {
202         return isAnnotationPresent(Reference.class);
203     }
204
205     /*
206      * (non-Javadoc)
207      * @see org.springframework.data.mapping.PersistentProperty#getAssociation()
208      */

209     @Nullable
210     @Override
211     public Association<P> getAssociation() {
212         return association.orElse(null);
213     }
214
215     /*
216      * (non-Javadoc)
217      * @see org.springframework.data.mapping.PersistentProperty#isCollectionLike()
218      */

219     @Override
220     public boolean isCollectionLike() {
221         return information.isCollectionLike();
222     }
223
224     /*
225      * (non-Javadoc)
226      * @see org.springframework.data.mapping.PersistentProperty#isMap()
227      */

228     @Override
229     public boolean isMap() {
230         return Map.class.isAssignableFrom(getType());
231     }
232
233     /*
234      * (non-Javadoc)
235      * @see org.springframework.data.mapping.PersistentProperty#isArray()
236      */

237     @Override
238     public boolean isArray() {
239         return getType().isArray();
240     }
241
242     /*
243      * (non-Javadoc)
244      * @see org.springframework.data.mapping.PersistentProperty#isEntity()
245      */

246     @Override
247     public boolean isEntity() {
248         return !isTransient() && entityTypeInformation.get().isPresent();
249     }
250
251     /*
252      * (non-Javadoc)
253      * @see org.springframework.data.mapping.PersistentProperty#getComponentType()
254      */

255     @Nullable
256     @Override
257     public Class<?> getComponentType() {
258         return isMap() || isCollectionLike() ? information.getRequiredComponentType().getType() : null;
259     }
260
261     /*
262      * (non-Javadoc)
263      * @see org.springframework.data.mapping.PersistentProperty#getMapValueType()
264      */

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     /*
281      * (non-Javadoc)
282      * @see org.springframework.data.mapping.PersistentProperty#getActualType()
283      */

284     @Override
285     public Class<?> getActualType() {
286         return information.getRequiredActualType().getType();
287     }
288
289     /*
290      * (non-Javadoc)
291      * @see org.springframework.data.mongodb.core.mapping.MongoPersistentProperty#usePropertyAccess()
292      */

293     public boolean usePropertyAccess() {
294         return usePropertyAccess.get();
295     }
296
297     /*
298      * (non-Javadoc)
299      * @see java.lang.Object#equals(java.lang.Object)
300      */

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     /*
318      * (non-Javadoc)
319      * @see java.lang.Object#hashCode()
320      */

321     @Override
322     public int hashCode() {
323         return this.hashCode.get();
324     }
325
326     /*
327      * (non-Javadoc)
328      * @see java.lang.Object#toString()
329      */

330     @Override
331     public String toString() {
332         return property.toString();
333     }
334 }
335