/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache license, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the license for the specific language governing permissions and
 * limitations under the license.
 */
package org.apache.logging.log4j.core.util;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

Utility class for working with Java Types and derivatives. This class is adapted heavily from the Spring Framework, specifically the TypeUtils class.
See Also:
Since:2.1
/** * Utility class for working with Java {@link Type}s and derivatives. This class is adapted heavily from the * <a href="http://projects.spring.io/spring-framework/">Spring Framework</a>, specifically the * <a href="http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/TypeUtils.html">TypeUtils</a> * class. * * @see java.lang.reflect.Type * @see java.lang.reflect.GenericArrayType * @see java.lang.reflect.ParameterizedType * @see java.lang.reflect.WildcardType * @see java.lang.Class * @since 2.1 */
public final class TypeUtil { private TypeUtil() { }
Gets all declared fields for the given class (including superclasses).
Params:
  • cls – the class to examine
See Also:
Returns:all declared fields for the given class (including superclasses).
/** * Gets all declared fields for the given class (including superclasses). * * @param cls the class to examine * @return all declared fields for the given class (including superclasses). * @see Class#getDeclaredFields() */
public static List<Field> getAllDeclaredFields(Class<?> cls) { final List<Field> fields = new ArrayList<>(); while (cls != null) { for (final Field field : cls.getDeclaredFields()) { fields.add(field); } cls = cls.getSuperclass(); } return fields; }
Indicates if two Types are assignment compatible.
Params:
  • lhs – the left hand side to check assignability to
  • rhs – the right hand side to check assignability from
See Also:
Returns:true if it is legal to assign a variable of type rhs to a variable of type lhs
/** * Indicates if two {@link Type}s are assignment compatible. * * @param lhs the left hand side to check assignability to * @param rhs the right hand side to check assignability from * @return {@code true} if it is legal to assign a variable of type {@code rhs} to a variable of type {@code lhs} * @see Class#isAssignableFrom(Class) */
public static boolean isAssignable(final Type lhs, final Type rhs) { Objects.requireNonNull(lhs, "No left hand side type provided"); Objects.requireNonNull(rhs, "No right hand side type provided"); if (lhs.equals(rhs)) { return true; } if (Object.class.equals(lhs)) { // everything is assignable to Object return true; } // raw type on left if (lhs instanceof Class<?>) { final Class<?> lhsClass = (Class<?>) lhs; if (rhs instanceof Class<?>) { // no generics involved final Class<?> rhsClass = (Class<?>) rhs; return lhsClass.isAssignableFrom(rhsClass); } if (rhs instanceof ParameterizedType) { // check to see if the parameterized type has the same raw type as the lhs; this is legal final Type rhsRawType = ((ParameterizedType) rhs).getRawType(); if (rhsRawType instanceof Class<?>) { return lhsClass.isAssignableFrom((Class<?>) rhsRawType); } } if (lhsClass.isArray() && rhs instanceof GenericArrayType) { // check for compatible array component types return isAssignable(lhsClass.getComponentType(), ((GenericArrayType) rhs).getGenericComponentType()); } } // parameterized type on left if (lhs instanceof ParameterizedType) { final ParameterizedType lhsType = (ParameterizedType) lhs; if (rhs instanceof Class<?>) { final Type lhsRawType = lhsType.getRawType(); if (lhsRawType instanceof Class<?>) { return ((Class<?>) lhsRawType).isAssignableFrom((Class<?>) rhs); } } else if (rhs instanceof ParameterizedType) { final ParameterizedType rhsType = (ParameterizedType) rhs; return isParameterizedAssignable(lhsType, rhsType); } } // generic array type on left if (lhs instanceof GenericArrayType) { final Type lhsComponentType = ((GenericArrayType) lhs).getGenericComponentType(); if (rhs instanceof Class<?>) { // raw type on right final Class<?> rhsClass = (Class<?>) rhs; if (rhsClass.isArray()) { return isAssignable(lhsComponentType, rhsClass.getComponentType()); } } else if (rhs instanceof GenericArrayType) { return isAssignable(lhsComponentType, ((GenericArrayType) rhs).getGenericComponentType()); } } // wildcard type on left if (lhs instanceof WildcardType) { return isWildcardAssignable((WildcardType) lhs, rhs); } // strange... return false; } private static boolean isParameterizedAssignable(final ParameterizedType lhs, final ParameterizedType rhs) { if (lhs.equals(rhs)) { // that was easy return true; } final Type[] lhsTypeArguments = lhs.getActualTypeArguments(); final Type[] rhsTypeArguments = rhs.getActualTypeArguments(); final int size = lhsTypeArguments.length; if (rhsTypeArguments.length != size) { // clearly incompatible types return false; } for (int i = 0; i < size; i++) { // verify all type arguments are assignable final Type lhsArgument = lhsTypeArguments[i]; final Type rhsArgument = rhsTypeArguments[i]; if (!lhsArgument.equals(rhsArgument) && !(lhsArgument instanceof WildcardType && isWildcardAssignable((WildcardType) lhsArgument, rhsArgument))) { return false; } } return true; } private static boolean isWildcardAssignable(final WildcardType lhs, final Type rhs) { final Type[] lhsUpperBounds = getEffectiveUpperBounds(lhs); final Type[] lhsLowerBounds = getEffectiveLowerBounds(lhs); if (rhs instanceof WildcardType) { // oh boy, this scenario requires checking a lot of assignability! final WildcardType rhsType = (WildcardType) rhs; final Type[] rhsUpperBounds = getEffectiveUpperBounds(rhsType); final Type[] rhsLowerBounds = getEffectiveLowerBounds(rhsType); for (final Type lhsUpperBound : lhsUpperBounds) { for (final Type rhsUpperBound : rhsUpperBounds) { if (!isBoundAssignable(lhsUpperBound, rhsUpperBound)) { return false; } } for (final Type rhsLowerBound : rhsLowerBounds) { if (!isBoundAssignable(lhsUpperBound, rhsLowerBound)) { return false; } } } for (final Type lhsLowerBound : lhsLowerBounds) { for (final Type rhsUpperBound : rhsUpperBounds) { if (!isBoundAssignable(rhsUpperBound, lhsLowerBound)) { return false; } } for (final Type rhsLowerBound : rhsLowerBounds) { if (!isBoundAssignable(rhsLowerBound, lhsLowerBound)) { return false; } } } } else { // phew, far less bounds to check for (final Type lhsUpperBound : lhsUpperBounds) { if (!isBoundAssignable(lhsUpperBound, rhs)) { return false; } } for (final Type lhsLowerBound : lhsLowerBounds) { if (!isBoundAssignable(lhsLowerBound, rhs)) { return false; } } } return true; } private static Type[] getEffectiveUpperBounds(final WildcardType type) { final Type[] upperBounds = type.getUpperBounds(); return upperBounds.length == 0 ? new Type[]{Object.class} : upperBounds; } private static Type[] getEffectiveLowerBounds(final WildcardType type) { final Type[] lowerBounds = type.getLowerBounds(); return lowerBounds.length == 0 ? new Type[]{null} : lowerBounds; } private static boolean isBoundAssignable(final Type lhs, final Type rhs) { return (rhs == null) || ((lhs != null) && isAssignable(lhs, rhs)); } }