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 }