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