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