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<?> clazz) {
58 return clazz;
59 } else if (type instanceof ParameterizedType parameterizedType) {
60 return (Class<?>) parameterizedType.getRawType();
61 } else if (type instanceof WildcardType wildcardType) {
62 Type[] upperBounds = wildcardType.getUpperBounds();
63 return getRawType(getUppermostType(upperBounds));
64 } else if (type instanceof GenericArrayType genericArrayType) {
65 Class<?> rawComponentType = getRawType(genericArrayType.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<?> typeVariable) {
72 return getRawType(getUppermostType(typeVariable.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<?> clazz) {
104 return clazz.isArray() ? new Type[] {clazz.getComponentType()} : NO_TYPES;
105 } else if (type instanceof ParameterizedType parameterizedType) {
106 return parameterizedType.getActualTypeArguments();
107 } else if (type instanceof GenericArrayType genericArrayType) {
108 return new Type[] {genericArrayType.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 parameterizedType) {
145 Type[] typeArguments = parameterizedType.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<?> typeVariable) {
190 Type actualType = bindings.apply(typeVariable);
191 if (actualType == null) {
192 throw new TypeNotBoundException("Type variable not found: " + typeVariable + " ( "
193 + typeVariable.getGenericDeclaration() + " ) ");
194 }
195 return actualType;
196 }
197 if (type instanceof ParameterizedType parameterizedType) {
198 Type[] typeArguments = parameterizedType.getActualTypeArguments();
199 Type[] typeArguments2 = new Type[typeArguments.length];
200 for (int i = 0; i < typeArguments.length; i++) {
201 typeArguments2[i] = bind(typeArguments[i], bindings);
202 }
203 return new ParameterizedTypeImpl(
204 parameterizedType.getOwnerType(), parameterizedType.getRawType(), typeArguments2);
205 }
206 if (type instanceof GenericArrayType genericArrayType) {
207 Type componentType = genericArrayType.getGenericComponentType();
208 return new GenericArrayTypeImpl(bind(componentType, bindings));
209 }
210 if (type instanceof WildcardType wildcardType) {
211 Type[] upperBounds = wildcardType.getUpperBounds();
212 Type[] upperBounds2 = new Type[upperBounds.length];
213 for (int i = 0; i < upperBounds.length; i++) {
214 upperBounds2[i] = bind(upperBounds[i], bindings);
215 }
216 Type[] lowerBounds = wildcardType.getLowerBounds();
217 Type[] lowerBounds2 = new Type[lowerBounds.length];
218 for (int i = 0; i < lowerBounds.length; i++) {
219 lowerBounds2[i] = bind(lowerBounds[i], bindings);
220 }
221 return new WildcardTypeImpl(upperBounds2, lowerBounds2);
222 }
223 throw new IllegalArgumentException("Unsupported type: " + type);
224 }
225
226
227
228
229
230
231
232
233
234 public static ParameterizedType parameterizedType(@Nullable Type ownerType, Type rawType, Type[] parameters) {
235 return new ParameterizedTypeImpl(ownerType, rawType, parameters);
236 }
237
238
239
240
241
242
243 public static ParameterizedType parameterizedType(Class<?> rawType, Type... parameters) {
244 return new ParameterizedTypeImpl(null, rawType, parameters);
245 }
246
247
248
249
250 public static Set<Type> getAllSuperTypes(Type original) {
251 Deque<Type> todo = new ArrayDeque<>();
252 todo.add(original);
253 Set<Type> done = new HashSet<>();
254 while (!todo.isEmpty()) {
255 Type type = todo.remove();
256 if (done.add(type)) {
257 Class<?> cls = getRawType(type);
258 Function<TypeVariable<?>, Type> bindings;
259 if (type instanceof ParameterizedType parameterizedType) {
260 Type[] typeArguments = parameterizedType.getActualTypeArguments();
261 TypeVariable<? extends Class<?>>[] typeVariables = cls.getTypeParameters();
262 bindings = v -> {
263 for (int i = 0; i < typeArguments.length; i++) {
264 Type typeArgument = typeArguments[i];
265 if (v.equals(typeVariables[i])) {
266 return typeArgument;
267 }
268 }
269 return null;
270 };
271 } else {
272 bindings = v -> null;
273 }
274 Type[] interfaces = cls.getGenericInterfaces();
275 for (Type itf : interfaces) {
276 try {
277 todo.add(bind(itf, bindings));
278 } catch (TypeNotBoundException e) {
279
280 }
281 }
282 Type supercls = cls.getGenericSuperclass();
283 if (supercls != null) {
284 try {
285 todo.add(bind(supercls, bindings));
286 } catch (TypeNotBoundException e) {
287
288 }
289 }
290 }
291 }
292 return done;
293 }
294
295 public static Type simplifyType(Type original) {
296 if (original instanceof Class) {
297 return original;
298 }
299
300 if (original instanceof GenericArrayType genericArrayType) {
301 Type componentType = genericArrayType.getGenericComponentType();
302 Type repackedComponentType = simplifyType(componentType);
303 if (componentType != repackedComponentType) {
304 return genericArrayType(repackedComponentType);
305 }
306 return original;
307 }
308
309 if (original instanceof ParameterizedType parameterizedType) {
310 Type[] typeArguments = parameterizedType.getActualTypeArguments();
311 Type[] repackedTypeArguments = simplifyTypes(typeArguments);
312
313 if (isAllObjects(repackedTypeArguments)) {
314 return parameterizedType.getRawType();
315 }
316
317 if (typeArguments != repackedTypeArguments) {
318 return parameterizedType(
319 parameterizedType.getOwnerType(), parameterizedType.getRawType(), repackedTypeArguments);
320 }
321 return original;
322 }
323
324 if (original instanceof TypeVariable) {
325 throw new IllegalArgumentException("Key should not contain a type variable: " + original);
326 }
327
328 if (original instanceof WildcardType wildcardType) {
329 Type[] upperBounds = wildcardType.getUpperBounds();
330 if (upperBounds.length == 1) {
331 Type upperBound = upperBounds[0];
332 if (upperBound != Object.class) {
333 return simplifyType(upperBound);
334 }
335 } else if (upperBounds.length > 1) {
336 throw new IllegalArgumentException("Multiple upper bounds not supported: " + original);
337 }
338
339 Type[] lowerBounds = wildcardType.getLowerBounds();
340 if (lowerBounds.length == 1) {
341 return simplifyType(lowerBounds[0]);
342 } else if (lowerBounds.length > 1) {
343 throw new IllegalArgumentException("Multiple lower bounds not supported: " + original);
344 }
345 return Object.class;
346 }
347
348 return original;
349 }
350
351 private static Type[] simplifyTypes(Type[] original) {
352 int length = original.length;
353 for (int i = 0; i < length; i++) {
354 Type typeArgument = original[i];
355 Type repackTypeArgument = simplifyType(typeArgument);
356 if (repackTypeArgument != typeArgument) {
357 Type[] repackedTypeArguments = new Type[length];
358 System.arraycopy(original, 0, repackedTypeArguments, 0, i);
359 repackedTypeArguments[i++] = repackTypeArgument;
360 for (; i < length; i++) {
361 repackedTypeArguments[i] = simplifyType(original[i]);
362 }
363 return repackedTypeArguments;
364 }
365 }
366 return original;
367 }
368
369 private static boolean isAllObjects(Type[] types) {
370 for (Type type : types) {
371 if (type != Object.class) {
372 return false;
373 }
374 }
375 return true;
376 }
377
378
379
380
381
382
383
384
385 public static boolean isAssignable(Type to, Type from) {
386
387 if (to instanceof Class<?> toClazz && from instanceof Class<?> fromClazz) {
388 return toClazz.isAssignableFrom(fromClazz);
389 }
390 return isAssignable(to, from, false);
391 }
392
393 private static boolean isAssignable(Type to, Type from, boolean strict) {
394 if (to instanceof WildcardType || from instanceof WildcardType) {
395 Type[] toUppers, toLowers;
396 if (to instanceof WildcardType wildcardTo) {
397 toUppers = wildcardTo.getUpperBounds();
398 toLowers = wildcardTo.getLowerBounds();
399 } else {
400 toUppers = new Type[] {to};
401 toLowers = strict ? toUppers : NO_TYPES;
402 }
403
404 Type[] fromUppers, fromLowers;
405 if (from instanceof WildcardType wildcardFrom) {
406 fromUppers = wildcardFrom.getUpperBounds();
407 fromLowers = wildcardFrom.getLowerBounds();
408 } else {
409 fromUppers = new Type[] {from};
410 fromLowers = strict ? fromUppers : NO_TYPES;
411 }
412
413 for (Type toUpper : toUppers) {
414 for (Type fromUpper : fromUppers) {
415 if (!isAssignable(toUpper, fromUpper, false)) {
416 return false;
417 }
418 }
419 }
420 if (toLowers.length == 0) {
421 return true;
422 }
423 if (fromLowers.length == 0) {
424 return false;
425 }
426 for (Type toLower : toLowers) {
427 for (Type fromLower : fromLowers) {
428 if (!isAssignable(fromLower, toLower, false)) {
429 return false;
430 }
431 }
432 }
433 return true;
434 }
435 if (to instanceof GenericArrayType) {
436 to = getRawType(to);
437 }
438 if (from instanceof GenericArrayType) {
439 from = getRawType(from);
440 }
441 if (!strict && to instanceof Class<?> toClazz) {
442 return toClazz.isAssignableFrom(getRawType(from));
443 }
444 Class<?> toRawClazz = getRawType(to);
445 Type[] toTypeArguments = getActualTypeArguments(to);
446 return isAssignable(toRawClazz, toTypeArguments, from, strict);
447 }
448
449 private static boolean isAssignable(Class<?> toRawClazz, Type[] toTypeArguments, Type from, boolean strict) {
450 Class<?> fromRawClazz = getRawType(from);
451 if (strict && !toRawClazz.equals(fromRawClazz)) {
452 return false;
453 }
454 if (!strict && !toRawClazz.isAssignableFrom(fromRawClazz)) {
455 return false;
456 }
457 if (toRawClazz.isArray()) {
458 return true;
459 }
460 Type[] fromTypeArguments = getActualTypeArguments(from);
461 if (toRawClazz == fromRawClazz) {
462 if (toTypeArguments.length > fromTypeArguments.length) {
463 return false;
464 }
465 for (int i = 0; i < toTypeArguments.length; i++) {
466 if (!isAssignable(toTypeArguments[i], fromTypeArguments[i], true)) {
467 return false;
468 }
469 }
470 return true;
471 }
472 Map<TypeVariable<?>, Type> typeBindings = getTypeBindings(from);
473 for (Type anInterface : fromRawClazz.getGenericInterfaces()) {
474 if (isAssignable(
475 toRawClazz,
476 toTypeArguments,
477 bind(anInterface, key -> typeBindings.getOrDefault(key, wildcardTypeAny())),
478 false)) {
479 return true;
480 }
481 }
482 Type superclass = fromRawClazz.getGenericSuperclass();
483 return superclass != null && isAssignable(toRawClazz, toTypeArguments, bind(superclass, typeBindings), false);
484 }
485
486 public static final class ParameterizedTypeImpl implements ParameterizedType {
487 private final @Nullable Type ownerType;
488 private final Type rawType;
489 private final Type[] actualTypeArguments;
490
491 ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type[] actualTypeArguments) {
492 this.ownerType = ownerType;
493 this.rawType = rawType;
494 this.actualTypeArguments = actualTypeArguments;
495 }
496
497 @Override
498 public Type getRawType() {
499 return rawType;
500 }
501
502 @Override
503 public Type[] getActualTypeArguments() {
504 return actualTypeArguments;
505 }
506
507 @Override
508 public @Nullable Type getOwnerType() {
509 return ownerType;
510 }
511
512 @Override
513 public int hashCode() {
514 return Objects.hashCode(ownerType) ^ Arrays.hashCode(actualTypeArguments) ^ rawType.hashCode();
515 }
516
517 @Override
518 public boolean equals(Object other) {
519 if (!(other instanceof ParameterizedType that)) {
520 return false;
521 }
522 return this.getRawType().equals(that.getRawType())
523 && Objects.equals(this.getOwnerType(), that.getOwnerType())
524 && Arrays.equals(this.getActualTypeArguments(), that.getActualTypeArguments());
525 }
526
527 @Override
528 public String toString() {
529 return rawType.getTypeName()
530 + Arrays.stream(actualTypeArguments).map(Types::toString).collect(joining(", ", "<", ">"));
531 }
532 }
533
534
535
536
537
538
539
540
541 public static WildcardType wildcardType(Type[] upperBounds, Type[] lowerBounds) {
542 return new WildcardTypeImpl(upperBounds, lowerBounds);
543 }
544
545
546
547
548
549
550
551
552 public static WildcardType wildcardTypeAny() {
553 return WILDCARD_TYPE_ANY;
554 }
555
556
557
558
559
560
561
562
563
564
565 public static WildcardType wildcardTypeExtends(Type upperBound) {
566 return new WildcardTypeImpl(new Type[] {upperBound}, NO_TYPES);
567 }
568
569
570
571
572
573
574
575
576
577
578 public static WildcardType wildcardTypeSuper(Type lowerBound) {
579 return new WildcardTypeImpl(NO_TYPES, new Type[] {lowerBound});
580 }
581
582 public static class WildcardTypeImpl implements WildcardType {
583 private final Type[] upperBounds;
584 private final Type[] lowerBounds;
585
586 WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
587 this.upperBounds = upperBounds;
588 this.lowerBounds = lowerBounds;
589 }
590
591 @Override
592 public Type[] getUpperBounds() {
593 return upperBounds;
594 }
595
596 @Override
597 public Type[] getLowerBounds() {
598 return lowerBounds;
599 }
600
601 @Override
602 public int hashCode() {
603 return Arrays.hashCode(upperBounds) ^ Arrays.hashCode(lowerBounds);
604 }
605
606 @Override
607 public boolean equals(Object other) {
608 if (!(other instanceof WildcardType that)) {
609 return false;
610 }
611 return Arrays.equals(this.getUpperBounds(), that.getUpperBounds())
612 && Arrays.equals(this.getLowerBounds(), that.getLowerBounds());
613 }
614
615 @Override
616 public String toString() {
617 return "?"
618 + (upperBounds.length == 0
619 ? ""
620 : " extends "
621 + Arrays.stream(upperBounds)
622 .map(Types::toString)
623 .collect(joining(" & ")))
624 + (lowerBounds.length == 0
625 ? ""
626 : " super "
627 + Arrays.stream(lowerBounds)
628 .map(Types::toString)
629 .collect(joining(" & ")));
630 }
631 }
632
633
634
635
636
637
638
639
640
641
642 public static GenericArrayType genericArrayType(Type componentType) {
643 return new GenericArrayTypeImpl(componentType);
644 }
645
646 public static final class GenericArrayTypeImpl implements GenericArrayType {
647 private final Type componentType;
648
649 GenericArrayTypeImpl(Type componentType) {
650 this.componentType = componentType;
651 }
652
653 @Override
654 public Type getGenericComponentType() {
655 return componentType;
656 }
657
658 @Override
659 public int hashCode() {
660 return componentType.hashCode();
661 }
662
663 @Override
664 public boolean equals(Object other) {
665 if (!(other instanceof GenericArrayType that)) {
666 return false;
667 }
668 return this.getGenericComponentType().equals(that.getGenericComponentType());
669 }
670
671 @Override
672 public String toString() {
673 return Types.toString(componentType) + "[]";
674 }
675 }
676
677 private static String toString(Type type) {
678 return type instanceof Class<?> clazz ? clazz.getName() : type.toString();
679 }
680
681
682
683
684
685
686 public static String getSimpleName(Type type) {
687 if (type instanceof Class<?> clazz) {
688 return clazz.getSimpleName();
689 } else if (type instanceof ParameterizedType parameterizedType) {
690 return Arrays.stream(parameterizedType.getActualTypeArguments())
691 .map(Types::getSimpleName)
692 .collect(joining(",", "<", ">"));
693 } else if (type instanceof WildcardType wildcardType) {
694 Type[] upperBounds = wildcardType.getUpperBounds();
695 Type[] lowerBounds = wildcardType.getLowerBounds();
696 return "?"
697 + (upperBounds.length == 0
698 ? ""
699 : " extends "
700 + Arrays.stream(upperBounds)
701 .map(Types::getSimpleName)
702 .collect(joining(" & ")))
703 + (lowerBounds.length == 0
704 ? ""
705 : " super "
706 + Arrays.stream(lowerBounds)
707 .map(Types::getSimpleName)
708 .collect(joining(" & ")));
709 } else if (type instanceof GenericArrayType genericArrayType) {
710 return Types.getSimpleName(genericArrayType.getGenericComponentType()) + "[]";
711 }
712
713 return type.getTypeName();
714 }
715
716 public static class TypeNotBoundException extends IllegalArgumentException {
717 public TypeNotBoundException(String s) {
718 super(s);
719 }
720 }
721 }