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