001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.reflect;
018
019import java.lang.reflect.Array;
020import java.lang.reflect.GenericArrayType;
021import java.lang.reflect.GenericDeclaration;
022import java.lang.reflect.ParameterizedType;
023import java.lang.reflect.Type;
024import java.lang.reflect.TypeVariable;
025import java.lang.reflect.WildcardType;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.HashMap;
030import java.util.HashSet;
031import java.util.List;
032import java.util.Map;
033import java.util.Objects;
034import java.util.Set;
035import java.util.TreeSet;
036
037import org.apache.commons.lang3.AppendableJoiner;
038import org.apache.commons.lang3.ArrayUtils;
039import org.apache.commons.lang3.ClassUtils;
040import org.apache.commons.lang3.ObjectUtils;
041import org.apache.commons.lang3.Validate;
042import org.apache.commons.lang3.builder.Builder;
043
044/**
045 * Utility methods focusing on type inspection, particularly with regard to generics.
046 *
047 * @since 3.0
048 */
049public class TypeUtils {
050
051    /**
052     * GenericArrayType implementation class.
053     */
054    private static final class GenericArrayTypeImpl implements GenericArrayType {
055        private final Type componentType;
056
057        /**
058         * Constructs a new instance.
059         *
060         * @param componentType of this array type.
061         */
062        private GenericArrayTypeImpl(final Type componentType) {
063            this.componentType = componentType;
064        }
065
066        /**
067         * {@inheritDoc}
068         */
069        @Override
070        public boolean equals(final Object obj) {
071            return obj == this || obj instanceof GenericArrayType && TypeUtils.equals(this, (GenericArrayType) obj);
072        }
073
074        /**
075         * {@inheritDoc}
076         */
077        @Override
078        public Type getGenericComponentType() {
079            return componentType;
080        }
081
082        /**
083         * {@inheritDoc}
084         */
085        @Override
086        public int hashCode() {
087            int result = 67 << 4;
088            result |= componentType.hashCode();
089            return result;
090        }
091
092        /**
093         * {@inheritDoc}
094         */
095        @Override
096        public String toString() {
097            return TypeUtils.toString(this);
098        }
099    }
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         * Constructs a new instance.
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} instance.
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} instance.
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         * Constructs a new instance.
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 (final Type type : argumentTypes) {
332            // toString() or you get a SO
333            GT_JOINER.join(builder, Objects.toString(type));
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 The class as a 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            GT_JOINER.join(buf, (Object[]) 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 Whether any of the type parameters associated with {@code type} are bound to variables.
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 whether the given types are equal.
480     *
481     * @param type1 The first type.
482     * @param type2 The second type.
483     * @return Whether the given types are equal.
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 the given type arrays are equal.
504     *
505     * @param type1 LHS.
506     * @param type2 RHS.
507     * @return Whether the given type arrays are equal.
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 wildcardType} equals {@code type}.
523     *
524     * @param wildcardType LHS.
525     * @param type         RHS.
526     * @return Whether {@code wildcardType} equals {@code type}.
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        final Type parentType = getClosestParentType(cls, toClass);
845        if (parentType instanceof ParameterizedType) {
846            final ParameterizedType parameterizedParentType = (ParameterizedType) parentType;
847            final Type[] parentTypeArgs = parameterizedParentType.getActualTypeArguments().clone();
848            for (int i = 0; i < parentTypeArgs.length; i++) {
849                final Type unrolled = unrollVariables(typeVarAssigns, parentTypeArgs[i]);
850                if (unrolled != null) {
851                    parentTypeArgs[i] = unrolled;
852                }
853            }
854            return getTypeArguments(parameterizeWithOwner(parameterizedParentType.getOwnerType(), (Class<?>) parameterizedParentType.getRawType(), parentTypeArgs), toClass, typeVarAssigns);
855        }
856        return getTypeArguments(parentType, toClass, typeVarAssigns);
857    }
858
859    /**
860     * 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
861     * {@link Map} are {@link Object} for the subtype {@link java.util.Properties Properties} even though the subtype does not directly implement the
862     * {@link Map} interface.
863     *
864     * <p>
865     * 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
866     * its inheritance hierarchy specify any type arguments.
867     * </p>
868     *
869     * <p>
870     * 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
871     * {@code type} and {@code toClass}. So with the above example, this method will also determine that the type arguments for {@link java.util.Hashtable
872     * Hashtable} are also both {@link Object}. In cases where the interface specified by {@code toClass} is (indirectly) implemented more than once (e.g. where
873     * {@code toClass} specifies the interface {@link Iterable Iterable} and {@code type} specifies a parameterized type that implements both
874     * {@link java.util.Set Set} and {@link java.util.Collection Collection}), this method will look at the inheritance hierarchy of only one of the
875     * implementations/subclasses; the first interface encountered that isn't a subinterface to one of the others in the {@code type} to {@code toClass}
876     * hierarchy.
877     * </p>
878     *
879     * @param type    the type from which to determine the type parameters of {@code toClass}
880     * @param toClass the class whose type parameters are to be determined based on the subtype {@code type}
881     * @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}
882     *         inclusive.
883     */
884    public static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, final Class<?> toClass) {
885        return getTypeArguments(type, toClass, null);
886    }
887
888    /**
889     * Gets a map of the type arguments of {@code type} in the context of {@code toClass}.
890     *
891     * @param type              the type in question.
892     * @param toClass           the class.
893     * @param subtypeVarAssigns a map with type variables.
894     * @return the {@link Map} with type arguments.
895     */
896    private static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, final Class<?> toClass, final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
897        if (type instanceof Class<?>) {
898            return getTypeArguments((Class<?>) type, toClass, subtypeVarAssigns);
899        }
900        if (type instanceof ParameterizedType) {
901            return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns);
902        }
903        if (type instanceof GenericArrayType) {
904            return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass.isArray() ? toClass.getComponentType() : toClass,
905                    subtypeVarAssigns);
906        }
907        // since wildcard types are not assignable to classes, should this just
908        // return null?
909        if (type instanceof WildcardType) {
910            for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
911                // find the first bound that is assignable to the target class
912                if (isAssignable(bound, toClass)) {
913                    return getTypeArguments(bound, toClass, subtypeVarAssigns);
914                }
915            }
916            return null;
917        }
918        if (type instanceof TypeVariable<?>) {
919            for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
920                // find the first bound that is assignable to the target class
921                if (isAssignable(bound, toClass)) {
922                    return getTypeArguments(bound, toClass, subtypeVarAssigns);
923                }
924            }
925            return null;
926        }
927        throw new IllegalStateException("found an unhandled type: " + type);
928    }
929
930    /**
931     * Tests whether the specified type denotes an array type.
932     *
933     * @param type the type to be checked.
934     * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}.
935     */
936    public static boolean isArrayType(final Type type) {
937        return type instanceof GenericArrayType || type instanceof Class<?> && ((Class<?>) type).isArray();
938    }
939
940    /**
941     * Tests if the subject type may be implicitly cast to the target class following the Java generics rules.
942     *
943     * @param type    the subject type to be assigned to the target type.
944     * @param toClass the target class.
945     * @return {@code true} if {@code type} is assignable to {@code toClass}.
946     */
947    private static boolean isAssignable(final Type type, final Class<?> toClass) {
948        if (type == null) {
949            // consistency with ClassUtils.isAssignable() behavior
950            return toClass == null || !toClass.isPrimitive();
951        }
952        // only a null type can be assigned to null type which
953        // would have cause the previous to return true
954        if (toClass == null) {
955            return false;
956        }
957        // all types are assignable to themselves
958        if (toClass.equals(type)) {
959            return true;
960        }
961        if (type instanceof Class<?>) {
962            // just comparing two classes
963            return ClassUtils.isAssignable((Class<?>) type, toClass);
964        }
965        if (type instanceof ParameterizedType) {
966            // only have to compare the raw type to the class
967            return isAssignable(getRawType((ParameterizedType) type), toClass);
968        }
969        // *
970        if (type instanceof TypeVariable<?>) {
971            // if any of the bounds are assignable to the class, then the
972            // type is assignable to the class.
973            for (final Type bound : ((TypeVariable<?>) type).getBounds()) {
974                if (isAssignable(bound, toClass)) {
975                    return true;
976                }
977            }
978            return false;
979        }
980        // the only classes to which a generic array type can be assigned
981        // are class Object and array classes
982        if (type instanceof GenericArrayType) {
983            return toClass.equals(Object.class)
984                    || toClass.isArray() && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass.getComponentType());
985        }
986        // wildcard types are not assignable to a class (though one would think
987        // "? super Object" would be assignable to Object)
988        if (type instanceof WildcardType) {
989            return false;
990        }
991        throw new IllegalStateException("found an unhandled type: " + type);
992    }
993
994    /**
995     * Tests if the subject type may be implicitly cast to the target generic array type following the Java generics rules.
996     *
997     * @param type               the subject type to be assigned to the target type.
998     * @param toGenericArrayType the target generic array type.
999     * @param typeVarAssigns     a map with type variables.
1000     * @return {@code true} if {@code type} is assignable to {@code toGenericArrayType}.
1001     */
1002    private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1003        if (type == null) {
1004            return true;
1005        }
1006        // only a null type can be assigned to null type which
1007        // would have cause the previous to return true
1008        if (toGenericArrayType == null) {
1009            return false;
1010        }
1011        // all types are assignable to themselves
1012        if (toGenericArrayType.equals(type)) {
1013            return true;
1014        }
1015        final Type toComponentType = toGenericArrayType.getGenericComponentType();
1016        if (type instanceof Class<?>) {
1017            final Class<?> cls = (Class<?>) type;
1018            // compare the component types
1019            return cls.isArray() && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
1020        }
1021        if (type instanceof GenericArrayType) {
1022            // compare the component types
1023            return isAssignable(((GenericArrayType) type).getGenericComponentType(), toComponentType, typeVarAssigns);
1024        }
1025        if (type instanceof WildcardType) {
1026            // so long as one of the upper bounds is assignable, it's good
1027            for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
1028                if (isAssignable(bound, toGenericArrayType)) {
1029                    return true;
1030                }
1031            }
1032            return false;
1033        }
1034        if (type instanceof TypeVariable<?>) {
1035            // probably should remove the following logic and just return false.
1036            // type variables cannot specify arrays as bounds.
1037            for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
1038                if (isAssignable(bound, toGenericArrayType)) {
1039                    return true;
1040                }
1041            }
1042            return false;
1043        }
1044        if (type instanceof ParameterizedType) {
1045            // the raw type of a parameterized type is never an array or
1046            // generic array, otherwise the declaration would look like this:
1047            // Collection[]< ? extends String > collection;
1048            return false;
1049        }
1050        throw new IllegalStateException("found an unhandled type: " + type);
1051    }
1052
1053    /**
1054     * Tests if the subject type may be implicitly cast to the target parameterized type following the Java generics rules.
1055     *
1056     * @param type                the subject type to be assigned to the target type.
1057     * @param toParameterizedType the target parameterized type.
1058     * @param typeVarAssigns      a map with type variables.
1059     * @return {@code true} if {@code type} is assignable to {@code toType}.
1060     */
1061    private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1062        if (type == null) {
1063            return true;
1064        }
1065        // only a null type can be assigned to null type which
1066        // would have cause the previous to return true
1067        if (toParameterizedType == null) {
1068            return false;
1069        }
1070        // cannot cast an array type to a parameterized type.
1071        if (type instanceof GenericArrayType) {
1072            return false;
1073        }
1074        // all types are assignable to themselves
1075        if (toParameterizedType.equals(type)) {
1076            return true;
1077        }
1078        // get the target type's raw type
1079        final Class<?> toClass = getRawType(toParameterizedType);
1080        // get the subject type's type arguments including owner type arguments
1081        // and supertype arguments up to and including the target class.
1082        final Map<TypeVariable<?>, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
1083        // null means the two types are not compatible
1084        if (fromTypeVarAssigns == null) {
1085            return false;
1086        }
1087        // compatible types, but there's no type arguments. this is equivalent
1088        // to comparing Map< ?, ? > to Map, and raw types are always assignable
1089        // to parameterized types.
1090        if (fromTypeVarAssigns.isEmpty()) {
1091            return true;
1092        }
1093        // get the target type's type arguments including owner type arguments
1094        final Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, toClass, typeVarAssigns);
1095        // Class<T> is not assignable to Class<S> if T is not S (even if T extends S)
1096        if (toClass.equals(Class.class)) {
1097            final TypeVariable<?>[] typeParams = toClass.getTypeParameters();
1098            if (typeParams.length > 0) {
1099                final Type toTypeArg = unrollVariableAssignments(typeParams[0], toTypeVarAssigns);
1100                final Type fromTypeArg = unrollVariableAssignments(typeParams[0], fromTypeVarAssigns);
1101                if (toTypeArg != null && (fromTypeArg == null || !toTypeArg.equals(fromTypeArg))) {
1102                    return false;
1103                }
1104            }
1105        }
1106        // now to check each type argument
1107        for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
1108            final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
1109            final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
1110            if (toTypeArg == null && fromTypeArg instanceof Class) {
1111                continue;
1112            }
1113            // parameters must either be absent from the subject type, within
1114            // the bounds of the wildcard type, or be an exact match to the
1115            // parameters of the target type.
1116            if (fromTypeArg != null && toTypeArg != null && !toTypeArg.equals(fromTypeArg)
1117                    && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg, typeVarAssigns))) {
1118                return false;
1119            }
1120        }
1121        return true;
1122    }
1123
1124    /**
1125     * 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
1126     * method returns the result of {@link ClassUtils#isAssignable(Class, Class)}.
1127     *
1128     * @param type   the subject type to be assigned to the target type.
1129     * @param toType the target type.
1130     * @return {@code true} if {@code type} is assignable to {@code toType}.
1131     */
1132    public static boolean isAssignable(final Type type, final Type toType) {
1133        return isAssignable(type, toType, null);
1134    }
1135
1136    /**
1137     * Tests if the subject type may be implicitly cast to the target type following the Java generics rules.
1138     *
1139     * @param type           the subject type to be assigned to the target type.
1140     * @param toType         the target type.
1141     * @param typeVarAssigns optional map of type variable assignments.
1142     * @return {@code true} if {@code type} is assignable to {@code toType}.
1143     */
1144    private static boolean isAssignable(final Type type, final Type toType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1145        if (toType == null || toType instanceof Class<?>) {
1146            return isAssignable(type, (Class<?>) toType);
1147        }
1148        if (toType instanceof ParameterizedType) {
1149            return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
1150        }
1151        if (toType instanceof GenericArrayType) {
1152            return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
1153        }
1154        if (toType instanceof WildcardType) {
1155            return isAssignable(type, (WildcardType) toType, typeVarAssigns);
1156        }
1157        if (toType instanceof TypeVariable<?>) {
1158            return isAssignable(type, (TypeVariable<?>) toType, typeVarAssigns);
1159        }
1160        throw new IllegalStateException("found an unhandled type: " + toType);
1161    }
1162
1163    /**
1164     * Tests if the subject type may be implicitly cast to the target type variable following the Java generics rules.
1165     *
1166     * @param type           the subject type to be assigned to the target type.
1167     * @param toTypeVariable the target type variable.
1168     * @param typeVarAssigns a map with type variables.
1169     * @return {@code true} if {@code type} is assignable to {@code toTypeVariable}.
1170     */
1171    private static boolean isAssignable(final Type type, final TypeVariable<?> toTypeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1172        if (type == null) {
1173            return true;
1174        }
1175        // only a null type can be assigned to null type which
1176        // would have cause the previous to return true
1177        if (toTypeVariable == null) {
1178            return false;
1179        }
1180        // all types are assignable to themselves
1181        if (toTypeVariable.equals(type)) {
1182            return true;
1183        }
1184        if (type instanceof TypeVariable<?>) {
1185            // a type variable is assignable to another type variable, if
1186            // and only if the former is the latter, extends the latter, or
1187            // is otherwise a descendant of the latter.
1188            final Type[] bounds = getImplicitBounds((TypeVariable<?>) type);
1189            for (final Type bound : bounds) {
1190                if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
1191                    return true;
1192                }
1193            }
1194        }
1195        if (type instanceof Class<?> || type instanceof ParameterizedType || type instanceof GenericArrayType || type instanceof WildcardType) {
1196            return false;
1197        }
1198        throw new IllegalStateException("found an unhandled type: " + type);
1199    }
1200
1201    /**
1202     * Tests if the subject type may be implicitly cast to the target wildcard type following the Java generics rules.
1203     *
1204     * @param type           the subject type to be assigned to the target type.
1205     * @param toWildcardType the target wildcard type.
1206     * @param typeVarAssigns a map with type variables.
1207     * @return {@code true} if {@code type} is assignable to {@code toWildcardType}.
1208     */
1209    private static boolean isAssignable(final Type type, final WildcardType toWildcardType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1210        if (type == null) {
1211            return true;
1212        }
1213        // only a null type can be assigned to null type which
1214        // would have cause the previous to return true
1215        if (toWildcardType == null) {
1216            return false;
1217        }
1218        // all types are assignable to themselves
1219        if (toWildcardType.equals(type)) {
1220            return true;
1221        }
1222        final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
1223        final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
1224        if (type instanceof WildcardType) {
1225            final WildcardType wildcardType = (WildcardType) type;
1226            final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
1227            final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
1228            for (Type toBound : toUpperBounds) {
1229                // if there are assignments for unresolved type variables,
1230                // now's the time to substitute them.
1231                toBound = substituteTypeVariables(toBound, typeVarAssigns);
1232                // each upper bound of the subject type has to be assignable to
1233                // each
1234                // upper bound of the target type
1235                for (final Type bound : upperBounds) {
1236                    if (!isAssignable(bound, toBound, typeVarAssigns)) {
1237                        return false;
1238                    }
1239                }
1240            }
1241            for (Type toBound : toLowerBounds) {
1242                // if there are assignments for unresolved type variables,
1243                // now's the time to substitute them.
1244                toBound = substituteTypeVariables(toBound, typeVarAssigns);
1245                // each lower bound of the target type has to be assignable to
1246                // each
1247                // lower bound of the subject type
1248                for (final Type bound : lowerBounds) {
1249                    if (!isAssignable(toBound, bound, typeVarAssigns)) {
1250                        return false;
1251                    }
1252                }
1253            }
1254            return true;
1255        }
1256        for (final Type toBound : toUpperBounds) {
1257            // if there are assignments for unresolved type variables,
1258            // now's the time to substitute them.
1259            if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns), typeVarAssigns)) {
1260                return false;
1261            }
1262        }
1263        for (final Type toBound : toLowerBounds) {
1264            // if there are assignments for unresolved type variables,
1265            // now's the time to substitute them.
1266            if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type, typeVarAssigns)) {
1267                return false;
1268            }
1269        }
1270        return true;
1271    }
1272
1273    /**
1274     * 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
1275     * which is in scope of A class, then it forms cycle.
1276     *
1277     * @param cls the class to test.
1278     * @return whether the class contains a cyclical reference.
1279     */
1280    private static boolean isCyclical(final Class<?> cls) {
1281        for (final TypeVariable<?> typeParameter : cls.getTypeParameters()) {
1282            for (final Type bound : typeParameter.getBounds()) {
1283                if (bound.getTypeName().contains(cls.getName())) {
1284                    return true;
1285                }
1286            }
1287        }
1288        return false;
1289    }
1290
1291    /**
1292     * Tests if the given value can be assigned to the target type following the Java generics rules.
1293     *
1294     * @param value the value to be checked.
1295     * @param type  the target type.
1296     * @return {@code true} if {@code value} is an instance of {@code type}.
1297     */
1298    public static boolean isInstance(final Object value, final Type type) {
1299        if (type == null) {
1300            return false;
1301        }
1302        return value == null ? !(type instanceof Class<?>) || !((Class<?>) type).isPrimitive() : isAssignable(value.getClass(), type, null);
1303    }
1304
1305    /**
1306     * Maps type variables.
1307     *
1308     * @param <T>               the generic type of the class in question.
1309     * @param cls               the class in question.
1310     * @param parameterizedType the parameterized type.
1311     * @param typeVarAssigns    the map to be filled.
1312     */
1313    private static <T> void mapTypeVariablesToArguments(final Class<T> cls, final ParameterizedType parameterizedType,
1314            final Map<TypeVariable<?>, Type> typeVarAssigns) {
1315        // capture the type variables from the owner type that have assignments
1316        final Type ownerType = parameterizedType.getOwnerType();
1317        if (ownerType instanceof ParameterizedType) {
1318            // recursion to make sure the owner's owner type gets processed
1319            mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
1320        }
1321        // parameterizedType is a generic interface/class (or it's in the owner
1322        // hierarchy of said interface/class) implemented/extended by the class
1323        // cls. Find out which type variables of cls are type arguments of
1324        // parameterizedType:
1325        final Type[] typeArgs = parameterizedType.getActualTypeArguments();
1326        // of the cls's type variables that are arguments of parameterizedType,
1327        // find out which ones can be determined from the super type's arguments
1328        final TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters();
1329        // use List view of type parameters of cls so the contains() method can be used:
1330        final List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls.getTypeParameters());
1331        for (int i = 0; i < typeArgs.length; i++) {
1332            final TypeVariable<?> typeVar = typeVars[i];
1333            final Type typeArg = typeArgs[i];
1334            // argument of parameterizedType is a type variable of cls
1335            if (typeVarList.contains(typeArg)
1336                    // type variable of parameterizedType has an assignment in
1337                    // the super type.
1338                    && typeVarAssigns.containsKey(typeVar)) {
1339                // map the assignment to the cls's type variable
1340                typeVarAssigns.put((TypeVariable<?>) typeArg, typeVarAssigns.get(typeVar));
1341            }
1342        }
1343    }
1344
1345    /**
1346     * 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
1347     * allowed).
1348     *
1349     * <p>
1350     * Example, with the variable type declaration:
1351     * </p>
1352     *
1353     * <pre>{@code
1354     * <K extends java.util.Collection<String> & java.util.List<String>>
1355     * }</pre>
1356     *
1357     * <p>
1358     * since {@link List} is a subinterface of {@link Collection}, this method will return the bounds as if the declaration had been:
1359     * </p>
1360     *
1361     * <pre>{@code
1362     * <K extends java.util.List<String>>
1363     * }</pre>
1364     *
1365     * @param bounds an array of types representing the upper bounds of either {@link WildcardType} or {@link TypeVariable}, not {@code null}.
1366     * @return an array containing the values from {@code bounds} minus the redundant types.
1367     * @throws NullPointerException if {@code bounds} is {@code null}.
1368     */
1369    public static Type[] normalizeUpperBounds(final Type[] bounds) {
1370        Objects.requireNonNull(bounds, "bounds");
1371        // don't bother if there's only one (or none) type
1372        if (bounds.length < 2) {
1373            return bounds;
1374        }
1375        final Set<Type> types = new HashSet<>(bounds.length);
1376        for (final Type type1 : bounds) {
1377            boolean subtypeFound = false;
1378            for (final Type type2 : bounds) {
1379                if (type1 != type2 && isAssignable(type2, type1, null)) {
1380                    subtypeFound = true;
1381                    break;
1382                }
1383            }
1384            if (!subtypeFound) {
1385                types.add(type1);
1386            }
1387        }
1388        return types.toArray(ArrayUtils.EMPTY_TYPE_ARRAY);
1389    }
1390
1391    /**
1392     * Delegates to {@link #normalizeUpperBounds(Type[])} unless {@code bounds} is empty in which case return an array with the element {@code Object.class}.
1393     *
1394     * @param bounds bounds an array of types representing the upper bounds of either {@link WildcardType} or {@link TypeVariable}, not {@code null}.
1395     * @return result from {@link #normalizeUpperBounds(Type[])} unless {@code bounds} is empty in which case return an array with the element
1396     *         {@code Object.class}.
1397     */
1398    private static Type[] normalizeUpperToObject(final Type[] bounds) {
1399        return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
1400    }
1401
1402    /**
1403     * Creates a parameterized type instance.
1404     *
1405     * @param rawClass        the raw class to create a parameterized type instance for.
1406     * @param typeVariableMap the map used for parameterization.
1407     * @return {@link ParameterizedType}.
1408     * @throws NullPointerException if either {@code rawClass} or {@code typeVariableMap} is {@code null}.
1409     * @since 3.2
1410     */
1411    public static final ParameterizedType parameterize(final Class<?> rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
1412        Objects.requireNonNull(rawClass, "rawClass");
1413        Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1414        return parameterizeWithOwner(null, rawClass, extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
1415    }
1416
1417    /**
1418     * Creates a parameterized type instance.
1419     *
1420     * @param rawClass      the raw class to create a parameterized type instance for.
1421     * @param typeArguments the types used for parameterization.
1422     * @return {@link ParameterizedType}.
1423     * @throws NullPointerException if {@code rawClass} is {@code null}.
1424     * @since 3.2
1425     */
1426    public static final ParameterizedType parameterize(final Class<?> rawClass, final Type... typeArguments) {
1427        return parameterizeWithOwner(null, rawClass, typeArguments);
1428    }
1429
1430    /**
1431     * Formats a {@link ParameterizedType} as a {@link String}.
1432     *
1433     * @param parameterizedType {@link ParameterizedType} to format.
1434     * @return String.
1435     */
1436    private static String parameterizedTypeToString(final ParameterizedType parameterizedType) {
1437        final StringBuilder builder = new StringBuilder();
1438        final Type useOwner = parameterizedType.getOwnerType();
1439        final Class<?> raw = (Class<?>) parameterizedType.getRawType();
1440        if (useOwner == null) {
1441            builder.append(raw.getName());
1442        } else {
1443            if (useOwner instanceof Class<?>) {
1444                builder.append(((Class<?>) useOwner).getName());
1445            } else {
1446                builder.append(useOwner);
1447            }
1448            builder.append('.').append(raw.getSimpleName());
1449        }
1450        final int[] recursiveTypeIndexes = findRecursiveTypes(parameterizedType);
1451        if (recursiveTypeIndexes.length > 0) {
1452            appendRecursiveTypes(builder, recursiveTypeIndexes, parameterizedType.getActualTypeArguments());
1453        } else {
1454            GT_JOINER.join(builder, (Object[]) parameterizedType.getActualTypeArguments());
1455        }
1456        return builder.toString();
1457    }
1458
1459    /**
1460     * Creates a parameterized type instance.
1461     *
1462     * @param owner           the owning type.
1463     * @param rawClass        the raw class to create a parameterized type instance for.
1464     * @param typeVariableMap the map used for parameterization.
1465     * @return {@link ParameterizedType}.
1466     * @throws NullPointerException if either {@code rawClass} or {@code typeVariableMap} is {@code null}.
1467     * @since 3.2
1468     */
1469    public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
1470        Objects.requireNonNull(rawClass, "rawClass");
1471        Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1472        return parameterizeWithOwner(owner, rawClass, extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
1473    }
1474
1475    /**
1476     * Creates a parameterized type instance.
1477     *
1478     * @param owner         the owning type.
1479     * @param rawClass      the raw class to create a parameterized type instance for.
1480     * @param typeArguments the types used for parameterization.
1481     * @return {@link ParameterizedType}.
1482     * @throws NullPointerException if {@code rawClass} is {@code null}.
1483     * @since 3.2
1484     */
1485    public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass, final Type... typeArguments) {
1486        Objects.requireNonNull(rawClass, "rawClass");
1487        final Type useOwner;
1488        if (rawClass.getEnclosingClass() == null) {
1489            Validate.isTrue(owner == null, "no owner allowed for top-level %s", rawClass);
1490            useOwner = null;
1491        } else if (owner == null) {
1492            useOwner = rawClass.getEnclosingClass();
1493        } else {
1494            Validate.isTrue(isAssignable(owner, rawClass.getEnclosingClass()), "%s is invalid owner type for parameterized %s", owner, rawClass);
1495            useOwner = owner;
1496        }
1497        Validate.noNullElements(typeArguments, "null type argument at index %s");
1498        Validate.isTrue(rawClass.getTypeParameters().length == typeArguments.length, "invalid number of type parameters specified: expected %d, got %d",
1499                rawClass.getTypeParameters().length, typeArguments.length);
1500        return new ParameterizedTypeImpl(rawClass, useOwner, typeArguments);
1501    }
1502
1503    /**
1504     * Finds the mapping for {@code type} in {@code typeVarAssigns}.
1505     *
1506     * @param type           the type to be replaced.
1507     * @param typeVarAssigns the map with type variables.
1508     * @return the replaced type.
1509     * @throws IllegalArgumentException if the type cannot be substituted.
1510     */
1511    private static Type substituteTypeVariables(final Type type, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1512        if (type instanceof TypeVariable<?> && typeVarAssigns != null) {
1513            final Type replacementType = typeVarAssigns.get(type);
1514            if (replacementType == null) {
1515                throw new IllegalArgumentException("missing assignment type for type variable " + type);
1516            }
1517            return replacementType;
1518        }
1519        return type;
1520    }
1521
1522    /**
1523     * Formats a {@link TypeVariable} including its {@link GenericDeclaration}.
1524     *
1525     * @param typeVariable the type variable to create a String representation for, not {@code null}.
1526     * @return String.
1527     * @throws NullPointerException if {@code typeVariable} is {@code null}.
1528     * @since 3.2
1529     */
1530    public static String toLongString(final TypeVariable<?> typeVariable) {
1531        Objects.requireNonNull(typeVariable, "typeVariable");
1532        final StringBuilder buf = new StringBuilder();
1533        final GenericDeclaration d = typeVariable.getGenericDeclaration();
1534        if (d instanceof Class<?>) {
1535            Class<?> c = (Class<?>) d;
1536            while (true) {
1537                if (c.getEnclosingClass() == null) {
1538                    buf.insert(0, c.getName());
1539                    break;
1540                }
1541                buf.insert(0, c.getSimpleName()).insert(0, '.');
1542                c = c.getEnclosingClass();
1543            }
1544        } else if (d instanceof Type) { // not possible as of now
1545            buf.append(toString((Type) d));
1546        } else {
1547            buf.append(d);
1548        }
1549        return buf.append(':').append(typeVariableToString(typeVariable)).toString();
1550    }
1551
1552    /**
1553     * Formats a given type as a Java-esque String.
1554     *
1555     * @param type the type to create a String representation for, not {@code null}.
1556     * @return String.
1557     * @throws NullPointerException if {@code type} is {@code null}.
1558     * @since 3.2
1559     */
1560    public static String toString(final Type type) {
1561        Objects.requireNonNull(type, "type");
1562        if (type instanceof Class<?>) {
1563            return classToString((Class<?>) type);
1564        }
1565        if (type instanceof ParameterizedType) {
1566            return parameterizedTypeToString((ParameterizedType) type);
1567        }
1568        if (type instanceof WildcardType) {
1569            return wildcardTypeToString((WildcardType) type);
1570        }
1571        if (type instanceof TypeVariable<?>) {
1572            return typeVariableToString((TypeVariable<?>) type);
1573        }
1574        if (type instanceof GenericArrayType) {
1575            return genericArrayTypeToString((GenericArrayType) type);
1576        }
1577        throw new IllegalArgumentException(ObjectUtils.identityToString(type));
1578    }
1579
1580    /**
1581     * Determines whether or not specified types satisfy the bounds of their mapped type variables. When a type parameter extends another (such as
1582     * {@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
1583     * to be specified, the dependencies must be included in {@code typeVarAssigns}.
1584     *
1585     * @param typeVariableMap specifies the potential types to be assigned to the type variables, not {@code null}.
1586     * @return whether or not the types can be assigned to their respective type variables.
1587     * @throws NullPointerException if {@code typeVariableMap} is {@code null}.
1588     */
1589    public static boolean typesSatisfyVariables(final Map<TypeVariable<?>, Type> typeVariableMap) {
1590        Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1591        // all types must be assignable to all the bounds of their mapped
1592        // type variable.
1593        for (final Map.Entry<TypeVariable<?>, Type> entry : typeVariableMap.entrySet()) {
1594            final TypeVariable<?> typeVar = entry.getKey();
1595            final Type type = entry.getValue();
1596            for (final Type bound : getImplicitBounds(typeVar)) {
1597                if (!isAssignable(type, substituteTypeVariables(bound, typeVariableMap), typeVariableMap)) {
1598                    return false;
1599                }
1600            }
1601        }
1602        return true;
1603    }
1604
1605    /**
1606     * Formats a {@link TypeVariable} as a {@link String}.
1607     *
1608     * @param typeVariable {@link TypeVariable} to format.
1609     * @return String.
1610     */
1611    private static String typeVariableToString(final TypeVariable<?> typeVariable) {
1612        final StringBuilder builder = new StringBuilder(typeVariable.getName());
1613        final Type[] bounds = typeVariable.getBounds();
1614        if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(bounds[0]))) {
1615            // https://issues.apache.org/jira/projects/LANG/issues/LANG-1698
1616            // There must be a better way to avoid a stack overflow on Java 17 and up.
1617            // Bounds are different in Java 17 and up where instead of Object you can get an interface like Comparable.
1618            final Type bound = bounds[0];
1619            boolean append = true;
1620            if (bound instanceof ParameterizedType) {
1621                final Type rawType = ((ParameterizedType) bound).getRawType();
1622                if (rawType instanceof Class && ((Class<?>) rawType).isInterface()) {
1623                    // Avoid recursion and stack overflow on Java 17 and up.
1624                    append = false;
1625                }
1626            }
1627            if (append) {
1628                builder.append(" extends ");
1629                AMP_JOINER.join(builder, bounds);
1630            }
1631        }
1632        return builder.toString();
1633    }
1634
1635    /**
1636     * Unrolls variables in a type bounds array.
1637     *
1638     * @param typeArguments assignments {@link Map}.
1639     * @param bounds        in which to expand variables.
1640     * @return {@code bounds} with any variables reassigned.
1641     */
1642    private static Type[] unrollBounds(final Map<TypeVariable<?>, Type> typeArguments, final Type[] bounds) {
1643        Type[] result = bounds;
1644        int i = 0;
1645        for (; i < result.length; i++) {
1646            final Type unrolled = unrollVariables(typeArguments, result[i]);
1647            if (unrolled == null) {
1648                result = ArrayUtils.remove(result, i--);
1649            } else {
1650                result[i] = unrolled;
1651            }
1652        }
1653        return result;
1654    }
1655
1656    /**
1657     * 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.
1658     *
1659     * @param typeVariable   the type variable to look up.
1660     * @param typeVarAssigns the map used for the look-up.
1661     * @return Type or {@code null} if some variable was not in the map.
1662     */
1663    private static Type unrollVariableAssignments(TypeVariable<?> typeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1664        Type result;
1665        do {
1666            result = typeVarAssigns.get(typeVariable);
1667            if (!(result instanceof TypeVariable<?>) || result.equals(typeVariable)) {
1668                break;
1669            }
1670            typeVariable = (TypeVariable<?>) result;
1671        } while (true);
1672        return result;
1673    }
1674
1675    /**
1676     * Gets a type representing {@code type} with variable assignments "unrolled."
1677     *
1678     * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}.
1679     * @param type          the type to unroll variable assignments for.
1680     * @return Type.
1681     * @since 3.2
1682     */
1683    public static Type unrollVariables(Map<TypeVariable<?>, Type> typeArguments, final Type type) {
1684        if (typeArguments == null) {
1685            typeArguments = Collections.emptyMap();
1686        }
1687        return unrollVariables(typeArguments, type, new HashSet<>());
1688    }
1689
1690    private static Type unrollVariables(final Map<TypeVariable<?>, Type> typeArguments, final Type type, final Set<TypeVariable<?>> visited) {
1691        if (containsTypeVariables(type)) {
1692            if (type instanceof TypeVariable<?>) {
1693                final TypeVariable<?> var = (TypeVariable<?>) type;
1694                if (!visited.add(var)) {
1695                    return var;
1696                }
1697                return unrollVariables(typeArguments, typeArguments.get(type), visited);
1698            }
1699            if (type instanceof ParameterizedType) {
1700                final ParameterizedType p = (ParameterizedType) type;
1701                final Map<TypeVariable<?>, Type> parameterizedTypeArguments;
1702                if (p.getOwnerType() == null) {
1703                    parameterizedTypeArguments = typeArguments;
1704                } else {
1705                    parameterizedTypeArguments = new HashMap<>(typeArguments);
1706                    parameterizedTypeArguments.putAll(getTypeArguments(p));
1707                }
1708                final Type[] args = p.getActualTypeArguments().clone();
1709                for (int i = 0; i < args.length; i++) {
1710                    final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i], visited);
1711                    if (unrolled != null) {
1712                        args[i] = unrolled;
1713                    }
1714                }
1715                return parameterizeWithOwner(p.getOwnerType(), (Class<?>) p.getRawType(), args);
1716            }
1717            if (type instanceof WildcardType) {
1718                final WildcardType wild = (WildcardType) type;
1719                return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds()))
1720                        .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build();
1721            }
1722        }
1723        return type;
1724    }
1725
1726    /**
1727     * Creates a new {@link WildcardTypeBuilder}.
1728     *
1729     * @return a new {@link WildcardTypeBuilder}.
1730     * @since 3.2
1731     */
1732    public static WildcardTypeBuilder wildcardType() {
1733        return new WildcardTypeBuilder();
1734    }
1735
1736    /**
1737     * Formats a {@link WildcardType} as a {@link String}.
1738     *
1739     * @param wildcardType {@link WildcardType} to format.
1740     * @return String.
1741     */
1742    private static String wildcardTypeToString(final WildcardType wildcardType) {
1743        final StringBuilder builder = new StringBuilder().append('?');
1744        final Type[] lowerBounds = wildcardType.getLowerBounds();
1745        final Type[] upperBounds = wildcardType.getUpperBounds();
1746        if (lowerBounds.length > 1 || lowerBounds.length == 1 && lowerBounds[0] != null) {
1747            AMP_JOINER.join(builder.append(" super "), lowerBounds);
1748        } else if (upperBounds.length > 1 || upperBounds.length == 1 && !Object.class.equals(upperBounds[0])) {
1749            AMP_JOINER.join(builder.append(" extends "), upperBounds);
1750        }
1751        return builder.toString();
1752    }
1753
1754    /**
1755     * Wraps the specified {@link Class} in a {@link Typed} wrapper.
1756     *
1757     * @param <T>  generic type.
1758     * @param type to wrap.
1759     * @return {@code Typed<T>}.
1760     * @since 3.2
1761     */
1762    public static <T> Typed<T> wrap(final Class<T> type) {
1763        return wrap((Type) type);
1764    }
1765
1766    /**
1767     * Wraps the specified {@link Type} in a {@link Typed} wrapper.
1768     *
1769     * @param <T>  inferred generic type.
1770     * @param type to wrap.
1771     * @return {@code Typed<T>}.
1772     * @since 3.2
1773     */
1774    public static <T> Typed<T> wrap(final Type type) {
1775        return () -> type;
1776    }
1777
1778    /**
1779     * {@link TypeUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as
1780     * {@code TypeUtils.isAssignable(cls, toClass)}.
1781     * <p>
1782     * This constructor is public to permit tools that require a JavaBean instance to operate.
1783     * </p>
1784     *
1785     * @deprecated TODO Make private in 4.0.
1786     */
1787    @Deprecated
1788    public TypeUtils() {
1789        // empty
1790    }
1791
1792}