blob: 5c2932ccf0b5617ad0e2bcc99e50297d03120dde [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package com.sun.beans;
26
27import java.lang.reflect.Array;
28import java.lang.reflect.GenericArrayType;
29import java.lang.reflect.ParameterizedType;
30import java.lang.reflect.Type;
31import java.lang.reflect.TypeVariable;
32import java.lang.reflect.WildcardType;
33import java.util.HashMap;
34import java.util.Map;
35
36import sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl;
37import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;
38
39/**
40 * This is utility class to resolve types.
41 *
42 * @since 1.7
43 *
44 * @author Eamonn McManus
45 * @author Sergey Malenkov
46 */
47public final class TypeResolver {
48 /**
49 * Replaces the given {@code type} in an inherited method
50 * with the actual type it has in the given {@code inClass}.
51 *
52 * <p>Although type parameters are not inherited by subclasses in the Java
53 * language, they <em>are</em> effectively inherited when using reflection.
54 * For example, if you declare an interface like this...</p>
55 *
56 * <pre>
57 * public interface StringToIntMap extends Map&lt;String,Integer> {}
58 * </pre>
59 *
60 * <p>...then StringToIntMap.class.getMethods() will show that it has methods
61 * like put(K,V) even though StringToIntMap has no type parameters. The K
62 * and V variables are the ones declared by Map, so
63 * {@link TypeVariable#getGenericDeclaration()} will return Map.class.</p>
64 *
65 * <p>The purpose of this method is to take a Type from a possibly-inherited
66 * method and replace it with the correct Type for the inheriting class.
67 * So given parameters of K and StringToIntMap.class in the above example,
68 * this method will return String.</p>
69 *
70 * @param inClass the base class used to resolve
71 * @param type the type to resolve
72 * @return a resolved type
73 *
74 * @see #getActualType(Class)
75 * @see #resolve(Type,Type)
76 */
77 public static Type resolveInClass(Class<?> inClass, Type type) {
78 return resolve(getActualType(inClass), type);
79 }
80
81 /**
82 * Replaces all {@code types} in the given array
83 * with the actual types they have in the given {@code inClass}.
84 *
85 * @param inClass the base class used to resolve
86 * @param types the array of types to resolve
87 * @return an array of resolved types
88 *
89 * @see #getActualType(Class)
90 * @see #resolve(Type,Type[])
91 */
92 public static Type[] resolveInClass(Class<?> inClass, Type[] types) {
93 return resolve(getActualType(inClass), types);
94 }
95
96 /**
97 * Replaces type variables of the given {@code formal} type
98 * with the types they stand for in the given {@code actual} type.
99 *
100 * <p>A ParameterizedType is a class with type parameters, and the values
101 * of those parameters. For example, Map&lt;K,V> is a generic class, and
102 * a corresponding ParameterizedType might look like
103 * Map&lt;K=String,V=Integer>. Given such a ParameterizedType, this method
104 * will replace K with String, or List&lt;K> with List&ltString;, or
105 * List&lt;? super K> with List&lt;? super String>.</p>
106 *
107 * <p>The {@code actual} argument to this method can also be a Class.
108 * In this case, either it is equivalent to a ParameterizedType with
109 * no parameters (for example, Integer.class), or it is equivalent to
110 * a "raw" ParameterizedType (for example, Map.class). In the latter
111 * case, every type parameter declared or inherited by the class is replaced
112 * by its "erasure". For a type parameter declared as &lt;T>, the erasure
113 * is Object. For a type parameter declared as &lt;T extends Number>,
114 * the erasure is Number.</p>
115 *
116 * <p>Although type parameters are not inherited by subclasses in the Java
117 * language, they <em>are</em> effectively inherited when using reflection.
118 * For example, if you declare an interface like this...</p>
119 *
120 * <pre>
121 * public interface StringToIntMap extends Map&lt;String,Integer> {}
122 * </pre>
123 *
124 * <p>...then StringToIntMap.class.getMethods() will show that it has methods
125 * like put(K,V) even though StringToIntMap has no type parameters. The K
126 * and V variables are the ones declared by Map, so
127 * {@link TypeVariable#getGenericDeclaration()} will return {@link Map Map.class}.</p>
128 *
129 * <p>For this reason, this method replaces inherited type parameters too.
130 * Therefore if this method is called with {@code actual} being
131 * StringToIntMap.class and {@code formal} being the K from Map,
132 * it will return {@link String String.class}.</p>
133 *
134 * <p>In the case where {@code actual} is a "raw" ParameterizedType, the
135 * inherited type parameters will also be replaced by their erasures.
136 * The erasure of a Class is the Class itself, so a "raw" subinterface of
137 * StringToIntMap will still show the K from Map as String.class. But
138 * in a case like this...
139 *
140 * <pre>
141 * public interface StringToIntListMap extends Map&lt;String,List&lt;Integer>> {}
142 * public interface RawStringToIntListMap extends StringToIntListMap {}
143 * </pre>
144 *
145 * <p>...the V inherited from Map will show up as List&lt;Integer> in
146 * StringToIntListMap, but as plain List in RawStringToIntListMap.</p>
147 *
148 * @param actual the type that supplies bindings for type variables
149 * @param formal the type where occurrences of the variables
150 * in {@code actual} will be replaced by the corresponding bound values
151 * @return a resolved type
152 *
153 * @see #TypeResolver(Type)
154 * @see #resolve(Type)
155 */
156 public static Type resolve(Type actual, Type formal) {
157 return new TypeResolver(actual).resolve(formal);
158 }
159
160 /**
161 * Replaces type variables of all formal types in the given array
162 * with the types they stand for in the given {@code actual} type.
163 *
164 * @param actual the type that supplies bindings for type variables
165 * @param formals the array of types to resolve
166 * @return an array of resolved types
167 *
168 * @see #TypeResolver(Type)
169 * @see #resolve(Type[])
170 */
171 public static Type[] resolve(Type actual, Type[] formals) {
172 return new TypeResolver(actual).resolve(formals);
173 }
174
175 /**
176 * Converts the given {@code type} to the corresponding class.
177 * This method implements the concept of type erasure,
178 * that is described in <a href="http://jscstage.sfbay.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.6">section 4.6</a>
179 * of Java Language Specification.
180 *
181 * @param type the array of types to convert
182 * @return a corresponding class
183 */
184 public static Class<?> erase(Type type) {
185 if (type instanceof Class) {
186 return (Class<?>) type;
187 }
188 if (type instanceof ParameterizedType) {
189 ParameterizedType pt = (ParameterizedType) type;
190 return (Class<?>) pt.getRawType();
191 }
192 if (type instanceof TypeVariable) {
193 TypeVariable tv = (TypeVariable)type;
194 Type[] bounds = tv.getBounds();
195 return (0 < bounds.length)
196 ? erase(bounds[0])
197 : Object.class;
198 }
199 if (type instanceof WildcardType) {
200 WildcardType wt = (WildcardType)type;
201 Type[] bounds = wt.getUpperBounds();
202 return (0 < bounds.length)
203 ? erase(bounds[0])
204 : Object.class;
205 }
206 if (type instanceof GenericArrayType) {
207 GenericArrayType gat = (GenericArrayType)type;
208 return Array.newInstance(erase(gat.getGenericComponentType()), 0).getClass();
209 }
210 throw new IllegalArgumentException("Unknown Type kind: " + type.getClass());
211 }
212
213 /**
214 * Converts all {@code types} in the given array
215 * to the corresponding classes.
216 *
217 * @param types the array of types to convert
218 * @return an array of corresponding classes
219 *
220 * @see #erase(Type)
221 */
222 public static Class[] erase(Type[] types) {
223 int length = types.length;
224 Class[] classes = new Class[length];
225 for (int i = 0; i < length; i++) {
226 classes[i] = TypeResolver.erase(types[i]);
227 }
228 return classes;
229 }
230
231
232 private final Map<TypeVariable<?>, Type> map
233 = new HashMap<TypeVariable<?>, Type>();
234
235 /**
236 * Constructs the type resolver for the given actual type.
237 *
238 * @param actual the type that supplies bindings for type variables
239 *
240 * @see #prepare(Type)
241 */
242 private TypeResolver(Type actual) {
243 prepare(actual);
244 }
245
246 /**
247 * Fills the map from type parameters
248 * to types as seen by the given {@code type}.
249 * The method is recursive because the {@code type}
250 * inherits mappings from its parent classes and interfaces.
251 * The {@code type} can be either a {@link Class Class}
252 * or a {@link ParameterizedType ParameterizedType}.
253 * If it is a {@link Class Class}, it is either equivalent
254 * to a {@link ParameterizedType ParameterizedType} with no parameters,
255 * or it represents the erasure of a {@link ParameterizedType ParameterizedType}.
256 *
257 * @param type the next type in the hierarchy
258 */
259 private void prepare(Type type) {
260 Class<?> raw = (Class<?>)((type instanceof Class<?>)
261 ? type
262 : ((ParameterizedType)type).getRawType());
263
264 TypeVariable<?>[] formals = raw.getTypeParameters();
265
266 Type[] actuals = (type instanceof Class<?>)
267 ? formals
268 : ((ParameterizedType)type).getActualTypeArguments();
269
270 assert formals.length == actuals.length;
271 for (int i = 0; i < formals.length; i++) {
272 this.map.put(formals[i], actuals[i]);
273 }
274 Type gSuperclass = raw.getGenericSuperclass();
275 if (gSuperclass != null) {
276 prepare(gSuperclass);
277 }
278 for (Type gInterface : raw.getGenericInterfaces()) {
279 prepare(gInterface);
280 }
281 // If type is the raw version of a parameterized class, we type-erase
282 // all of its type variables, including inherited ones.
283 if (type instanceof Class<?> && formals.length > 0) {
284 for (Map.Entry<TypeVariable<?>, Type> entry : this.map.entrySet()) {
285 entry.setValue(erase(entry.getValue()));
286 }
287 }
288 }
289
290 /**
291 * Replaces the given {@code formal} type
292 * with the type it stand for in this type resolver.
293 *
294 * @param formal the array of types to resolve
295 * @return a resolved type
296 */
297 private Type resolve(Type formal) {
298 if (formal instanceof Class) {
299 return formal;
300 }
301 if (formal instanceof GenericArrayType) {
302 Type comp = ((GenericArrayType)formal).getGenericComponentType();
303 comp = resolve(comp);
304 return (comp instanceof Class)
305 ? Array.newInstance((Class<?>)comp, 0).getClass()
306 : GenericArrayTypeImpl.make(comp);
307 }
308 if (formal instanceof ParameterizedType) {
309 ParameterizedType fpt = (ParameterizedType)formal;
310 Type[] actuals = resolve(fpt.getActualTypeArguments());
311 return ParameterizedTypeImpl.make(
312 (Class<?>)fpt.getRawType(), actuals, fpt.getOwnerType());
313 }
314 if (formal instanceof WildcardType) {
315 WildcardType fwt = (WildcardType)formal;
316 Type[] upper = resolve(fwt.getUpperBounds());
317 Type[] lower = resolve(fwt.getLowerBounds());
318 return new WildcardTypeImpl(upper, lower);
319 }
320 if (!(formal instanceof TypeVariable)) {
321 throw new IllegalArgumentException("Bad Type kind: " + formal.getClass());
322 }
323 Type actual = this.map.get((TypeVariable) formal);
324 if (actual == null || actual.equals(formal)) {
325 return formal;
326 }
327 actual = fixGenericArray(actual);
328 return resolve(actual);
329 // A variable can be bound to another variable that is itself bound
330 // to something. For example, given:
331 // class Super<T> {...}
332 // class Mid<X> extends Super<T> {...}
333 // class Sub extends Mid<String>
334 // the variable T is bound to X, which is in turn bound to String.
335 // So if we have to resolve T, we need the tail recursion here.
336 }
337
338 /**
339 * Replaces all formal types in the given array
340 * with the types they stand for in this type resolver.
341 *
342 * @param formals the array of types to resolve
343 * @return an array of resolved types
344 *
345 * @see #resolve(Type)
346 */
347 private Type[] resolve(Type[] formals) {
348 int length = formals.length;
349 Type[] actuals = new Type[length];
350 for (int i = 0; i < length; i++) {
351 actuals[i] = resolve(formals[i]);
352 }
353 return actuals;
354 }
355
356 /**
357 * Replaces a {@link GenericArrayType GenericArrayType}
358 * with plain array class where it is possible.
359 * Bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5041784">5041784</a>
360 * is that arrays of non-generic type sometimes show up
361 * as {@link GenericArrayType GenericArrayType} when using reflection.
362 * For example, a {@code String[]} might show up
363 * as a {@link GenericArrayType GenericArrayType}
364 * where {@link GenericArrayType#getGenericComponentType getGenericComponentType}
365 * is {@code String.class}. This violates the specification,
366 * which says that {@link GenericArrayType GenericArrayType}
367 * is used when the component type is a type variable or parameterized type.
368 * We fit the specification here.
369 *
370 * @param type the type to fix
371 * @return a corresponding type for the generic array type,
372 * or the same type as {@code type}
373 */
374 private static Type fixGenericArray(Type type) {
375 if (type instanceof GenericArrayType) {
376 Type comp = ((GenericArrayType)type).getGenericComponentType();
377 comp = fixGenericArray(comp);
378 if (comp instanceof Class) {
379 return Array.newInstance((Class<?>)comp, 0).getClass();
380 }
381 }
382 return type;
383 }
384
385 /**
386 * Replaces a {@link Class Class} with type parameters
387 * with a {@link ParameterizedType ParameterizedType}
388 * where every parameter is bound to itself.
389 * When calling {@link #resolveInClass} in the context of {@code inClass},
390 * we can't just pass {@code inClass} as the {@code actual} parameter,
391 * because if {@code inClass} has type parameters
392 * that would be interpreted as accessing the raw type,
393 * so we would get unwanted erasure.
394 * This is why we bind each parameter to itself.
395 * If {@code inClass} does have type parameters and has methods
396 * where those parameters appear in the return type or argument types,
397 * we will correctly leave those types alone.
398 *
399 * @param inClass the base class used to resolve
400 * @return a parameterized type for the class,
401 * or the same class as {@code inClass}
402 */
403 private static Type getActualType(Class<?> inClass) {
404 Type[] params = inClass.getTypeParameters();
405 return (params.length == 0)
406 ? inClass
407 : ParameterizedTypeImpl.make(
408 inClass, params, inClass.getEnclosingClass());
409 }
410}