1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.di.impl;
20
21 import java.lang.reflect.*;
22 import java.util.*;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.function.Function;
25
26 import org.apache.maven.api.annotations.Nullable;
27
28 import static java.util.stream.Collectors.joining;
29
30
31
32
33 public class Types {
34 public static final Type[] NO_TYPES = new Type[0];
35 public static final WildcardType WILDCARD_TYPE_ANY = new WildcardTypeImpl(new Type[] {Object.class}, new Type[0]);
36 private static final Map<Type, Map<TypeVariable<?>, Type>> TYPE_BINDINGS_CACHE = new ConcurrentHashMap<>();
37
38
39
40
41
42
43
44 public static Class<?> getRawType(Type type) {
45 if (type instanceof Class) {
46 return (Class<?>) type;
47 } else if (type instanceof ParameterizedType) {
48 return (Class<?>) ((ParameterizedType) type).getRawType();
49 } else if (type instanceof WildcardType) {
50 Type[] upperBounds = ((WildcardType) type).getUpperBounds();
51 return getRawType(getUppermostType(upperBounds));
52 } else if (type instanceof GenericArrayType) {
53 Class<?> rawComponentType = getRawType(((GenericArrayType) type).getGenericComponentType());
54 try {
55 return Class.forName("[L" + rawComponentType.getName() + ";");
56 } catch (ClassNotFoundException e) {
57 throw new RuntimeException(e);
58 }
59 } else if (type instanceof TypeVariable) {
60 return getRawType(getUppermostType(((TypeVariable<?>) type).getBounds()));
61 } else {
62 throw new IllegalArgumentException("Unsupported type: " + type);
63 }
64 }
65
66
67
68
69 public static Type getUppermostType(Type[] types) {
70 Type result = types[0];
71 for (int i = 1; i < types.length; i++) {
72 Type type = types[i];
73 if (isAssignable(type, result)) {
74 result = type;
75 continue;
76 } else if (isAssignable(result, type)) {
77 continue;
78 }
79 throw new IllegalArgumentException("Unrelated types: " + result + " , " + type);
80 }
81 return result;
82 }
83
84
85
86
87
88
89
90 public static Type[] getActualTypeArguments(Type type) {
91 if (type instanceof Class) {
92 return ((Class<?>) type).isArray() ? new Type[] {((Class<?>) type).getComponentType()} : NO_TYPES;
93 } else if (type instanceof ParameterizedType) {
94 return ((ParameterizedType) type).getActualTypeArguments();
95 } else if (type instanceof GenericArrayType) {
96 return new Type[] {((GenericArrayType) type).getGenericComponentType()};
97 }
98 throw new IllegalArgumentException("Unsupported type: " + type);
99 }
100
101
102
103
104 public static Map<TypeVariable<?>, Type> getTypeBindings(Type type) {
105 Type[] typeArguments = getActualTypeArguments(type);
106 if (typeArguments.length == 0) {
107 return Collections.emptyMap();
108 }
109 TypeVariable<?>[] typeVariables = getRawType(type).getTypeParameters();
110 Map<TypeVariable<?>, Type> map = new HashMap<>();
111 for (int i = 0; i < typeVariables.length; i++) {
112 map.put(typeVariables[i], typeArguments[i]);
113 }
114 return map;
115 }
116
117
118
119
120
121 public static Map<TypeVariable<?>, Type> getAllTypeBindings(Type type) {
122 return TYPE_BINDINGS_CACHE.computeIfAbsent(type, t -> {
123 Map<TypeVariable<?>, Type> mapping = new HashMap<>();
124 getAllTypeBindingsImpl(t, mapping);
125 return mapping;
126 });
127 }
128
129 private static void getAllTypeBindingsImpl(Type type, Map<TypeVariable<?>, Type> mapping) {
130 Class<?> cls = getRawType(type);
131
132 if (type instanceof ParameterizedType) {
133 Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
134 if (typeArguments.length != 0) {
135 TypeVariable<? extends Class<?>>[] typeVariables = cls.getTypeParameters();
136 for (int i = 0; i < typeArguments.length; i++) {
137 Type typeArgument = typeArguments[i];
138 mapping.put(
139 typeVariables[i],
140 typeArgument instanceof TypeVariable
141 ? Objects.requireNonNull(mapping.get(typeArgument))
142 : typeArgument);
143 }
144 }
145 }
146
147 Type superclass = cls.getGenericSuperclass();
148 if (superclass != null) {
149 getAllTypeBindingsImpl(superclass, mapping);
150 }
151
152 for (Type anInterface : cls.getGenericInterfaces()) {
153 getAllTypeBindingsImpl(anInterface, mapping);
154 }
155 }
156
157
158
159
160
161
162
163 public static Type bind(Type type, Map<TypeVariable<?>, Type> bindings) {
164 return bind(type, bindings::get);
165 }
166
167
168
169
170
171
172
173 public static Type bind(Type type, Function<TypeVariable<?>, Type> bindings) {
174 if (type instanceof Class) {
175 return type;
176 }
177 if (type instanceof TypeVariable<?>) {
178 TypeVariable<?> typeVariable = (TypeVariable<?>) type;
179 Type actualType = bindings.apply(typeVariable);
180 if (actualType == null) {
181 throw new TypeNotBoundException("Type variable not found: " + typeVariable + " ( "
182 + typeVariable.getGenericDeclaration() + " ) ");
183 }
184 return actualType;
185 }
186 if (type instanceof ParameterizedType) {
187 ParameterizedType parameterizedType = (ParameterizedType) type;
188 Type[] typeArguments = parameterizedType.getActualTypeArguments();
189 Type[] typeArguments2 = new Type[typeArguments.length];
190 for (int i = 0; i < typeArguments.length; i++) {
191 typeArguments2[i] = bind(typeArguments[i], bindings);
192 }
193 return new ParameterizedTypeImpl(
194 parameterizedType.getOwnerType(), parameterizedType.getRawType(), typeArguments2);
195 }
196 if (type instanceof GenericArrayType) {
197 Type componentType = ((GenericArrayType) type).getGenericComponentType();
198 return new GenericArrayTypeImpl(bind(componentType, bindings));
199 }
200 if (type instanceof WildcardType) {
201 WildcardType wildcardType = (WildcardType) type;
202 Type[] upperBounds = wildcardType.getUpperBounds();
203 Type[] upperBounds2 = new Type[upperBounds.length];
204 for (int i = 0; i < upperBounds.length; i++) {
205 upperBounds2[i] = bind(upperBounds[i], bindings);
206 }
207 Type[] lowerBounds = wildcardType.getLowerBounds();
208 Type[] lowerBounds2 = new Type[lowerBounds.length];
209 for (int i = 0; i < lowerBounds.length; i++) {
210 lowerBounds2[i] = bind(lowerBounds[i], bindings);
211 }
212 return new WildcardTypeImpl(upperBounds2, lowerBounds2);
213 }
214 throw new IllegalArgumentException("Unsupported type: " + type);
215 }
216
217
218
219
220
221
222
223
224
225 public static ParameterizedType parameterizedType(@Nullable Type ownerType, Type rawType, Type[] parameters) {
226 return new ParameterizedTypeImpl(ownerType, rawType, parameters);
227 }
228
229
230
231
232
233
234 public static ParameterizedType parameterizedType(Class<?> rawType, Type... parameters) {
235 return new ParameterizedTypeImpl(null, rawType, parameters);
236 }
237
238
239
240
241 public static Set<Type> getAllSuperTypes(Type original) {
242 Deque<Type> todo = new ArrayDeque<>();
243 todo.add(original);
244 Set<Type> done = new HashSet<>();
245 while (!todo.isEmpty()) {
246 Type type = todo.remove();
247 if (done.add(type)) {
248 Class<?> cls = getRawType(type);
249 Function<TypeVariable<?>, Type> bindings;
250 if (type instanceof ParameterizedType) {
251 Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
252 TypeVariable<? extends Class<?>>[] typeVariables = cls.getTypeParameters();
253 bindings = v -> {
254 for (int i = 0; i < typeArguments.length; i++) {
255 Type typeArgument = typeArguments[i];
256 if (v.equals(typeVariables[i])) {
257 return typeArgument;
258 }
259 }
260 return null;
261 };
262 } else {
263 bindings = v -> null;
264 }
265 Type[] interfaces = cls.getGenericInterfaces();
266 for (Type itf : interfaces) {
267 try {
268 todo.add(bind(itf, bindings));
269 } catch (TypeNotBoundException e) {
270
271 }
272 }
273 Type supercls = cls.getGenericSuperclass();
274 if (supercls != null) {
275 try {
276 todo.add(bind(supercls, bindings));
277 } catch (TypeNotBoundException e) {
278
279 }
280 }
281 }
282 }
283 return done;
284 }
285
286 public static Type simplifyType(Type original) {
287 if (original instanceof Class) {
288 return original;
289 }
290
291 if (original instanceof GenericArrayType) {
292 Type componentType = ((GenericArrayType) original).getGenericComponentType();
293 Type repackedComponentType = simplifyType(componentType);
294 if (componentType != repackedComponentType) {
295 return genericArrayType(repackedComponentType);
296 }
297 return original;
298 }
299
300 if (original instanceof ParameterizedType) {
301 ParameterizedType parameterizedType = (ParameterizedType) original;
302 Type[] typeArguments = parameterizedType.getActualTypeArguments();
303 Type[] repackedTypeArguments = simplifyTypes(typeArguments);
304
305 if (isAllObjects(repackedTypeArguments)) {
306 return parameterizedType.getRawType();
307 }
308
309 if (typeArguments != repackedTypeArguments) {
310 return parameterizedType(
311 parameterizedType.getOwnerType(), parameterizedType.getRawType(), repackedTypeArguments);
312 }
313 return original;
314 }
315
316 if (original instanceof TypeVariable) {
317 throw new IllegalArgumentException("Key should not contain a type variable: " + original);
318 }
319
320 if (original instanceof WildcardType) {
321 WildcardType wildcardType = (WildcardType) original;
322 Type[] upperBounds = wildcardType.getUpperBounds();
323 if (upperBounds.length == 1) {
324 Type upperBound = upperBounds[0];
325 if (upperBound != Object.class) {
326 return simplifyType(upperBound);
327 }
328 } else if (upperBounds.length > 1) {
329 throw new IllegalArgumentException("Multiple upper bounds not supported: " + original);
330 }
331
332 Type[] lowerBounds = wildcardType.getLowerBounds();
333 if (lowerBounds.length == 1) {
334 return simplifyType(lowerBounds[0]);
335 } else if (lowerBounds.length > 1) {
336 throw new IllegalArgumentException("Multiple lower bounds not supported: " + original);
337 }
338 return Object.class;
339 }
340
341 return original;
342 }
343
344 private static Type[] simplifyTypes(Type[] original) {
345 int length = original.length;
346 for (int i = 0; i < length; i++) {
347 Type typeArgument = original[i];
348 Type repackTypeArgument = simplifyType(typeArgument);
349 if (repackTypeArgument != typeArgument) {
350 Type[] repackedTypeArguments = new Type[length];
351 System.arraycopy(original, 0, repackedTypeArguments, 0, i);
352 repackedTypeArguments[i++] = repackTypeArgument;
353 for (; i < length; i++) {
354 repackedTypeArguments[i] = simplifyType(original[i]);
355 }
356 return repackedTypeArguments;
357 }
358 }
359 return original;
360 }
361
362 private static boolean isAllObjects(Type[] types) {
363 for (Type type : types) {
364 if (type != Object.class) {
365 return false;
366 }
367 }
368 return true;
369 }
370
371
372
373
374
375
376
377
378 public static boolean isAssignable(Type to, Type from) {
379
380 if (to instanceof Class && from instanceof Class) {
381 return ((Class<?>) to).isAssignableFrom((Class<?>) from);
382 }
383 return isAssignable(to, from, false);
384 }
385
386 private static boolean isAssignable(Type to, Type from, boolean strict) {
387 if (to instanceof WildcardType || from instanceof WildcardType) {
388 Type[] toUppers, toLowers;
389 if (to instanceof WildcardType) {
390 WildcardType wildcardTo = (WildcardType) to;
391 toUppers = wildcardTo.getUpperBounds();
392 toLowers = wildcardTo.getLowerBounds();
393 } else {
394 toUppers = new Type[] {to};
395 toLowers = strict ? toUppers : NO_TYPES;
396 }
397
398 Type[] fromUppers, fromLowers;
399 if (from instanceof WildcardType) {
400 WildcardType wildcardTo = (WildcardType) to;
401 fromUppers = wildcardTo.getUpperBounds();
402 fromLowers = wildcardTo.getLowerBounds();
403 } else {
404 fromUppers = new Type[] {from};
405 fromLowers = strict ? fromUppers : NO_TYPES;
406 }
407
408 for (Type toUpper : toUppers) {
409 for (Type fromUpper : fromUppers) {
410 if (!isAssignable(toUpper, fromUpper, false)) {
411 return false;
412 }
413 }
414 }
415 if (toLowers.length == 0) {
416 return true;
417 }
418 if (fromLowers.length == 0) {
419 return false;
420 }
421 for (Type toLower : toLowers) {
422 for (Type fromLower : fromLowers) {
423 if (!isAssignable(fromLower, toLower, false)) {
424 return false;
425 }
426 }
427 }
428 return true;
429 }
430 if (to instanceof GenericArrayType) {
431 to = getRawType(to);
432 }
433 if (from instanceof GenericArrayType) {
434 from = getRawType(from);
435 }
436 if (!strict && to instanceof Class) {
437 return ((Class<?>) to).isAssignableFrom(getRawType(from));
438 }
439 Class<?> toRawClazz = getRawType(to);
440 Type[] toTypeArguments = getActualTypeArguments(to);
441 return isAssignable(toRawClazz, toTypeArguments, from, strict);
442 }
443
444 private static boolean isAssignable(Class<?> toRawClazz, Type[] toTypeArguments, Type from, boolean strict) {
445 Class<?> fromRawClazz = getRawType(from);
446 if (strict && !toRawClazz.equals(fromRawClazz)) {
447 return false;
448 }
449 if (!strict && !toRawClazz.isAssignableFrom(fromRawClazz)) {
450 return false;
451 }
452 if (toRawClazz.isArray()) {
453 return true;
454 }
455 Type[] fromTypeArguments = getActualTypeArguments(from);
456 if (toRawClazz == fromRawClazz) {
457 if (toTypeArguments.length > fromTypeArguments.length) {
458 return false;
459 }
460 for (int i = 0; i < toTypeArguments.length; i++) {
461 if (!isAssignable(toTypeArguments[i], fromTypeArguments[i], true)) {
462 return false;
463 }
464 }
465 return true;
466 }
467 Map<TypeVariable<?>, Type> typeBindings = getTypeBindings(from);
468 for (Type anInterface : fromRawClazz.getGenericInterfaces()) {
469 if (isAssignable(
470 toRawClazz,
471 toTypeArguments,
472 bind(anInterface, key -> typeBindings.getOrDefault(key, wildcardTypeAny())),
473 false)) {
474 return true;
475 }
476 }
477 Type superclass = fromRawClazz.getGenericSuperclass();
478 return superclass != null && isAssignable(toRawClazz, toTypeArguments, bind(superclass, typeBindings), false);
479 }
480
481 public static final class ParameterizedTypeImpl implements ParameterizedType {
482 private final @Nullable Type ownerType;
483 private final Type rawType;
484 private final Type[] actualTypeArguments;
485
486 ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type[] actualTypeArguments) {
487 this.ownerType = ownerType;
488 this.rawType = rawType;
489 this.actualTypeArguments = actualTypeArguments;
490 }
491
492 @Override
493 public Type getRawType() {
494 return rawType;
495 }
496
497 @Override
498 public Type[] getActualTypeArguments() {
499 return actualTypeArguments;
500 }
501
502 @Override
503 public @Nullable Type getOwnerType() {
504 return ownerType;
505 }
506
507 @Override
508 public int hashCode() {
509 return Objects.hashCode(ownerType) ^ Arrays.hashCode(actualTypeArguments) ^ rawType.hashCode();
510 }
511
512 @Override
513 public boolean equals(Object other) {
514 if (!(other instanceof ParameterizedType)) {
515 return false;
516 }
517 ParameterizedType that = (ParameterizedType) other;
518 return this.getRawType().equals(that.getRawType())
519 && Objects.equals(this.getOwnerType(), that.getOwnerType())
520 && Arrays.equals(this.getActualTypeArguments(), that.getActualTypeArguments());
521 }
522
523 @Override
524 public String toString() {
525 return rawType.getTypeName()
526 + Arrays.stream(actualTypeArguments).map(Types::toString).collect(joining(", ", "<", ">"));
527 }
528 }
529
530
531
532
533
534
535
536
537 public static WildcardType wildcardType(Type[] upperBounds, Type[] lowerBounds) {
538 return new WildcardTypeImpl(upperBounds, lowerBounds);
539 }
540
541
542
543
544
545
546
547
548 public static WildcardType wildcardTypeAny() {
549 return WILDCARD_TYPE_ANY;
550 }
551
552
553
554
555
556
557
558
559
560
561 public static WildcardType wildcardTypeExtends(Type upperBound) {
562 return new WildcardTypeImpl(new Type[] {upperBound}, NO_TYPES);
563 }
564
565
566
567
568
569
570
571
572
573
574 public static WildcardType wildcardTypeSuper(Type lowerBound) {
575 return new WildcardTypeImpl(NO_TYPES, new Type[] {lowerBound});
576 }
577
578 public static class WildcardTypeImpl implements WildcardType {
579 private final Type[] upperBounds;
580 private final Type[] lowerBounds;
581
582 WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
583 this.upperBounds = upperBounds;
584 this.lowerBounds = lowerBounds;
585 }
586
587 @Override
588 public Type[] getUpperBounds() {
589 return upperBounds;
590 }
591
592 @Override
593 public Type[] getLowerBounds() {
594 return lowerBounds;
595 }
596
597 @Override
598 public int hashCode() {
599 return Arrays.hashCode(upperBounds) ^ Arrays.hashCode(lowerBounds);
600 }
601
602 @Override
603 public boolean equals(Object other) {
604 if (!(other instanceof WildcardType)) {
605 return false;
606 }
607 WildcardType that = (WildcardType) other;
608 return Arrays.equals(this.getUpperBounds(), that.getUpperBounds())
609 && Arrays.equals(this.getLowerBounds(), that.getLowerBounds());
610 }
611
612 @Override
613 public String toString() {
614 return "?"
615 + (upperBounds.length == 0
616 ? ""
617 : " extends "
618 + Arrays.stream(upperBounds)
619 .map(Types::toString)
620 .collect(joining(" & ")))
621 + (lowerBounds.length == 0
622 ? ""
623 : " super "
624 + Arrays.stream(lowerBounds)
625 .map(Types::toString)
626 .collect(joining(" & ")));
627 }
628 }
629
630
631
632
633
634
635
636
637
638
639 public static GenericArrayType genericArrayType(Type componentType) {
640 return new GenericArrayTypeImpl(componentType);
641 }
642
643 public static final class GenericArrayTypeImpl implements GenericArrayType {
644 private final Type componentType;
645
646 GenericArrayTypeImpl(Type componentType) {
647 this.componentType = componentType;
648 }
649
650 @Override
651 public Type getGenericComponentType() {
652 return componentType;
653 }
654
655 @Override
656 public int hashCode() {
657 return componentType.hashCode();
658 }
659
660 @Override
661 public boolean equals(Object other) {
662 if (!(other instanceof GenericArrayType)) {
663 return false;
664 }
665 GenericArrayType that = (GenericArrayType) other;
666 return this.getGenericComponentType().equals(that.getGenericComponentType());
667 }
668
669 @Override
670 public String toString() {
671 return Types.toString(componentType) + "[]";
672 }
673 }
674
675 private static String toString(Type type) {
676 return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
677 }
678
679
680
681
682
683
684 public static String getSimpleName(Type type) {
685 if (type instanceof Class) {
686 return ((Class<?>) type).getSimpleName();
687 } else if (type instanceof ParameterizedType) {
688 return Arrays.stream(((ParameterizedType) type).getActualTypeArguments())
689 .map(Types::getSimpleName)
690 .collect(joining(",", "<", ">"));
691 } else if (type instanceof WildcardType) {
692 WildcardType wildcardType = (WildcardType) type;
693 Type[] upperBounds = wildcardType.getUpperBounds();
694 Type[] lowerBounds = wildcardType.getLowerBounds();
695 return "?"
696 + (upperBounds.length == 0
697 ? ""
698 : " extends "
699 + Arrays.stream(upperBounds)
700 .map(Types::getSimpleName)
701 .collect(joining(" & ")))
702 + (lowerBounds.length == 0
703 ? ""
704 : " super "
705 + Arrays.stream(lowerBounds)
706 .map(Types::getSimpleName)
707 .collect(joining(" & ")));
708 } else if (type instanceof GenericArrayType) {
709 return Types.getSimpleName(((GenericArrayType) type).getGenericComponentType()) + "[]";
710 }
711
712 return type.getTypeName();
713 }
714
715 public static class TypeNotBoundException extends IllegalArgumentException {
716 public TypeNotBoundException(String s) {
717 super(s);
718 }
719 }
720 }