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}