1 /*
2  * Copyright 2002-2010 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.util;
18 
19 import java.lang.reflect.GenericArrayType;
20 import java.lang.reflect.ParameterizedType;
21 import java.lang.reflect.Type;
22 import java.lang.reflect.WildcardType;
23 
24 import org.springframework.util.ClassUtils;
25 
26 /**
27  * Utility to work with Java 5 generic type parameters.
28  * Mainly for internal use within the framework.
29  *
30  * @author Ramnivas Laddad
31  * @author Juergen Hoeller
32  * @author Chris Beams
33  * @since 2.0.7
34  */
35 public abstract class TypeUtils {
36 
37 	/**
38 	 * Check if the right-hand side type may be assigned to the left-hand side
39 	 * type following the Java generics rules.
40 	 * @param lhsType the target type
41 	 * @param rhsType the value type that should be assigned to the target type
42 	 * @return true if rhs is assignable to lhs
43 	 */
isAssignable(Type lhsType, Type rhsType)44 	public static boolean isAssignable(Type lhsType, Type rhsType) {
45 		Assert.notNull(lhsType, "Left-hand side type must not be null");
46 		Assert.notNull(rhsType, "Right-hand side type must not be null");
47 
48 		// all types are assignable to themselves and to class Object
49 		if (lhsType.equals(rhsType) || lhsType.equals(Object.class)) {
50 			return true;
51 		}
52 
53 		if (lhsType instanceof Class<?>) {
54 			Class<?> lhsClass = (Class<?>) lhsType;
55 
56 			// just comparing two classes
57 			if (rhsType instanceof Class<?>) {
58 				return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsType);
59 			}
60 
61 			if (rhsType instanceof ParameterizedType) {
62 				Type rhsRaw = ((ParameterizedType) rhsType).getRawType();
63 
64 				// a parameterized type is always assignable to its raw class type
65 				if (rhsRaw instanceof Class<?>) {
66 					return ClassUtils.isAssignable(lhsClass, (Class<?>) rhsRaw);
67 				}
68 			}
69 			else if (lhsClass.isArray() && rhsType instanceof GenericArrayType) {
70 				Type rhsComponent = ((GenericArrayType) rhsType).getGenericComponentType();
71 
72 				return isAssignable(lhsClass.getComponentType(), rhsComponent);
73 			}
74 		}
75 
76 		// parameterized types are only assignable to other parameterized types and class types
77 		if (lhsType instanceof ParameterizedType) {
78 			if (rhsType instanceof Class<?>) {
79 				Type lhsRaw = ((ParameterizedType) lhsType).getRawType();
80 
81 				if (lhsRaw instanceof Class<?>) {
82 					return ClassUtils.isAssignable((Class<?>) lhsRaw, (Class<?>) rhsType);
83 				}
84 			}
85 			else if (rhsType instanceof ParameterizedType) {
86 				return isAssignable((ParameterizedType) lhsType, (ParameterizedType) rhsType);
87 			}
88 		}
89 
90 		if (lhsType instanceof GenericArrayType) {
91 			Type lhsComponent = ((GenericArrayType) lhsType).getGenericComponentType();
92 
93 			if (rhsType instanceof Class<?>) {
94 				Class<?> rhsClass = (Class<?>) rhsType;
95 
96 				if (rhsClass.isArray()) {
97 					return isAssignable(lhsComponent, rhsClass.getComponentType());
98 				}
99 			}
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 
isAssignable(ParameterizedType lhsType, ParameterizedType rhsType)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 
isAssignable(WildcardType lhsType, Type rhsType)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 
isAssignableBound(Type lhsType, Type rhsType)216 	public static boolean isAssignableBound(Type lhsType, Type rhsType) {
217 		if (rhsType == null) {
218 			return true;
219 		}
220 
221 		if (lhsType == null) {
222 			return false;
223 		}
224 		return isAssignable(lhsType, rhsType);
225 	}
226 
227 }
228