1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.reflect; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static com.google.common.collect.Iterables.transform; 22 23 import com.google.common.annotations.VisibleForTesting; 24 import com.google.common.base.Function; 25 import com.google.common.base.Joiner; 26 import com.google.common.base.Objects; 27 import com.google.common.base.Predicates; 28 import com.google.common.collect.ImmutableList; 29 import com.google.common.collect.Iterables; 30 31 import java.io.Serializable; 32 import java.lang.reflect.Array; 33 import java.lang.reflect.GenericArrayType; 34 import java.lang.reflect.GenericDeclaration; 35 import java.lang.reflect.ParameterizedType; 36 import java.lang.reflect.Type; 37 import java.lang.reflect.TypeVariable; 38 import java.lang.reflect.WildcardType; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.concurrent.atomic.AtomicReference; 42 43 import javax.annotation.Nullable; 44 45 /** 46 * Utilities for working with {@link Type}. 47 * 48 * @author Ben Yu 49 */ 50 final class Types { 51 52 /** Class#toString without the "class " and "interface " prefixes */ 53 private static final Function<Type, String> TYPE_TO_STRING = 54 new Function<Type, String>() { 55 @Override public String apply(Type from) { 56 return Types.toString(from); 57 } 58 }; 59 60 private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null"); 61 62 /** Returns the array type of {@code componentType}. */ newArrayType(Type componentType)63 static Type newArrayType(Type componentType) { 64 if (componentType instanceof WildcardType) { 65 WildcardType wildcard = (WildcardType) componentType; 66 Type[] lowerBounds = wildcard.getLowerBounds(); 67 checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds."); 68 if (lowerBounds.length == 1) { 69 return supertypeOf(newArrayType(lowerBounds[0])); 70 } else { 71 Type[] upperBounds = wildcard.getUpperBounds(); 72 checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound."); 73 return subtypeOf(newArrayType(upperBounds[0])); 74 } 75 } 76 return JavaVersion.CURRENT.newArrayType(componentType); 77 } 78 79 /** 80 * Returns a type where {@code rawType} is parameterized by 81 * {@code arguments} and is owned by {@code ownerType}. 82 */ newParameterizedTypeWithOwner( @ullable Type ownerType, Class<?> rawType, Type... arguments)83 static ParameterizedType newParameterizedTypeWithOwner( 84 @Nullable Type ownerType, Class<?> rawType, Type... arguments) { 85 if (ownerType == null) { 86 return newParameterizedType(rawType, arguments); 87 } 88 // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE 89 checkNotNull(arguments); 90 checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType); 91 return new ParameterizedTypeImpl(ownerType, rawType, arguments); 92 } 93 94 /** 95 * Returns a type where {@code rawType} is parameterized by 96 * {@code arguments}. 97 */ newParameterizedType(Class<?> rawType, Type... arguments)98 static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) { 99 return new ParameterizedTypeImpl( 100 ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments); 101 } 102 103 /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */ 104 private enum ClassOwnership { 105 106 OWNED_BY_ENCLOSING_CLASS { 107 @Nullable 108 @Override getOwnerType(Class<?> rawType)109 Class<?> getOwnerType(Class<?> rawType) { 110 return rawType.getEnclosingClass(); 111 } 112 }, 113 LOCAL_CLASS_HAS_NO_OWNER { 114 @Nullable 115 @Override getOwnerType(Class<?> rawType)116 Class<?> getOwnerType(Class<?> rawType) { 117 if (rawType.isLocalClass()) { 118 return null; 119 } else { 120 return rawType.getEnclosingClass(); 121 } 122 } 123 }; 124 getOwnerType(Class<?> rawType)125 @Nullable abstract Class<?> getOwnerType(Class<?> rawType); 126 127 static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior(); 128 detectJvmBehavior()129 private static ClassOwnership detectJvmBehavior() { 130 class LocalClass<T> {} 131 Class<?> subclass = new LocalClass<String>() {}.getClass(); 132 ParameterizedType parameterizedType = (ParameterizedType) 133 subclass.getGenericSuperclass(); 134 for (ClassOwnership behavior : ClassOwnership.values()) { 135 if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) { 136 return behavior; 137 } 138 } 139 throw new AssertionError(); 140 } 141 } 142 143 /** 144 * Returns a new {@link TypeVariable} that belongs to {@code declaration} with 145 * {@code name} and {@code bounds}. 146 */ newArtificialTypeVariable( D declaration, String name, Type... bounds)147 static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable( 148 D declaration, String name, Type... bounds) { 149 return new TypeVariableImpl<D>( 150 declaration, 151 name, 152 (bounds.length == 0) 153 ? new Type[] { Object.class } 154 : bounds); 155 } 156 157 /** Returns a new {@link WildcardType} with {@code upperBound}. */ subtypeOf(Type upperBound)158 @VisibleForTesting static WildcardType subtypeOf(Type upperBound) { 159 return new WildcardTypeImpl(new Type[0], new Type[] { upperBound }); 160 } 161 162 /** Returns a new {@link WildcardType} with {@code lowerBound}. */ supertypeOf(Type lowerBound)163 @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) { 164 return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class }); 165 } 166 167 /** 168 * Returns human readable string representation of {@code type}. 169 * <ul> 170 * <li> For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are 171 * returned. 172 * <li> For any class, {@code theClass.getName()} are returned. 173 * <li> For all other types, {@code type.toString()} are returned. 174 * </ul> 175 */ toString(Type type)176 static String toString(Type type) { 177 return (type instanceof Class) 178 ? ((Class<?>) type).getName() 179 : type.toString(); 180 } 181 getComponentType(Type type)182 @Nullable static Type getComponentType(Type type) { 183 checkNotNull(type); 184 final AtomicReference<Type> result = new AtomicReference<Type>(); 185 new TypeVisitor() { 186 @Override void visitTypeVariable(TypeVariable<?> t) { 187 result.set(subtypeOfComponentType(t.getBounds())); 188 } 189 @Override void visitWildcardType(WildcardType t) { 190 result.set(subtypeOfComponentType(t.getUpperBounds())); 191 } 192 @Override void visitGenericArrayType(GenericArrayType t) { 193 result.set(t.getGenericComponentType()); 194 } 195 @Override void visitClass(Class<?> t) { 196 result.set(t.getComponentType()); 197 } 198 }.visit(type); 199 return result.get(); 200 } 201 202 /** 203 * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null 204 * otherwise. 205 */ subtypeOfComponentType(Type[] bounds)206 @Nullable private static Type subtypeOfComponentType(Type[] bounds) { 207 for (Type bound : bounds) { 208 Type componentType = getComponentType(bound); 209 if (componentType != null) { 210 // Only the first bound can be a class or array. 211 // Bounds after the first can only be interfaces. 212 if (componentType instanceof Class) { 213 Class<?> componentClass = (Class<?>) componentType; 214 if (componentClass.isPrimitive()) { 215 return componentClass; 216 } 217 } 218 return subtypeOf(componentType); 219 } 220 } 221 return null; 222 } 223 224 private static final class GenericArrayTypeImpl 225 implements GenericArrayType, Serializable { 226 227 private final Type componentType; 228 GenericArrayTypeImpl(Type componentType)229 GenericArrayTypeImpl(Type componentType) { 230 this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType); 231 } 232 getGenericComponentType()233 @Override public Type getGenericComponentType() { 234 return componentType; 235 } 236 toString()237 @Override public String toString() { 238 return Types.toString(componentType) + "[]"; 239 } 240 hashCode()241 @Override public int hashCode() { 242 return componentType.hashCode(); 243 } 244 equals(Object obj)245 @Override public boolean equals(Object obj) { 246 if (obj instanceof GenericArrayType) { 247 GenericArrayType that = (GenericArrayType) obj; 248 return Objects.equal( 249 getGenericComponentType(), that.getGenericComponentType()); 250 } 251 return false; 252 } 253 254 private static final long serialVersionUID = 0; 255 } 256 257 private static final class ParameterizedTypeImpl 258 implements ParameterizedType, Serializable { 259 260 private final Type ownerType; 261 private final ImmutableList<Type> argumentsList; 262 private final Class<?> rawType; 263 ParameterizedTypeImpl( @ullable Type ownerType, Class<?> rawType, Type[] typeArguments)264 ParameterizedTypeImpl( 265 @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) { 266 checkNotNull(rawType); 267 checkArgument(typeArguments.length == rawType.getTypeParameters().length); 268 disallowPrimitiveType(typeArguments, "type parameter"); 269 this.ownerType = ownerType; 270 this.rawType = rawType; 271 this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments); 272 } 273 getActualTypeArguments()274 @Override public Type[] getActualTypeArguments() { 275 return toArray(argumentsList); 276 } 277 getRawType()278 @Override public Type getRawType() { 279 return rawType; 280 } 281 getOwnerType()282 @Override public Type getOwnerType() { 283 return ownerType; 284 } 285 toString()286 @Override public String toString() { 287 StringBuilder builder = new StringBuilder(); 288 if (ownerType != null) { 289 builder.append(Types.toString(ownerType)).append('.'); 290 } 291 builder.append(rawType.getName()) 292 .append('<') 293 .append(COMMA_JOINER.join(transform(argumentsList, TYPE_TO_STRING))) 294 .append('>'); 295 return builder.toString(); 296 } 297 hashCode()298 @Override public int hashCode() { 299 return (ownerType == null ? 0 : ownerType.hashCode()) 300 ^ argumentsList.hashCode() ^ rawType.hashCode(); 301 } 302 equals(Object other)303 @Override public boolean equals(Object other) { 304 if (!(other instanceof ParameterizedType)) { 305 return false; 306 } 307 ParameterizedType that = (ParameterizedType) other; 308 return getRawType().equals(that.getRawType()) 309 && Objects.equal(getOwnerType(), that.getOwnerType()) 310 && Arrays.equals( 311 getActualTypeArguments(), that.getActualTypeArguments()); 312 } 313 314 private static final long serialVersionUID = 0; 315 } 316 317 private static final class TypeVariableImpl<D extends GenericDeclaration> 318 implements TypeVariable<D> { 319 320 private final D genericDeclaration; 321 private final String name; 322 private final ImmutableList<Type> bounds; 323 TypeVariableImpl(D genericDeclaration, String name, Type[] bounds)324 TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) { 325 disallowPrimitiveType(bounds, "bound for type variable"); 326 this.genericDeclaration = checkNotNull(genericDeclaration); 327 this.name = checkNotNull(name); 328 this.bounds = ImmutableList.copyOf(bounds); 329 } 330 getBounds()331 @Override public Type[] getBounds() { 332 return toArray(bounds); 333 } 334 getGenericDeclaration()335 @Override public D getGenericDeclaration() { 336 return genericDeclaration; 337 } 338 getName()339 @Override public String getName() { 340 return name; 341 } 342 toString()343 @Override public String toString() { 344 return name; 345 } 346 hashCode()347 @Override public int hashCode() { 348 return genericDeclaration.hashCode() ^ name.hashCode(); 349 } 350 equals(Object obj)351 @Override public boolean equals(Object obj) { 352 if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) { 353 // equal only to our TypeVariable implementation with identical bounds 354 if (obj instanceof TypeVariableImpl) { 355 TypeVariableImpl<?> that = (TypeVariableImpl<?>) obj; 356 return name.equals(that.getName()) 357 && genericDeclaration.equals(that.getGenericDeclaration()) 358 && bounds.equals(that.bounds); 359 } 360 return false; 361 } else { 362 // equal to any TypeVariable implementation regardless of bounds 363 if (obj instanceof TypeVariable) { 364 TypeVariable<?> that = (TypeVariable<?>) obj; 365 return name.equals(that.getName()) 366 && genericDeclaration.equals(that.getGenericDeclaration()); 367 } 368 return false; 369 } 370 } 371 } 372 373 static final class WildcardTypeImpl implements WildcardType, Serializable { 374 375 private final ImmutableList<Type> lowerBounds; 376 private final ImmutableList<Type> upperBounds; 377 WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds)378 WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) { 379 disallowPrimitiveType(lowerBounds, "lower bound for wildcard"); 380 disallowPrimitiveType(upperBounds, "upper bound for wildcard"); 381 this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds); 382 this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds); 383 } 384 getLowerBounds()385 @Override public Type[] getLowerBounds() { 386 return toArray(lowerBounds); 387 } 388 getUpperBounds()389 @Override public Type[] getUpperBounds() { 390 return toArray(upperBounds); 391 } 392 equals(Object obj)393 @Override public boolean equals(Object obj) { 394 if (obj instanceof WildcardType) { 395 WildcardType that = (WildcardType) obj; 396 return lowerBounds.equals(Arrays.asList(that.getLowerBounds())) 397 && upperBounds.equals(Arrays.asList(that.getUpperBounds())); 398 } 399 return false; 400 } 401 hashCode()402 @Override public int hashCode() { 403 return lowerBounds.hashCode() ^ upperBounds.hashCode(); 404 } 405 toString()406 @Override public String toString() { 407 StringBuilder builder = new StringBuilder("?"); 408 for (Type lowerBound : lowerBounds) { 409 builder.append(" super ").append(Types.toString(lowerBound)); 410 } 411 for (Type upperBound : filterUpperBounds(upperBounds)) { 412 builder.append(" extends ").append(Types.toString(upperBound)); 413 } 414 return builder.toString(); 415 } 416 417 private static final long serialVersionUID = 0; 418 } 419 toArray(Collection<Type> types)420 private static Type[] toArray(Collection<Type> types) { 421 return types.toArray(new Type[types.size()]); 422 } 423 filterUpperBounds(Iterable<Type> bounds)424 private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) { 425 return Iterables.filter( 426 bounds, Predicates.not(Predicates.<Type>equalTo(Object.class))); 427 } 428 disallowPrimitiveType(Type[] types, String usedAs)429 private static void disallowPrimitiveType(Type[] types, String usedAs) { 430 for (Type type : types) { 431 if (type instanceof Class) { 432 Class<?> cls = (Class<?>) type; 433 checkArgument(!cls.isPrimitive(), 434 "Primitive type '%s' used as %s", cls, usedAs); 435 } 436 } 437 } 438 439 /** Returns the {@code Class} object of arrays with {@code componentType}. */ getArrayClass(Class<?> componentType)440 static Class<?> getArrayClass(Class<?> componentType) { 441 // TODO(user): This is not the most efficient way to handle generic 442 // arrays, but is there another way to extract the array class in a 443 // non-hacky way (i.e. using String value class names- "[L...")? 444 return Array.newInstance(componentType, 0).getClass(); 445 } 446 447 // TODO(benyu): Once we are on Java 7, delete this abstraction 448 enum JavaVersion { 449 450 JAVA6 { newArrayType(Type componentType)451 @Override GenericArrayType newArrayType(Type componentType) { 452 return new GenericArrayTypeImpl(componentType); 453 } usedInGenericType(Type type)454 @Override Type usedInGenericType(Type type) { 455 checkNotNull(type); 456 if (type instanceof Class) { 457 Class<?> cls = (Class<?>) type; 458 if (cls.isArray()) { 459 return new GenericArrayTypeImpl(cls.getComponentType()); 460 } 461 } 462 return type; 463 } 464 }, 465 JAVA7 { newArrayType(Type componentType)466 @Override Type newArrayType(Type componentType) { 467 if (componentType instanceof Class) { 468 return getArrayClass((Class<?>) componentType); 469 } else { 470 return new GenericArrayTypeImpl(componentType); 471 } 472 } usedInGenericType(Type type)473 @Override Type usedInGenericType(Type type) { 474 return checkNotNull(type); 475 } 476 } 477 ; 478 479 static final JavaVersion CURRENT = 480 (new TypeCapture<int[]>() {}.capture() instanceof Class) 481 ? JAVA7 : JAVA6; newArrayType(Type componentType)482 abstract Type newArrayType(Type componentType); usedInGenericType(Type type)483 abstract Type usedInGenericType(Type type); 484 usedInGenericType(Type[] types)485 final ImmutableList<Type> usedInGenericType(Type[] types) { 486 ImmutableList.Builder<Type> builder = ImmutableList.builder(); 487 for (Type type : types) { 488 builder.add(usedInGenericType(type)); 489 } 490 return builder.build(); 491 } 492 } 493 494 /** 495 * Per https://code.google.com/p/guava-libraries/issues/detail?id=1635, 496 * In JDK 1.7.0_51-b13, TypeVariableImpl.equals() is changed to no longer be equal to custom 497 * TypeVariable implementations. As a result, we need to make sure our TypeVariable implementation 498 * respects symmetry. 499 * Moreover, we don't want to reconstruct a native type variable <A> using our implementation 500 * unless some of its bounds have changed in resolution. This avoids creating unequal TypeVariable 501 * implementation unnecessarily. When the bounds do change, however, it's fine for the synthetic 502 * TypeVariable to be unequal to any native TypeVariable anyway. 503 */ 504 static final class NativeTypeVariableEquals<X> { 505 static final boolean NATIVE_TYPE_VARIABLE_ONLY = 506 !NativeTypeVariableEquals.class.getTypeParameters()[0].equals( 507 newArtificialTypeVariable(NativeTypeVariableEquals.class, "X")); 508 } 509 Types()510 private Types() {} 511 } 512