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