|
|
|
|
|
|
|
|
|
|
|
|
|
|
 | 19 | 19 | import static com.google.common.base.Preconditions.checkArgument; |
| 20 | 20 | import static com.google.common.base.Preconditions.checkNotNull; |
| 21 | 21 | import static com.google.common.collect.Iterables.transform; |
| 22 | 22 | |
| 23 | 23 | import com.google.common.annotations.VisibleForTesting; |
| 24 | 24 | import com.google.common.base.Function; |
| 25 | 25 | import com.google.common.base.Joiner; |
| 26 | 26 | import com.google.common.base.Objects; |
| 27 | 27 | import com.google.common.base.Predicates; |
| 28 | 28 | import com.google.common.collect.ImmutableList; |
|
| 29 | +import com.google.common.collect.ImmutableMap; |
| 29 | 30 | import com.google.common.collect.Iterables; |
| 30 | 31 | |
| 31 | 32 | import java.io.Serializable; |
| 32 | 33 | import java.lang.reflect.Array; |
| 33 | 34 | import java.lang.reflect.GenericArrayType; |
| 34 | 35 | import java.lang.reflect.GenericDeclaration; |
|
| 36 | +import java.lang.reflect.InvocationHandler; |
|
| 37 | +import java.lang.reflect.InvocationTargetException; |
|
| 38 | +import java.lang.reflect.Method; |
| 35 | 39 | import java.lang.reflect.ParameterizedType; |
|
| 40 | +import java.lang.reflect.Proxy; |
| 36 | 41 | import java.lang.reflect.Type; |
| 37 | 42 | import java.lang.reflect.TypeVariable; |
| 38 | 43 | import java.lang.reflect.WildcardType; |
|
| 44 | +import java.security.AccessControlException; |
| 39 | 45 | import java.util.Arrays; |
| 40 | 46 | import java.util.Collection; |
| 41 | 47 | import java.util.concurrent.atomic.AtomicReference; |
| 42 | 48 | |
| 43 | 49 | import javax.annotation.Nullable; |
| 44 | 50 | |
| 45 | 51 | /** |
| 46 | 52 | * Utilities for working with {@link Type}. |
| 47 | 53 | * |
| 48 | 54 | * @author Ben Yu |
|
|
|
| 139 | 145 | throw new AssertionError(); |
| 140 | 146 | } |
| 141 | 147 | } |
| 142 | 148 | |
| 143 | 149 | /** |
| 144 | 150 | * Returns a new {@link TypeVariable} that belongs to {@code declaration} with |
| 145 | 151 | * {@code name} and {@code bounds}. |
| 146 | 152 | */ |
| 147 | 153 | static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable( |
| 148 | 154 | D declaration, String name, Type... bounds) { |
| 149 |
| - return new TypeVariableImpl<D>( |
|
| 155 | + return newTypeVariableImpl( |
| 150 | 156 | declaration, |
| 151 | 157 | name, |
| 152 | 158 | (bounds.length == 0) |
| 153 | 159 | ? new Type[] { Object.class } |
| 154 | 160 | : bounds); |
| 155 | 161 | } |
| 156 | 162 | |
| 157 | 163 | /** Returns a new {@link WildcardType} with {@code upperBound}. */ |
| 158 | 164 | @VisibleForTesting static WildcardType subtypeOf(Type upperBound) { |
| 159 | 165 | return new WildcardTypeImpl(new Type[0], new Type[] { upperBound }); |
|
|
|
| 307 | 313 | ParameterizedType that = (ParameterizedType) other; |
| 308 | 314 | return getRawType().equals(that.getRawType()) |
| 309 | 315 | && Objects.equal(getOwnerType(), that.getOwnerType()) |
| 310 | 316 | && Arrays.equals( |
| 311 | 317 | getActualTypeArguments(), that.getActualTypeArguments()); |
| 312 | 318 | } |
| 313 | 319 | |
| 314 | 320 | private static final long serialVersionUID = 0; |
| 315 | 321 | } |
| 316 | 322 | |
| 317 |
| - private static final class TypeVariableImpl<D extends GenericDeclaration> |
| 318 |
| - implements TypeVariable<D> { |
|
| 323 | + private static <D extends GenericDeclaration> TypeVariable<D> newTypeVariableImpl( |
|
| 324 | + D genericDeclaration, String name, Type[] bounds) { |
|
| 325 | + TypeVariableImpl<D> typeVariableImpl = |
|
| 326 | + new TypeVariableImpl<D>(genericDeclaration, name, bounds); |
|
| 327 | + @SuppressWarnings("unchecked") |
|
| 328 | + TypeVariable<D> typeVariable = Reflection.newProxy( |
|
| 329 | + TypeVariable.class, new TypeVariableInvocationHandler(typeVariableImpl)); |
|
| 330 | + return typeVariable; |
|
| 331 | + } |
|
| 332 | + |
|
| 333 | + /** |
|
| 334 | + * Invocation handler to work around a compatibility problem between Java 7 and Java 8. |
|
| 335 | + * |
|
| 336 | + * <p>Java 8 introduced a new method {@code getAnnotatedBounds()} in the {@link TypeVariable} |
|
| 337 | + * interface, whose return type {@code AnnotatedType[]} is also new in Java 8. That means that we |
|
| 338 | + * cannot implement that interface in source code in a way that will compile on both Java 7 and |
|
| 339 | + * Java 8. If we include the {@code getAnnotatedBounds()} method then its return type means |
|
| 340 | + * it won't compile on Java 7, while if we don't include the method then the compiler will |
|
| 341 | + * complain that an abstract method is unimplemented. So instead we use a dynamic proxy to |
|
| 342 | + * get an implementation. If the method being called on the {@code TypeVariable} instance has |
|
| 343 | + * the same name as one of the public methods of {@link TypeVariableImpl}, the proxy calls |
|
| 344 | + * the same method on its instance of {@code TypeVariableImpl}. Otherwise it throws {@link |
|
| 345 | + * UnsupportedOperationException}; this should only apply to {@code getAnnotatedBounds()}. This |
|
| 346 | + * does mean that users on Java 8 who obtain an instance of {@code TypeVariable} from {@link |
|
| 347 | + * TypeResolver#resolveType} will not be able to call {@code getAnnotatedBounds()} on it, but that |
|
| 348 | + * should hopefully be rare. |
|
| 349 | + * |
|
| 350 | + * <p>This workaround should be removed at a distant future time when we no longer support Java |
|
| 351 | + * versions earlier than 8. |
|
| 352 | + */ |
|
| 353 | + private static final class TypeVariableInvocationHandler implements InvocationHandler { |
|
| 354 | + private static final ImmutableMap<String, Method> typeVariableMethods; |
|
| 355 | + static { |
|
| 356 | + ImmutableMap.Builder<String, Method> builder = ImmutableMap.builder(); |
|
| 357 | + for (Method method : TypeVariableImpl.class.getMethods()) { |
|
| 358 | + if (method.getDeclaringClass().equals(TypeVariableImpl.class)) { |
|
| 359 | + try { |
|
| 360 | + method.setAccessible(true); |
|
| 361 | + } catch (AccessControlException e) { |
|
| 362 | + // OK: the method is accessible to us anyway. The setAccessible call is only for |
|
| 363 | + // unusual execution environments where that might not be true. |
|
| 364 | + } |
|
| 365 | + builder.put(method.getName(), method); |
|
| 366 | + } |
|
| 367 | + } |
|
| 368 | + typeVariableMethods = builder.build(); |
|
| 369 | + } |
|
| 370 | + |
|
| 371 | + private final TypeVariableImpl<?> typeVariableImpl; |
|
| 372 | + |
|
| 373 | + TypeVariableInvocationHandler(TypeVariableImpl<?> typeVariableImpl) { |
|
| 374 | + this.typeVariableImpl = typeVariableImpl; |
|
| 375 | + } |
|
| 376 | + |
|
| 377 | + @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
|
| 378 | + String methodName = method.getName(); |
|
| 379 | + Method typeVariableMethod = typeVariableMethods.get(methodName); |
|
| 380 | + if (typeVariableMethod == null) { |
|
| 381 | + throw new UnsupportedOperationException(methodName); |
|
| 382 | + } else { |
|
| 383 | + try { |
|
| 384 | + return typeVariableMethod.invoke(typeVariableImpl, args); |
|
| 385 | + } catch (InvocationTargetException e) { |
|
| 386 | + throw e.getCause(); |
|
| 387 | + } |
|
| 388 | + } |
|
| 389 | + } |
|
| 390 | + } |
|
| 391 | + |
|
| 392 | + private static final class TypeVariableImpl<D extends GenericDeclaration> { |
| 319 | 393 | |
| 320 | 394 | private final D genericDeclaration; |
| 321 | 395 | private final String name; |
| 322 | 396 | private final ImmutableList<Type> bounds; |
| 323 | 397 | |
| 324 | 398 | TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) { |
| 325 | 399 | disallowPrimitiveType(bounds, "bound for type variable"); |
| 326 | 400 | this.genericDeclaration = checkNotNull(genericDeclaration); |
| 327 | 401 | this.name = checkNotNull(name); |
| 328 | 402 | this.bounds = ImmutableList.copyOf(bounds); |
| 329 | 403 | } |
| 330 | 404 | |
| 331 |
| - @Override public Type[] getBounds() { |
|
| 405 | + public Type[] getBounds() { |
| 332 | 406 | return toArray(bounds); |
| 333 | 407 | } |
| 334 | 408 | |
| 335 |
| - @Override public D getGenericDeclaration() { |
|
| 409 | + public D getGenericDeclaration() { |
| 336 | 410 | return genericDeclaration; |
| 337 | 411 | } |
| 338 | 412 | |
| 339 |
| - @Override public String getName() { |
|
| 413 | + public String getName() { |
|
| 414 | + return name; |
|
| 415 | + } |
|
| 416 | + |
|
| 417 | + public String getTypeName() { |
| 340 | 418 | return name; |
| 341 | 419 | } |
| 342 | 420 | |
| 343 | 421 | @Override public String toString() { |
| 344 | 422 | return name; |
| 345 | 423 | } |
| 346 | 424 | |
| 347 | 425 | @Override public int hashCode() { |
| 348 | 426 | return genericDeclaration.hashCode() ^ name.hashCode(); |
| 349 | 427 | } |
| 350 | 428 | |
| 351 | 429 | @Override public boolean equals(Object obj) { |
| 352 | 430 | if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { |
| 353 | 431 | // equal only to our TypeVariable implementation with identical bounds |
| 354 |
| - if (obj instanceof TypeVariableImpl) { |
| 355 |
| - TypeVariableImpl<?> that = (TypeVariableImpl<?>) obj; |
|
| 432 | + if (obj != null |
|
| 433 | + && Proxy.isProxyClass(obj.getClass()) |
|
| 434 | + && Proxy.getInvocationHandler(obj) instanceof TypeVariableInvocationHandler) { |
|
| 435 | + TypeVariableInvocationHandler typeVariableInvocationHandler = |
|
| 436 | + (TypeVariableInvocationHandler) Proxy.getInvocationHandler(obj); |
|
| 437 | + TypeVariableImpl<?> that = typeVariableInvocationHandler.typeVariableImpl; |
| 356 | 438 | return name.equals(that.getName()) |
| 357 | 439 | && genericDeclaration.equals(that.getGenericDeclaration()) |
| 358 | 440 | && bounds.equals(that.bounds); |
| 359 | 441 | } |
| 360 | 442 | return false; |
| 361 | 443 | } else { |
| 362 | 444 | // equal to any TypeVariable implementation regardless of bounds |
| 363 | 445 | if (obj instanceof TypeVariable) { |
| 364 | 446 | TypeVariable<?> that = (TypeVariable<?>) obj; |
| 365 | 447 | return name.equals(that.getName()) |