1 /* 2 * Copyright (c) 2007 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockito.internal.util.reflection; 6 7 8 import org.mockito.Incubating; 9 import org.mockito.exceptions.base.MockitoException; 10 import org.mockito.internal.util.Checks; 11 12 import java.lang.reflect.*; 13 import java.util.*; 14 15 16 /** 17 * This class can retrieve generic meta-data that the compiler stores on classes 18 * and accessible members. 19 * 20 * <p> 21 * The main idea of this code is to create a Map that will help to resolve return types. 22 * In order to actually work with nested generics, this map will have to be passed along new instances 23 * as a type context. 24 * </p> 25 * 26 * <p> 27 * Hence : 28 * <ul> 29 * <li>A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real 30 * <code>Class</code> or from a <code>ParameterizedType</code>, other types are not yet supported.</li> 31 * 32 * <li>Then from this metadata, we can extract meta-data for a generic return type of a method, using 33 * {@link #resolveGenericReturnType(Method)}.</li> 34 * </ul> 35 * </p> 36 * 37 * <p> 38 * For now this code support the following kind of generic declarations : 39 * <pre class="code"><code class="java"> 40 * interface GenericsNest<K extends Comparable<K> & Cloneable> extends Map<K, Set<Number>> { 41 * Set<Number> remove(Object key); // override with fixed ParameterizedType 42 * List<? super Integer> returning_wildcard_with_class_lower_bound(); 43 * List<? super K> returning_wildcard_with_typeVar_lower_bound(); 44 * List<? extends K> returning_wildcard_with_typeVar_upper_bound(); 45 * K returningK(); 46 * <O extends K> List<O> paramType_with_type_params(); 47 * <S, T extends S> T two_type_params(); 48 * <O extends K> O typeVar_with_type_params(); 49 * Number returningNonGeneric(); 50 * } 51 * </code></pre> 52 * 53 * @see #inferFrom(Type) 54 * @see #resolveGenericReturnType(Method) 55 * @see org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs 56 */ 57 @Incubating 58 public abstract class GenericMetadataSupport { 59 60 // public static MockitoLogger logger = new ConsoleMockitoLogger(); 61 62 /** 63 * Represents actual type variables resolved for current class. 64 */ 65 protected Map<TypeVariable, Type> contextualActualTypeParameters = new HashMap<TypeVariable, Type>(); 66 67 68 protected void registerTypeVariablesOn(Type classType) { 69 if (!(classType instanceof ParameterizedType)) { 70 return; 71 } 72 ParameterizedType parameterizedType = (ParameterizedType) classType; 73 TypeVariable[] typeParameters = ((Class<?>) parameterizedType.getRawType()).getTypeParameters(); 74 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); 75 for (int i = 0; i < actualTypeArguments.length; i++) { 76 TypeVariable typeParameter = typeParameters[i]; 77 Type actualTypeArgument = actualTypeArguments[i]; 78 79 if (actualTypeArgument instanceof WildcardType) { 80 contextualActualTypeParameters.put(typeParameter, boundsOf((WildcardType) actualTypeArgument)); 81 } else { 82 contextualActualTypeParameters.put(typeParameter, actualTypeArgument); 83 } 84 // logger.log("For '" + parameterizedType + "' found type variable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualTypeArgument + "(" + System.identityHashCode(typeParameter) + ")" + "' }"); 85 } 86 } 87 88 protected void registerTypeParametersOn(TypeVariable[] typeParameters) { 89 for (TypeVariable typeParameter : typeParameters) { 90 contextualActualTypeParameters.put(typeParameter, boundsOf(typeParameter)); 91 // logger.log("For '" + typeParameter.getGenericDeclaration() + "' found type variable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + boundsOf(typeParameter) + "' }"); 92 } 93 } 94 95 /** 96 * @param typeParameter The TypeVariable parameter 97 * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable 98 * then retrieve BoundedType of this TypeVariable 99 */ 100 private BoundedType boundsOf(TypeVariable typeParameter) { 101 if (typeParameter.getBounds()[0] instanceof TypeVariable) { 102 return boundsOf((TypeVariable) typeParameter.getBounds()[0]); 103 } 104 return new TypeVarBoundedType(typeParameter); 105 } 106 107 /** 108 * @param wildCard The WildCard type 109 * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable 110 * then retrieve BoundedType of this TypeVariable 111 */ 112 private BoundedType boundsOf(WildcardType wildCard) { 113 /* 114 * According to JLS(http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1): 115 * - Lower and upper can't coexist: (for instance, this is not allowed: <? extends List<String> & super MyInterface>) 116 * - Multiple bounds are not supported (for instance, this is not allowed: <? extends List<String> & MyInterface>) 117 */ 118 119 WildCardBoundedType wildCardBoundedType = new WildCardBoundedType(wildCard); 120 if (wildCardBoundedType.firstBound() instanceof TypeVariable) { 121 return boundsOf((TypeVariable) wildCardBoundedType.firstBound()); 122 } 123 124 return wildCardBoundedType; 125 } 126 127 128 129 /** 130 * @return Raw type of the current instance. 131 */ 132 public abstract Class<?> rawType(); 133 134 135 136 /** 137 * @return Returns extra interfaces <strong>if relevant</strong>, otherwise empty List. 138 */ 139 public List<Type> extraInterfaces() { 140 return Collections.emptyList(); 141 } 142 143 /** 144 * @return Returns an array with the raw types of {@link #extraInterfaces()} <strong>if relevant</strong>. 145 */ 146 public Class<?>[] rawExtraInterfaces() { 147 return new Class[0]; 148 } 149 150 151 152 /** 153 * @return Actual type arguments matching the type variables of the raw type represented by this {@link GenericMetadataSupport} instance. 154 */ 155 public Map<TypeVariable, Type> actualTypeArguments() { 156 TypeVariable[] typeParameters = rawType().getTypeParameters(); 157 LinkedHashMap<TypeVariable, Type> actualTypeArguments = new LinkedHashMap<TypeVariable, Type>(); 158 159 for (TypeVariable typeParameter : typeParameters) { 160 161 Type actualType = getActualTypeArgumentFor(typeParameter); 162 163 actualTypeArguments.put(typeParameter, actualType); 164 // logger.log("For '" + rawType().getCanonicalName() + "' returning explicit TypeVariable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualType +"' }"); 165 } 166 167 return actualTypeArguments; 168 } 169 170 protected Type getActualTypeArgumentFor(TypeVariable typeParameter) { 171 Type type = this.contextualActualTypeParameters.get(typeParameter); 172 if (type instanceof TypeVariable) { 173 TypeVariable typeVariable = (TypeVariable) type; 174 return getActualTypeArgumentFor(typeVariable); 175 } 176 177 return type; 178 } 179 180 181 182 /** 183 * Resolve current method generic return type to a {@link GenericMetadataSupport}. 184 * 185 * @param method Method to resolve the return type. 186 * @return {@link GenericMetadataSupport} representing this generic return type. 187 */ 188 public GenericMetadataSupport resolveGenericReturnType(Method method) { 189 Type genericReturnType = method.getGenericReturnType(); 190 // logger.log("Method '" + method.toGenericString() + "' has return type : " + genericReturnType.getClass().getInterfaces()[0].getSimpleName() + " : " + genericReturnType); 191 192 if (genericReturnType instanceof Class) { 193 return new NotGenericReturnTypeSupport(genericReturnType); 194 } 195 if (genericReturnType instanceof ParameterizedType) { 196 return new ParameterizedReturnType(this, method.getTypeParameters(), (ParameterizedType) method.getGenericReturnType()); 197 } 198 if (genericReturnType instanceof TypeVariable) { 199 return new TypeVariableReturnType(this, method.getTypeParameters(), (TypeVariable) genericReturnType); 200 } 201 202 throw new MockitoException("Ouch, it shouldn't happen, type '" + genericReturnType.getClass().getCanonicalName() + "' on method : '" + method.toGenericString() + "' is not supported : " + genericReturnType); 203 } 204 205 /** 206 * Create an new instance of {@link GenericMetadataSupport} inferred from a {@link Type}. 207 * 208 * <p> 209 * At the moment <code>type</code> can only be a {@link Class} or a {@link ParameterizedType}, otherwise 210 * it'll throw a {@link MockitoException}. 211 * </p> 212 * 213 * @param type The class from which the {@link GenericMetadataSupport} should be built. 214 * @return The new {@link GenericMetadataSupport}. 215 * @throws MockitoException Raised if type is not a {@link Class} or a {@link ParameterizedType}. 216 */ 217 public static GenericMetadataSupport inferFrom(Type type) { 218 Checks.checkNotNull(type, "type"); 219 if (type instanceof Class) { 220 return new FromClassGenericMetadataSupport((Class<?>) type); 221 } 222 if (type instanceof ParameterizedType) { 223 return new FromParameterizedTypeGenericMetadataSupport((ParameterizedType) type); 224 } 225 226 throw new MockitoException("Type meta-data for this Type (" + type.getClass().getCanonicalName() + ") is not supported : " + type); 227 } 228 229 230 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 231 //// Below are specializations of GenericMetadataSupport that could handle retrieval of possible Types 232 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 233 234 /** 235 * Generic metadata implementation for {@link Class}. 236 * 237 * Offer support to retrieve generic metadata on a {@link Class} by reading type parameters and type variables on 238 * the class and its ancestors and interfaces. 239 */ 240 private static class FromClassGenericMetadataSupport extends GenericMetadataSupport { 241 private Class<?> clazz; 242 243 public FromClassGenericMetadataSupport(Class<?> clazz) { 244 this.clazz = clazz; 245 readActualTypeParametersOnDeclaringClass(); 246 } 247 248 private void readActualTypeParametersOnDeclaringClass() { 249 registerTypeParametersOn(clazz.getTypeParameters()); 250 registerTypeVariablesOn(clazz.getGenericSuperclass()); 251 for (Type genericInterface : clazz.getGenericInterfaces()) { 252 registerTypeVariablesOn(genericInterface); 253 } 254 } 255 256 @Override 257 public Class<?> rawType() { 258 return clazz; 259 } 260 } 261 262 263 /** 264 * Generic metadata implementation for "standalone" {@link ParameterizedType}. 265 * 266 * Offer support to retrieve generic metadata on a {@link ParameterizedType} by reading type variables of 267 * the related raw type and declared type variable of this parameterized type. 268 * 269 * This class is not designed to work on ParameterizedType returned by {@link Method#getGenericReturnType()}, as 270 * the ParameterizedType instance return in these cases could have Type Variables that refer to type declaration(s). 271 * That's what meant the "standalone" word at the beginning of the Javadoc. 272 * Instead use {@link ParameterizedReturnType}. 273 */ 274 private static class FromParameterizedTypeGenericMetadataSupport extends GenericMetadataSupport { 275 private ParameterizedType parameterizedType; 276 277 public FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType) { 278 this.parameterizedType = parameterizedType; 279 readActualTypeParameters(); 280 } 281 282 private void readActualTypeParameters() { 283 registerTypeVariablesOn(parameterizedType.getRawType()); 284 registerTypeVariablesOn(parameterizedType); 285 } 286 287 @Override 288 public Class<?> rawType() { 289 return (Class<?>) parameterizedType.getRawType(); 290 } 291 } 292 293 294 /** 295 * Generic metadata specific to {@link ParameterizedType} returned via {@link Method#getGenericReturnType()}. 296 */ 297 private static class ParameterizedReturnType extends GenericMetadataSupport { 298 private final ParameterizedType parameterizedType; 299 private final TypeVariable[] typeParameters; 300 301 public ParameterizedReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, ParameterizedType parameterizedType) { 302 this.parameterizedType = parameterizedType; 303 this.typeParameters = typeParameters; 304 this.contextualActualTypeParameters = source.contextualActualTypeParameters; 305 306 readTypeParameters(); 307 readTypeVariables(); 308 } 309 310 private void readTypeParameters() { 311 registerTypeParametersOn(typeParameters); 312 } 313 314 private void readTypeVariables() { 315 registerTypeVariablesOn(parameterizedType); 316 } 317 318 @Override 319 public Class<?> rawType() { 320 return (Class<?>) parameterizedType.getRawType(); 321 } 322 323 } 324 325 326 /** 327 * Generic metadata for {@link TypeVariable} returned via {@link Method#getGenericReturnType()}. 328 */ 329 private static class TypeVariableReturnType extends GenericMetadataSupport { 330 private final TypeVariable typeVariable; 331 private final TypeVariable[] typeParameters; 332 private Class<?> rawType; 333 334 335 336 public TypeVariableReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, TypeVariable typeVariable) { 337 this.typeParameters = typeParameters; 338 this.typeVariable = typeVariable; 339 this.contextualActualTypeParameters = source.contextualActualTypeParameters; 340 341 readTypeParameters(); 342 readTypeVariables(); 343 } 344 345 private void readTypeParameters() { 346 registerTypeParametersOn(typeParameters); 347 } 348 349 private void readTypeVariables() { 350 for (Type type : typeVariable.getBounds()) { 351 registerTypeVariablesOn(type); 352 } 353 registerTypeVariablesOn(getActualTypeArgumentFor(typeVariable)); 354 } 355 356 @Override 357 public Class<?> rawType() { 358 if (rawType == null) { 359 rawType = extractRawTypeOf(typeVariable); 360 } 361 return rawType; 362 } 363 364 private Class<?> extractRawTypeOf(Type type) { 365 if (type instanceof Class) { 366 return (Class<?>) type; 367 } 368 if (type instanceof ParameterizedType) { 369 return (Class<?>) ((ParameterizedType) type).getRawType(); 370 } 371 if (type instanceof BoundedType) { 372 return extractRawTypeOf(((BoundedType) type).firstBound()); 373 } 374 if (type instanceof TypeVariable) { 375 /* 376 * If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared 377 * on the class definition, such as such as List<E>. 378 */ 379 return extractRawTypeOf(contextualActualTypeParameters.get(type)); 380 } 381 throw new MockitoException("Raw extraction not supported for : '" + type + "'"); 382 } 383 384 @Override 385 public List<Type> extraInterfaces() { 386 Type type = extractActualBoundedTypeOf(typeVariable); 387 if (type instanceof BoundedType) { 388 return Arrays.asList(((BoundedType) type).interfaceBounds()); 389 } 390 if (type instanceof ParameterizedType) { 391 return Collections.singletonList(type); 392 } 393 if (type instanceof Class) { 394 return Collections.emptyList(); 395 } 396 throw new MockitoException("Cannot extract extra-interfaces from '" + typeVariable + "' : '" + type + "'"); 397 } 398 399 /** 400 * @return Returns an array with the extracted raw types of {@link #extraInterfaces()}. 401 * @see #extractRawTypeOf(java.lang.reflect.Type) 402 */ 403 public Class<?>[] rawExtraInterfaces() { 404 List<Type> extraInterfaces = extraInterfaces(); 405 List<Class<?>> rawExtraInterfaces = new ArrayList<Class<?>>(); 406 for (Type extraInterface : extraInterfaces) { 407 Class<?> rawInterface = extractRawTypeOf(extraInterface); 408 // avoid interface collision with actual raw type (with typevariables, resolution ca be quite aggressive) 409 if(!rawType().equals(rawInterface)) { 410 rawExtraInterfaces.add(rawInterface); 411 } 412 } 413 return rawExtraInterfaces.toArray(new Class[rawExtraInterfaces.size()]); 414 } 415 416 private Type extractActualBoundedTypeOf(Type type) { 417 if (type instanceof TypeVariable) { 418 /* 419 If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared 420 on the class definition, such as such as List<E>. 421 */ 422 return extractActualBoundedTypeOf(contextualActualTypeParameters.get(type)); 423 } 424 if (type instanceof BoundedType) { 425 Type actualFirstBound = extractActualBoundedTypeOf(((BoundedType) type).firstBound()); 426 if (!(actualFirstBound instanceof BoundedType)) { 427 return type; // avoid going one step further, ie avoid : O(TypeVar) -> K(TypeVar) -> Some ParamType 428 } 429 return actualFirstBound; 430 } 431 return type; // irrelevant, we don't manage other types as they are not bounded. 432 } 433 } 434 435 436 437 /** 438 * Non-Generic metadata for {@link Class} returned via {@link Method#getGenericReturnType()}. 439 */ 440 private static class NotGenericReturnTypeSupport extends GenericMetadataSupport { 441 private final Class<?> returnType; 442 443 public NotGenericReturnTypeSupport(Type genericReturnType) { 444 returnType = (Class<?>) genericReturnType; 445 } 446 447 @Override 448 public Class<?> rawType() { 449 return returnType; 450 } 451 } 452 453 454 455 /** 456 * Type representing bounds of a type 457 * 458 * @see TypeVarBoundedType 459 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a> 460 * @see WildCardBoundedType 461 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1</a> 462 */ 463 public static interface BoundedType extends Type { 464 Type firstBound(); 465 466 Type[] interfaceBounds(); 467 } 468 469 /** 470 * Type representing bounds of a type variable, allows to keep all bounds information. 471 * 472 * <p>It uses the first bound in the array, as this array is never null and always contains at least 473 * one element (Object is always here if no bounds are declared).</p> 474 * 475 * <p>If upper bounds are declared with SomeClass and additional interfaces, then firstBound will be SomeClass and 476 * interfacesBound will be an array of the additional interfaces. 477 * 478 * i.e. <code>SomeClass</code>. 479 * <pre class="code"><code class="java"> 480 * interface UpperBoundedTypeWithClass<E extends Comparable<E> & Cloneable> { 481 * E get(); 482 * } 483 * // will return Comparable type 484 * </code></pre> 485 * </p> 486 * 487 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a> 488 */ 489 public static class TypeVarBoundedType implements BoundedType { 490 private TypeVariable typeVariable; 491 492 493 public TypeVarBoundedType(TypeVariable typeVariable) { 494 this.typeVariable = typeVariable; 495 } 496 497 /** 498 * @return either a class or an interface (parameterized or not), if no bounds declared Object is returned. 499 */ 500 public Type firstBound() { 501 return typeVariable.getBounds()[0]; // 502 } 503 504 /** 505 * On a Type Variable (typeVar extends C_0 & I_1 & I_2 & etc), will return an array 506 * containing I_1 and I_2. 507 * 508 * @return other bounds for this type, these bounds can only be only interfaces as the JLS says, 509 * empty array if no other bound declared. 510 */ 511 public Type[] interfaceBounds() { 512 Type[] interfaceBounds = new Type[typeVariable.getBounds().length - 1]; 513 System.arraycopy(typeVariable.getBounds(), 1, interfaceBounds, 0, typeVariable.getBounds().length - 1); 514 return interfaceBounds; 515 } 516 517 @Override 518 public boolean equals(Object o) { 519 if (this == o) return true; 520 if (o == null || getClass() != o.getClass()) return false; 521 522 return typeVariable.equals(((TypeVarBoundedType) o).typeVariable); 523 524 } 525 526 @Override 527 public int hashCode() { 528 return typeVariable.hashCode(); 529 } 530 531 @Override 532 public String toString() { 533 final StringBuilder sb = new StringBuilder(); 534 sb.append("{firstBound=").append(firstBound()); 535 sb.append(", interfaceBounds=").append(Arrays.deepToString(interfaceBounds())); 536 sb.append('}'); 537 return sb.toString(); 538 } 539 540 public TypeVariable typeVariable() { 541 return typeVariable; 542 } 543 } 544 545 /** 546 * Type representing bounds of a wildcard, allows to keep all bounds information. 547 * 548 * <p>The JLS says that lower bound and upper bound are mutually exclusive, and that multiple bounds 549 * are not allowed. 550 * 551 * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a> 552 */ 553 public static class WildCardBoundedType implements BoundedType { 554 private WildcardType wildcard; 555 556 557 public WildCardBoundedType(WildcardType wildcard) { 558 this.wildcard = wildcard; 559 } 560 561 /** 562 * @return The first bound, either a type or a reference to a TypeVariable 563 */ 564 public Type firstBound() { 565 Type[] lowerBounds = wildcard.getLowerBounds(); 566 Type[] upperBounds = wildcard.getUpperBounds(); 567 568 return lowerBounds.length != 0 ? lowerBounds[0] : upperBounds[0]; 569 } 570 571 /** 572 * @return An empty array as, wildcard don't support multiple bounds. 573 */ 574 public Type[] interfaceBounds() { 575 return new Type[0]; 576 } 577 578 @Override 579 public boolean equals(Object o) { 580 if (this == o) return true; 581 if (o == null || getClass() != o.getClass()) return false; 582 583 return wildcard.equals(((TypeVarBoundedType) o).typeVariable); 584 585 } 586 587 @Override 588 public int hashCode() { 589 return wildcard.hashCode(); 590 } 591 592 @Override 593 public String toString() { 594 final StringBuilder sb = new StringBuilder(); 595 sb.append("{firstBound=").append(firstBound()); 596 sb.append(", interfaceBounds=[]}"); 597 return sb.toString(); 598 } 599 600 public WildcardType wildCard() { 601 return wildcard; 602 } 603 } 604 605 } 606 607 608