View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.reflect;
18  
19  import java.lang.reflect.Array;
20  import java.lang.reflect.GenericArrayType;
21  import java.lang.reflect.GenericDeclaration;
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.Arrays;
27  import java.util.Collection;
28  import java.util.Collections;
29  import java.util.HashMap;
30  import java.util.HashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Objects;
34  import java.util.Set;
35  import java.util.TreeSet;
36  
37  import org.apache.commons.lang3.AppendableJoiner;
38  import org.apache.commons.lang3.ArrayUtils;
39  import org.apache.commons.lang3.ClassUtils;
40  import org.apache.commons.lang3.ObjectUtils;
41  import org.apache.commons.lang3.Validate;
42  import org.apache.commons.lang3.builder.Builder;
43  
44  /**
45   * Utility methods focusing on type inspection, particularly with regard to generics.
46   *
47   * @since 3.0
48   */
49  public class TypeUtils {
50  
51      /**
52       * GenericArrayType implementation class.
53       */
54      private static final class GenericArrayTypeImpl implements GenericArrayType {
55          private final Type componentType;
56  
57          /**
58           * Constructor
59           *
60           * @param componentType of this array type
61           */
62          private GenericArrayTypeImpl(final Type componentType) {
63              this.componentType = componentType;
64          }
65  
66          /**
67           * {@inheritDoc}
68           */
69          @Override
70          public boolean equals(final Object obj) {
71              return obj == this || obj instanceof GenericArrayType && TypeUtils.equals(this, (GenericArrayType) obj);
72          }
73  
74          /**
75           * {@inheritDoc}
76           */
77          @Override
78          public Type getGenericComponentType() {
79              return componentType;
80          }
81  
82          /**
83           * {@inheritDoc}
84           */
85          @Override
86          public int hashCode() {
87              int result = 67 << 4;
88              result |= componentType.hashCode();
89              return result;
90          }
91  
92          /**
93           * {@inheritDoc}
94           */
95          @Override
96          public String toString() {
97              return TypeUtils.toString(this);
98          }
99      }
100 
101     /**
102      * ParameterizedType implementation class.
103      */
104     private static final class ParameterizedTypeImpl implements ParameterizedType {
105         private final Class<?> raw;
106         private final Type useOwner;
107         private final Type[] typeArguments;
108 
109         /**
110          * Constructor
111          *
112          * @param rawClass      type
113          * @param useOwner      owner type to use, if any
114          * @param typeArguments formal type arguments
115          */
116         private ParameterizedTypeImpl(final Class<?> rawClass, final Type useOwner, final Type[] typeArguments) {
117             this.raw = rawClass;
118             this.useOwner = useOwner;
119             this.typeArguments = Arrays.copyOf(typeArguments, typeArguments.length, Type[].class);
120         }
121 
122         /**
123          * {@inheritDoc}
124          */
125         @Override
126         public boolean equals(final Object obj) {
127             return obj == this || obj instanceof ParameterizedType && TypeUtils.equals(this, (ParameterizedType) obj);
128         }
129 
130         /**
131          * {@inheritDoc}
132          */
133         @Override
134         public Type[] getActualTypeArguments() {
135             return typeArguments.clone();
136         }
137 
138         /**
139          * {@inheritDoc}
140          */
141         @Override
142         public Type getOwnerType() {
143             return useOwner;
144         }
145 
146         /**
147          * {@inheritDoc}
148          */
149         @Override
150         public Type getRawType() {
151             return raw;
152         }
153 
154         /**
155          * {@inheritDoc}
156          */
157         @Override
158         public int hashCode() {
159             int result = 71 << 4;
160             result |= raw.hashCode();
161             result <<= 4;
162             result |= Objects.hashCode(useOwner);
163             result <<= 8;
164             result |= Arrays.hashCode(typeArguments);
165             return result;
166         }
167 
168         /**
169          * {@inheritDoc}
170          */
171         @Override
172         public String toString() {
173             return TypeUtils.toString(this);
174         }
175     }
176 
177     /**
178      * {@link WildcardType} builder.
179      *
180      * @since 3.2
181      */
182     public static class WildcardTypeBuilder implements Builder<WildcardType> {
183         private Type[] upperBounds;
184 
185         private Type[] lowerBounds;
186 
187         /**
188          * Constructor
189          */
190         private WildcardTypeBuilder() {
191         }
192 
193         /**
194          * {@inheritDoc}
195          */
196         @Override
197         public WildcardType build() {
198             return new WildcardTypeImpl(upperBounds, lowerBounds);
199         }
200 
201         /**
202          * Specify lower bounds of the wildcard type to build.
203          *
204          * @param bounds to set
205          * @return {@code this}
206          */
207         public WildcardTypeBuilder withLowerBounds(final Type... bounds) {
208             this.lowerBounds = bounds;
209             return this;
210         }
211 
212         /**
213          * Specify upper bounds of the wildcard type to build.
214          *
215          * @param bounds to set
216          * @return {@code this}
217          */
218         public WildcardTypeBuilder withUpperBounds(final Type... bounds) {
219             this.upperBounds = bounds;
220             return this;
221         }
222     }
223 
224     /**
225      * WildcardType implementation class.
226      */
227     private static final class WildcardTypeImpl implements WildcardType {
228         private final Type[] upperBounds;
229         private final Type[] lowerBounds;
230 
231         /**
232          * Constructor
233          *
234          * @param upperBounds of this type
235          * @param lowerBounds of this type
236          */
237         private WildcardTypeImpl(final Type[] upperBounds, final Type[] lowerBounds) {
238             this.upperBounds = ObjectUtils.getIfNull(upperBounds, ArrayUtils.EMPTY_TYPE_ARRAY);
239             this.lowerBounds = ObjectUtils.getIfNull(lowerBounds, ArrayUtils.EMPTY_TYPE_ARRAY);
240         }
241 
242         /**
243          * {@inheritDoc}
244          */
245         @Override
246         public boolean equals(final Object obj) {
247             return obj == this || obj instanceof WildcardType && TypeUtils.equals(this, (WildcardType) obj);
248         }
249 
250         /**
251          * {@inheritDoc}
252          */
253         @Override
254         public Type[] getLowerBounds() {
255             return lowerBounds.clone();
256         }
257 
258         /**
259          * {@inheritDoc}
260          */
261         @Override
262         public Type[] getUpperBounds() {
263             return upperBounds.clone();
264         }
265 
266         /**
267          * {@inheritDoc}
268          */
269         @Override
270         public int hashCode() {
271             int result = 73 << 8;
272             result |= Arrays.hashCode(upperBounds);
273             result <<= 8;
274             result |= Arrays.hashCode(lowerBounds);
275             return result;
276         }
277 
278         /**
279          * {@inheritDoc}
280          */
281         @Override
282         public String toString() {
283             return TypeUtils.toString(this);
284         }
285     }
286 
287     /**
288      * Ampersand sign joiner.
289      */
290     // @formatter:off
291     private static final AppendableJoiner<Type> AMP_JOINER = AppendableJoiner.<Type>builder()
292             .setDelimiter(" & ")
293             .setElementAppender((a, e) -> a.append(toString(e)))
294             .get();
295     // @formatter:on
296 
297     /**
298      * Method classToString joiner.
299      */
300     // @formatter:off
301     private static final AppendableJoiner<TypeVariable<Class<?>>> CTJ_JOINER = AppendableJoiner.<TypeVariable<Class<?>>>builder()
302         .setDelimiter(", ")
303         .setElementAppender((a, e) -> a.append(anyToString(e)))
304         .get();
305     // @formatter:on
306 
307     /**
308      * Greater than and lesser than sign joiner.
309      */
310     // @formatter:off
311     private static final AppendableJoiner<Object> GT_JOINER = AppendableJoiner.builder()
312             .setPrefix("<")
313             .setSuffix(">")
314             .setDelimiter(", ")
315             .setElementAppender((a, e) -> a.append(anyToString(e)))
316             .get();
317     // @formatter:on
318 
319     /**
320      * A wildcard instance matching {@code ?}.
321      *
322      * @since 3.2
323      */
324     public static final WildcardType WILDCARD_ALL = wildcardType().withUpperBounds(Object.class).build();
325 
326     private static <T> String anyToString(final T object) {
327         return object instanceof Type ? toString((Type) object) : object.toString();
328     }
329 
330     private static void appendRecursiveTypes(final StringBuilder builder, final int[] recursiveTypeIndexes, final Type[] argumentTypes) {
331         for (int i = 0; i < recursiveTypeIndexes.length; i++) {
332             // toString() or SO
333             GT_JOINER.join(builder, argumentTypes[i].toString());
334         }
335         final Type[] argumentsFiltered = ArrayUtils.removeAll(argumentTypes, recursiveTypeIndexes);
336         if (argumentsFiltered.length > 0) {
337             GT_JOINER.join(builder, (Object[]) argumentsFiltered);
338         }
339     }
340 
341     /**
342      * Formats a {@link Class} as a {@link String}.
343      *
344      * @param cls {@link Class} to format
345      * @return String
346      */
347     private static <T> String classToString(final Class<T> cls) {
348         if (cls.isArray()) {
349             return toString(cls.getComponentType()) + "[]";
350         }
351         if (isCyclical(cls)) {
352             return cls.getSimpleName() + "(cycle)";
353         }
354         final StringBuilder buf = new StringBuilder();
355         if (cls.getEnclosingClass() != null) {
356             buf.append(classToString(cls.getEnclosingClass())).append('.').append(cls.getSimpleName());
357         } else {
358             buf.append(cls.getName());
359         }
360         if (cls.getTypeParameters().length > 0) {
361             CTJ_JOINER.join(buf, (TypeVariable[]) cls.getTypeParameters());
362         }
363         return buf.toString();
364     }
365 
366     /**
367      * Tests, recursively, whether any of the type parameters associated with {@code type} are bound to variables.
368      *
369      * @param type the type to check for type variables
370      * @return boolean
371      * @since 3.2
372      */
373     public static boolean containsTypeVariables(final Type type) {
374         if (type instanceof TypeVariable<?>) {
375             return true;
376         }
377         if (type instanceof Class<?>) {
378             return ((Class<?>) type).getTypeParameters().length > 0;
379         }
380         if (type instanceof ParameterizedType) {
381             for (final Type arg : ((ParameterizedType) type).getActualTypeArguments()) {
382                 if (containsTypeVariables(arg)) {
383                     return true;
384                 }
385             }
386             return false;
387         }
388         if (type instanceof WildcardType) {
389             final WildcardType wild = (WildcardType) type;
390             return containsTypeVariables(getImplicitLowerBounds(wild)[0]) || containsTypeVariables(getImplicitUpperBounds(wild)[0]);
391         }
392         if (type instanceof GenericArrayType) {
393             return containsTypeVariables(((GenericArrayType) type).getGenericComponentType());
394         }
395         return false;
396     }
397 
398     private static boolean containsVariableTypeSameParametrizedTypeBound(final TypeVariable<?> typeVariable, final ParameterizedType parameterizedType) {
399         return ArrayUtils.contains(typeVariable.getBounds(), parameterizedType);
400     }
401 
402     /**
403      * Tries to determine the type arguments of a class/interface based on a super parameterized type's type arguments. This method is the inverse of
404      * {@link #getTypeArguments(Type, Class)} which gets a class/interface's type arguments based on a subtype. It is far more limited in determining the type
405      * arguments for the subject class's type variables in that it can only determine those parameters that map from the subject {@link Class} object to the
406      * supertype.
407      *
408      * <p>
409      * Example: {@link java.util.TreeSet TreeSet} sets its parameter as the parameter for {@link java.util.NavigableSet NavigableSet}, which in turn sets the
410      * parameter of {@link java.util.SortedSet}, which in turn sets the parameter of {@link Set}, which in turn sets the parameter of
411      * {@link java.util.Collection}, which in turn sets the parameter of {@link Iterable}. Since {@link TreeSet}'s parameter maps (indirectly) to
412      * {@link Iterable}'s parameter, it will be able to determine that based on the super type {@code Iterable<? extends
413      * Map<Integer, ? extends Collection<?>>>}, the parameter of {@link TreeSet} is {@code ? extends Map<Integer, ? extends
414      * Collection<?>>}.
415      * </p>
416      *
417      * @param cls                    the class whose type parameters are to be determined, not {@code null}
418      * @param superParameterizedType the super type from which {@code cls}'s type arguments are to be determined, not {@code null}
419      * @return a {@link Map} of the type assignments that could be determined for the type variables in each type in the inheritance hierarchy from {@code type}
420      *         to {@code toClass} inclusive.
421      * @throws NullPointerException if either {@code cls} or {@code superParameterizedType} is {@code null}
422      */
423     public static Map<TypeVariable<?>, Type> determineTypeArguments(final Class<?> cls, final ParameterizedType superParameterizedType) {
424         Objects.requireNonNull(cls, "cls");
425         Objects.requireNonNull(superParameterizedType, "superParameterizedType");
426         final Class<?> superClass = getRawType(superParameterizedType);
427         // compatibility check
428         if (!isAssignable(cls, superClass)) {
429             return null;
430         }
431         if (cls.equals(superClass)) {
432             return getTypeArguments(superParameterizedType, superClass, null);
433         }
434         // get the next class in the inheritance hierarchy
435         final Type midType = getClosestParentType(cls, superClass);
436         // can only be a class or a parameterized type
437         if (midType instanceof Class<?>) {
438             return determineTypeArguments((Class<?>) midType, superParameterizedType);
439         }
440         final ParameterizedType midParameterizedType = (ParameterizedType) midType;
441         final Class<?> midClass = getRawType(midParameterizedType);
442         // get the type variables of the mid class that map to the type
443         // arguments of the super class
444         final Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(midClass, superParameterizedType);
445         // map the arguments of the mid type to the class type variables
446         mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
447         return typeVarAssigns;
448     }
449 
450     /**
451      * Tests whether {@code t} equals {@code a}.
452      *
453      * @param genericArrayType LHS
454      * @param type             RHS
455      * @return boolean
456      */
457     private static boolean equals(final GenericArrayType genericArrayType, final Type type) {
458         return type instanceof GenericArrayType && equals(genericArrayType.getGenericComponentType(), ((GenericArrayType) type).getGenericComponentType());
459     }
460 
461     /**
462      * Tests whether {@code t} equals {@code p}.
463      *
464      * @param parameterizedType LHS
465      * @param type              RHS
466      * @return boolean
467      */
468     private static boolean equals(final ParameterizedType parameterizedType, final Type type) {
469         if (type instanceof ParameterizedType) {
470             final ParameterizedType other = (ParameterizedType) type;
471             if (equals(parameterizedType.getRawType(), other.getRawType()) && equals(parameterizedType.getOwnerType(), other.getOwnerType())) {
472                 return equals(parameterizedType.getActualTypeArguments(), other.getActualTypeArguments());
473             }
474         }
475         return false;
476     }
477 
478     /**
479      * Tests equality of types.
480      *
481      * @param type1 the first type
482      * @param type2 the second type
483      * @return boolean
484      * @since 3.2
485      */
486     public static boolean equals(final Type type1, final Type type2) {
487         if (Objects.equals(type1, type2)) {
488             return true;
489         }
490         if (type1 instanceof ParameterizedType) {
491             return equals((ParameterizedType) type1, type2);
492         }
493         if (type1 instanceof GenericArrayType) {
494             return equals((GenericArrayType) type1, type2);
495         }
496         if (type1 instanceof WildcardType) {
497             return equals((WildcardType) type1, type2);
498         }
499         return false;
500     }
501 
502     /**
503      * Tests whether {@code t1} equals {@code t2}.
504      *
505      * @param type1 LHS
506      * @param type2 RHS
507      * @return boolean
508      */
509     private static boolean equals(final Type[] type1, final Type[] type2) {
510         if (type1.length == type2.length) {
511             for (int i = 0; i < type1.length; i++) {
512                 if (!equals(type1[i], type2[i])) {
513                     return false;
514                 }
515             }
516             return true;
517         }
518         return false;
519     }
520 
521     /**
522      * Tests whether {@code t} equals {@code w}.
523      *
524      * @param wildcardType LHS
525      * @param type         RHS
526      * @return boolean
527      */
528     private static boolean equals(final WildcardType wildcardType, final Type type) {
529         if (type instanceof WildcardType) {
530             final WildcardType other = (WildcardType) type;
531             return equals(getImplicitLowerBounds(wildcardType), getImplicitLowerBounds(other))
532                     && equals(getImplicitUpperBounds(wildcardType), getImplicitUpperBounds(other));
533         }
534         return false;
535     }
536 
537     /**
538      * Helper method to establish the formal parameters for a parameterized type.
539      *
540      * @param mappings  map containing the assignments
541      * @param variables expected map keys
542      * @return array of map values corresponding to specified keys
543      */
544     private static Type[] extractTypeArgumentsFrom(final Map<TypeVariable<?>, Type> mappings, final TypeVariable<?>[] variables) {
545         final Type[] result = new Type[variables.length];
546         int index = 0;
547         for (final TypeVariable<?> var : variables) {
548             Validate.isTrue(mappings.containsKey(var), () -> String.format("missing argument mapping for %s", toString(var)));
549             result[index++] = mappings.get(var);
550         }
551         return result;
552     }
553 
554     private static int[] findRecursiveTypes(final ParameterizedType parameterizedType) {
555         final Type[] filteredArgumentTypes = Arrays.copyOf(parameterizedType.getActualTypeArguments(), parameterizedType.getActualTypeArguments().length);
556         int[] indexesToRemove = {};
557         for (int i = 0; i < filteredArgumentTypes.length; i++) {
558             if (filteredArgumentTypes[i] instanceof TypeVariable<?>
559                     && containsVariableTypeSameParametrizedTypeBound((TypeVariable<?>) filteredArgumentTypes[i], parameterizedType)) {
560                 indexesToRemove = ArrayUtils.add(indexesToRemove, i);
561             }
562         }
563         return indexesToRemove;
564     }
565 
566     /**
567      * Creates a generic array type instance.
568      *
569      * @param componentType the type of the elements of the array. For example the component type of {@code boolean[]} is {@code boolean}
570      * @return {@link GenericArrayType}
571      * @since 3.2
572      */
573     public static GenericArrayType genericArrayType(final Type componentType) {
574         return new GenericArrayTypeImpl(Objects.requireNonNull(componentType, "componentType"));
575     }
576 
577     /**
578      * Formats a {@link GenericArrayType} as a {@link String}.
579      *
580      * @param genericArrayType {@link GenericArrayType} to format
581      * @return String
582      */
583     private static String genericArrayTypeToString(final GenericArrayType genericArrayType) {
584         return String.format("%s[]", toString(genericArrayType.getGenericComponentType()));
585     }
586 
587     /**
588      * Gets the array component type of {@code type}.
589      *
590      * @param type the type to be checked
591      * @return component type or null if type is not an array type
592      */
593     public static Type getArrayComponentType(final Type type) {
594         if (type instanceof Class<?>) {
595             final Class<?> cls = (Class<?>) type;
596             return cls.isArray() ? cls.getComponentType() : null;
597         }
598         if (type instanceof GenericArrayType) {
599             return ((GenericArrayType) type).getGenericComponentType();
600         }
601         return null;
602     }
603 
604     /**
605      * Gets the closest parent type to the super class specified by {@code superClass}.
606      *
607      * @param cls        the class in question
608      * @param superClass the super class
609      * @return the closes parent type
610      */
611     private static Type getClosestParentType(final Class<?> cls, final Class<?> superClass) {
612         // only look at the interfaces if the super class is also an interface
613         if (superClass.isInterface()) {
614             // get the generic interfaces of the subject class
615             final Type[] interfaceTypes = cls.getGenericInterfaces();
616             // will hold the best generic interface match found
617             Type genericInterface = null;
618             // find the interface closest to the super class
619             for (final Type midType : interfaceTypes) {
620                 final Class<?> midClass;
621                 if (midType instanceof ParameterizedType) {
622                     midClass = getRawType((ParameterizedType) midType);
623                 } else if (midType instanceof Class<?>) {
624                     midClass = (Class<?>) midType;
625                 } else {
626                     throw new IllegalStateException("Unexpected generic" + " interface type found: " + midType);
627                 }
628                 // check if this interface is further up the inheritance chain
629                 // than the previously found match
630                 if (isAssignable(midClass, superClass) && isAssignable(genericInterface, (Type) midClass)) {
631                     genericInterface = midType;
632                 }
633             }
634             // found a match?
635             if (genericInterface != null) {
636                 return genericInterface;
637             }
638         }
639         // none of the interfaces were descendants of the target class, so the
640         // super class has to be one, instead
641         return cls.getGenericSuperclass();
642     }
643 
644     /**
645      * Gets an array containing the sole type of {@link Object} if {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it returns the result of
646      * {@link TypeVariable#getBounds()} passed into {@link #normalizeUpperBounds}.
647      *
648      * @param typeVariable the subject type variable, not {@code null}
649      * @return a non-empty array containing the bounds of the type variable, which could be {@link Object}.
650      * @throws NullPointerException if {@code typeVariable} is {@code null}
651      */
652     public static Type[] getImplicitBounds(final TypeVariable<?> typeVariable) {
653         return normalizeUpperToObject(Objects.requireNonNull(typeVariable, "typeVariable").getBounds());
654     }
655 
656     /**
657      * Gets an array containing a single value of {@code null} if {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise, it returns the result
658      * of {@link WildcardType#getLowerBounds()}.
659      *
660      * @param wildcardType the subject wildcard type, not {@code null}
661      * @return a non-empty array containing the lower bounds of the wildcard type, which could be null.
662      * @throws NullPointerException if {@code wildcardType} is {@code null}
663      */
664     public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) {
665         Objects.requireNonNull(wildcardType, "wildcardType");
666         final Type[] bounds = wildcardType.getLowerBounds();
667         return bounds.length == 0 ? new Type[] { null } : bounds;
668     }
669 
670     /**
671      * Gets an array containing the sole value of {@link Object} if {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise, it returns the
672      * result of {@link WildcardType#getUpperBounds()} passed into {@link #normalizeUpperBounds}.
673      *
674      * @param wildcardType the subject wildcard type, not {@code null}
675      * @return a non-empty array containing the upper bounds of the wildcard type.
676      * @throws NullPointerException if {@code wildcardType} is {@code null}
677      */
678     public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) {
679         return normalizeUpperToObject(Objects.requireNonNull(wildcardType, "wildcardType").getUpperBounds());
680     }
681 
682     /**
683      * Transforms the passed in type to a {@link Class} object. Type-checking method of convenience.
684      *
685      * @param parameterizedType the type to be converted
686      * @return the corresponding {@link Class} object
687      * @throws IllegalStateException if the conversion fails
688      */
689     private static Class<?> getRawType(final ParameterizedType parameterizedType) {
690         final Type rawType = parameterizedType.getRawType();
691         // check if raw type is a Class object
692         // not currently necessary, but since the return type is Type instead of
693         // Class, there's enough reason to believe that future versions of Java
694         // may return other Type implementations. And type-safety checking is
695         // rarely a bad idea.
696         if (!(rawType instanceof Class<?>)) {
697             throw new IllegalStateException("Type of rawType: " + rawType);
698         }
699         return (Class<?>) rawType;
700     }
701 
702     /**
703      * Gets the raw type of a Java type, given its context. Primarily for use with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do not know
704      * the runtime type of {@code type}: if you know you have a {@link Class} instance, it is already raw; if you know you have a {@link ParameterizedType}, its
705      * raw type is only a method call away.
706      *
707      * @param type          to resolve
708      * @param assigningType type to be resolved against
709      * @return the resolved {@link Class} object or {@code null} if the type could not be resolved
710      */
711     public static Class<?> getRawType(final Type type, final Type assigningType) {
712         if (type instanceof Class<?>) {
713             // it is raw, no problem
714             return (Class<?>) type;
715         }
716         if (type instanceof ParameterizedType) {
717             // simple enough to get the raw type of a ParameterizedType
718             return getRawType((ParameterizedType) type);
719         }
720         if (type instanceof TypeVariable<?>) {
721             if (assigningType == null) {
722                 return null;
723             }
724             // get the entity declaring this type variable
725             final Object genericDeclaration = ((TypeVariable<?>) type).getGenericDeclaration();
726             // can't get the raw type of a method- or constructor-declared type
727             // variable
728             if (!(genericDeclaration instanceof Class<?>)) {
729                 return null;
730             }
731             // get the type arguments for the declaring class/interface based
732             // on the enclosing type
733             final Map<TypeVariable<?>, Type> typeVarAssigns = getTypeArguments(assigningType, (Class<?>) genericDeclaration);
734             // enclosingType has to be a subclass (or subinterface) of the
735             // declaring type
736             if (typeVarAssigns == null) {
737                 return null;
738             }
739             // get the argument assigned to this type variable
740             final Type typeArgument = typeVarAssigns.get(type);
741             if (typeArgument == null) {
742                 return null;
743             }
744             // get the argument for this type variable
745             return getRawType(typeArgument, assigningType);
746         }
747         if (type instanceof GenericArrayType) {
748             // get raw component type
749             final Class<?> rawComponentType = getRawType(((GenericArrayType) type).getGenericComponentType(), assigningType);
750             // create array type from raw component type and return its class
751             return rawComponentType != null ? Array.newInstance(rawComponentType, 0).getClass() : null;
752         }
753         // (hand-waving) this is not the method you're looking for
754         if (type instanceof WildcardType) {
755             return null;
756         }
757         throw new IllegalArgumentException("unknown type: " + type);
758     }
759 
760     /**
761      * Gets a map of the type arguments of a class in the context of {@code toClass}.
762      *
763      * @param cls               the class in question
764      * @param toClass           the context class
765      * @param subtypeVarAssigns a map with type variables
766      * @return the {@link Map} with type arguments
767      */
768     private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, final Class<?> toClass, final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
769         // make sure they're assignable
770         if (!isAssignable(cls, toClass)) {
771             return null;
772         }
773         // can't work with primitives
774         if (cls.isPrimitive()) {
775             // both classes are primitives?
776             if (toClass.isPrimitive()) {
777                 // dealing with widening here. No type arguments to be
778                 // harvested with these two types.
779                 return new HashMap<>();
780             }
781             // work with wrapper the wrapper class instead of the primitive
782             cls = ClassUtils.primitiveToWrapper(cls);
783         }
784         // create a copy of the incoming map, or an empty one if it's null
785         final HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
786         // has target class been reached?
787         if (toClass.equals(cls)) {
788             return typeVarAssigns;
789         }
790         // walk the inheritance hierarchy until the target class is reached
791         return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
792     }
793 
794     /**
795      * Gets all the type arguments for this parameterized type including owner hierarchy arguments such as {@code Outer<K, V>.Inner<T>.DeepInner<E>} . The
796      * arguments are returned in a {@link Map} specifying the argument type for each {@link TypeVariable}.
797      *
798      * @param type specifies the subject parameterized type from which to harvest the parameters.
799      * @return a {@link Map} of the type arguments to their respective type variables.
800      */
801     public static Map<TypeVariable<?>, Type> getTypeArguments(final ParameterizedType type) {
802         return getTypeArguments(type, getRawType(type), null);
803     }
804 
805     /**
806      * Gets a map of the type arguments of a parameterized type in the context of {@code toClass}.
807      *
808      * @param parameterizedType the parameterized type
809      * @param toClass           the class
810      * @param subtypeVarAssigns a map with type variables
811      * @return the {@link Map} with type arguments
812      */
813     private static Map<TypeVariable<?>, Type> getTypeArguments(final ParameterizedType parameterizedType, final Class<?> toClass,
814             final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
815         final Class<?> cls = getRawType(parameterizedType);
816         // make sure they're assignable
817         if (!isAssignable(cls, toClass)) {
818             return null;
819         }
820         final Type ownerType = parameterizedType.getOwnerType();
821         final Map<TypeVariable<?>, Type> typeVarAssigns;
822         if (ownerType instanceof ParameterizedType) {
823             // get the owner type arguments first
824             final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
825             typeVarAssigns = getTypeArguments(parameterizedOwnerType, getRawType(parameterizedOwnerType), subtypeVarAssigns);
826         } else {
827             // no owner, prep the type variable assignments map
828             typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
829         }
830         // get the subject parameterized type's arguments
831         final Type[] typeArgs = parameterizedType.getActualTypeArguments();
832         // and get the corresponding type variables from the raw class
833         final TypeVariable<?>[] typeParams = cls.getTypeParameters();
834         // map the arguments to their respective type variables
835         for (int i = 0; i < typeParams.length; i++) {
836             final Type typeArg = typeArgs[i];
837             typeVarAssigns.put(typeParams[i], typeVarAssigns.getOrDefault(typeArg, typeArg));
838         }
839         if (toClass.equals(cls)) {
840             // target class has been reached. Done.
841             return typeVarAssigns;
842         }
843         // walk the inheritance hierarchy until the target class is reached
844         return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
845     }
846 
847     /**
848      * Gets the type arguments of a class/interface based on a subtype. For instance, this method will determine that both of the parameters for the interface
849      * {@link Map} are {@link Object} for the subtype {@link java.util.Properties Properties} even though the subtype does not directly implement the
850      * {@link Map} interface.
851      *
852      * <p>
853      * This method returns {@code null} if {@code type} is not assignable to {@code toClass}. It returns an empty map if none of the classes or interfaces in
854      * its inheritance hierarchy specify any type arguments.
855      * </p>
856      *
857      * <p>
858      * A side effect of this method is that it also retrieves the type arguments for the classes and interfaces that are part of the hierarchy between
859      * {@code type} and {@code toClass}. So with the above example, this method will also determine that the type arguments for {@link java.util.Hashtable
860      * Hashtable} are also both {@link Object}. In cases where the interface specified by {@code toClass} is (indirectly) implemented more than once (e.g. where
861      * {@code toClass} specifies the interface {@link Iterable Iterable} and {@code type} specifies a parameterized type that implements both
862      * {@link java.util.Set Set} and {@link java.util.Collection Collection}), this method will look at the inheritance hierarchy of only one of the
863      * implementations/subclasses; the first interface encountered that isn't a subinterface to one of the others in the {@code type} to {@code toClass}
864      * hierarchy.
865      * </p>
866      *
867      * @param type    the type from which to determine the type parameters of {@code toClass}
868      * @param toClass the class whose type parameters are to be determined based on the subtype {@code type}
869      * @return a {@link Map} of the type assignments for the type variables in each type in the inheritance hierarchy from {@code type} to {@code toClass}
870      *         inclusive.
871      */
872     public static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, final Class<?> toClass) {
873         return getTypeArguments(type, toClass, null);
874     }
875 
876     /**
877      * Gets a map of the type arguments of {@code type} in the context of {@code toClass}.
878      *
879      * @param type              the type in question
880      * @param toClass           the class
881      * @param subtypeVarAssigns a map with type variables
882      * @return the {@link Map} with type arguments
883      */
884     private static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, final Class<?> toClass, final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
885         if (type instanceof Class<?>) {
886             return getTypeArguments((Class<?>) type, toClass, subtypeVarAssigns);
887         }
888         if (type instanceof ParameterizedType) {
889             return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns);
890         }
891         if (type instanceof GenericArrayType) {
892             return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass.isArray() ? toClass.getComponentType() : toClass,
893                     subtypeVarAssigns);
894         }
895         // since wildcard types are not assignable to classes, should this just
896         // return null?
897         if (type instanceof WildcardType) {
898             for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
899                 // find the first bound that is assignable to the target class
900                 if (isAssignable(bound, toClass)) {
901                     return getTypeArguments(bound, toClass, subtypeVarAssigns);
902                 }
903             }
904             return null;
905         }
906         if (type instanceof TypeVariable<?>) {
907             for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
908                 // find the first bound that is assignable to the target class
909                 if (isAssignable(bound, toClass)) {
910                     return getTypeArguments(bound, toClass, subtypeVarAssigns);
911                 }
912             }
913             return null;
914         }
915         throw new IllegalStateException("found an unhandled type: " + type);
916     }
917 
918     /**
919      * Tests whether the specified type denotes an array type.
920      *
921      * @param type the type to be checked
922      * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}.
923      */
924     public static boolean isArrayType(final Type type) {
925         return type instanceof GenericArrayType || type instanceof Class<?> && ((Class<?>) type).isArray();
926     }
927 
928     /**
929      * Tests if the subject type may be implicitly cast to the target class following the Java generics rules.
930      *
931      * @param type    the subject type to be assigned to the target type
932      * @param toClass the target class
933      * @return {@code true} if {@code type} is assignable to {@code toClass}.
934      */
935     private static boolean isAssignable(final Type type, final Class<?> toClass) {
936         if (type == null) {
937             // consistency with ClassUtils.isAssignable() behavior
938             return toClass == null || !toClass.isPrimitive();
939         }
940         // only a null type can be assigned to null type which
941         // would have cause the previous to return true
942         if (toClass == null) {
943             return false;
944         }
945         // all types are assignable to themselves
946         if (toClass.equals(type)) {
947             return true;
948         }
949         if (type instanceof Class<?>) {
950             // just comparing two classes
951             return ClassUtils.isAssignable((Class<?>) type, toClass);
952         }
953         if (type instanceof ParameterizedType) {
954             // only have to compare the raw type to the class
955             return isAssignable(getRawType((ParameterizedType) type), toClass);
956         }
957         // *
958         if (type instanceof TypeVariable<?>) {
959             // if any of the bounds are assignable to the class, then the
960             // type is assignable to the class.
961             for (final Type bound : ((TypeVariable<?>) type).getBounds()) {
962                 if (isAssignable(bound, toClass)) {
963                     return true;
964                 }
965             }
966             return false;
967         }
968         // the only classes to which a generic array type can be assigned
969         // are class Object and array classes
970         if (type instanceof GenericArrayType) {
971             return toClass.equals(Object.class)
972                     || toClass.isArray() && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass.getComponentType());
973         }
974         // wildcard types are not assignable to a class (though one would think
975         // "? super Object" would be assignable to Object)
976         if (type instanceof WildcardType) {
977             return false;
978         }
979         throw new IllegalStateException("found an unhandled type: " + type);
980     }
981 
982     /**
983      * Tests if the subject type may be implicitly cast to the target generic array type following the Java generics rules.
984      *
985      * @param type               the subject type to be assigned to the target type
986      * @param toGenericArrayType the target generic array type
987      * @param typeVarAssigns     a map with type variables
988      * @return {@code true} if {@code type} is assignable to {@code toGenericArrayType}.
989      */
990     private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
991         if (type == null) {
992             return true;
993         }
994         // only a null type can be assigned to null type which
995         // would have cause the previous to return true
996         if (toGenericArrayType == null) {
997             return false;
998         }
999         // all types are assignable to themselves
1000         if (toGenericArrayType.equals(type)) {
1001             return true;
1002         }
1003         final Type toComponentType = toGenericArrayType.getGenericComponentType();
1004         if (type instanceof Class<?>) {
1005             final Class<?> cls = (Class<?>) type;
1006             // compare the component types
1007             return cls.isArray() && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
1008         }
1009         if (type instanceof GenericArrayType) {
1010             // compare the component types
1011             return isAssignable(((GenericArrayType) type).getGenericComponentType(), toComponentType, typeVarAssigns);
1012         }
1013         if (type instanceof WildcardType) {
1014             // so long as one of the upper bounds is assignable, it's good
1015             for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
1016                 if (isAssignable(bound, toGenericArrayType)) {
1017                     return true;
1018                 }
1019             }
1020             return false;
1021         }
1022         if (type instanceof TypeVariable<?>) {
1023             // probably should remove the following logic and just return false.
1024             // type variables cannot specify arrays as bounds.
1025             for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
1026                 if (isAssignable(bound, toGenericArrayType)) {
1027                     return true;
1028                 }
1029             }
1030             return false;
1031         }
1032         if (type instanceof ParameterizedType) {
1033             // the raw type of a parameterized type is never an array or
1034             // generic array, otherwise the declaration would look like this:
1035             // Collection[]< ? extends String > collection;
1036             return false;
1037         }
1038         throw new IllegalStateException("found an unhandled type: " + type);
1039     }
1040 
1041     /**
1042      * Tests if the subject type may be implicitly cast to the target parameterized type following the Java generics rules.
1043      *
1044      * @param type                the subject type to be assigned to the target type
1045      * @param toParameterizedType the target parameterized type
1046      * @param typeVarAssigns      a map with type variables
1047      * @return {@code true} if {@code type} is assignable to {@code toType}.
1048      */
1049     private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1050         if (type == null) {
1051             return true;
1052         }
1053         // only a null type can be assigned to null type which
1054         // would have cause the previous to return true
1055         if (toParameterizedType == null) {
1056             return false;
1057         }
1058         // cannot cast an array type to a parameterized type.
1059         if (type instanceof GenericArrayType) {
1060             return false;
1061         }
1062         // all types are assignable to themselves
1063         if (toParameterizedType.equals(type)) {
1064             return true;
1065         }
1066         // get the target type's raw type
1067         final Class<?> toClass = getRawType(toParameterizedType);
1068         // get the subject type's type arguments including owner type arguments
1069         // and supertype arguments up to and including the target class.
1070         final Map<TypeVariable<?>, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
1071         // null means the two types are not compatible
1072         if (fromTypeVarAssigns == null) {
1073             return false;
1074         }
1075         // compatible types, but there's no type arguments. this is equivalent
1076         // to comparing Map< ?, ? > to Map, and raw types are always assignable
1077         // to parameterized types.
1078         if (fromTypeVarAssigns.isEmpty()) {
1079             return true;
1080         }
1081         // get the target type's type arguments including owner type arguments
1082         final Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, toClass, typeVarAssigns);
1083         // now to check each type argument
1084         for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
1085             final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
1086             final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
1087             if (toTypeArg == null && fromTypeArg instanceof Class) {
1088                 continue;
1089             }
1090             // parameters must either be absent from the subject type, within
1091             // the bounds of the wildcard type, or be an exact match to the
1092             // parameters of the target type.
1093             if (fromTypeArg != null && toTypeArg != null && !toTypeArg.equals(fromTypeArg)
1094                     && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg, typeVarAssigns))) {
1095                 return false;
1096             }
1097         }
1098         return true;
1099     }
1100 
1101     /**
1102      * Tests if the subject type may be implicitly cast to the target type following the Java generics rules. If both types are {@link Class} objects, the
1103      * method returns the result of {@link ClassUtils#isAssignable(Class, Class)}.
1104      *
1105      * @param type   the subject type to be assigned to the target type
1106      * @param toType the target type
1107      * @return {@code true} if {@code type} is assignable to {@code toType}.
1108      */
1109     public static boolean isAssignable(final Type type, final Type toType) {
1110         return isAssignable(type, toType, null);
1111     }
1112 
1113     /**
1114      * Tests if the subject type may be implicitly cast to the target type following the Java generics rules.
1115      *
1116      * @param type           the subject type to be assigned to the target type
1117      * @param toType         the target type
1118      * @param typeVarAssigns optional map of type variable assignments
1119      * @return {@code true} if {@code type} is assignable to {@code toType}.
1120      */
1121     private static boolean isAssignable(final Type type, final Type toType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1122         if (toType == null || toType instanceof Class<?>) {
1123             return isAssignable(type, (Class<?>) toType);
1124         }
1125         if (toType instanceof ParameterizedType) {
1126             return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
1127         }
1128         if (toType instanceof GenericArrayType) {
1129             return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
1130         }
1131         if (toType instanceof WildcardType) {
1132             return isAssignable(type, (WildcardType) toType, typeVarAssigns);
1133         }
1134         if (toType instanceof TypeVariable<?>) {
1135             return isAssignable(type, (TypeVariable<?>) toType, typeVarAssigns);
1136         }
1137         throw new IllegalStateException("found an unhandled type: " + toType);
1138     }
1139 
1140     /**
1141      * Tests if the subject type may be implicitly cast to the target type variable following the Java generics rules.
1142      *
1143      * @param type           the subject type to be assigned to the target type
1144      * @param toTypeVariable the target type variable
1145      * @param typeVarAssigns a map with type variables
1146      * @return {@code true} if {@code type} is assignable to {@code toTypeVariable}.
1147      */
1148     private static boolean isAssignable(final Type type, final TypeVariable<?> toTypeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1149         if (type == null) {
1150             return true;
1151         }
1152         // only a null type can be assigned to null type which
1153         // would have cause the previous to return true
1154         if (toTypeVariable == null) {
1155             return false;
1156         }
1157         // all types are assignable to themselves
1158         if (toTypeVariable.equals(type)) {
1159             return true;
1160         }
1161         if (type instanceof TypeVariable<?>) {
1162             // a type variable is assignable to another type variable, if
1163             // and only if the former is the latter, extends the latter, or
1164             // is otherwise a descendant of the latter.
1165             final Type[] bounds = getImplicitBounds((TypeVariable<?>) type);
1166             for (final Type bound : bounds) {
1167                 if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
1168                     return true;
1169                 }
1170             }
1171         }
1172         if (type instanceof Class<?> || type instanceof ParameterizedType || type instanceof GenericArrayType || type instanceof WildcardType) {
1173             return false;
1174         }
1175         throw new IllegalStateException("found an unhandled type: " + type);
1176     }
1177 
1178     /**
1179      * Tests if the subject type may be implicitly cast to the target wildcard type following the Java generics rules.
1180      *
1181      * @param type           the subject type to be assigned to the target type
1182      * @param toWildcardType the target wildcard type
1183      * @param typeVarAssigns a map with type variables
1184      * @return {@code true} if {@code type} is assignable to {@code toWildcardType}.
1185      */
1186     private static boolean isAssignable(final Type type, final WildcardType toWildcardType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1187         if (type == null) {
1188             return true;
1189         }
1190         // only a null type can be assigned to null type which
1191         // would have cause the previous to return true
1192         if (toWildcardType == null) {
1193             return false;
1194         }
1195         // all types are assignable to themselves
1196         if (toWildcardType.equals(type)) {
1197             return true;
1198         }
1199         final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
1200         final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
1201         if (type instanceof WildcardType) {
1202             final WildcardType wildcardType = (WildcardType) type;
1203             final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
1204             final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
1205             for (Type toBound : toUpperBounds) {
1206                 // if there are assignments for unresolved type variables,
1207                 // now's the time to substitute them.
1208                 toBound = substituteTypeVariables(toBound, typeVarAssigns);
1209                 // each upper bound of the subject type has to be assignable to
1210                 // each
1211                 // upper bound of the target type
1212                 for (final Type bound : upperBounds) {
1213                     if (!isAssignable(bound, toBound, typeVarAssigns)) {
1214                         return false;
1215                     }
1216                 }
1217             }
1218             for (Type toBound : toLowerBounds) {
1219                 // if there are assignments for unresolved type variables,
1220                 // now's the time to substitute them.
1221                 toBound = substituteTypeVariables(toBound, typeVarAssigns);
1222                 // each lower bound of the target type has to be assignable to
1223                 // each
1224                 // lower bound of the subject type
1225                 for (final Type bound : lowerBounds) {
1226                     if (!isAssignable(toBound, bound, typeVarAssigns)) {
1227                         return false;
1228                     }
1229                 }
1230             }
1231             return true;
1232         }
1233         for (final Type toBound : toUpperBounds) {
1234             // if there are assignments for unresolved type variables,
1235             // now's the time to substitute them.
1236             if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns), typeVarAssigns)) {
1237                 return false;
1238             }
1239         }
1240         for (final Type toBound : toLowerBounds) {
1241             // if there are assignments for unresolved type variables,
1242             // now's the time to substitute them.
1243             if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type, typeVarAssigns)) {
1244                 return false;
1245             }
1246         }
1247         return true;
1248     }
1249 
1250     /**
1251      * Tests whether the class contains a cyclical reference in the qualified name of a class. If any of the type parameters of A class is extending X class
1252      * which is in scope of A class, then it forms cycle.
1253      *
1254      * @param cls the class to test.
1255      * @return whether the class contains a cyclical reference.
1256      */
1257     private static boolean isCyclical(final Class<?> cls) {
1258         for (final TypeVariable<?> typeParameter : cls.getTypeParameters()) {
1259             for (final Type bound : typeParameter.getBounds()) {
1260                 if (bound.getTypeName().contains(cls.getName())) {
1261                     return true;
1262                 }
1263             }
1264         }
1265         return false;
1266     }
1267 
1268     /**
1269      * Tests if the given value can be assigned to the target type following the Java generics rules.
1270      *
1271      * @param value the value to be checked
1272      * @param type  the target type
1273      * @return {@code true} if {@code value} is an instance of {@code type}.
1274      */
1275     public static boolean isInstance(final Object value, final Type type) {
1276         if (type == null) {
1277             return false;
1278         }
1279         return value == null ? !(type instanceof Class<?>) || !((Class<?>) type).isPrimitive() : isAssignable(value.getClass(), type, null);
1280     }
1281 
1282     /**
1283      * Maps type variables.
1284      *
1285      * @param <T>               the generic type of the class in question
1286      * @param cls               the class in question
1287      * @param parameterizedType the parameterized type
1288      * @param typeVarAssigns    the map to be filled
1289      */
1290     private static <T> void mapTypeVariablesToArguments(final Class<T> cls, final ParameterizedType parameterizedType,
1291             final Map<TypeVariable<?>, Type> typeVarAssigns) {
1292         // capture the type variables from the owner type that have assignments
1293         final Type ownerType = parameterizedType.getOwnerType();
1294         if (ownerType instanceof ParameterizedType) {
1295             // recursion to make sure the owner's owner type gets processed
1296             mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
1297         }
1298         // parameterizedType is a generic interface/class (or it's in the owner
1299         // hierarchy of said interface/class) implemented/extended by the class
1300         // cls. Find out which type variables of cls are type arguments of
1301         // parameterizedType:
1302         final Type[] typeArgs = parameterizedType.getActualTypeArguments();
1303         // of the cls's type variables that are arguments of parameterizedType,
1304         // find out which ones can be determined from the super type's arguments
1305         final TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters();
1306         // use List view of type parameters of cls so the contains() method can be used:
1307         final List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls.getTypeParameters());
1308         for (int i = 0; i < typeArgs.length; i++) {
1309             final TypeVariable<?> typeVar = typeVars[i];
1310             final Type typeArg = typeArgs[i];
1311             // argument of parameterizedType is a type variable of cls
1312             if (typeVarList.contains(typeArg)
1313                     // type variable of parameterizedType has an assignment in
1314                     // the super type.
1315                     && typeVarAssigns.containsKey(typeVar)) {
1316                 // map the assignment to the cls's type variable
1317                 typeVarAssigns.put((TypeVariable<?>) typeArg, typeVarAssigns.get(typeVar));
1318             }
1319         }
1320     }
1321 
1322     /**
1323      * Strips out the redundant upper bound types in type variable types and wildcard types (or it would with wildcard types if multiple upper bounds were
1324      * allowed).
1325      *
1326      * <p>
1327      * Example, with the variable type declaration:
1328      * </p>
1329      *
1330      * <pre>{@code
1331      * <K extends java.util.Collection<String> & java.util.List<String>>
1332      * }</pre>
1333      *
1334      * <p>
1335      * since {@link List} is a subinterface of {@link Collection}, this method will return the bounds as if the declaration had been:
1336      * </p>
1337      *
1338      * <pre>{@code
1339      * <K extends java.util.List<String>>
1340      * }</pre>
1341      *
1342      * @param bounds an array of types representing the upper bounds of either {@link WildcardType} or {@link TypeVariable}, not {@code null}.
1343      * @return an array containing the values from {@code bounds} minus the redundant types.
1344      * @throws NullPointerException if {@code bounds} is {@code null}
1345      */
1346     public static Type[] normalizeUpperBounds(final Type[] bounds) {
1347         Objects.requireNonNull(bounds, "bounds");
1348         // don't bother if there's only one (or none) type
1349         if (bounds.length < 2) {
1350             return bounds;
1351         }
1352         final Set<Type> types = new HashSet<>(bounds.length);
1353         for (final Type type1 : bounds) {
1354             boolean subtypeFound = false;
1355             for (final Type type2 : bounds) {
1356                 if (type1 != type2 && isAssignable(type2, type1, null)) {
1357                     subtypeFound = true;
1358                     break;
1359                 }
1360             }
1361             if (!subtypeFound) {
1362                 types.add(type1);
1363             }
1364         }
1365         return types.toArray(ArrayUtils.EMPTY_TYPE_ARRAY);
1366     }
1367 
1368     /**
1369      * Delegates to {@link #normalizeUpperBounds(Type[])} unless {@code bounds} is empty in which case return an array with the element {@code Object.class}.
1370      *
1371      * @param bounds bounds an array of types representing the upper bounds of either {@link WildcardType} or {@link TypeVariable}, not {@code null}.
1372      * @return result from {@link #normalizeUpperBounds(Type[])} unless {@code bounds} is empty in which case return an array with the element
1373      *         {@code Object.class}.
1374      */
1375     private static Type[] normalizeUpperToObject(final Type[] bounds) {
1376         return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
1377     }
1378 
1379     /**
1380      * Creates a parameterized type instance.
1381      *
1382      * @param rawClass        the raw class to create a parameterized type instance for
1383      * @param typeVariableMap the map used for parameterization
1384      * @return {@link ParameterizedType}
1385      * @throws NullPointerException if either {@code rawClass} or {@code typeVariableMap} is {@code null}
1386      * @since 3.2
1387      */
1388     public static final ParameterizedType parameterize(final Class<?> rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
1389         Objects.requireNonNull(rawClass, "rawClass");
1390         Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1391         return parameterizeWithOwner(null, rawClass, extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
1392     }
1393 
1394     /**
1395      * Creates a parameterized type instance.
1396      *
1397      * @param rawClass      the raw class to create a parameterized type instance for
1398      * @param typeArguments the types used for parameterization
1399      * @return {@link ParameterizedType}
1400      * @throws NullPointerException if {@code rawClass} is {@code null}
1401      * @since 3.2
1402      */
1403     public static final ParameterizedType parameterize(final Class<?> rawClass, final Type... typeArguments) {
1404         return parameterizeWithOwner(null, rawClass, typeArguments);
1405     }
1406 
1407     /**
1408      * Formats a {@link ParameterizedType} as a {@link String}.
1409      *
1410      * @param parameterizedType {@link ParameterizedType} to format
1411      * @return String
1412      */
1413     private static String parameterizedTypeToString(final ParameterizedType parameterizedType) {
1414         final StringBuilder builder = new StringBuilder();
1415         final Type useOwner = parameterizedType.getOwnerType();
1416         final Class<?> raw = (Class<?>) parameterizedType.getRawType();
1417         if (useOwner == null) {
1418             builder.append(raw.getName());
1419         } else {
1420             if (useOwner instanceof Class<?>) {
1421                 builder.append(((Class<?>) useOwner).getName());
1422             } else {
1423                 builder.append(useOwner);
1424             }
1425             builder.append('.').append(raw.getSimpleName());
1426         }
1427         final int[] recursiveTypeIndexes = findRecursiveTypes(parameterizedType);
1428         if (recursiveTypeIndexes.length > 0) {
1429             appendRecursiveTypes(builder, recursiveTypeIndexes, parameterizedType.getActualTypeArguments());
1430         } else {
1431             GT_JOINER.join(builder, (Object[]) parameterizedType.getActualTypeArguments());
1432         }
1433         return builder.toString();
1434     }
1435 
1436     /**
1437      * Creates a parameterized type instance.
1438      *
1439      * @param owner           the owning type
1440      * @param rawClass        the raw class to create a parameterized type instance for
1441      * @param typeVariableMap the map used for parameterization
1442      * @return {@link ParameterizedType}
1443      * @throws NullPointerException if either {@code rawClass} or {@code typeVariableMap} is {@code null}
1444      * @since 3.2
1445      */
1446     public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
1447         Objects.requireNonNull(rawClass, "rawClass");
1448         Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1449         return parameterizeWithOwner(owner, rawClass, extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
1450     }
1451 
1452     /**
1453      * Creates a parameterized type instance.
1454      *
1455      * @param owner         the owning type
1456      * @param rawClass      the raw class to create a parameterized type instance for
1457      * @param typeArguments the types used for parameterization
1458      * @return {@link ParameterizedType}
1459      * @throws NullPointerException if {@code rawClass} is {@code null}
1460      * @since 3.2
1461      */
1462     public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass, final Type... typeArguments) {
1463         Objects.requireNonNull(rawClass, "rawClass");
1464         final Type useOwner;
1465         if (rawClass.getEnclosingClass() == null) {
1466             Validate.isTrue(owner == null, "no owner allowed for top-level %s", rawClass);
1467             useOwner = null;
1468         } else if (owner == null) {
1469             useOwner = rawClass.getEnclosingClass();
1470         } else {
1471             Validate.isTrue(isAssignable(owner, rawClass.getEnclosingClass()), "%s is invalid owner type for parameterized %s", owner, rawClass);
1472             useOwner = owner;
1473         }
1474         Validate.noNullElements(typeArguments, "null type argument at index %s");
1475         Validate.isTrue(rawClass.getTypeParameters().length == typeArguments.length, "invalid number of type parameters specified: expected %d, got %d",
1476                 rawClass.getTypeParameters().length, typeArguments.length);
1477         return new ParameterizedTypeImpl(rawClass, useOwner, typeArguments);
1478     }
1479 
1480     /**
1481      * Finds the mapping for {@code type} in {@code typeVarAssigns}.
1482      *
1483      * @param type           the type to be replaced
1484      * @param typeVarAssigns the map with type variables
1485      * @return the replaced type
1486      * @throws IllegalArgumentException if the type cannot be substituted
1487      */
1488     private static Type substituteTypeVariables(final Type type, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1489         if (type instanceof TypeVariable<?> && typeVarAssigns != null) {
1490             final Type replacementType = typeVarAssigns.get(type);
1491             if (replacementType == null) {
1492                 throw new IllegalArgumentException("missing assignment type for type variable " + type);
1493             }
1494             return replacementType;
1495         }
1496         return type;
1497     }
1498 
1499     /**
1500      * Formats a {@link TypeVariable} including its {@link GenericDeclaration}.
1501      *
1502      * @param typeVariable the type variable to create a String representation for, not {@code null}
1503      * @return String
1504      * @throws NullPointerException if {@code typeVariable} is {@code null}
1505      * @since 3.2
1506      */
1507     public static String toLongString(final TypeVariable<?> typeVariable) {
1508         Objects.requireNonNull(typeVariable, "typeVariable");
1509         final StringBuilder buf = new StringBuilder();
1510         final GenericDeclaration d = typeVariable.getGenericDeclaration();
1511         if (d instanceof Class<?>) {
1512             Class<?> c = (Class<?>) d;
1513             while (true) {
1514                 if (c.getEnclosingClass() == null) {
1515                     buf.insert(0, c.getName());
1516                     break;
1517                 }
1518                 buf.insert(0, c.getSimpleName()).insert(0, '.');
1519                 c = c.getEnclosingClass();
1520             }
1521         } else if (d instanceof Type) { // not possible as of now
1522             buf.append(toString((Type) d));
1523         } else {
1524             buf.append(d);
1525         }
1526         return buf.append(':').append(typeVariableToString(typeVariable)).toString();
1527     }
1528 
1529     /**
1530      * Formats a given type as a Java-esque String.
1531      *
1532      * @param type the type to create a String representation for, not {@code null}
1533      * @return String
1534      * @throws NullPointerException if {@code type} is {@code null}
1535      * @since 3.2
1536      */
1537     public static String toString(final Type type) {
1538         Objects.requireNonNull(type, "type");
1539         if (type instanceof Class<?>) {
1540             return classToString((Class<?>) type);
1541         }
1542         if (type instanceof ParameterizedType) {
1543             return parameterizedTypeToString((ParameterizedType) type);
1544         }
1545         if (type instanceof WildcardType) {
1546             return wildcardTypeToString((WildcardType) type);
1547         }
1548         if (type instanceof TypeVariable<?>) {
1549             return typeVariableToString((TypeVariable<?>) type);
1550         }
1551         if (type instanceof GenericArrayType) {
1552             return genericArrayTypeToString((GenericArrayType) type);
1553         }
1554         throw new IllegalArgumentException(ObjectUtils.identityToString(type));
1555     }
1556 
1557     /**
1558      * Determines whether or not specified types satisfy the bounds of their mapped type variables. When a type parameter extends another (such as
1559      * {@code <T, S extends T>}), uses another as a type parameter (such as {@code <T, S extends Comparable>>}), or otherwise depends on another type variable
1560      * to be specified, the dependencies must be included in {@code typeVarAssigns}.
1561      *
1562      * @param typeVariableMap specifies the potential types to be assigned to the type variables, not {@code null}.
1563      * @return whether or not the types can be assigned to their respective type variables.
1564      * @throws NullPointerException if {@code typeVariableMap} is {@code null}
1565      */
1566     public static boolean typesSatisfyVariables(final Map<TypeVariable<?>, Type> typeVariableMap) {
1567         Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1568         // all types must be assignable to all the bounds of their mapped
1569         // type variable.
1570         for (final Map.Entry<TypeVariable<?>, Type> entry : typeVariableMap.entrySet()) {
1571             final TypeVariable<?> typeVar = entry.getKey();
1572             final Type type = entry.getValue();
1573 
1574             for (final Type bound : getImplicitBounds(typeVar)) {
1575                 if (!isAssignable(type, substituteTypeVariables(bound, typeVariableMap), typeVariableMap)) {
1576                     return false;
1577                 }
1578             }
1579         }
1580         return true;
1581     }
1582 
1583     /**
1584      * Formats a {@link TypeVariable} as a {@link String}.
1585      *
1586      * @param typeVariable {@link TypeVariable} to format
1587      * @return String
1588      */
1589     private static String typeVariableToString(final TypeVariable<?> typeVariable) {
1590         final StringBuilder builder = new StringBuilder(typeVariable.getName());
1591         final Type[] bounds = typeVariable.getBounds();
1592         if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(bounds[0]))) {
1593             // https://issues.apache.org/jira/projects/LANG/issues/LANG-1698
1594             // There must be a better way to avoid a stack overflow on Java 17 and up.
1595             // Bounds are different in Java 17 and up where instead of Object you can get an interface like Comparable.
1596             final Type bound = bounds[0];
1597             boolean append = true;
1598             if (bound instanceof ParameterizedType) {
1599                 final Type rawType = ((ParameterizedType) bound).getRawType();
1600                 if (rawType instanceof Class && ((Class<?>) rawType).isInterface()) {
1601                     // Avoid recursion and stack overflow on Java 17 and up.
1602                     append = false;
1603                 }
1604             }
1605             if (append) {
1606                 builder.append(" extends ");
1607                 AMP_JOINER.join(builder, bounds);
1608             }
1609         }
1610         return builder.toString();
1611     }
1612 
1613     /**
1614      * Unrolls variables in a type bounds array.
1615      *
1616      * @param typeArguments assignments {@link Map}
1617      * @param bounds        in which to expand variables
1618      * @return {@code bounds} with any variables reassigned
1619      */
1620     private static Type[] unrollBounds(final Map<TypeVariable<?>, Type> typeArguments, final Type[] bounds) {
1621         Type[] result = bounds;
1622         int i = 0;
1623         for (; i < result.length; i++) {
1624             final Type unrolled = unrollVariables(typeArguments, result[i]);
1625             if (unrolled == null) {
1626                 result = ArrayUtils.remove(result, i--);
1627             } else {
1628                 result[i] = unrolled;
1629             }
1630         }
1631         return result;
1632     }
1633 
1634     /**
1635      * Looks up {@code typeVariable} in {@code typeVarAssigns} <em>transitively</em>, i.e. keep looking until the value found is <em>not</em> a type variable.
1636      *
1637      * @param typeVariable   the type variable to look up
1638      * @param typeVarAssigns the map used for the look-up
1639      * @return Type or {@code null} if some variable was not in the map
1640      */
1641     private static Type unrollVariableAssignments(TypeVariable<?> typeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1642         Type result;
1643         do {
1644             result = typeVarAssigns.get(typeVariable);
1645             if (!(result instanceof TypeVariable<?>) || result.equals(typeVariable)) {
1646                 break;
1647             }
1648             typeVariable = (TypeVariable<?>) result;
1649         } while (true);
1650         return result;
1651     }
1652 
1653     /**
1654      * Gets a type representing {@code type} with variable assignments "unrolled."
1655      *
1656      * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}
1657      * @param type          the type to unroll variable assignments for
1658      * @return Type
1659      * @since 3.2
1660      */
1661     public static Type unrollVariables(Map<TypeVariable<?>, Type> typeArguments, final Type type) {
1662         if (typeArguments == null) {
1663             typeArguments = Collections.emptyMap();
1664         }
1665         if (containsTypeVariables(type)) {
1666             if (type instanceof TypeVariable<?>) {
1667                 return unrollVariables(typeArguments, typeArguments.get(type));
1668             }
1669             if (type instanceof ParameterizedType) {
1670                 final ParameterizedType p = (ParameterizedType) type;
1671                 final Map<TypeVariable<?>, Type> parameterizedTypeArguments;
1672                 if (p.getOwnerType() == null) {
1673                     parameterizedTypeArguments = typeArguments;
1674                 } else {
1675                     parameterizedTypeArguments = new HashMap<>(typeArguments);
1676                     parameterizedTypeArguments.putAll(getTypeArguments(p));
1677                 }
1678                 final Type[] args = p.getActualTypeArguments();
1679                 for (int i = 0; i < args.length; i++) {
1680                     final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]);
1681                     if (unrolled != null) {
1682                         args[i] = unrolled;
1683                     }
1684                 }
1685                 return parameterizeWithOwner(p.getOwnerType(), (Class<?>) p.getRawType(), args);
1686             }
1687             if (type instanceof WildcardType) {
1688                 final WildcardType wild = (WildcardType) type;
1689                 return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds()))
1690                         .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build();
1691             }
1692         }
1693         return type;
1694     }
1695 
1696     /**
1697      * Gets a {@link WildcardTypeBuilder}.
1698      *
1699      * @return {@link WildcardTypeBuilder}
1700      * @since 3.2
1701      */
1702     public static WildcardTypeBuilder wildcardType() {
1703         return new WildcardTypeBuilder();
1704     }
1705 
1706     /**
1707      * Formats a {@link WildcardType} as a {@link String}.
1708      *
1709      * @param wildcardType {@link WildcardType} to format
1710      * @return String
1711      */
1712     private static String wildcardTypeToString(final WildcardType wildcardType) {
1713         final StringBuilder builder = new StringBuilder().append('?');
1714         final Type[] lowerBounds = wildcardType.getLowerBounds();
1715         final Type[] upperBounds = wildcardType.getUpperBounds();
1716         if (lowerBounds.length > 1 || lowerBounds.length == 1 && lowerBounds[0] != null) {
1717             AMP_JOINER.join(builder.append(" super "), lowerBounds);
1718         } else if (upperBounds.length > 1 || upperBounds.length == 1 && !Object.class.equals(upperBounds[0])) {
1719             AMP_JOINER.join(builder.append(" extends "), upperBounds);
1720         }
1721         return builder.toString();
1722     }
1723 
1724     /**
1725      * Wraps the specified {@link Class} in a {@link Typed} wrapper.
1726      *
1727      * @param <T>  generic type
1728      * @param type to wrap
1729      * @return {@code Typed<T>}
1730      * @since 3.2
1731      */
1732     public static <T> Typed<T> wrap(final Class<T> type) {
1733         return wrap((Type) type);
1734     }
1735 
1736     /**
1737      * Wraps the specified {@link Type} in a {@link Typed} wrapper.
1738      *
1739      * @param <T>  inferred generic type
1740      * @param type to wrap
1741      * @return {@code Typed<T>}
1742      * @since 3.2
1743      */
1744     public static <T> Typed<T> wrap(final Type type) {
1745         return () -> type;
1746     }
1747 
1748     /**
1749      * {@link TypeUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as
1750      * {@code TypeUtils.isAssignable(cls, toClass)}.
1751      * <p>
1752      * This constructor is public to permit tools that require a JavaBean instance to operate.
1753      * </p>
1754      *
1755      * @deprecated TODO Make private in 4.0.
1756      */
1757     @Deprecated
1758     public TypeUtils() {
1759         // empty
1760     }
1761 
1762 }