1 /** 2 * Copyright (C) 2008 Google Inc. 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 18 package com.google.inject.internal; 19 20 import static com.google.common.base.Preconditions.checkArgument; 21 import static com.google.common.base.Preconditions.checkNotNull; 22 23 import com.google.common.base.Objects; 24 import com.google.common.collect.ImmutableMap; 25 import com.google.inject.ConfigurationException; 26 import com.google.inject.Key; 27 import com.google.inject.TypeLiteral; 28 import com.google.inject.util.Types; 29 30 import java.io.Serializable; 31 import java.lang.reflect.Array; 32 import java.lang.reflect.GenericArrayType; 33 import java.lang.reflect.GenericDeclaration; 34 import java.lang.reflect.ParameterizedType; 35 import java.lang.reflect.Type; 36 import java.lang.reflect.TypeVariable; 37 import java.lang.reflect.WildcardType; 38 import java.util.Arrays; 39 import java.util.Map; 40 import java.util.NoSuchElementException; 41 42 /** 43 * Static methods for working with types that we aren't publishing in the 44 * public {@code Types} API. 45 * 46 * @author jessewilson@google.com (Jesse Wilson) 47 */ 48 public class MoreTypes { 49 50 public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {}; 51 MoreTypes()52 private MoreTypes() {} 53 54 private static final Map<TypeLiteral<?>, TypeLiteral<?>> PRIMITIVE_TO_WRAPPER 55 = new ImmutableMap.Builder<TypeLiteral<?>, TypeLiteral<?>>() 56 .put(TypeLiteral.get(boolean.class), TypeLiteral.get(Boolean.class)) 57 .put(TypeLiteral.get(byte.class), TypeLiteral.get(Byte.class)) 58 .put(TypeLiteral.get(short.class), TypeLiteral.get(Short.class)) 59 .put(TypeLiteral.get(int.class), TypeLiteral.get(Integer.class)) 60 .put(TypeLiteral.get(long.class), TypeLiteral.get(Long.class)) 61 .put(TypeLiteral.get(float.class), TypeLiteral.get(Float.class)) 62 .put(TypeLiteral.get(double.class), TypeLiteral.get(Double.class)) 63 .put(TypeLiteral.get(char.class), TypeLiteral.get(Character.class)) 64 .put(TypeLiteral.get(void.class), TypeLiteral.get(Void.class)) 65 .build(); 66 67 /** 68 * Returns a key that doesn't hold any references to parent classes. 69 * This is necessary for anonymous keys, so ensure we don't hold a ref 70 * to the containing module (or class) forever. 71 */ canonicalizeKey(Key<T> key)72 public static <T> Key<T> canonicalizeKey(Key<T> key) { 73 // If we know this isn't a subclass, return as-is. 74 // Otherwise, recreate the key to avoid the subclass 75 if (key.getClass() == Key.class) { 76 return key; 77 } else if (key.getAnnotation() != null) { 78 return Key.get(key.getTypeLiteral(), key.getAnnotation()); 79 } else if (key.getAnnotationType() != null) { 80 return Key.get(key.getTypeLiteral(), key.getAnnotationType()); 81 } else { 82 return Key.get(key.getTypeLiteral()); 83 } 84 } 85 86 /** 87 * Returns an type that's appropriate for use in a key. 88 * 89 * <p>If the raw type of {@code typeLiteral} is a {@code javax.inject.Provider}, this returns a 90 * {@code com.google.inject.Provider} with the same type parameters. 91 * 92 * <p>If the type is a primitive, the corresponding wrapper type will be returned. 93 * 94 * @throws ConfigurationException if {@code type} contains a type variable 95 */ canonicalizeForKey(TypeLiteral<T> typeLiteral)96 public static <T> TypeLiteral<T> canonicalizeForKey(TypeLiteral<T> typeLiteral) { 97 Type type = typeLiteral.getType(); 98 if (!isFullySpecified(type)) { 99 Errors errors = new Errors().keyNotFullySpecified(typeLiteral); 100 throw new ConfigurationException(errors.getMessages()); 101 } 102 103 if (typeLiteral.getRawType() == javax.inject.Provider.class) { 104 ParameterizedType parameterizedType = (ParameterizedType) type; 105 106 // the following casts are generally unsafe, but com.google.inject.Provider extends 107 // javax.inject.Provider and is covariant 108 @SuppressWarnings("unchecked") 109 TypeLiteral<T> guiceProviderType = (TypeLiteral<T>) TypeLiteral.get( 110 Types.providerOf(parameterizedType.getActualTypeArguments()[0])); 111 return guiceProviderType; 112 } 113 114 @SuppressWarnings("unchecked") 115 TypeLiteral<T> wrappedPrimitives = (TypeLiteral<T>) PRIMITIVE_TO_WRAPPER.get(typeLiteral); 116 if (wrappedPrimitives != null) { 117 return wrappedPrimitives; 118 } 119 120 // If we know this isn't a subclass, return as-is. 121 if (typeLiteral.getClass() == TypeLiteral.class) { 122 return typeLiteral; 123 } 124 125 // recreate the TypeLiteral to avoid anonymous TypeLiterals from holding refs to their 126 // surrounding classes. 127 @SuppressWarnings("unchecked") 128 TypeLiteral<T> recreated = (TypeLiteral<T>) TypeLiteral.get(typeLiteral.getType()); 129 return recreated; 130 } 131 132 /** 133 * Returns true if {@code type} is free from type variables. 134 */ isFullySpecified(Type type)135 private static boolean isFullySpecified(Type type) { 136 if (type instanceof Class) { 137 return true; 138 139 } else if (type instanceof CompositeType) { 140 return ((CompositeType) type).isFullySpecified(); 141 142 } else if (type instanceof TypeVariable){ 143 return false; 144 145 } else { 146 return ((CompositeType) canonicalize(type)).isFullySpecified(); 147 } 148 } 149 150 /** 151 * Returns a type that is functionally equal but not necessarily equal 152 * according to {@link Object#equals(Object) Object.equals()}. The returned 153 * type is {@link Serializable}. 154 */ canonicalize(Type type)155 public static Type canonicalize(Type type) { 156 if (type instanceof Class) { 157 Class<?> c = (Class<?>) type; 158 return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c; 159 160 } else if (type instanceof CompositeType) { 161 return type; 162 163 } else if (type instanceof ParameterizedType) { 164 ParameterizedType p = (ParameterizedType) type; 165 return new ParameterizedTypeImpl(p.getOwnerType(), 166 p.getRawType(), p.getActualTypeArguments()); 167 168 } else if (type instanceof GenericArrayType) { 169 GenericArrayType g = (GenericArrayType) type; 170 return new GenericArrayTypeImpl(g.getGenericComponentType()); 171 172 } else if (type instanceof WildcardType) { 173 WildcardType w = (WildcardType) type; 174 return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); 175 176 } else { 177 // type is either serializable as-is or unsupported 178 return type; 179 } 180 } 181 getRawType(Type type)182 public static Class<?> getRawType(Type type) { 183 if (type instanceof Class<?>) { 184 // type is a normal class. 185 return (Class<?>) type; 186 187 } else if (type instanceof ParameterizedType) { 188 ParameterizedType parameterizedType = (ParameterizedType) type; 189 190 // I'm not exactly sure why getRawType() returns Type instead of Class. 191 // Neal isn't either but suspects some pathological case related 192 // to nested classes exists. 193 Type rawType = parameterizedType.getRawType(); 194 checkArgument(rawType instanceof Class, 195 "Expected a Class, but <%s> is of type %s", type, type.getClass().getName()); 196 return (Class<?>) rawType; 197 198 } else if (type instanceof GenericArrayType) { 199 Type componentType = ((GenericArrayType)type).getGenericComponentType(); 200 return Array.newInstance(getRawType(componentType), 0).getClass(); 201 202 } else if (type instanceof TypeVariable) { 203 // we could use the variable's bounds, but that'll won't work if there are multiple. 204 // having a raw type that's more general than necessary is okay 205 return Object.class; 206 207 } else { 208 throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " 209 + "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName()); 210 } 211 } 212 213 /** 214 * Returns true if {@code a} and {@code b} are equal. 215 */ equals(Type a, Type b)216 public static boolean equals(Type a, Type b) { 217 if (a == b) { 218 // also handles (a == null && b == null) 219 return true; 220 221 } else if (a instanceof Class) { 222 // Class already specifies equals(). 223 return a.equals(b); 224 225 } else if (a instanceof ParameterizedType) { 226 if (!(b instanceof ParameterizedType)) { 227 return false; 228 } 229 230 // TODO: save a .clone() call 231 ParameterizedType pa = (ParameterizedType) a; 232 ParameterizedType pb = (ParameterizedType) b; 233 return Objects.equal(pa.getOwnerType(), pb.getOwnerType()) 234 && pa.getRawType().equals(pb.getRawType()) 235 && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); 236 237 } else if (a instanceof GenericArrayType) { 238 if (!(b instanceof GenericArrayType)) { 239 return false; 240 } 241 242 GenericArrayType ga = (GenericArrayType) a; 243 GenericArrayType gb = (GenericArrayType) b; 244 return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); 245 246 } else if (a instanceof WildcardType) { 247 if (!(b instanceof WildcardType)) { 248 return false; 249 } 250 251 WildcardType wa = (WildcardType) a; 252 WildcardType wb = (WildcardType) b; 253 return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) 254 && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); 255 256 } else if (a instanceof TypeVariable) { 257 if (!(b instanceof TypeVariable)) { 258 return false; 259 } 260 TypeVariable<?> va = (TypeVariable) a; 261 TypeVariable<?> vb = (TypeVariable) b; 262 return va.getGenericDeclaration().equals(vb.getGenericDeclaration()) 263 && va.getName().equals(vb.getName()); 264 265 } else { 266 // This isn't a type we support. Could be a generic array type, wildcard type, etc. 267 return false; 268 } 269 } 270 hashCodeOrZero(Object o)271 private static int hashCodeOrZero(Object o) { 272 return o != null ? o.hashCode() : 0; 273 } 274 typeToString(Type type)275 public static String typeToString(Type type) { 276 return type instanceof Class ? ((Class) type).getName() : type.toString(); 277 } 278 279 /** 280 * Returns the generic supertype for {@code type}. For example, given a class {@code IntegerSet}, 281 * the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the result 282 * when the supertype is {@code Collection.class} is {@code Collection<Integer>}. 283 */ getGenericSupertype(Type type, Class<?> rawType, Class<?> toResolve)284 public static Type getGenericSupertype(Type type, Class<?> rawType, Class<?> toResolve) { 285 if (toResolve == rawType) { 286 return type; 287 } 288 289 // we skip searching through interfaces if unknown is an interface 290 if (toResolve.isInterface()) { 291 Class[] interfaces = rawType.getInterfaces(); 292 for (int i = 0, length = interfaces.length; i < length; i++) { 293 if (interfaces[i] == toResolve) { 294 return rawType.getGenericInterfaces()[i]; 295 } else if (toResolve.isAssignableFrom(interfaces[i])) { 296 return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve); 297 } 298 } 299 } 300 301 // check our supertypes 302 if (!rawType.isInterface()) { 303 while (rawType != Object.class) { 304 Class<?> rawSupertype = rawType.getSuperclass(); 305 if (rawSupertype == toResolve) { 306 return rawType.getGenericSuperclass(); 307 } else if (toResolve.isAssignableFrom(rawSupertype)) { 308 return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve); 309 } 310 rawType = rawSupertype; 311 } 312 } 313 314 // we can't resolve this further 315 return toResolve; 316 } 317 resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown)318 public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) { 319 Class<?> declaredByRaw = declaringClassOf(unknown); 320 321 // we can't reduce this further 322 if (declaredByRaw == null) { 323 return unknown; 324 } 325 326 Type declaredBy = getGenericSupertype(type, rawType, declaredByRaw); 327 if (declaredBy instanceof ParameterizedType) { 328 int index = indexOf(declaredByRaw.getTypeParameters(), unknown); 329 return ((ParameterizedType) declaredBy).getActualTypeArguments()[index]; 330 } 331 332 return unknown; 333 } 334 indexOf(Object[] array, Object toFind)335 private static int indexOf(Object[] array, Object toFind) { 336 for (int i = 0; i < array.length; i++) { 337 if (toFind.equals(array[i])) { 338 return i; 339 } 340 } 341 throw new NoSuchElementException(); 342 } 343 344 /** 345 * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by 346 * a class. 347 */ declaringClassOf(TypeVariable typeVariable)348 private static Class<?> declaringClassOf(TypeVariable typeVariable) { 349 GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); 350 return genericDeclaration instanceof Class 351 ? (Class<?>) genericDeclaration 352 : null; 353 } 354 355 public static class ParameterizedTypeImpl 356 implements ParameterizedType, Serializable, CompositeType { 357 private final Type ownerType; 358 private final Type rawType; 359 private final Type[] typeArguments; 360 ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments)361 public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { 362 // require an owner type if the raw type needs it 363 ensureOwnerType(ownerType, rawType); 364 365 this.ownerType = ownerType == null ? null : canonicalize(ownerType); 366 this.rawType = canonicalize(rawType); 367 this.typeArguments = typeArguments.clone(); 368 for (int t = 0; t < this.typeArguments.length; t++) { 369 checkNotNull(this.typeArguments[t], "type parameter"); 370 checkNotPrimitive(this.typeArguments[t], "type parameters"); 371 this.typeArguments[t] = canonicalize(this.typeArguments[t]); 372 } 373 } 374 getActualTypeArguments()375 public Type[] getActualTypeArguments() { 376 return typeArguments.clone(); 377 } 378 getRawType()379 public Type getRawType() { 380 return rawType; 381 } 382 getOwnerType()383 public Type getOwnerType() { 384 return ownerType; 385 } 386 isFullySpecified()387 public boolean isFullySpecified() { 388 if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) { 389 return false; 390 } 391 392 if (!MoreTypes.isFullySpecified(rawType)) { 393 return false; 394 } 395 396 for (Type type : typeArguments) { 397 if (!MoreTypes.isFullySpecified(type)) { 398 return false; 399 } 400 } 401 402 return true; 403 } 404 equals(Object other)405 @Override public boolean equals(Object other) { 406 return other instanceof ParameterizedType 407 && MoreTypes.equals(this, (ParameterizedType) other); 408 } 409 hashCode()410 @Override public int hashCode() { 411 return Arrays.hashCode(typeArguments) 412 ^ rawType.hashCode() 413 ^ hashCodeOrZero(ownerType); 414 } 415 toString()416 @Override public String toString() { 417 StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1)); 418 stringBuilder.append(typeToString(rawType)); 419 420 if (typeArguments.length == 0) { 421 return stringBuilder.toString(); 422 } 423 424 stringBuilder.append("<").append(typeToString(typeArguments[0])); 425 for (int i = 1; i < typeArguments.length; i++) { 426 stringBuilder.append(", ").append(typeToString(typeArguments[i])); 427 } 428 return stringBuilder.append(">").toString(); 429 } 430 ensureOwnerType(Type ownerType, Type rawType)431 private static void ensureOwnerType(Type ownerType, Type rawType) { 432 if (rawType instanceof Class<?>) { 433 Class rawTypeAsClass = (Class) rawType; 434 checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null, 435 "No owner type for enclosed %s", rawType); 436 checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null, 437 "Owner type for unenclosed %s", rawType); 438 } 439 } 440 441 private static final long serialVersionUID = 0; 442 } 443 444 public static class GenericArrayTypeImpl 445 implements GenericArrayType, Serializable, CompositeType { 446 private final Type componentType; 447 GenericArrayTypeImpl(Type componentType)448 public GenericArrayTypeImpl(Type componentType) { 449 this.componentType = canonicalize(componentType); 450 } 451 getGenericComponentType()452 public Type getGenericComponentType() { 453 return componentType; 454 } 455 isFullySpecified()456 public boolean isFullySpecified() { 457 return MoreTypes.isFullySpecified(componentType); 458 } 459 equals(Object o)460 @Override public boolean equals(Object o) { 461 return o instanceof GenericArrayType 462 && MoreTypes.equals(this, (GenericArrayType) o); 463 } 464 hashCode()465 @Override public int hashCode() { 466 return componentType.hashCode(); 467 } 468 toString()469 @Override public String toString() { 470 return typeToString(componentType) + "[]"; 471 } 472 473 private static final long serialVersionUID = 0; 474 } 475 476 /** 477 * The WildcardType interface supports multiple upper bounds and multiple 478 * lower bounds. We only support what the Java 6 language needs - at most one 479 * bound. If a lower bound is set, the upper bound must be Object.class. 480 */ 481 public static class WildcardTypeImpl implements WildcardType, Serializable, CompositeType { 482 private final Type upperBound; 483 private final Type lowerBound; 484 WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds)485 public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { 486 checkArgument(lowerBounds.length <= 1, "Must have at most one lower bound."); 487 checkArgument(upperBounds.length == 1, "Must have exactly one upper bound."); 488 489 if (lowerBounds.length == 1) { 490 checkNotNull(lowerBounds[0], "lowerBound"); 491 checkNotPrimitive(lowerBounds[0], "wildcard bounds"); 492 checkArgument(upperBounds[0] == Object.class, "bounded both ways"); 493 this.lowerBound = canonicalize(lowerBounds[0]); 494 this.upperBound = Object.class; 495 496 } else { 497 checkNotNull(upperBounds[0], "upperBound"); 498 checkNotPrimitive(upperBounds[0], "wildcard bounds"); 499 this.lowerBound = null; 500 this.upperBound = canonicalize(upperBounds[0]); 501 } 502 } 503 getUpperBounds()504 public Type[] getUpperBounds() { 505 return new Type[] { upperBound }; 506 } 507 getLowerBounds()508 public Type[] getLowerBounds() { 509 return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY; 510 } 511 isFullySpecified()512 public boolean isFullySpecified() { 513 return MoreTypes.isFullySpecified(upperBound) 514 && (lowerBound == null || MoreTypes.isFullySpecified(lowerBound)); 515 } 516 equals(Object other)517 @Override public boolean equals(Object other) { 518 return other instanceof WildcardType 519 && MoreTypes.equals(this, (WildcardType) other); 520 } 521 hashCode()522 @Override public int hashCode() { 523 // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()); 524 return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) 525 ^ (31 + upperBound.hashCode()); 526 } 527 toString()528 @Override public String toString() { 529 if (lowerBound != null) { 530 return "? super " + typeToString(lowerBound); 531 } else if (upperBound == Object.class) { 532 return "?"; 533 } else { 534 return "? extends " + typeToString(upperBound); 535 } 536 } 537 538 private static final long serialVersionUID = 0; 539 } 540 checkNotPrimitive(Type type, String use)541 private static void checkNotPrimitive(Type type, String use) { 542 checkArgument(!(type instanceof Class<?>) || !((Class) type).isPrimitive(), 543 "Primitive types are not allowed in %s: %s", use, type); 544 } 545 546 /** A type formed from other types, such as arrays, parameterized types or wildcard types */ 547 private interface CompositeType { 548 /** Returns true if there are no type variables in this type. */ isFullySpecified()549 boolean isFullySpecified(); 550 } 551 } 552