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 024/** 025 * Utility to work with Java 5 generic type parameters. 026 * Mainly for internal use within the framework. 027 * 028 * @author Ramnivas Laddad 029 * @author Juergen Hoeller 030 * @author Chris Beams 031 * @since 2.0.7 032 */ 033public abstract class TypeUtils { 034 035 /** 036 * Check if the right-hand side type may be assigned to the left-hand side 037 * type following the Java generics rules. 038 * @param lhsType the target type 039 * @param rhsType the value type that should be assigned to the target type 040 * @return true if rhs is assignable to lhs 041 */ 042 public static boolean isAssignable(Type lhsType, Type rhsType) { 043 Assert.notNull(lhsType, "Left-hand side type must not be null"); 044 Assert.notNull(rhsType, "Right-hand side type must not be null"); 045 046 // all types are assignable to themselves and to class Object 047 if (lhsType.equals(rhsType) || Object.class == lhsType) { 048 return true; 049 } 050 051 if (lhsType instanceof Class) { 052 Class<?> lhsClass = (Class<?>) lhsType; 053 054 // just comparing two classes 055 if (rhsType instanceof Class) { 056 return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsType); 057 } 058 059 if (rhsType instanceof ParameterizedType) { 060 Type rhsRaw = ((ParameterizedType) rhsType).getRawType(); 061 062 // a parameterized type is always assignable to its raw class type 063 if (rhsRaw instanceof Class) { 064 return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsRaw); 065 } 066 } 067 else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) { 068 Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType(); 069 070 return isAssignable(lhsClass.getComponentType(), rhsComponent); 071 } 072 } 073 074 // parameterized types are only assignable to other parameterized types and class types 075 if (lhsType instanceof ParameterizedType) { 076 if (rhsType instanceof Class) { 077 Type lhsRaw = ((ParameterizedType) lhsType).getRawType(); 078 079 if (lhsRaw instanceof Class) { 080 return ClassUtils.isAssignable((Class<?>) lhsRaw, (Class<?>) rhsType); 081 } 082 } 083 else if (rhsType instanceof ParameterizedType) { 084 return isAssignable((ParameterizedType) lhsType, (ParameterizedType) rhsType); 085 } 086 } 087 088 if (lhsType instanceof GenericArrayType) { 089 Type lhsComponent = ((GenericArrayType) lhsType).getGenericComponentType(); 090 091 if (rhsType instanceof Class) { 092 Class<?> rhsClass = (Class<?>) rhsType; 093 094 if (rhsClass.isArray()) { 095 return isAssignable(lhsComponent, rhsClass.getComponentType()); 096 } 097 } 098 else if (rhsType instanceof GenericArrayType) { 099 Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType(); 100 101 return isAssignable(lhsComponent, rhsComponent); 102 } 103 } 104 105 if (lhsType instanceof WildcardType) { 106 return isAssignable((WildcardType) lhsType, rhsType); 107 } 108 109 return false; 110 } 111 112 private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) { 113 if (lhsType.equals(rhsType)) { 114 return true; 115 } 116 117 Type[] lhsTypeArguments = lhsType.getActualTypeArguments(); 118 Type[] rhsTypeArguments = rhsType.getActualTypeArguments(); 119 120 if (lhsTypeArguments.length != rhsTypeArguments.length) { 121 return false; 122 } 123 124 for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) { 125 Type lhsArg = lhsTypeArguments[i]; 126 Type rhsArg = rhsTypeArguments[i]; 127 128 if (!lhsArg.equals(rhsArg) && 129 !(lhsArg instanceof WildcardType && isAssignable((WildcardType) lhsArg, rhsArg))) { 130 return false; 131 } 132 } 133 134 return true; 135 } 136 137 private static boolean isAssignable(WildcardType lhsType, Type rhsType) { 138 Type[] lUpperBounds = lhsType.getUpperBounds(); 139 140 // supply the implicit upper bound if none are specified 141 if (lUpperBounds.length == 0) { 142 lUpperBounds = new Type[] { Object.class }; 143 } 144 145 Type[] lLowerBounds = lhsType.getLowerBounds(); 146 147 // supply the implicit lower bound if none are specified 148 if (lLowerBounds.length == 0) { 149 lLowerBounds = new Type[] { null }; 150 } 151 152 if (rhsType instanceof WildcardType) { 153 // both the upper and lower bounds of the right-hand side must be 154 // completely enclosed in the upper and lower bounds of the left- 155 // hand side. 156 WildcardType rhsWcType = (WildcardType) rhsType; 157 Type[] rUpperBounds = rhsWcType.getUpperBounds(); 158 159 if (rUpperBounds.length == 0) { 160 rUpperBounds = new Type[] { Object.class }; 161 } 162 163 Type[] rLowerBounds = rhsWcType.getLowerBounds(); 164 165 if (rLowerBounds.length == 0) { 166 rLowerBounds = new Type[] { null }; 167 } 168 169 for (Type lBound : lUpperBounds) { 170 for (Type rBound : rUpperBounds) { 171 if (!isAssignableBound(lBound, rBound)) { 172 return false; 173 } 174 } 175 176 for (Type rBound : rLowerBounds) { 177 if (!isAssignableBound(lBound, rBound)) { 178 return false; 179 } 180 } 181 } 182 183 for (Type lBound : lLowerBounds) { 184 for (Type rBound : rUpperBounds) { 185 if (!isAssignableBound(rBound, lBound)) { 186 return false; 187 } 188 } 189 190 for (Type rBound : rLowerBounds) { 191 if (!isAssignableBound(rBound, lBound)) { 192 return false; 193 } 194 } 195 } 196 } 197 else { 198 for (Type lBound : lUpperBounds) { 199 if (!isAssignableBound(lBound, rhsType)) { 200 return false; 201 } 202 } 203 204 for (Type lBound : lLowerBounds) { 205 if (!isAssignableBound(rhsType, lBound)) { 206 return false; 207 } 208 } 209 } 210 211 return true; 212 } 213 214 public static boolean isAssignableBound(Type lhsType, Type rhsType) { 215 if (rhsType == null) { 216 return true; 217 } 218 if (lhsType == null) { 219 return false; 220 } 221 return isAssignable(lhsType, rhsType); 222 } 223 224}