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