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