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 }