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;
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 */
037public 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}