001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.util; 018 019import java.lang.reflect.GenericArrayType; 020import java.lang.reflect.ParameterizedType; 021import java.lang.reflect.Type; 022import java.lang.reflect.WildcardType; 023 024import org.springframework.lang.Nullable; 025 026/** 027 * Utility to work with Java 5 generic type parameters. 028 * Mainly for internal use within the framework. 029 * 030 * @author Ramnivas Laddad 031 * @author Juergen Hoeller 032 * @author Chris Beams 033 * @since 2.0.7 034 */ 035public abstract class TypeUtils { 036 037 /** 038 * Check if the right-hand side type may be assigned to the left-hand side 039 * type following the Java generics rules. 040 * @param lhsType the target type 041 * @param rhsType the value type that should be assigned to the target type 042 * @return true if rhs is assignable to lhs 043 */ 044 public static boolean isAssignable(Type lhsType, Type rhsType) { 045 Assert.notNull(lhsType, "Left-hand side type must not be null"); 046 Assert.notNull(rhsType, "Right-hand side type must not be null"); 047 048 // all types are assignable to themselves and to class Object 049 if (lhsType.equals(rhsType) || Object.class == lhsType) { 050 return true; 051 } 052 053 if (lhsType instanceof Class) { 054 Class<?> lhsClass = (Class<?>) lhsType; 055 056 // just comparing two classes 057 if (rhsType instanceof Class) { 058 return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsType); 059 } 060 061 if (rhsType instanceof ParameterizedType) { 062 Type rhsRaw = ((ParameterizedType) rhsType).getRawType(); 063 064 // a parameterized type is always assignable to its raw class type 065 if (rhsRaw instanceof Class) { 066 return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsRaw); 067 } 068 } 069 else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) { 070 Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType(); 071 072 return isAssignable(lhsClass.getComponentType(), rhsComponent); 073 } 074 } 075 076 // parameterized types are only assignable to other parameterized types and class types 077 if (lhsType instanceof ParameterizedType) { 078 if (rhsType instanceof Class) { 079 Type lhsRaw = ((ParameterizedType) lhsType).getRawType(); 080 081 if (lhsRaw instanceof Class) { 082 return ClassUtils.isAssignable((Class<?>) lhsRaw, (Class<?>) rhsType); 083 } 084 } 085 else if (rhsType instanceof ParameterizedType) { 086 return isAssignable((ParameterizedType) lhsType, (ParameterizedType) rhsType); 087 } 088 } 089 090 if (lhsType instanceof GenericArrayType) { 091 Type lhsComponent = ((GenericArrayType) lhsType).getGenericComponentType(); 092 093 if (rhsType instanceof Class) { 094 Class<?> rhsClass = (Class<?>) rhsType; 095 096 if (rhsClass.isArray()) { 097 return isAssignable(lhsComponent, rhsClass.getComponentType()); 098 } 099 } 100 else if (rhsType instanceof GenericArrayType) { 101 Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType(); 102 103 return isAssignable(lhsComponent, rhsComponent); 104 } 105 } 106 107 if (lhsType instanceof WildcardType) { 108 return isAssignable((WildcardType) lhsType, rhsType); 109 } 110 111 return false; 112 } 113 114 private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) { 115 if (lhsType.equals(rhsType)) { 116 return true; 117 } 118 119 Type[] lhsTypeArguments = lhsType.getActualTypeArguments(); 120 Type[] rhsTypeArguments = rhsType.getActualTypeArguments(); 121 122 if (lhsTypeArguments.length != rhsTypeArguments.length) { 123 return false; 124 } 125 126 for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) { 127 Type lhsArg = lhsTypeArguments[i]; 128 Type rhsArg = rhsTypeArguments[i]; 129 130 if (!lhsArg.equals(rhsArg) && 131 !(lhsArg instanceof WildcardType && isAssignable((WildcardType) lhsArg, rhsArg))) { 132 return false; 133 } 134 } 135 136 return true; 137 } 138 139 private static boolean isAssignable(WildcardType lhsType, Type rhsType) { 140 Type[] lUpperBounds = lhsType.getUpperBounds(); 141 142 // supply the implicit upper bound if none are specified 143 if (lUpperBounds.length == 0) { 144 lUpperBounds = new Type[] { Object.class }; 145 } 146 147 Type[] lLowerBounds = lhsType.getLowerBounds(); 148 149 // supply the implicit lower bound if none are specified 150 if (lLowerBounds.length == 0) { 151 lLowerBounds = new Type[] { null }; 152 } 153 154 if (rhsType instanceof WildcardType) { 155 // both the upper and lower bounds of the right-hand side must be 156 // completely enclosed in the upper and lower bounds of the left- 157 // hand side. 158 WildcardType rhsWcType = (WildcardType) rhsType; 159 Type[] rUpperBounds = rhsWcType.getUpperBounds(); 160 161 if (rUpperBounds.length == 0) { 162 rUpperBounds = new Type[] { Object.class }; 163 } 164 165 Type[] rLowerBounds = rhsWcType.getLowerBounds(); 166 167 if (rLowerBounds.length == 0) { 168 rLowerBounds = new Type[] { null }; 169 } 170 171 for (Type lBound : lUpperBounds) { 172 for (Type rBound : rUpperBounds) { 173 if (!isAssignableBound(lBound, rBound)) { 174 return false; 175 } 176 } 177 178 for (Type rBound : rLowerBounds) { 179 if (!isAssignableBound(lBound, rBound)) { 180 return false; 181 } 182 } 183 } 184 185 for (Type lBound : lLowerBounds) { 186 for (Type rBound : rUpperBounds) { 187 if (!isAssignableBound(rBound, lBound)) { 188 return false; 189 } 190 } 191 192 for (Type rBound : rLowerBounds) { 193 if (!isAssignableBound(rBound, lBound)) { 194 return false; 195 } 196 } 197 } 198 } 199 else { 200 for (Type lBound : lUpperBounds) { 201 if (!isAssignableBound(lBound, rhsType)) { 202 return false; 203 } 204 } 205 206 for (Type lBound : lLowerBounds) { 207 if (!isAssignableBound(rhsType, lBound)) { 208 return false; 209 } 210 } 211 } 212 213 return true; 214 } 215 216 public static boolean isAssignableBound(@Nullable Type lhsType, @Nullable Type rhsType) { 217 if (rhsType == null) { 218 return true; 219 } 220 if (lhsType == null) { 221 return false; 222 } 223 return isAssignable(lhsType, rhsType); 224 } 225 226}