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