1 /*
2  * Copyright 2008-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.util;
17
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.Method;
20 import java.util.List;
21
22 import org.springframework.lang.Nullable;
23
24 /**
25  * Interface to access property types and resolving generics on the way. Starting with a {@link ClassTypeInformation}
26  * you can traverse properties using {@link #getProperty(String)} to access type information.
27  *
28  * @author Oliver Gierke
29  * @author Mark Paluch
30  */

31 public interface TypeInformation<S> {
32
33     /**
34      * Returns the {@link TypeInformation}s for the parameters of the given {@link Constructor}.
35      *
36      * @param constructor must not be {@literal null}.
37      * @return
38      */

39     List<TypeInformation<?>> getParameterTypes(Constructor<?> constructor);
40
41     /**
42      * Returns the property information for the property with the given name. Supports property traversal through dot
43      * notation.
44      *
45      * @param property
46      * @return
47      */

48     @Nullable
49     TypeInformation<?> getProperty(String property);
50
51     /**
52      * Returns the property information for the property with the given name or throw {@link IllegalArgumentException} if
53      * the type information cannot be resolved. Supports property traversal through dot notation.
54      *
55      * @param property
56      * @return
57      * @throws IllegalArgumentException if the type information cannot be resolved.
58      * @since 2.0
59      */

60     default TypeInformation<?> getRequiredProperty(String property) {
61
62         TypeInformation<?> typeInformation = getProperty(property);
63
64         if (typeInformation != null) {
65             return typeInformation;
66         }
67
68         throw new IllegalArgumentException(
69                 String.format("Could not find required property %s on %s!", property, getType()));
70     }
71
72     /**
73      * Returns whether the type can be considered a collection, which means it's a container of elements, e.g. a
74      * {@link java.util.Collection} and {@link java.lang.reflect.Array} or anything implementing {@link Iterable}. If this
75      * returns {@literal true} you can expect {@link #getComponentType()} to return a non-{@literal null} value.
76      *
77      * @return
78      */

79     boolean isCollectionLike();
80
81     /**
82      * Returns the component type for {@link java.util.Collection}s or the key type for {@link java.util.Map}s.
83      *
84      * @return
85      */

86     @Nullable
87     TypeInformation<?> getComponentType();
88
89     /**
90      * Returns the component type for {@link java.util.Collection}s, the key type for {@link java.util.Map}s or the single
91      * generic type if available. Throws {@link IllegalStateException} if the component value type cannot be resolved.
92      *
93      * @return
94      * @throws IllegalStateException if the component type cannot be resolved, e.g. if a raw type is used or the type is
95      *           not generic in the first place.
96      * @since 2.0
97      */

98     default TypeInformation<?> getRequiredComponentType() {
99
100         TypeInformation<?> componentType = getComponentType();
101
102         if (componentType != null) {
103             return componentType;
104         }
105
106         throw new IllegalStateException(String.format("Can't resolve required component type for %s!", getType()));
107     }
108
109     /**
110      * Returns whether the property is a {@link java.util.Map}. If this returns {@literal true} you can expect
111      * {@link #getComponentType()} as well as {@link #getMapValueType()} to return something not {@literal null}.
112      *
113      * @return
114      */

115     boolean isMap();
116
117     /**
118      * Will return the type of the value in case the underlying type is a {@link java.util.Map}.
119      *
120      * @return
121      */

122     @Nullable
123     TypeInformation<?> getMapValueType();
124
125     /**
126      * Will return the type of the value in case the underlying type is a {@link java.util.Map} or throw
127      * {@link IllegalStateException} if the map value type cannot be resolved.
128      *
129      * @return
130      * @throws IllegalStateException if the map value type cannot be resolved, usually due to the current
131      *           {@link java.util.Map} type being a raw one.
132      * @since 2.0
133      */

134     default TypeInformation<?> getRequiredMapValueType() {
135
136         TypeInformation<?> mapValueType = getMapValueType();
137
138         if (mapValueType != null) {
139             return mapValueType;
140         }
141
142         throw new IllegalStateException(String.format("Can't resolve required map value type for %s!", getType()));
143     }
144
145     /**
146      * Returns the type of the property. Will resolve generics and the generic context of
147      *
148      * @return
149      */

150     Class<S> getType();
151
152     /**
153      * Returns a {@link ClassTypeInformation} to represent the {@link TypeInformation} of the raw type of the current
154      * instance.
155      *
156      * @return
157      */

158     ClassTypeInformation<?> getRawTypeInformation();
159
160     /**
161      * Transparently returns the {@link java.util.Map} value type if the type is a {@link java.util.Map}, returns the
162      * component type if the type {@link #isCollectionLike()} or the simple type if none of this applies.
163      *
164      * @return the map value, collection component type or the current type, {@literal null} it the current type is a raw
165      *         {@link java.util.Map} or {@link java.util.Collection}.
166      */

167     @Nullable
168     TypeInformation<?> getActualType();
169
170     /**
171      * Transparently returns the {@link java.util.Map} value type if the type is a {@link java.util.Map}, returns the
172      * component type if the type {@link #isCollectionLike()} or the simple type if none of this applies.
173      *
174      * @return
175      * @throws IllegalArgumentException if the current type is a raw {@link java.util.Map} or {@link java.util.Collection}
176      *           and no value or component type is available.
177      * @since 2.0
178      */

179     default TypeInformation<?> getRequiredActualType() {
180
181         TypeInformation<?> result = getActualType();
182
183         if (result == null) {
184             throw new IllegalStateException(
185                     "Expected to be able to resolve a type but got null! This usually stems from types implementing raw Map or Collection interfaces!");
186         }
187
188         return result;
189     }
190
191     /**
192      * Returns a {@link TypeInformation} for the return type of the given {@link Method}. Will potentially resolve
193      * generics information against the current types type parameter bindings.
194      *
195      * @param method must not be {@literal null}.
196      * @return
197      */

198     TypeInformation<?> getReturnType(Method method);
199
200     /**
201      * Returns the {@link TypeInformation}s for the parameters of the given {@link Method}.
202      *
203      * @param method must not be {@literal null}.
204      * @return
205      */

206     List<TypeInformation<?>> getParameterTypes(Method method);
207
208     /**
209      * Returns the {@link TypeInformation} for the given raw super type.
210      *
211      * @param superType must not be {@literal null}.
212      * @return the {@link TypeInformation} for the given raw super type or {@literal null} in case the current
213      *         {@link TypeInformation} does not implement the given type.
214      */

215     @Nullable
216     TypeInformation<?> getSuperTypeInformation(Class<?> superType);
217
218     /**
219      * Returns the {@link TypeInformation} for the given raw super type.
220      *
221      * @param superType must not be {@literal null}.
222      * @return the {@link TypeInformation} for the given raw super type.
223      * @throws IllegalArgumentException in case the current {@link TypeInformation} does not implement the given type.
224      * @since 2.0
225      */

226     default TypeInformation<?> getRequiredSuperTypeInformation(Class<?> superType) {
227
228         TypeInformation<?> result = getSuperTypeInformation(superType);
229
230         if (result == null) {
231             throw new IllegalArgumentException(String.format(
232                     "Can't retrieve super type information for %s! Does current type really implement the given one?",
233                     superType));
234         }
235
236         return result;
237     }
238
239     /**
240      * Returns if the current {@link TypeInformation} can be safely assigned to the given one. Mimics semantics of
241      * {@link Class#isAssignableFrom(Class)} but takes generics into account. Thus it will allow to detect that a
242      * {@code List<Long>} is assignable to {@code List<? extends Number>}.
243      *
244      * @param target
245      * @return
246      */

247     boolean isAssignableFrom(TypeInformation<?> target);
248
249     /**
250      * Returns the {@link TypeInformation} for the type arguments of the current {@link TypeInformation}.
251      *
252      * @return
253      */

254     List<TypeInformation<?>> getTypeArguments();
255
256     /**
257      * Specializes the given (raw) {@link ClassTypeInformation} using the context of the current potentially parameterized
258      * type, basically turning the given raw type into a parameterized one. Will return the given type as is if no
259      * generics are involved.
260      *
261      * @param type must not be {@literal null}.
262      * @return will never be {@literal null}.
263      */

264     TypeInformation<? extends S> specialize(ClassTypeInformation<?> type);
265
266     /**
267      * Returns whether the current type is a sub type of the given one, i.e. whether it's assignable but not the same one.
268      * 
269      * @param type must not be {@literal null}.
270      * @return
271      * @since 2.2
272      */

273     default boolean isSubTypeOf(Class<?> type) {
274         return !type.equals(getType()) && type.isAssignableFrom(getType());
275     }
276 }
277