001 // Copyright 2008, 2010 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.ioc.internal.util;
016
017 import java.lang.reflect.*;
018 import java.util.LinkedList;
019
020 /**
021 * Static methods related to the use of JDK 1.5 generics.
022 */
023 @SuppressWarnings("unchecked")
024 public class GenericsUtils
025 {
026 /**
027 * Analyzes the method in the context of containingClass and returns the Class that is represented by
028 * the method's generic return type. Any parameter information in the generic return type is lost. If you want
029 * to preserve the type parameters of the return type consider using
030 * {@link #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)}.
031 *
032 * @param containingClass class which either contains or inherited the method
033 * @param method method from which to extract the return type
034 * @return the class represented by the methods generic return type, resolved based on the context .
035 * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Method)
036 * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
037 * @see #asClass(java.lang.reflect.Type)
038 */
039 public static Class<?> extractGenericReturnType(Class<?> containingClass, Method method)
040 {
041 return asClass(resolve(method.getGenericReturnType(), containingClass));
042 }
043
044
045 /**
046 * Analyzes the field in the context of containingClass and returns the Class that is represented by
047 * the field's generic type. Any parameter information in the generic type is lost, if you want
048 * to preserve the type parameters of the return type consider using
049 * {@link #getTypeVariableIndex(java.lang.reflect.TypeVariable)}.
050 *
051 * @param containingClass class which either contains or inherited the field
052 * @param field field from which to extract the type
053 * @return the class represented by the field's generic type, resolved based on the containingClass.
054 * @see #extractActualType(java.lang.reflect.Type, java.lang.reflect.Field)
055 * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
056 * @see #asClass(java.lang.reflect.Type)
057 */
058 public static Class extractGenericFieldType(Class containingClass, Field field)
059 {
060 return asClass(resolve(field.getGenericType(), containingClass));
061 }
062
063 /**
064 * Analyzes the method in the context of containingClass and returns the Class that is represented by
065 * the method's generic return type. Any parameter information in the generic return type is lost.
066 *
067 * @param containingType Type which is/represents the class that either contains or inherited the method
068 * @param method method from which to extract the generic return type
069 * @return the generic type represented by the methods generic return type, resolved based on the containingType.
070 * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
071 */
072 public static Type extractActualType(Type containingType, Method method)
073 {
074 return resolve(method.getGenericReturnType(), containingType);
075 }
076
077 /**
078 * Analyzes the method in the context of containingClass and returns the Class that is represented by
079 * the method's generic return type. Any parameter information in the generic return type is lost.
080 *
081 * @param containingType Type which is/represents the class that either contains or inherited the field
082 * @param field field from which to extract the generic return type
083 * @return the generic type represented by the methods generic return type, resolved based on the containingType.
084 * @see #resolve(java.lang.reflect.Type,java.lang.reflect.Type)
085 */
086 public static Type extractActualType(Type containingType, Field field)
087 {
088 return resolve(field.getGenericType(), containingType);
089 }
090
091 /**
092 * Resolves the type parameter based on the context of the containingType.
093 * <p/>
094 * {@link java.lang.reflect.TypeVariable} will be unwrapped to the type argument resolved form the class
095 * hierarchy. This may be something other than a simple Class if the type argument is a ParameterizedType for
096 * instance (e.g. List<E>; List<Map<Long, String>>, E would be returned as a ParameterizedType with the raw
097 * type Map and type arguments Long and String.
098 * <p/>
099 *
100 * @param type
101 * the generic type (ParameterizedType, GenericArrayType, WildcardType, TypeVariable) to be resolved
102 * @param containingType
103 * the type which his
104 * @return
105 * the type resolved to the best of our ability.
106 * @since 5.2.?
107 */
108 public static Type resolve(final Type type, final Type containingType)
109 {
110 // The type isn't generic. (String, Long, etc)
111 if (type instanceof Class)
112 return type;
113
114 // List<T>, List<String>, List<T extends Number>
115 if (type instanceof ParameterizedType)
116 return resolve((ParameterizedType) type, containingType);
117
118 // T[], List<String>[], List<T>[]
119 if (type instanceof GenericArrayType)
120 return resolve((GenericArrayType) type, containingType);
121
122 // List<? extends T>, List<? extends Object & Comparable & Serializable>
123 if (type instanceof WildcardType)
124 return resolve((WildcardType) type, containingType);
125
126 // T
127 if (type instanceof TypeVariable)
128 return resolve((TypeVariable) type, containingType);
129
130 // I'm leaning towards an exception here.
131 return type;
132 }
133
134
135 /**
136 * Determines if the suspected super type is assignable from the suspected sub type.
137 *
138 * @param suspectedSuperType
139 * e.g. GenericDAO<Pet, String>
140 * @param suspectedSubType
141 * e.g. PetDAO extends GenericDAO<Pet,String>
142 * @return
143 * true if (sourceType)targetClass is a valid cast
144 */
145 public static boolean isAssignableFrom(Type suspectedSuperType, Type suspectedSubType)
146 {
147 final Class suspectedSuperClass = asClass(suspectedSuperType);
148 final Class suspectedSubClass = asClass(suspectedSubType);
149
150 // The raw types need to be compatible.
151 if (!suspectedSuperClass.isAssignableFrom(suspectedSubClass))
152 {
153 return false;
154 }
155
156 // From this point we know that the raw types are assignable.
157 // We need to figure out what the generic parameters in the targetClass are
158 // as they pertain to the sourceType.
159
160 if (suspectedSuperType instanceof WildcardType)
161 {
162 // ? extends Number
163 // needs to match all the bounds (there will only be upper bounds or lower bounds
164 for (Type t : ((WildcardType) suspectedSuperType).getUpperBounds())
165 {
166 if (!isAssignableFrom(t, suspectedSubType)) return false;
167 }
168 for (Type t : ((WildcardType) suspectedSuperType).getLowerBounds())
169 {
170 if (!isAssignableFrom(suspectedSubType, t)) return false;
171 }
172 return true;
173 }
174
175 Type curType = suspectedSubType;
176 Class curClass;
177
178 while (curType != null && !curType.equals(Object.class))
179 {
180 curClass = asClass(curType);
181
182 if (curClass.equals(suspectedSuperClass))
183 {
184 final Type resolved = resolve(curType, suspectedSubType);
185
186 if (suspectedSuperType instanceof Class)
187 {
188 if ( resolved instanceof Class )
189 return suspectedSuperType.equals(resolved);
190
191 // They may represent the same class, but the suspectedSuperType is not parameterized. The parameter
192 // types default to Object so they must be a match.
193 // e.g. Pair p = new StringLongPair();
194 // Pair p = new Pair<? extends Number, String>
195
196 return true;
197 }
198
199 if (suspectedSuperType instanceof ParameterizedType)
200 {
201 if (resolved instanceof ParameterizedType)
202 {
203 final Type[] type1Arguments = ((ParameterizedType) suspectedSuperType).getActualTypeArguments();
204 final Type[] type2Arguments = ((ParameterizedType) resolved).getActualTypeArguments();
205 if (type1Arguments.length != type2Arguments.length) return false;
206
207 for (int i = 0; i < type1Arguments.length; ++i)
208 {
209 if (!isAssignableFrom(type1Arguments[i], type2Arguments[i])) return false;
210 }
211 return true;
212 }
213 }
214 else if (suspectedSuperType instanceof GenericArrayType)
215 {
216 if (resolved instanceof GenericArrayType)
217 {
218 return isAssignableFrom(
219 ((GenericArrayType) suspectedSuperType).getGenericComponentType(),
220 ((GenericArrayType) resolved).getGenericComponentType()
221 );
222 }
223 }
224
225 return false;
226 }
227
228 final Type[] types = curClass.getGenericInterfaces();
229 for (Type t : types)
230 {
231 final Type resolved = resolve(t, suspectedSubType);
232 if (isAssignableFrom(suspectedSuperType, resolved))
233 return true;
234 }
235
236 curType = curClass.getGenericSuperclass();
237 }
238 return false;
239 }
240
241 /**
242 * Get the class represented by the reflected type.
243 * This method is lossy; You cannot recover the type information from the class that is returned.
244 * <p/>
245 * {@code TypeVariable} the first bound is returned. If your type variable extends multiple interfaces that information
246 * is lost.
247 * <p/>
248 * {@code WildcardType} the first lower bound is returned. If the wildcard is defined with upper bounds
249 * then {@code Object} is returned.
250 *
251 * @param actualType
252 * a Class, ParameterizedType, GenericArrayType
253 * @return the un-parameterized class associated with the type.
254 */
255 public static Class asClass(Type actualType)
256 {
257 if (actualType instanceof Class) return (Class) actualType;
258
259 if (actualType instanceof ParameterizedType)
260 {
261 final Type rawType = ((ParameterizedType) actualType).getRawType();
262 // The sun implementation returns getRawType as Class<?>, but there is room in the interface for it to be
263 // some other Type. We'll assume it's a Class.
264 // TODO: consider logging or throwing our own exception for that day when "something else" causes some confusion
265 return (Class) rawType;
266 }
267
268 if (actualType instanceof GenericArrayType)
269 {
270 final Type type = ((GenericArrayType) actualType).getGenericComponentType();
271 return Array.newInstance(asClass(type), 0).getClass();
272 }
273
274 if (actualType instanceof TypeVariable)
275 {
276 // Support for List<T extends Number>
277 // There is always at least one bound. If no bound is specified in the source then it will be Object.class
278 return asClass(((TypeVariable) actualType).getBounds()[0]);
279 }
280
281 if (actualType instanceof WildcardType)
282 {
283 final WildcardType wildcardType = (WildcardType) actualType;
284 final Type[] bounds = wildcardType.getLowerBounds();
285 if (bounds != null && bounds.length > 0)
286 {
287 return asClass(bounds[0]);
288 }
289 // If there is no lower bounds then the only thing that makes sense is Object.
290 return Object.class;
291 }
292
293 throw new RuntimeException(String.format("Unable to convert %s to Class.", actualType));
294 }
295
296 /**
297 * Convert the type into a string. The string representation approximates the code that would be used to define the
298 * type.
299 *
300 * @param type - the type.
301 * @return a string representation of the type, similar to how it was declared.
302 */
303 public static String toString(Type type)
304 {
305 if ( type instanceof ParameterizedType ) return toString((ParameterizedType)type);
306 if ( type instanceof WildcardType ) return toString((WildcardType)type);
307 if ( type instanceof GenericArrayType) return toString((GenericArrayType)type);
308 if ( type instanceof Class )
309 {
310 final Class theClass = (Class) type;
311 return (theClass.isArray() ? theClass.getName() + "[]" : theClass.getName());
312 }
313 return type.toString();
314 }
315
316 /**
317 * Method to resolve a TypeVariable to it's most
318 * <a href="http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#112582">reifiable</a> form.
319 * <p/>
320 * <p/>
321 * How to resolve a TypeVariable:<br/>
322 * All of the TypeVariables defined by a generic class will be given a Type by any class that extends it. The Type
323 * given may or may not be reifiable; it may be another TypeVariable for instance.
324 * <p/>
325 * Consider <br/>
326 * <i>class Pair>A,B> { A getA(){...}; ...}</i><br/>
327 * <i>class StringLongPair extends Pair>String, Long> { }</i><br/>
328 * <p/>
329 * To resolve the actual return type of Pair.getA() you must first resolve the TypeVariable "A".
330 * We can do that by first finding the index of "A" in the Pair.class.getTypeParameters() array of TypeVariables.
331 * <p/>
332 * To get to the Type provided by StringLongPair you access the generics information by calling
333 * StringLongPair.class.getGenericSuperclass; this will be a ParameterizedType. ParameterizedType gives you access
334 * to the actual type arguments provided to Pair by StringLongPair. The array is in the same order as the array in
335 * Pair.class.getTypeParameters so you can use the index we discovered earlier to extract the Type; String.class.
336 * <p/>
337 * When extracting Types we only have to consider the superclass hierarchy and not the interfaces implemented by
338 * the class. When a class implements a generic interface it must provide types for the interface and any generic
339 * methods implemented from the interface will be re-defined by the class with it's generic type variables.
340 *
341 * @param typeVariable - the type variable to resolve.
342 * @param containingType - the shallowest class in the class hierarchy (furthest from Object) where typeVariable is defined.
343 * @return a Type that has had all possible TypeVariables resolved that have been defined between the type variable
344 * declaration and the containingType.
345 */
346 private static Type resolve(TypeVariable typeVariable, Type containingType)
347 {
348 // The generic declaration is either a Class, Method or Constructor
349 final GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
350
351 if (!(genericDeclaration instanceof Class))
352 {
353 // It's a method or constructor. The best we can do here is try to resolve the bounds
354 // e.g. <T extends E> T getT(T param){} where E is defined by the class.
355 final Type bounds0 = typeVariable.getBounds()[0];
356 return resolve(bounds0, containingType);
357 }
358
359 final Class typeVariableOwner = (Class) genericDeclaration;
360
361 // find the typeOwner in the containingType's hierarchy
362 final LinkedList<Type> stack = new LinkedList<Type>();
363
364 // If you pass a List<Long> as the containingType then the TypeVariable is going to be resolved by the
365 // containingType and not the super class.
366 if (containingType instanceof ParameterizedType)
367 {
368 stack.add(containingType);
369 }
370
371 Class theClass = asClass(containingType);
372 Type genericSuperclass = theClass.getGenericSuperclass();
373 while (genericSuperclass != null && // true for interfaces with no superclass
374 !theClass.equals(Object.class) &&
375 !theClass.equals(typeVariableOwner))
376 {
377 stack.addFirst(genericSuperclass);
378 theClass = asClass(genericSuperclass);
379 genericSuperclass = theClass.getGenericSuperclass();
380 }
381
382 int i = getTypeVariableIndex(typeVariable);
383 Type resolved = typeVariable;
384 for (Type t : stack)
385 {
386 if (t instanceof ParameterizedType)
387 {
388 resolved = ((ParameterizedType) t).getActualTypeArguments()[i];
389 if (resolved instanceof Class) return resolved;
390 if (resolved instanceof TypeVariable)
391 {
392 // Need to look at the next class in the hierarchy
393 i = getTypeVariableIndex((TypeVariable) resolved);
394 continue;
395 }
396 return resolve(resolved, containingType);
397 }
398 }
399
400 // the only way we get here is if resolved is still a TypeVariable, otherwise an
401 // exception is thrown or a value is returned.
402 return ((TypeVariable) resolved).getBounds()[0];
403 }
404
405 /**
406 * @param type - something like List<T>[] or List<? extends T>[] or T[]
407 * @param containingType - the shallowest type in the hierarchy where type is defined.
408 * @return either the passed type if no changes required or a copy with a best effort resolve of the component type.
409 */
410 private static GenericArrayType resolve(GenericArrayType type, Type containingType)
411 {
412 final Type componentType = type.getGenericComponentType();
413
414 if (!(componentType instanceof Class))
415 {
416 final Type resolved = resolve(componentType, containingType);
417 return create(resolved);
418 }
419
420 return type;
421 }
422
423 /**
424 * @param type - something like List<T>, List<T extends Number>
425 * @param containingType - the shallowest type in the hierarchy where type is defined.
426 * @return the passed type if nothing to resolve or a copy of the type with the type arguments resolved.
427 */
428 private static ParameterizedType resolve(ParameterizedType type, Type containingType)
429 {
430 // Use a copy because we're going to modify it.
431 final Type[] types = type.getActualTypeArguments().clone();
432
433 boolean modified = resolve(types, containingType);
434 return modified ? create(type.getRawType(), type.getOwnerType(), types) : type;
435 }
436
437 /**
438 * @param type - something like List<? super T>, List<<? extends T>, List<? extends T & Comparable<? super T>>
439 * @param containingType - the shallowest type in the hierarchy where type is defined.
440 * @return the passed type if nothing to resolve or a copy of the type with the upper and lower bounds resolved.
441 */
442 private static WildcardType resolve(WildcardType type, Type containingType)
443 {
444 // Use a copy because we're going to modify them.
445 final Type[] upper = type.getUpperBounds().clone();
446 final Type[] lower = type.getLowerBounds().clone();
447
448 boolean modified = resolve(upper, containingType);
449 modified = modified || resolve(lower, containingType);
450
451 return modified ? create(upper, lower) : type;
452 }
453
454 /**
455 * @param types - Array of types to resolve. The unresolved type is replaced in the array with the resolved type.
456 * @param containingType - the shallowest type in the hierarchy where type is defined.
457 * @return true if any of the types were resolved.
458 */
459 private static boolean resolve(Type[] types, Type containingType)
460 {
461 boolean modified = false;
462 for (int i = 0; i < types.length; ++i)
463 {
464 Type t = types[i];
465 if (!(t instanceof Class))
466 {
467 modified = true;
468 final Type resolved = resolve(t, containingType);
469 if (!resolved.equals(t))
470 {
471 types[i] = resolved;
472 modified = true;
473 }
474 }
475 }
476 return modified;
477 }
478
479 /**
480 * @param rawType - the un-parameterized type.
481 * @param ownerType - the outer class or null if the class is not defined within another class.
482 * @param typeArguments - type arguments.
483 * @return a copy of the type with the typeArguments replaced.
484 */
485 static ParameterizedType create(final Type rawType, final Type ownerType, final Type[] typeArguments)
486 {
487 return new ParameterizedType()
488 {
489 public Type[] getActualTypeArguments()
490 {
491 return typeArguments;
492 }
493
494 public Type getRawType()
495 {
496 return rawType;
497 }
498
499 public Type getOwnerType()
500 {
501 return ownerType;
502 }
503
504 @Override
505 public String toString()
506 {
507 return GenericsUtils.toString(this);
508 }
509 };
510 }
511
512 static GenericArrayType create(final Type componentType)
513 {
514 return new GenericArrayType()
515 {
516 public Type getGenericComponentType()
517 {
518 return componentType;
519 }
520
521 @Override
522 public String toString()
523 {
524 return GenericsUtils.toString(this);
525 }
526 };
527 }
528
529 /**
530 * @param upperBounds - e.g. ? extends Number
531 * @param lowerBounds - e.g. ? super Long
532 * @return An new copy of the type with the upper and lower bounds replaced.
533 */
534 static WildcardType create(final Type[] upperBounds, final Type[] lowerBounds)
535 {
536
537 return new WildcardType()
538 {
539 public Type[] getUpperBounds()
540 {
541 return upperBounds;
542 }
543
544 public Type[] getLowerBounds()
545 {
546 return lowerBounds;
547 }
548
549 @Override
550 public String toString()
551 {
552 return GenericsUtils.toString(this);
553 }
554 };
555 }
556
557 static String toString(ParameterizedType pt)
558 {
559 String s = toString(pt.getActualTypeArguments());
560 return String.format("%s<%s>", toString(pt.getRawType()), s);
561 }
562
563 static String toString(GenericArrayType gat)
564 {
565 return String.format("%s[]", toString(gat.getGenericComponentType()));
566 }
567
568 static String toString(WildcardType wt)
569 {
570 final boolean isSuper = wt.getLowerBounds().length > 0;
571 return String.format("? %s %s",
572 isSuper ? "super" : "extends",
573 isSuper ? toString(wt.getLowerBounds()) : toString(wt.getLowerBounds()));
574 }
575
576 static String toString(Type[] types)
577 {
578 StringBuilder sb = new StringBuilder();
579 for ( Type t : types )
580 {
581 sb.append(toString(t)).append(", ");
582 }
583 return sb.substring(0, sb.length() - 2);// drop last ,
584 }
585
586 /**
587 * Find the index of the TypeVariable in the classes parameters. The offset can be used on a subclass to find
588 * the actual type.
589 *
590 * @param typeVariable - the type variable in question.
591 * @return the index of the type variable in it's declaring class/method/constructor's type parameters.
592 */
593 private static int getTypeVariableIndex(final TypeVariable typeVariable)
594 {
595 // the label from the class (the T in List<T>, the K or V in Map<K,V>, etc)
596 final String typeVarName = typeVariable.getName();
597 final TypeVariable[] typeParameters = typeVariable.getGenericDeclaration().getTypeParameters();
598 for (int typeArgumentIndex = 0; typeArgumentIndex < typeParameters.length; typeArgumentIndex++)
599 {
600 // The .equals for TypeVariable may not be compatible, a name check should be sufficient.
601 if (typeParameters[typeArgumentIndex].getName().equals(typeVarName))
602 return typeArgumentIndex;
603 }
604
605 // The only way this could happen is if the TypeVariable is hand built incorrectly, or it's corrupted.
606 throw new RuntimeException(
607 String.format("%s does not have a TypeVariable matching %s", typeVariable.getGenericDeclaration(), typeVariable));
608 }
609 }