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