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         * Constructor
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         * 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
427        final Class<?> superClass = getRawType(superParameterizedType);
428
429        // compatibility check
430        if (!isAssignable(cls, superClass)) {
431            return null;
432        }
433
434        if (cls.equals(superClass)) {
435            return getTypeArguments(superParameterizedType, superClass, null);
436        }
437
438        // get the next class in the inheritance hierarchy
439        final Type midType = getClosestParentType(cls, superClass);
440
441        // can only be a class or a parameterized type
442        if (midType instanceof Class<?>) {
443            return determineTypeArguments((Class<?>) midType, superParameterizedType);
444        }
445
446        final ParameterizedType midParameterizedType = (ParameterizedType) midType;
447        final Class<?> midClass = getRawType(midParameterizedType);
448        // get the type variables of the mid class that map to the type
449        // arguments of the super class
450        final Map<TypeVariable<?>, Type> typeVarAssigns = determineTypeArguments(midClass, superParameterizedType);
451        // map the arguments of the mid type to the class type variables
452        mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
453
454        return typeVarAssigns;
455    }
456
457    /**
458     * Tests whether {@code t} equals {@code a}.
459     *
460     * @param genericArrayType LHS
461     * @param type             RHS
462     * @return boolean
463     */
464    private static boolean equals(final GenericArrayType genericArrayType, final Type type) {
465        return type instanceof GenericArrayType && equals(genericArrayType.getGenericComponentType(), ((GenericArrayType) type).getGenericComponentType());
466    }
467
468    /**
469     * Tests whether {@code t} equals {@code p}.
470     *
471     * @param parameterizedType LHS
472     * @param type              RHS
473     * @return boolean
474     */
475    private static boolean equals(final ParameterizedType parameterizedType, final Type type) {
476        if (type instanceof ParameterizedType) {
477            final ParameterizedType other = (ParameterizedType) type;
478            if (equals(parameterizedType.getRawType(), other.getRawType()) && equals(parameterizedType.getOwnerType(), other.getOwnerType())) {
479                return equals(parameterizedType.getActualTypeArguments(), other.getActualTypeArguments());
480            }
481        }
482        return false;
483    }
484
485    /**
486     * Tests equality of types.
487     *
488     * @param type1 the first type
489     * @param type2 the second type
490     * @return boolean
491     * @since 3.2
492     */
493    public static boolean equals(final Type type1, final Type type2) {
494        if (Objects.equals(type1, type2)) {
495            return true;
496        }
497        if (type1 instanceof ParameterizedType) {
498            return equals((ParameterizedType) type1, type2);
499        }
500        if (type1 instanceof GenericArrayType) {
501            return equals((GenericArrayType) type1, type2);
502        }
503        if (type1 instanceof WildcardType) {
504            return equals((WildcardType) type1, type2);
505        }
506        return false;
507    }
508
509    /**
510     * Tests whether {@code t1} equals {@code t2}.
511     *
512     * @param type1 LHS
513     * @param type2 RHS
514     * @return boolean
515     */
516    private static boolean equals(final Type[] type1, final Type[] type2) {
517        if (type1.length == type2.length) {
518            for (int i = 0; i < type1.length; i++) {
519                if (!equals(type1[i], type2[i])) {
520                    return false;
521                }
522            }
523            return true;
524        }
525        return false;
526    }
527
528    /**
529     * Tests whether {@code t} equals {@code w}.
530     *
531     * @param wildcardType LHS
532     * @param type         RHS
533     * @return boolean
534     */
535    private static boolean equals(final WildcardType wildcardType, final Type type) {
536        if (type instanceof WildcardType) {
537            final WildcardType other = (WildcardType) type;
538            return equals(getImplicitLowerBounds(wildcardType), getImplicitLowerBounds(other))
539                    && equals(getImplicitUpperBounds(wildcardType), getImplicitUpperBounds(other));
540        }
541        return false;
542    }
543
544    /**
545     * Helper method to establish the formal parameters for a parameterized type.
546     *
547     * @param mappings  map containing the assignments
548     * @param variables expected map keys
549     * @return array of map values corresponding to specified keys
550     */
551    private static Type[] extractTypeArgumentsFrom(final Map<TypeVariable<?>, Type> mappings, final TypeVariable<?>[] variables) {
552        final Type[] result = new Type[variables.length];
553        int index = 0;
554        for (final TypeVariable<?> var : variables) {
555            Validate.isTrue(mappings.containsKey(var), () -> String.format("missing argument mapping for %s", toString(var)));
556            result[index++] = mappings.get(var);
557        }
558        return result;
559    }
560
561    private static int[] findRecursiveTypes(final ParameterizedType parameterizedType) {
562        final Type[] filteredArgumentTypes = Arrays.copyOf(parameterizedType.getActualTypeArguments(), parameterizedType.getActualTypeArguments().length);
563        int[] indexesToRemove = {};
564        for (int i = 0; i < filteredArgumentTypes.length; i++) {
565            if (filteredArgumentTypes[i] instanceof TypeVariable<?>
566                    && containsVariableTypeSameParametrizedTypeBound((TypeVariable<?>) filteredArgumentTypes[i], parameterizedType)) {
567                indexesToRemove = ArrayUtils.add(indexesToRemove, i);
568            }
569        }
570        return indexesToRemove;
571    }
572
573    /**
574     * Creates a generic array type instance.
575     *
576     * @param componentType the type of the elements of the array. For example the component type of {@code boolean[]} is {@code boolean}
577     * @return {@link GenericArrayType}
578     * @since 3.2
579     */
580    public static GenericArrayType genericArrayType(final Type componentType) {
581        return new GenericArrayTypeImpl(Objects.requireNonNull(componentType, "componentType"));
582    }
583
584    /**
585     * Formats a {@link GenericArrayType} as a {@link String}.
586     *
587     * @param genericArrayType {@link GenericArrayType} to format
588     * @return String
589     */
590    private static String genericArrayTypeToString(final GenericArrayType genericArrayType) {
591        return String.format("%s[]", toString(genericArrayType.getGenericComponentType()));
592    }
593
594    /**
595     * Gets the array component type of {@code type}.
596     *
597     * @param type the type to be checked
598     * @return component type or null if type is not an array type
599     */
600    public static Type getArrayComponentType(final Type type) {
601        if (type instanceof Class<?>) {
602            final Class<?> cls = (Class<?>) type;
603            return cls.isArray() ? cls.getComponentType() : null;
604        }
605        if (type instanceof GenericArrayType) {
606            return ((GenericArrayType) type).getGenericComponentType();
607        }
608        return null;
609    }
610
611    /**
612     * Gets the closest parent type to the super class specified by {@code superClass}.
613     *
614     * @param cls        the class in question
615     * @param superClass the super class
616     * @return the closes parent type
617     */
618    private static Type getClosestParentType(final Class<?> cls, final Class<?> superClass) {
619        // only look at the interfaces if the super class is also an interface
620        if (superClass.isInterface()) {
621            // get the generic interfaces of the subject class
622            final Type[] interfaceTypes = cls.getGenericInterfaces();
623            // will hold the best generic interface match found
624            Type genericInterface = null;
625
626            // find the interface closest to the super class
627            for (final Type midType : interfaceTypes) {
628                final Class<?> midClass;
629
630                if (midType instanceof ParameterizedType) {
631                    midClass = getRawType((ParameterizedType) midType);
632                } else if (midType instanceof Class<?>) {
633                    midClass = (Class<?>) midType;
634                } else {
635                    throw new IllegalStateException("Unexpected generic" + " interface type found: " + midType);
636                }
637
638                // check if this interface is further up the inheritance chain
639                // than the previously found match
640                if (isAssignable(midClass, superClass) && isAssignable(genericInterface, (Type) midClass)) {
641                    genericInterface = midType;
642                }
643            }
644
645            // found a match?
646            if (genericInterface != null) {
647                return genericInterface;
648            }
649        }
650
651        // none of the interfaces were descendants of the target class, so the
652        // super class has to be one, instead
653        return cls.getGenericSuperclass();
654    }
655
656    /**
657     * Gets an array containing the sole type of {@link Object} if {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it returns the result of
658     * {@link TypeVariable#getBounds()} passed into {@link #normalizeUpperBounds}.
659     *
660     * @param typeVariable the subject type variable, not {@code null}
661     * @return a non-empty array containing the bounds of the type variable.
662     * @throws NullPointerException if {@code typeVariable} is {@code null}
663     */
664    public static Type[] getImplicitBounds(final TypeVariable<?> typeVariable) {
665        Objects.requireNonNull(typeVariable, "typeVariable");
666        final Type[] bounds = typeVariable.getBounds();
667
668        return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
669    }
670
671    /**
672     * Gets an array containing a single value of {@code null} if {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise, it returns the result
673     * of {@link WildcardType#getLowerBounds()}.
674     *
675     * @param wildcardType the subject wildcard type, not {@code null}
676     * @return a non-empty array containing the lower bounds of the wildcard type.
677     * @throws NullPointerException if {@code wildcardType} is {@code null}
678     */
679    public static Type[] getImplicitLowerBounds(final WildcardType wildcardType) {
680        Objects.requireNonNull(wildcardType, "wildcardType");
681        final Type[] bounds = wildcardType.getLowerBounds();
682
683        return bounds.length == 0 ? new Type[] { null } : bounds;
684    }
685
686    /**
687     * Gets an array containing the sole value of {@link Object} if {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise, it returns the
688     * result of {@link WildcardType#getUpperBounds()} passed into {@link #normalizeUpperBounds}.
689     *
690     * @param wildcardType the subject wildcard type, not {@code null}
691     * @return a non-empty array containing the upper bounds of the wildcard type.
692     * @throws NullPointerException if {@code wildcardType} is {@code null}
693     */
694    public static Type[] getImplicitUpperBounds(final WildcardType wildcardType) {
695        Objects.requireNonNull(wildcardType, "wildcardType");
696        final Type[] bounds = wildcardType.getUpperBounds();
697
698        return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds);
699    }
700
701    /**
702     * Transforms the passed in type to a {@link Class} object. Type-checking method of convenience.
703     *
704     * @param parameterizedType the type to be converted
705     * @return the corresponding {@link Class} object
706     * @throws IllegalStateException if the conversion fails
707     */
708    private static Class<?> getRawType(final ParameterizedType parameterizedType) {
709        final Type rawType = parameterizedType.getRawType();
710
711        // check if raw type is a Class object
712        // not currently necessary, but since the return type is Type instead of
713        // Class, there's enough reason to believe that future versions of Java
714        // may return other Type implementations. And type-safety checking is
715        // rarely a bad idea.
716        if (!(rawType instanceof Class<?>)) {
717            throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType);
718        }
719
720        return (Class<?>) rawType;
721    }
722
723    /**
724     * 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
725     * 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
726     * raw type is only a method call away.
727     *
728     * @param type          to resolve
729     * @param assigningType type to be resolved against
730     * @return the resolved {@link Class} object or {@code null} if the type could not be resolved
731     */
732    public static Class<?> getRawType(final Type type, final Type assigningType) {
733        if (type instanceof Class<?>) {
734            // it is raw, no problem
735            return (Class<?>) type;
736        }
737
738        if (type instanceof ParameterizedType) {
739            // simple enough to get the raw type of a ParameterizedType
740            return getRawType((ParameterizedType) type);
741        }
742
743        if (type instanceof TypeVariable<?>) {
744            if (assigningType == null) {
745                return null;
746            }
747
748            // get the entity declaring this type variable
749            final Object genericDeclaration = ((TypeVariable<?>) type).getGenericDeclaration();
750
751            // can't get the raw type of a method- or constructor-declared type
752            // variable
753            if (!(genericDeclaration instanceof Class<?>)) {
754                return null;
755            }
756
757            // get the type arguments for the declaring class/interface based
758            // on the enclosing type
759            final Map<TypeVariable<?>, Type> typeVarAssigns = getTypeArguments(assigningType, (Class<?>) genericDeclaration);
760
761            // enclosingType has to be a subclass (or subinterface) of the
762            // declaring type
763            if (typeVarAssigns == null) {
764                return null;
765            }
766
767            // get the argument assigned to this type variable
768            final Type typeArgument = typeVarAssigns.get(type);
769
770            if (typeArgument == null) {
771                return null;
772            }
773
774            // get the argument for this type variable
775            return getRawType(typeArgument, assigningType);
776        }
777
778        if (type instanceof GenericArrayType) {
779            // get raw component type
780            final Class<?> rawComponentType = getRawType(((GenericArrayType) type).getGenericComponentType(), assigningType);
781
782            // create array type from raw component type and return its class
783            return rawComponentType != null ? Array.newInstance(rawComponentType, 0).getClass() : null;
784        }
785
786        // (hand-waving) this is not the method you're looking for
787        if (type instanceof WildcardType) {
788            return null;
789        }
790
791        throw new IllegalArgumentException("unknown type: " + type);
792    }
793
794    /**
795     * Gets a map of the type arguments of a class in the context of {@code toClass}.
796     *
797     * @param cls               the class in question
798     * @param toClass           the context class
799     * @param subtypeVarAssigns a map with type variables
800     * @return the {@link Map} with type arguments
801     */
802    private static Map<TypeVariable<?>, Type> getTypeArguments(Class<?> cls, final Class<?> toClass, final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
803        // make sure they're assignable
804        if (!isAssignable(cls, toClass)) {
805            return null;
806        }
807
808        // can't work with primitives
809        if (cls.isPrimitive()) {
810            // both classes are primitives?
811            if (toClass.isPrimitive()) {
812                // dealing with widening here. No type arguments to be
813                // harvested with these two types.
814                return new HashMap<>();
815            }
816
817            // work with wrapper the wrapper class instead of the primitive
818            cls = ClassUtils.primitiveToWrapper(cls);
819        }
820
821        // create a copy of the incoming map, or an empty one if it's null
822        final HashMap<TypeVariable<?>, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
823
824        // has target class been reached?
825        if (toClass.equals(cls)) {
826            return typeVarAssigns;
827        }
828
829        // walk the inheritance hierarchy until the target class is reached
830        return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
831    }
832
833    /**
834     * Gets all the type arguments for this parameterized type including owner hierarchy arguments such as {@code Outer<K, V>.Inner<T>.DeepInner<E>} . The
835     * arguments are returned in a {@link Map} specifying the argument type for each {@link TypeVariable}.
836     *
837     * @param type specifies the subject parameterized type from which to harvest the parameters.
838     * @return a {@link Map} of the type arguments to their respective type variables.
839     */
840    public static Map<TypeVariable<?>, Type> getTypeArguments(final ParameterizedType type) {
841        return getTypeArguments(type, getRawType(type), null);
842    }
843
844    /**
845     * Gets a map of the type arguments of a parameterized type in the context of {@code toClass}.
846     *
847     * @param parameterizedType the parameterized type
848     * @param toClass           the class
849     * @param subtypeVarAssigns a map with type variables
850     * @return the {@link Map} with type arguments
851     */
852    private static Map<TypeVariable<?>, Type> getTypeArguments(final ParameterizedType parameterizedType, final Class<?> toClass,
853            final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
854        final Class<?> cls = getRawType(parameterizedType);
855
856        // make sure they're assignable
857        if (!isAssignable(cls, toClass)) {
858            return null;
859        }
860
861        final Type ownerType = parameterizedType.getOwnerType();
862        final Map<TypeVariable<?>, Type> typeVarAssigns;
863
864        if (ownerType instanceof ParameterizedType) {
865            // get the owner type arguments first
866            final ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType;
867            typeVarAssigns = getTypeArguments(parameterizedOwnerType, getRawType(parameterizedOwnerType), subtypeVarAssigns);
868        } else {
869            // no owner, prep the type variable assignments map
870            typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(subtypeVarAssigns);
871        }
872
873        // get the subject parameterized type's arguments
874        final Type[] typeArgs = parameterizedType.getActualTypeArguments();
875        // and get the corresponding type variables from the raw class
876        final TypeVariable<?>[] typeParams = cls.getTypeParameters();
877
878        // map the arguments to their respective type variables
879        for (int i = 0; i < typeParams.length; i++) {
880            final Type typeArg = typeArgs[i];
881            typeVarAssigns.put(typeParams[i], typeVarAssigns.getOrDefault(typeArg, typeArg));
882        }
883
884        if (toClass.equals(cls)) {
885            // target class has been reached. Done.
886            return typeVarAssigns;
887        }
888
889        // walk the inheritance hierarchy until the target class is reached
890        return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns);
891    }
892
893    /**
894     * 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
895     * {@link Map} are {@link Object} for the subtype {@link java.util.Properties Properties} even though the subtype does not directly implement the
896     * {@link Map} interface.
897     *
898     * <p>
899     * 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
900     * its inheritance hierarchy specify any type arguments.
901     * </p>
902     *
903     * <p>
904     * 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
905     * {@code type} and {@code toClass}. So with the above example, this method will also determine that the type arguments for {@link java.util.Hashtable
906     * Hashtable} are also both {@link Object}. In cases where the interface specified by {@code toClass} is (indirectly) implemented more than once (e.g. where
907     * {@code toClass} specifies the interface {@link Iterable Iterable} and {@code type} specifies a parameterized type that implements both
908     * {@link java.util.Set Set} and {@link java.util.Collection Collection}), this method will look at the inheritance hierarchy of only one of the
909     * implementations/subclasses; the first interface encountered that isn't a subinterface to one of the others in the {@code type} to {@code toClass}
910     * hierarchy.
911     * </p>
912     *
913     * @param type    the type from which to determine the type parameters of {@code toClass}
914     * @param toClass the class whose type parameters are to be determined based on the subtype {@code type}
915     * @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}
916     *         inclusive.
917     */
918    public static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, final Class<?> toClass) {
919        return getTypeArguments(type, toClass, null);
920    }
921
922    /**
923     * Gets a map of the type arguments of {@code type} in the context of {@code toClass}.
924     *
925     * @param type              the type in question
926     * @param toClass           the class
927     * @param subtypeVarAssigns a map with type variables
928     * @return the {@link Map} with type arguments
929     */
930    private static Map<TypeVariable<?>, Type> getTypeArguments(final Type type, final Class<?> toClass, final Map<TypeVariable<?>, Type> subtypeVarAssigns) {
931        if (type instanceof Class<?>) {
932            return getTypeArguments((Class<?>) type, toClass, subtypeVarAssigns);
933        }
934
935        if (type instanceof ParameterizedType) {
936            return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns);
937        }
938
939        if (type instanceof GenericArrayType) {
940            return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass.isArray() ? toClass.getComponentType() : toClass,
941                    subtypeVarAssigns);
942        }
943
944        // since wildcard types are not assignable to classes, should this just
945        // return null?
946        if (type instanceof WildcardType) {
947            for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
948                // find the first bound that is assignable to the target class
949                if (isAssignable(bound, toClass)) {
950                    return getTypeArguments(bound, toClass, subtypeVarAssigns);
951                }
952            }
953
954            return null;
955        }
956
957        if (type instanceof TypeVariable<?>) {
958            for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
959                // find the first bound that is assignable to the target class
960                if (isAssignable(bound, toClass)) {
961                    return getTypeArguments(bound, toClass, subtypeVarAssigns);
962                }
963            }
964
965            return null;
966        }
967        throw new IllegalStateException("found an unhandled type: " + type);
968    }
969
970    /**
971     * Tests whether the specified type denotes an array type.
972     *
973     * @param type the type to be checked
974     * @return {@code true} if {@code type} is an array class or a {@link GenericArrayType}.
975     */
976    public static boolean isArrayType(final Type type) {
977        return type instanceof GenericArrayType || type instanceof Class<?> && ((Class<?>) type).isArray();
978    }
979
980    /**
981     * Tests if the subject type may be implicitly cast to the target class following the Java generics rules.
982     *
983     * @param type    the subject type to be assigned to the target type
984     * @param toClass the target class
985     * @return {@code true} if {@code type} is assignable to {@code toClass}.
986     */
987    private static boolean isAssignable(final Type type, final Class<?> toClass) {
988        if (type == null) {
989            // consistency with ClassUtils.isAssignable() behavior
990            return toClass == null || !toClass.isPrimitive();
991        }
992
993        // only a null type can be assigned to null type which
994        // would have cause the previous to return true
995        if (toClass == null) {
996            return false;
997        }
998
999        // all types are assignable to themselves
1000        if (toClass.equals(type)) {
1001            return true;
1002        }
1003
1004        if (type instanceof Class<?>) {
1005            // just comparing two classes
1006            return ClassUtils.isAssignable((Class<?>) type, toClass);
1007        }
1008
1009        if (type instanceof ParameterizedType) {
1010            // only have to compare the raw type to the class
1011            return isAssignable(getRawType((ParameterizedType) type), toClass);
1012        }
1013
1014        // *
1015        if (type instanceof TypeVariable<?>) {
1016            // if any of the bounds are assignable to the class, then the
1017            // type is assignable to the class.
1018            for (final Type bound : ((TypeVariable<?>) type).getBounds()) {
1019                if (isAssignable(bound, toClass)) {
1020                    return true;
1021                }
1022            }
1023
1024            return false;
1025        }
1026
1027        // the only classes to which a generic array type can be assigned
1028        // are class Object and array classes
1029        if (type instanceof GenericArrayType) {
1030            return toClass.equals(Object.class)
1031                    || toClass.isArray() && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass.getComponentType());
1032        }
1033
1034        // wildcard types are not assignable to a class (though one would think
1035        // "? super Object" would be assignable to Object)
1036        if (type instanceof WildcardType) {
1037            return false;
1038        }
1039
1040        throw new IllegalStateException("found an unhandled type: " + type);
1041    }
1042
1043    /**
1044     * Tests if the subject type may be implicitly cast to the target generic array type following the Java generics rules.
1045     *
1046     * @param type               the subject type to be assigned to the target type
1047     * @param toGenericArrayType the target generic array type
1048     * @param typeVarAssigns     a map with type variables
1049     * @return {@code true} if {@code type} is assignable to {@code toGenericArrayType}.
1050     */
1051    private static boolean isAssignable(final Type type, final GenericArrayType toGenericArrayType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1052        if (type == null) {
1053            return true;
1054        }
1055
1056        // only a null type can be assigned to null type which
1057        // would have cause the previous to return true
1058        if (toGenericArrayType == null) {
1059            return false;
1060        }
1061
1062        // all types are assignable to themselves
1063        if (toGenericArrayType.equals(type)) {
1064            return true;
1065        }
1066
1067        final Type toComponentType = toGenericArrayType.getGenericComponentType();
1068
1069        if (type instanceof Class<?>) {
1070            final Class<?> cls = (Class<?>) type;
1071
1072            // compare the component types
1073            return cls.isArray() && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns);
1074        }
1075
1076        if (type instanceof GenericArrayType) {
1077            // compare the component types
1078            return isAssignable(((GenericArrayType) type).getGenericComponentType(), toComponentType, typeVarAssigns);
1079        }
1080
1081        if (type instanceof WildcardType) {
1082            // so long as one of the upper bounds is assignable, it's good
1083            for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
1084                if (isAssignable(bound, toGenericArrayType)) {
1085                    return true;
1086                }
1087            }
1088
1089            return false;
1090        }
1091
1092        if (type instanceof TypeVariable<?>) {
1093            // probably should remove the following logic and just return false.
1094            // type variables cannot specify arrays as bounds.
1095            for (final Type bound : getImplicitBounds((TypeVariable<?>) type)) {
1096                if (isAssignable(bound, toGenericArrayType)) {
1097                    return true;
1098                }
1099            }
1100
1101            return false;
1102        }
1103
1104        if (type instanceof ParameterizedType) {
1105            // the raw type of a parameterized type is never an array or
1106            // generic array, otherwise the declaration would look like this:
1107            // Collection[]< ? extends String > collection;
1108            return false;
1109        }
1110
1111        throw new IllegalStateException("found an unhandled type: " + type);
1112    }
1113
1114    /**
1115     * Tests if the subject type may be implicitly cast to the target parameterized type following the Java generics rules.
1116     *
1117     * @param type                the subject type to be assigned to the target type
1118     * @param toParameterizedType the target parameterized type
1119     * @param typeVarAssigns      a map with type variables
1120     * @return {@code true} if {@code type} is assignable to {@code toType}.
1121     */
1122    private static boolean isAssignable(final Type type, final ParameterizedType toParameterizedType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1123        if (type == null) {
1124            return true;
1125        }
1126
1127        // only a null type can be assigned to null type which
1128        // would have cause the previous to return true
1129        if (toParameterizedType == null) {
1130            return false;
1131        }
1132
1133        // cannot cast an array type to a parameterized type.
1134        if (type instanceof GenericArrayType) {
1135            return false;
1136        }
1137
1138        // all types are assignable to themselves
1139        if (toParameterizedType.equals(type)) {
1140            return true;
1141        }
1142
1143        // get the target type's raw type
1144        final Class<?> toClass = getRawType(toParameterizedType);
1145        // get the subject type's type arguments including owner type arguments
1146        // and supertype arguments up to and including the target class.
1147        final Map<TypeVariable<?>, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null);
1148
1149        // null means the two types are not compatible
1150        if (fromTypeVarAssigns == null) {
1151            return false;
1152        }
1153
1154        // compatible types, but there's no type arguments. this is equivalent
1155        // to comparing Map< ?, ? > to Map, and raw types are always assignable
1156        // to parameterized types.
1157        if (fromTypeVarAssigns.isEmpty()) {
1158            return true;
1159        }
1160
1161        // get the target type's type arguments including owner type arguments
1162        final Map<TypeVariable<?>, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, toClass, typeVarAssigns);
1163
1164        // now to check each type argument
1165        for (final TypeVariable<?> var : toTypeVarAssigns.keySet()) {
1166            final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
1167            final Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns);
1168
1169            if (toTypeArg == null && fromTypeArg instanceof Class) {
1170                continue;
1171            }
1172
1173            // parameters must either be absent from the subject type, within
1174            // the bounds of the wildcard type, or be an exact match to the
1175            // parameters of the target type.
1176            if (fromTypeArg != null && toTypeArg != null && !toTypeArg.equals(fromTypeArg)
1177                    && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg, typeVarAssigns))) {
1178                return false;
1179            }
1180        }
1181        return true;
1182    }
1183
1184    /**
1185     * 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
1186     * method returns the result of {@link ClassUtils#isAssignable(Class, Class)}.
1187     *
1188     * @param type   the subject type to be assigned to the target type
1189     * @param toType the target type
1190     * @return {@code true} if {@code type} is assignable to {@code toType}.
1191     */
1192    public static boolean isAssignable(final Type type, final Type toType) {
1193        return isAssignable(type, toType, null);
1194    }
1195
1196    /**
1197     * Tests if the subject type may be implicitly cast to the target type following the Java generics rules.
1198     *
1199     * @param type           the subject type to be assigned to the target type
1200     * @param toType         the target type
1201     * @param typeVarAssigns optional map of type variable assignments
1202     * @return {@code true} if {@code type} is assignable to {@code toType}.
1203     */
1204    private static boolean isAssignable(final Type type, final Type toType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1205        if (toType == null || toType instanceof Class<?>) {
1206            return isAssignable(type, (Class<?>) toType);
1207        }
1208
1209        if (toType instanceof ParameterizedType) {
1210            return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
1211        }
1212
1213        if (toType instanceof GenericArrayType) {
1214            return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
1215        }
1216
1217        if (toType instanceof WildcardType) {
1218            return isAssignable(type, (WildcardType) toType, typeVarAssigns);
1219        }
1220
1221        if (toType instanceof TypeVariable<?>) {
1222            return isAssignable(type, (TypeVariable<?>) toType, typeVarAssigns);
1223        }
1224
1225        throw new IllegalStateException("found an unhandled type: " + toType);
1226    }
1227
1228    /**
1229     * Tests if the subject type may be implicitly cast to the target type variable following the Java generics rules.
1230     *
1231     * @param type           the subject type to be assigned to the target type
1232     * @param toTypeVariable the target type variable
1233     * @param typeVarAssigns a map with type variables
1234     * @return {@code true} if {@code type} is assignable to {@code toTypeVariable}.
1235     */
1236    private static boolean isAssignable(final Type type, final TypeVariable<?> toTypeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1237        if (type == null) {
1238            return true;
1239        }
1240
1241        // only a null type can be assigned to null type which
1242        // would have cause the previous to return true
1243        if (toTypeVariable == null) {
1244            return false;
1245        }
1246
1247        // all types are assignable to themselves
1248        if (toTypeVariable.equals(type)) {
1249            return true;
1250        }
1251
1252        if (type instanceof TypeVariable<?>) {
1253            // a type variable is assignable to another type variable, if
1254            // and only if the former is the latter, extends the latter, or
1255            // is otherwise a descendant of the latter.
1256            final Type[] bounds = getImplicitBounds((TypeVariable<?>) type);
1257
1258            for (final Type bound : bounds) {
1259                if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
1260                    return true;
1261                }
1262            }
1263        }
1264
1265        if (type instanceof Class<?> || type instanceof ParameterizedType || type instanceof GenericArrayType || type instanceof WildcardType) {
1266            return false;
1267        }
1268
1269        throw new IllegalStateException("found an unhandled type: " + type);
1270    }
1271
1272    /**
1273     * Tests if the subject type may be implicitly cast to the target wildcard type following the Java generics rules.
1274     *
1275     * @param type           the subject type to be assigned to the target type
1276     * @param toWildcardType the target wildcard type
1277     * @param typeVarAssigns a map with type variables
1278     * @return {@code true} if {@code type} is assignable to {@code toWildcardType}.
1279     */
1280    private static boolean isAssignable(final Type type, final WildcardType toWildcardType, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1281        if (type == null) {
1282            return true;
1283        }
1284
1285        // only a null type can be assigned to null type which
1286        // would have cause the previous to return true
1287        if (toWildcardType == null) {
1288            return false;
1289        }
1290
1291        // all types are assignable to themselves
1292        if (toWildcardType.equals(type)) {
1293            return true;
1294        }
1295
1296        final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
1297        final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
1298
1299        if (type instanceof WildcardType) {
1300            final WildcardType wildcardType = (WildcardType) type;
1301            final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
1302            final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
1303
1304            for (Type toBound : toUpperBounds) {
1305                // if there are assignments for unresolved type variables,
1306                // now's the time to substitute them.
1307                toBound = substituteTypeVariables(toBound, typeVarAssigns);
1308
1309                // each upper bound of the subject type has to be assignable to
1310                // each
1311                // upper bound of the target type
1312                for (final Type bound : upperBounds) {
1313                    if (!isAssignable(bound, toBound, typeVarAssigns)) {
1314                        return false;
1315                    }
1316                }
1317            }
1318
1319            for (Type toBound : toLowerBounds) {
1320                // if there are assignments for unresolved type variables,
1321                // now's the time to substitute them.
1322                toBound = substituteTypeVariables(toBound, typeVarAssigns);
1323
1324                // each lower bound of the target type has to be assignable to
1325                // each
1326                // lower bound of the subject type
1327                for (final Type bound : lowerBounds) {
1328                    if (!isAssignable(toBound, bound, typeVarAssigns)) {
1329                        return false;
1330                    }
1331                }
1332            }
1333            return true;
1334        }
1335
1336        for (final Type toBound : toUpperBounds) {
1337            // if there are assignments for unresolved type variables,
1338            // now's the time to substitute them.
1339            if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns), typeVarAssigns)) {
1340                return false;
1341            }
1342        }
1343
1344        for (final Type toBound : toLowerBounds) {
1345            // if there are assignments for unresolved type variables,
1346            // now's the time to substitute them.
1347            if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type, typeVarAssigns)) {
1348                return false;
1349            }
1350        }
1351        return true;
1352    }
1353
1354    /**
1355     * 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
1356     * which is in scope of A class, then it forms cycle.
1357     *
1358     * @param cls the class to test.
1359     * @return whether the class contains a cyclical reference.
1360     */
1361    private static boolean isCyclical(final Class<?> cls) {
1362        for (final TypeVariable<?> typeParameter : cls.getTypeParameters()) {
1363            for (final Type bound : typeParameter.getBounds()) {
1364                if (bound.getTypeName().contains(cls.getName())) {
1365                    return true;
1366                }
1367            }
1368        }
1369        return false;
1370    }
1371
1372    /**
1373     * Tests if the given value can be assigned to the target type following the Java generics rules.
1374     *
1375     * @param value the value to be checked
1376     * @param type  the target type
1377     * @return {@code true} if {@code value} is an instance of {@code type}.
1378     */
1379    public static boolean isInstance(final Object value, final Type type) {
1380        if (type == null) {
1381            return false;
1382        }
1383
1384        return value == null ? !(type instanceof Class<?>) || !((Class<?>) type).isPrimitive() : isAssignable(value.getClass(), type, null);
1385    }
1386
1387    /**
1388     * Maps type variables.
1389     *
1390     * @param <T>               the generic type of the class in question
1391     * @param cls               the class in question
1392     * @param parameterizedType the parameterized type
1393     * @param typeVarAssigns    the map to be filled
1394     */
1395    private static <T> void mapTypeVariablesToArguments(final Class<T> cls, final ParameterizedType parameterizedType,
1396            final Map<TypeVariable<?>, Type> typeVarAssigns) {
1397        // capture the type variables from the owner type that have assignments
1398        final Type ownerType = parameterizedType.getOwnerType();
1399
1400        if (ownerType instanceof ParameterizedType) {
1401            // recursion to make sure the owner's owner type gets processed
1402            mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns);
1403        }
1404
1405        // parameterizedType is a generic interface/class (or it's in the owner
1406        // hierarchy of said interface/class) implemented/extended by the class
1407        // cls. Find out which type variables of cls are type arguments of
1408        // parameterizedType:
1409        final Type[] typeArgs = parameterizedType.getActualTypeArguments();
1410
1411        // of the cls's type variables that are arguments of parameterizedType,
1412        // find out which ones can be determined from the super type's arguments
1413        final TypeVariable<?>[] typeVars = getRawType(parameterizedType).getTypeParameters();
1414
1415        // use List view of type parameters of cls so the contains() method can be used:
1416        final List<TypeVariable<Class<T>>> typeVarList = Arrays.asList(cls.getTypeParameters());
1417
1418        for (int i = 0; i < typeArgs.length; i++) {
1419            final TypeVariable<?> typeVar = typeVars[i];
1420            final Type typeArg = typeArgs[i];
1421
1422            // argument of parameterizedType is a type variable of cls
1423            if (typeVarList.contains(typeArg)
1424                    // type variable of parameterizedType has an assignment in
1425                    // the super type.
1426                    && typeVarAssigns.containsKey(typeVar)) {
1427                // map the assignment to the cls's type variable
1428                typeVarAssigns.put((TypeVariable<?>) typeArg, typeVarAssigns.get(typeVar));
1429            }
1430        }
1431    }
1432
1433    /**
1434     * 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
1435     * allowed).
1436     *
1437     * <p>
1438     * Example, with the variable type declaration:
1439     * </p>
1440     *
1441     * <pre>{@code
1442     * <K extends java.util.Collection<String> & java.util.List<String>>
1443     * }</pre>
1444     *
1445     * <p>
1446     * since {@link List} is a subinterface of {@link Collection}, this method will return the bounds as if the declaration had been:
1447     * </p>
1448     *
1449     * <pre>{@code
1450     * <K extends java.util.List<String>>
1451     * }</pre>
1452     *
1453     * @param bounds an array of types representing the upper bounds of either {@link WildcardType} or {@link TypeVariable}, not {@code null}.
1454     * @return an array containing the values from {@code bounds} minus the redundant types.
1455     * @throws NullPointerException if {@code bounds} is {@code null}
1456     */
1457    public static Type[] normalizeUpperBounds(final Type[] bounds) {
1458        Objects.requireNonNull(bounds, "bounds");
1459        // don't bother if there's only one (or none) type
1460        if (bounds.length < 2) {
1461            return bounds;
1462        }
1463
1464        final Set<Type> types = new HashSet<>(bounds.length);
1465
1466        for (final Type type1 : bounds) {
1467            boolean subtypeFound = false;
1468
1469            for (final Type type2 : bounds) {
1470                if (type1 != type2 && isAssignable(type2, type1, null)) {
1471                    subtypeFound = true;
1472                    break;
1473                }
1474            }
1475
1476            if (!subtypeFound) {
1477                types.add(type1);
1478            }
1479        }
1480
1481        return types.toArray(ArrayUtils.EMPTY_TYPE_ARRAY);
1482    }
1483
1484    /**
1485     * Creates a parameterized type instance.
1486     *
1487     * @param rawClass        the raw class to create a parameterized type instance for
1488     * @param typeVariableMap the map used for parameterization
1489     * @return {@link ParameterizedType}
1490     * @throws NullPointerException if either {@code rawClass} or {@code typeVariableMap} is {@code null}
1491     * @since 3.2
1492     */
1493    public static final ParameterizedType parameterize(final Class<?> rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
1494        Objects.requireNonNull(rawClass, "rawClass");
1495        Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1496        return parameterizeWithOwner(null, rawClass, extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
1497    }
1498
1499    /**
1500     * Creates a parameterized type instance.
1501     *
1502     * @param rawClass      the raw class to create a parameterized type instance for
1503     * @param typeArguments the types used for parameterization
1504     * @return {@link ParameterizedType}
1505     * @throws NullPointerException if {@code rawClass} is {@code null}
1506     * @since 3.2
1507     */
1508    public static final ParameterizedType parameterize(final Class<?> rawClass, final Type... typeArguments) {
1509        return parameterizeWithOwner(null, rawClass, typeArguments);
1510    }
1511
1512    /**
1513     * Formats a {@link ParameterizedType} as a {@link String}.
1514     *
1515     * @param parameterizedType {@link ParameterizedType} to format
1516     * @return String
1517     */
1518    private static String parameterizedTypeToString(final ParameterizedType parameterizedType) {
1519        final StringBuilder builder = new StringBuilder();
1520        final Type useOwner = parameterizedType.getOwnerType();
1521        final Class<?> raw = (Class<?>) parameterizedType.getRawType();
1522        if (useOwner == null) {
1523            builder.append(raw.getName());
1524        } else {
1525            if (useOwner instanceof Class<?>) {
1526                builder.append(((Class<?>) useOwner).getName());
1527            } else {
1528                builder.append(useOwner);
1529            }
1530            builder.append('.').append(raw.getSimpleName());
1531        }
1532        final int[] recursiveTypeIndexes = findRecursiveTypes(parameterizedType);
1533        if (recursiveTypeIndexes.length > 0) {
1534            appendRecursiveTypes(builder, recursiveTypeIndexes, parameterizedType.getActualTypeArguments());
1535        } else {
1536            GT_JOINER.join(builder, (Object[]) parameterizedType.getActualTypeArguments());
1537        }
1538        return builder.toString();
1539    }
1540
1541    /**
1542     * Creates a parameterized type instance.
1543     *
1544     * @param owner           the owning type
1545     * @param rawClass        the raw class to create a parameterized type instance for
1546     * @param typeVariableMap the map used for parameterization
1547     * @return {@link ParameterizedType}
1548     * @throws NullPointerException if either {@code rawClass} or {@code typeVariableMap} is {@code null}
1549     * @since 3.2
1550     */
1551    public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass, final Map<TypeVariable<?>, Type> typeVariableMap) {
1552        Objects.requireNonNull(rawClass, "rawClass");
1553        Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1554        return parameterizeWithOwner(owner, rawClass, extractTypeArgumentsFrom(typeVariableMap, rawClass.getTypeParameters()));
1555    }
1556
1557    /**
1558     * Creates a parameterized type instance.
1559     *
1560     * @param owner         the owning type
1561     * @param rawClass      the raw class to create a parameterized type instance for
1562     * @param typeArguments the types used for parameterization
1563     * @return {@link ParameterizedType}
1564     * @throws NullPointerException if {@code rawClass} is {@code null}
1565     * @since 3.2
1566     */
1567    public static final ParameterizedType parameterizeWithOwner(final Type owner, final Class<?> rawClass, final Type... typeArguments) {
1568        Objects.requireNonNull(rawClass, "rawClass");
1569        final Type useOwner;
1570        if (rawClass.getEnclosingClass() == null) {
1571            Validate.isTrue(owner == null, "no owner allowed for top-level %s", rawClass);
1572            useOwner = null;
1573        } else if (owner == null) {
1574            useOwner = rawClass.getEnclosingClass();
1575        } else {
1576            Validate.isTrue(isAssignable(owner, rawClass.getEnclosingClass()), "%s is invalid owner type for parameterized %s", owner, rawClass);
1577            useOwner = owner;
1578        }
1579        Validate.noNullElements(typeArguments, "null type argument at index %s");
1580        Validate.isTrue(rawClass.getTypeParameters().length == typeArguments.length, "invalid number of type parameters specified: expected %d, got %d",
1581                rawClass.getTypeParameters().length, typeArguments.length);
1582
1583        return new ParameterizedTypeImpl(rawClass, useOwner, typeArguments);
1584    }
1585
1586    /**
1587     * Finds the mapping for {@code type} in {@code typeVarAssigns}.
1588     *
1589     * @param type           the type to be replaced
1590     * @param typeVarAssigns the map with type variables
1591     * @return the replaced type
1592     * @throws IllegalArgumentException if the type cannot be substituted
1593     */
1594    private static Type substituteTypeVariables(final Type type, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1595        if (type instanceof TypeVariable<?> && typeVarAssigns != null) {
1596            final Type replacementType = typeVarAssigns.get(type);
1597
1598            if (replacementType == null) {
1599                throw new IllegalArgumentException("missing assignment type for type variable " + type);
1600            }
1601            return replacementType;
1602        }
1603        return type;
1604    }
1605
1606    /**
1607     * Formats a {@link TypeVariable} including its {@link GenericDeclaration}.
1608     *
1609     * @param typeVariable the type variable to create a String representation for, not {@code null}
1610     * @return String
1611     * @throws NullPointerException if {@code typeVariable} is {@code null}
1612     * @since 3.2
1613     */
1614    public static String toLongString(final TypeVariable<?> typeVariable) {
1615        Objects.requireNonNull(typeVariable, "typeVariable");
1616        final StringBuilder buf = new StringBuilder();
1617        final GenericDeclaration d = typeVariable.getGenericDeclaration();
1618        if (d instanceof Class<?>) {
1619            Class<?> c = (Class<?>) d;
1620            while (true) {
1621                if (c.getEnclosingClass() == null) {
1622                    buf.insert(0, c.getName());
1623                    break;
1624                }
1625                buf.insert(0, c.getSimpleName()).insert(0, '.');
1626                c = c.getEnclosingClass();
1627            }
1628        } else if (d instanceof Type) { // not possible as of now
1629            buf.append(toString((Type) d));
1630        } else {
1631            buf.append(d);
1632        }
1633        return buf.append(':').append(typeVariableToString(typeVariable)).toString();
1634    }
1635
1636    /**
1637     * Formats a given type as a Java-esque String.
1638     *
1639     * @param type the type to create a String representation for, not {@code null}
1640     * @return String
1641     * @throws NullPointerException if {@code type} is {@code null}
1642     * @since 3.2
1643     */
1644    public static String toString(final Type type) {
1645        Objects.requireNonNull(type, "type");
1646        if (type instanceof Class<?>) {
1647            return classToString((Class<?>) type);
1648        }
1649        if (type instanceof ParameterizedType) {
1650            return parameterizedTypeToString((ParameterizedType) type);
1651        }
1652        if (type instanceof WildcardType) {
1653            return wildcardTypeToString((WildcardType) type);
1654        }
1655        if (type instanceof TypeVariable<?>) {
1656            return typeVariableToString((TypeVariable<?>) type);
1657        }
1658        if (type instanceof GenericArrayType) {
1659            return genericArrayTypeToString((GenericArrayType) type);
1660        }
1661        throw new IllegalArgumentException(ObjectUtils.identityToString(type));
1662    }
1663
1664    /**
1665     * Determines whether or not specified types satisfy the bounds of their mapped type variables. When a type parameter extends another (such as
1666     * {@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
1667     * to be specified, the dependencies must be included in {@code typeVarAssigns}.
1668     *
1669     * @param typeVariableMap specifies the potential types to be assigned to the type variables, not {@code null}.
1670     * @return whether or not the types can be assigned to their respective type variables.
1671     * @throws NullPointerException if {@code typeVariableMap} is {@code null}
1672     */
1673    public static boolean typesSatisfyVariables(final Map<TypeVariable<?>, Type> typeVariableMap) {
1674        Objects.requireNonNull(typeVariableMap, "typeVariableMap");
1675        // all types must be assignable to all the bounds of their mapped
1676        // type variable.
1677        for (final Map.Entry<TypeVariable<?>, Type> entry : typeVariableMap.entrySet()) {
1678            final TypeVariable<?> typeVar = entry.getKey();
1679            final Type type = entry.getValue();
1680
1681            for (final Type bound : getImplicitBounds(typeVar)) {
1682                if (!isAssignable(type, substituteTypeVariables(bound, typeVariableMap), typeVariableMap)) {
1683                    return false;
1684                }
1685            }
1686        }
1687        return true;
1688    }
1689
1690    /**
1691     * Formats a {@link TypeVariable} as a {@link String}.
1692     *
1693     * @param typeVariable {@link TypeVariable} to format
1694     * @return String
1695     */
1696    private static String typeVariableToString(final TypeVariable<?> typeVariable) {
1697        final StringBuilder builder = new StringBuilder(typeVariable.getName());
1698        final Type[] bounds = typeVariable.getBounds();
1699        if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(bounds[0]))) {
1700            // https://issues.apache.org/jira/projects/LANG/issues/LANG-1698
1701            // There must be a better way to avoid a stack overflow on Java 17 and up.
1702            // Bounds are different in Java 17 and up where instead of Object you can get an interface like Comparable.
1703            final Type bound = bounds[0];
1704            boolean append = true;
1705            if (bound instanceof ParameterizedType) {
1706                final Type rawType = ((ParameterizedType) bound).getRawType();
1707                if (rawType instanceof Class && ((Class<?>) rawType).isInterface()) {
1708                    // Avoid recursion and stack overflow on Java 17 and up.
1709                    append = false;
1710                }
1711            }
1712            if (append) {
1713                builder.append(" extends ");
1714                AMP_JOINER.join(builder, bounds);
1715            }
1716        }
1717        return builder.toString();
1718    }
1719
1720    /**
1721     * Unrolls variables in a type bounds array.
1722     *
1723     * @param typeArguments assignments {@link Map}
1724     * @param bounds        in which to expand variables
1725     * @return {@code bounds} with any variables reassigned
1726     */
1727    private static Type[] unrollBounds(final Map<TypeVariable<?>, Type> typeArguments, final Type[] bounds) {
1728        Type[] result = bounds;
1729        int i = 0;
1730        for (; i < result.length; i++) {
1731            final Type unrolled = unrollVariables(typeArguments, result[i]);
1732            if (unrolled == null) {
1733                result = ArrayUtils.remove(result, i--);
1734            } else {
1735                result[i] = unrolled;
1736            }
1737        }
1738        return result;
1739    }
1740
1741    /**
1742     * 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.
1743     *
1744     * @param typeVariable   the type variable to look up
1745     * @param typeVarAssigns the map used for the look-up
1746     * @return Type or {@code null} if some variable was not in the map
1747     */
1748    private static Type unrollVariableAssignments(TypeVariable<?> typeVariable, final Map<TypeVariable<?>, Type> typeVarAssigns) {
1749        Type result;
1750        do {
1751            result = typeVarAssigns.get(typeVariable);
1752            if (!(result instanceof TypeVariable<?>) || result.equals(typeVariable)) {
1753                break;
1754            }
1755            typeVariable = (TypeVariable<?>) result;
1756        } while (true);
1757        return result;
1758    }
1759
1760    /**
1761     * Gets a type representing {@code type} with variable assignments "unrolled."
1762     *
1763     * @param typeArguments as from {@link TypeUtils#getTypeArguments(Type, Class)}
1764     * @param type          the type to unroll variable assignments for
1765     * @return Type
1766     * @since 3.2
1767     */
1768    public static Type unrollVariables(Map<TypeVariable<?>, Type> typeArguments, final Type type) {
1769        if (typeArguments == null) {
1770            typeArguments = Collections.emptyMap();
1771        }
1772        if (containsTypeVariables(type)) {
1773            if (type instanceof TypeVariable<?>) {
1774                return unrollVariables(typeArguments, typeArguments.get(type));
1775            }
1776            if (type instanceof ParameterizedType) {
1777                final ParameterizedType p = (ParameterizedType) type;
1778                final Map<TypeVariable<?>, Type> parameterizedTypeArguments;
1779                if (p.getOwnerType() == null) {
1780                    parameterizedTypeArguments = typeArguments;
1781                } else {
1782                    parameterizedTypeArguments = new HashMap<>(typeArguments);
1783                    parameterizedTypeArguments.putAll(getTypeArguments(p));
1784                }
1785                final Type[] args = p.getActualTypeArguments();
1786                for (int i = 0; i < args.length; i++) {
1787                    final Type unrolled = unrollVariables(parameterizedTypeArguments, args[i]);
1788                    if (unrolled != null) {
1789                        args[i] = unrolled;
1790                    }
1791                }
1792                return parameterizeWithOwner(p.getOwnerType(), (Class<?>) p.getRawType(), args);
1793            }
1794            if (type instanceof WildcardType) {
1795                final WildcardType wild = (WildcardType) type;
1796                return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild.getUpperBounds()))
1797                        .withLowerBounds(unrollBounds(typeArguments, wild.getLowerBounds())).build();
1798            }
1799        }
1800        return type;
1801    }
1802
1803    /**
1804     * Gets a {@link WildcardTypeBuilder}.
1805     *
1806     * @return {@link WildcardTypeBuilder}
1807     * @since 3.2
1808     */
1809    public static WildcardTypeBuilder wildcardType() {
1810        return new WildcardTypeBuilder();
1811    }
1812
1813    /**
1814     * Formats a {@link WildcardType} as a {@link String}.
1815     *
1816     * @param wildcardType {@link WildcardType} to format
1817     * @return String
1818     */
1819    private static String wildcardTypeToString(final WildcardType wildcardType) {
1820        final StringBuilder builder = new StringBuilder().append('?');
1821        final Type[] lowerBounds = wildcardType.getLowerBounds();
1822        final Type[] upperBounds = wildcardType.getUpperBounds();
1823        if (lowerBounds.length > 1 || lowerBounds.length == 1 && lowerBounds[0] != null) {
1824            AMP_JOINER.join(builder.append(" super "), lowerBounds);
1825        } else if (upperBounds.length > 1 || upperBounds.length == 1 && !Object.class.equals(upperBounds[0])) {
1826            AMP_JOINER.join(builder.append(" extends "), upperBounds);
1827        }
1828        return builder.toString();
1829    }
1830
1831    /**
1832     * Wraps the specified {@link Class} in a {@link Typed} wrapper.
1833     *
1834     * @param <T>  generic type
1835     * @param type to wrap
1836     * @return {@code Typed<T>}
1837     * @since 3.2
1838     */
1839    public static <T> Typed<T> wrap(final Class<T> type) {
1840        return wrap((Type) type);
1841    }
1842
1843    /**
1844     * Wraps the specified {@link Type} in a {@link Typed} wrapper.
1845     *
1846     * @param <T>  inferred generic type
1847     * @param type to wrap
1848     * @return {@code Typed<T>}
1849     * @since 3.2
1850     */
1851    public static <T> Typed<T> wrap(final Type type) {
1852        return () -> type;
1853    }
1854
1855    /**
1856     * {@link TypeUtils} instances should NOT be constructed in standard programming. Instead, the class should be used as
1857     * {@code TypeUtils.isAssignable(cls, toClass)}.
1858     * <p>
1859     * This constructor is public to permit tools that require a JavaBean instance to operate.
1860     * </p>
1861     *
1862     * @deprecated TODO Make private in 4.0.
1863     */
1864    @Deprecated
1865    public TypeUtils() {
1866        // empty
1867    }
1868
1869}