    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     *      http://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     */
    017    package org.apache.logging.log4j.core.util;
    018
    019    import java.lang.reflect.GenericArrayType;
    020    import java.lang.reflect.ParameterizedType;
    021    import java.lang.reflect.Type;
    022    import java.lang.reflect.WildcardType;
    023
    024    /**
    025     * Utility class for working with Java {@link Type}s and derivatives. This class is adapted heavily from the
    026     * <a href="http://projects.spring.io/spring-framework/">Spring Framework</a>, specifically the
    027     * <a href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/TypeUtils.html">TypeUtils</a>
    028     * class.
    029     *
    030     * @see java.lang.reflect.Type
    031     * @see java.lang.reflect.GenericArrayType
    032     * @see java.lang.reflect.ParameterizedType
    033     * @see java.lang.reflect.WildcardType
    034     * @see java.lang.Class
    035     * @since 2.1
    036     */
    037    public final class TypeUtil {
    038        private TypeUtil() {
    039        }
    040
    041        /**
    042         * Indicates if two {@link Type}s are assignment compatible.
    043         *
    044         * @param lhs the left hand side to check assignability to
    045         * @param rhs the right hand side to check assignability from
    046         * @return {@code true} if it is legal to assign a variable of type {@code rhs} to a variable of type {@code lhs}
    047         * @see Class#isAssignableFrom(Class)
    048         */
    049        public static boolean isAssignable(final Type lhs, final Type rhs) {
    050            Assert.requireNonNull(lhs, "No left hand side type provided");
    051            Assert.requireNonNull(rhs, "No right hand side type provided");
    052            if (lhs.equals(rhs)) {
    053                return true;
    054            }
    055            if (Object.class.equals(lhs)) {
    056                // everything is assignable to Object
    057                return true;
    058            }
    059            // raw type on left
    060            if (lhs instanceof Class<?>) {
    061                final Class<?> lhsClass = (Class<?>) lhs;
    062                if (rhs instanceof Class<?>) {
    063                    // no generics involved
    064                    final Class<?> rhsClass = (Class<?>) rhs;
    065                    return lhsClass.isAssignableFrom(rhsClass);
    066                }
    067                if (rhs instanceof ParameterizedType) {
    068                    // check to see if the parameterized type has the same raw type as the lhs; this is legal
    069                    final Type rhsRawType = ((ParameterizedType) rhs).getRawType();
    070                    if (rhsRawType instanceof Class<?>) {
    071                        return lhsClass.isAssignableFrom((Class<?>) rhsRawType);
    072                    }
    073                }
    074                if (lhsClass.isArray() && rhs instanceof GenericArrayType) {
    075                    // check for compatible array component types
    076                    return isAssignable(lhsClass.getComponentType(), ((GenericArrayType) rhs).getGenericComponentType());
    077                }
    078            }
    079            // parameterized type on left
    080            if (lhs instanceof ParameterizedType) {
    081                final ParameterizedType lhsType = (ParameterizedType) lhs;
    082                if (rhs instanceof Class<?>) {
    083                    final Type lhsRawType = lhsType.getRawType();
    084                    if (lhsRawType instanceof Class<?>) {
    085                        return ((Class<?>) lhsRawType).isAssignableFrom((Class<?>) rhs);
    086                    }
    087                } else if (rhs instanceof ParameterizedType) {
    088                    final ParameterizedType rhsType = (ParameterizedType) rhs;
    089                    return isParameterizedAssignable(lhsType, rhsType);
    090                }
    091            }
    092            // generic array type on left
    093            if (lhs instanceof GenericArrayType) {
    094                final Type lhsComponentType = ((GenericArrayType) lhs).getGenericComponentType();
    095                if (rhs instanceof Class<?>) {
    096                    // raw type on right
    097                    final Class<?> rhsClass = (Class<?>) rhs;
    098                    if (rhsClass.isArray()) {
    099                        return isAssignable(lhsComponentType, rhsClass.getComponentType());
    100                    }
    101                } else if (rhs instanceof GenericArrayType) {
    102                    return isAssignable(lhsComponentType, ((GenericArrayType) rhs).getGenericComponentType());
    103                }
    104            }
    105            // wildcard type on left
    106            if (lhs instanceof WildcardType) {
    107                return isWildcardAssignable((WildcardType) lhs, rhs);
    108            }
    109            // strange...
    110            return false;
    111        }
    112
    113        private static boolean isParameterizedAssignable(final ParameterizedType lhs, final ParameterizedType rhs) {
    114            if (lhs.equals(rhs)) {
    115                // that was easy
    116                return true;
    117            }
    118            final Type[] lhsTypeArguments = lhs.getActualTypeArguments();
    119            final Type[] rhsTypeArguments = rhs.getActualTypeArguments();
    120            final int size = lhsTypeArguments.length;
    121            if (rhsTypeArguments.length != size) {
    122                // clearly incompatible types
    123                return false;
    124            }
    125            for (int i = 0; i < size; i++) {
    126                // verify all type arguments are assignable
    127                final Type lhsArgument = lhsTypeArguments[i];
    128                final Type rhsArgument = rhsTypeArguments[i];
    129                if (!lhsArgument.equals(rhsArgument) &&
    130                    !(lhsArgument instanceof WildcardType &&
    131                        isWildcardAssignable((WildcardType) lhsArgument, rhsArgument))) {
    132                    return false;
    133                }
    134            }
    135            return true;
    136        }
    137
    138        private static boolean isWildcardAssignable(final WildcardType lhs, final Type rhs) {
    139            final Type[] lhsUpperBounds = getEffectiveUpperBounds(lhs);
    140            final Type[] lhsLowerBounds = getEffectiveLowerBounds(lhs);
    141            if (rhs instanceof WildcardType) {
    142                // oh boy, this scenario requires checking a lot of assignability!
    143                final WildcardType rhsType = (WildcardType) rhs;
    144                final Type[] rhsUpperBounds = getEffectiveUpperBounds(rhsType);
    145                final Type[] rhsLowerBounds = getEffectiveLowerBounds(rhsType);
    146                for (final Type lhsUpperBound : lhsUpperBounds) {
    147                    for (final Type rhsUpperBound : rhsUpperBounds) {
    148                        if (!isBoundAssignable(lhsUpperBound, rhsUpperBound)) {
    149                            return false;
    150                        }
    151                    }
    152                    for (final Type rhsLowerBound : rhsLowerBounds) {
    153                        if (!isBoundAssignable(lhsUpperBound, rhsLowerBound)) {
    154                            return false;
    155                        }
    156                    }
    157                }
    158                for (final Type lhsLowerBound : lhsLowerBounds) {
    159                    for (final Type rhsUpperBound : rhsUpperBounds) {
    160                        if (!isBoundAssignable(rhsUpperBound, lhsLowerBound)) {
    161                            return false;
    162                        }
    163                    }
    164                    for (final Type rhsLowerBound : rhsLowerBounds) {
    165                        if (!isBoundAssignable(rhsLowerBound, lhsLowerBound)) {
    166                            return false;
    167                        }
    168                    }
    169                }
    170            } else {
    171                // phew, far less bounds to check
    172                for (final Type lhsUpperBound : lhsUpperBounds) {
    173                    if (!isBoundAssignable(lhsUpperBound, rhs)) {
    174                        return false;
    175                    }
    176                }
    177                for (final Type lhsLowerBound : lhsLowerBounds) {
    178                    if (!isBoundAssignable(lhsLowerBound, rhs)) {
    179                        return false;
    180                    }
    181                }
    182            }
    183            return true;
    184        }
    185
    186        private static Type[] getEffectiveUpperBounds(final WildcardType type) {
    187            final Type[] upperBounds = type.getUpperBounds();
    188            return upperBounds.length == 0 ? new Type[]{Object.class} : upperBounds;
    189        }
    190
    191        private static Type[] getEffectiveLowerBounds(final WildcardType type) {
    192            final Type[] lowerBounds = type.getLowerBounds();
    193            return lowerBounds.length == 0 ? new Type[]{null} : lowerBounds;
    194        }
    195
    196        private static boolean isBoundAssignable(final Type lhs, final Type rhs) {
    197            return (rhs == null) || ((lhs != null) && isAssignable(lhs, rhs));
    198        }
    199    }