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    }