1
16 package org.springframework.data.util;
17
18 import java.lang.reflect.ParameterizedType;
19 import java.lang.reflect.Type;
20 import java.lang.reflect.TypeVariable;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Optional;
29 import java.util.Set;
30 import java.util.stream.IntStream;
31
32 import org.springframework.lang.Nullable;
33 import org.springframework.util.StringUtils;
34
35
43 class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T> {
44
45 private final ParameterizedType type;
46 private final Lazy<Boolean> resolved;
47
48
54 public ParameterizedTypeInformation(ParameterizedType type, TypeDiscoverer<?> parent) {
55
56 super(type, parent, calculateTypeVariables(type, parent));
57
58 this.type = type;
59 this.resolved = Lazy.of(() -> isResolvedCompletely());
60 }
61
62
66 @Override
67 @Nullable
68 protected TypeInformation<?> doGetMapValueType() {
69
70 if (Map.class.isAssignableFrom(getType())) {
71
72 Type[] arguments = type.getActualTypeArguments();
73
74 if (arguments.length > 1) {
75 return createInfo(arguments[1]);
76 }
77 }
78
79 Class<?> rawType = getType();
80
81 Set<Type> supertypes = new HashSet<>();
82 Optional.ofNullable(rawType.getGenericSuperclass()).ifPresent(supertypes::add);
83 supertypes.addAll(Arrays.asList(rawType.getGenericInterfaces()));
84
85 Optional<TypeInformation<?>> result = supertypes.stream()
86 .map(it -> Pair.of(it, resolveType(it)))
87 .filter(it -> Map.class.isAssignableFrom(it.getSecond()))
88 .<TypeInformation<?>> map(it -> {
89
90 ParameterizedType parameterizedSupertype = (ParameterizedType) it.getFirst();
91 Type[] arguments = parameterizedSupertype.getActualTypeArguments();
92 return createInfo(arguments[1]);
93 }).findFirst();
94
95 return result.orElseGet(super::doGetMapValueType);
96 }
97
98
102 @Override
103 public List<TypeInformation<?>> getTypeArguments() {
104
105 List<TypeInformation<?>> result = new ArrayList<>();
106
107 for (Type argument : type.getActualTypeArguments()) {
108 result.add(createInfo(argument));
109 }
110
111 return result;
112 }
113
114
118 @Override
119 public boolean isAssignableFrom(TypeInformation<?> target) {
120
121 if (this.equals(target)) {
122 return true;
123 }
124
125 Class<T> rawType = getType();
126 Class<?> rawTargetType = target.getType();
127
128 if (!rawType.isAssignableFrom(rawTargetType)) {
129 return false;
130 }
131
132 TypeInformation<?> otherTypeInformation = rawType.equals(rawTargetType) ? target
133 : target.getSuperTypeInformation(rawType);
134
135 List<TypeInformation<?>> myParameters = getTypeArguments();
136 List<TypeInformation<?>> typeParameters = otherTypeInformation == null ? Collections.emptyList()
137 : otherTypeInformation.getTypeArguments();
138
139 if (myParameters.size() != typeParameters.size()) {
140 return false;
141 }
142
143 for (int i = 0; i < myParameters.size(); i++) {
144 if (!myParameters.get(i).isAssignableFrom(typeParameters.get(i))) {
145 return false;
146 }
147 }
148
149 return true;
150 }
151
152
156 @Override
157 @Nullable
158 protected TypeInformation<?> doGetComponentType() {
159 return createInfo(type.getActualTypeArguments()[0]);
160 }
161
162
166 @Override
167 @SuppressWarnings("unchecked")
168 public TypeInformation<? extends T> specialize(ClassTypeInformation<?> type) {
169
170 if (isResolvedCompletely()) {
171 return (TypeInformation<? extends T>) type;
172 }
173
174 TypeInformation<?> asSupertype = type.getSuperTypeInformation(getType());
175
176 if (asSupertype == null || !ParameterizedTypeInformation.class.isInstance(asSupertype)) {
177 return super.specialize(type);
178 }
179
180 return ((ParameterizedTypeInformation<?>) asSupertype).isResolvedCompletely()
181 ? (TypeInformation<? extends T>) type
182 : super.specialize(type);
183 }
184
185
189 @Override
190 public boolean equals(@Nullable Object obj) {
191
192 if (obj == this) {
193 return true;
194 }
195
196 if (!(obj instanceof ParameterizedTypeInformation)) {
197 return false;
198 }
199
200 ParameterizedTypeInformation<?> that = (ParameterizedTypeInformation<?>) obj;
201
202 if (this.isResolved() && that.isResolved()) {
203 return this.type.equals(that.type);
204 }
205
206 return super.equals(obj);
207 }
208
209
213 @Override
214 public int hashCode() {
215 return isResolved() ? this.type.hashCode() : super.hashCode();
216 }
217
218
222 @Override
223 public String toString() {
224
225 return String.format("%s<%s>", getType().getName(),
226 StringUtils.collectionToCommaDelimitedString(getTypeArguments()));
227 }
228
229 private boolean isResolved() {
230 return resolved.get();
231 }
232
233 private boolean isResolvedCompletely() {
234
235 Type[] typeArguments = type.getActualTypeArguments();
236
237 if (typeArguments.length == 0) {
238 return false;
239 }
240
241 for (Type typeArgument : typeArguments) {
242
243 TypeInformation<?> info = createInfo(typeArgument);
244
245 if (info instanceof ParameterizedTypeInformation) {
246 if (!((ParameterizedTypeInformation<?>) info).isResolvedCompletely()) {
247 return false;
248 }
249 }
250
251 if (!(info instanceof ClassTypeInformation)) {
252 return false;
253 }
254 }
255
256 return true;
257 }
258
259
267 private static Map<TypeVariable<?>, Type> calculateTypeVariables(ParameterizedType type, TypeDiscoverer<?> parent) {
268
269 Class<?> resolvedType = parent.resolveType(type);
270 TypeVariable<?>[] typeParameters = resolvedType.getTypeParameters();
271 Type[] arguments = type.getActualTypeArguments();
272
273 Map<TypeVariable<?>, Type> localTypeVariables = new HashMap<>(parent.getTypeVariableMap());
274
275 IntStream.range(0, typeParameters.length)
276 .mapToObj(it -> Pair.of(typeParameters[it], flattenTypeVariable(arguments[it], localTypeVariables)))
277 .forEach(it -> localTypeVariables.put(it.getFirst(), it.getSecond()));
278
279 return localTypeVariables;
280 }
281
282
290 private static Type flattenTypeVariable(Type source, Map<TypeVariable<?>, Type> variables) {
291
292 if (!(source instanceof TypeVariable)) {
293 return source;
294 }
295
296 Type value = variables.get(source);
297
298 return value == null ? source : flattenTypeVariable(value, variables);
299 }
300 }
301