001package com.google.inject.internal; 002 003import com.google.common.base.Objects; 004import static com.google.common.base.Preconditions.checkArgument; 005import static com.google.common.base.Preconditions.checkNotNull; 006import java.io.Serializable; 007import java.lang.reflect.Array; 008import java.lang.reflect.GenericArrayType; 009import java.lang.reflect.ParameterizedType; 010import java.lang.reflect.Type; 011import java.lang.reflect.TypeVariable; 012import java.lang.reflect.WildcardType; 013import java.util.Arrays; 014 015/** 016 * Static methods for working with types that we aren't publishing in the 017 * public {@code Types} API. 018 * 019 * @author jessewilson@google.com (Jesse Wilson) 020 */ 021public class MoreTypes { 022 023 public static final Type[] EMPTY_TYPE_ARRAY = new Type[] {}; 024 025 private MoreTypes() {} 026 027 /** 028 * Returns true if {@code type} is free from type variables. 029 */ 030 private static boolean isFullySpecified(Type type) { 031 if (type instanceof Class) { 032 return true; 033 034 } else if (type instanceof CompositeType) { 035 return ((CompositeType) type).isFullySpecified(); 036 037 } else if (type instanceof TypeVariable){ 038 return false; 039 040 } else { 041 return ((CompositeType) canonicalize(type)).isFullySpecified(); 042 } 043 } 044 045 /** 046 * Returns a type that is functionally equal but not necessarily equal 047 * according to {@link Object#equals(Object) Object.equals()}. The returned 048 * type is {@link Serializable}. 049 */ 050 public static Type canonicalize(Type type) { 051 if (type instanceof Class) { 052 Class<?> c = (Class<?>) type; 053 return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c; 054 055 } else if (type instanceof CompositeType) { 056 return type; 057 058 } else if (type instanceof ParameterizedType) { 059 ParameterizedType p = (ParameterizedType) type; 060 return new ParameterizedTypeImpl(p.getOwnerType(), 061 p.getRawType(), p.getActualTypeArguments()); 062 063 } else if (type instanceof GenericArrayType) { 064 GenericArrayType g = (GenericArrayType) type; 065 return new GenericArrayTypeImpl(g.getGenericComponentType()); 066 067 } else if (type instanceof WildcardType) { 068 WildcardType w = (WildcardType) type; 069 return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds()); 070 071 } else { 072 // type is either serializable as-is or unsupported 073 return type; 074 } 075 } 076 077 public static Class<?> getRawType(Type type) { 078 if (type instanceof Class<?>) { 079 // type is a normal class. 080 return (Class<?>) type; 081 082 } else if (type instanceof ParameterizedType) { 083 ParameterizedType parameterizedType = (ParameterizedType) type; 084 085 // I'm not exactly sure why getRawType() returns Type instead of Class. 086 // Neal isn't either but suspects some pathological case related 087 // to nested classes exists. 088 Type rawType = parameterizedType.getRawType(); 089 checkArgument(rawType instanceof Class, 090 "Expected a Class, but <%s> is of type %s", type, type.getClass().getName()); 091 return (Class<?>) rawType; 092 093 } else if (type instanceof GenericArrayType) { 094 Type componentType = ((GenericArrayType)type).getGenericComponentType(); 095 return Array.newInstance(getRawType(componentType), 0).getClass(); 096 097 } else if (type instanceof TypeVariable) { 098 // we could use the variable's bounds, but that'll won't work if there are multiple. 099 // having a raw type that's more general than necessary is okay 100 return Object.class; 101 102 } else { 103 throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " 104 + "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName()); 105 } 106 } 107 108 /** 109 * Returns true if {@code a} and {@code b} are equal. 110 */ 111 public static boolean equals(Type a, Type b) { 112 if (a == b) { 113 // also handles (a == null && b == null) 114 return true; 115 116 } else if (a instanceof Class) { 117 // Class already specifies equals(). 118 return a.equals(b); 119 120 } else if (a instanceof ParameterizedType) { 121 if (!(b instanceof ParameterizedType)) { 122 return false; 123 } 124 125 // TODO: save a .clone() call 126 ParameterizedType pa = (ParameterizedType) a; 127 ParameterizedType pb = (ParameterizedType) b; 128 return Objects.equal(pa.getOwnerType(), pb.getOwnerType()) 129 && pa.getRawType().equals(pb.getRawType()) 130 && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments()); 131 132 } else if (a instanceof GenericArrayType) { 133 if (!(b instanceof GenericArrayType)) { 134 return false; 135 } 136 137 GenericArrayType ga = (GenericArrayType) a; 138 GenericArrayType gb = (GenericArrayType) b; 139 return equals(ga.getGenericComponentType(), gb.getGenericComponentType()); 140 141 } else if (a instanceof WildcardType) { 142 if (!(b instanceof WildcardType)) { 143 return false; 144 } 145 146 WildcardType wa = (WildcardType) a; 147 WildcardType wb = (WildcardType) b; 148 return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) 149 && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds()); 150 151 } else if (a instanceof TypeVariable) { 152 if (!(b instanceof TypeVariable)) { 153 return false; 154 } 155 TypeVariable<?> va = (TypeVariable) a; 156 TypeVariable<?> vb = (TypeVariable) b; 157 return va.getGenericDeclaration() == vb.getGenericDeclaration() 158 && va.getName().equals(vb.getName()); 159 160 } else { 161 // This isn't a type we support. Could be a generic array type, wildcard type, etc. 162 return false; 163 } 164 } 165 166 private static int hashCodeOrZero(Object o) { 167 return o != null ? o.hashCode() : 0; 168 } 169 170 public static String typeToString(Type type) { 171 return type instanceof Class ? ((Class) type).getName() : type.toString(); 172 } 173 174 public static class ParameterizedTypeImpl 175 implements ParameterizedType, Serializable, CompositeType { 176 private final Type ownerType; 177 private final Type rawType; 178 private final Type[] typeArguments; 179 180 public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { 181 // require an owner type if the raw type needs it 182 if (rawType instanceof Class<?>) { 183 Class rawTypeAsClass = (Class) rawType; 184 checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null, 185 "No owner type for enclosed %s", rawType); 186 checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null, 187 "Owner type for unenclosed %s", rawType); 188 } 189 190 this.ownerType = ownerType == null ? null : canonicalize(ownerType); 191 this.rawType = canonicalize(rawType); 192 this.typeArguments = typeArguments.clone(); 193 for (int t = 0; t < this.typeArguments.length; t++) { 194 checkNotNull(this.typeArguments[t], "type parameter"); 195 checkNotPrimitive(this.typeArguments[t], "type parameters"); 196 this.typeArguments[t] = canonicalize(this.typeArguments[t]); 197 } 198 } 199 200 public Type[] getActualTypeArguments() { 201 return typeArguments.clone(); 202 } 203 204 public Type getRawType() { 205 return rawType; 206 } 207 208 public Type getOwnerType() { 209 return ownerType; 210 } 211 212 public boolean isFullySpecified() { 213 if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) { 214 return false; 215 } 216 217 if (!MoreTypes.isFullySpecified(rawType)) { 218 return false; 219 } 220 221 for (Type type : typeArguments) { 222 if (!MoreTypes.isFullySpecified(type)) { 223 return false; 224 } 225 } 226 227 return true; 228 } 229 230 @Override public boolean equals(Object other) { 231 return other instanceof ParameterizedType 232 && MoreTypes.equals(this, (ParameterizedType) other); 233 } 234 235 @Override public int hashCode() { 236 return Arrays.hashCode(typeArguments) 237 ^ rawType.hashCode() 238 ^ hashCodeOrZero(ownerType); 239 } 240 241 @Override public String toString() { 242 StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1)); 243 stringBuilder.append(typeToString(rawType)); 244 245 if (typeArguments.length == 0) { 246 return stringBuilder.toString(); 247 } 248 249 stringBuilder.append("<").append(typeToString(typeArguments[0])); 250 for (int i = 1; i < typeArguments.length; i++) { 251 stringBuilder.append(", ").append(typeToString(typeArguments[i])); 252 } 253 return stringBuilder.append(">").toString(); 254 } 255 256 private static final long serialVersionUID = 0; 257 } 258 259 public static class GenericArrayTypeImpl 260 implements GenericArrayType, Serializable, CompositeType { 261 private final Type componentType; 262 263 public GenericArrayTypeImpl(Type componentType) { 264 this.componentType = canonicalize(componentType); 265 } 266 267 public Type getGenericComponentType() { 268 return componentType; 269 } 270 271 public boolean isFullySpecified() { 272 return MoreTypes.isFullySpecified(componentType); 273 } 274 275 @Override public boolean equals(Object o) { 276 return o instanceof GenericArrayType 277 && MoreTypes.equals(this, (GenericArrayType) o); 278 } 279 280 @Override public int hashCode() { 281 return componentType.hashCode(); 282 } 283 284 @Override public String toString() { 285 return typeToString(componentType) + "[]"; 286 } 287 288 private static final long serialVersionUID = 0; 289 } 290 291 /** 292 * The WildcardType interface supports multiple upper bounds and multiple 293 * lower bounds. We only support what the Java 6 language needs - at most one 294 * bound. If a lower bound is set, the upper bound must be Object.class. 295 */ 296 public static class WildcardTypeImpl implements WildcardType, Serializable, CompositeType { 297 private final Type upperBound; 298 private final Type lowerBound; 299 300 public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { 301 checkArgument(lowerBounds.length <= 1, "Must have at most one lower bound."); 302 checkArgument(upperBounds.length == 1, "Must have exactly one upper bound."); 303 304 if (lowerBounds.length == 1) { 305 checkNotNull(lowerBounds[0], "lowerBound"); 306 checkNotPrimitive(lowerBounds[0], "wildcard bounds"); 307 checkArgument(upperBounds[0] == Object.class, "bounded both ways"); 308 this.lowerBound = canonicalize(lowerBounds[0]); 309 this.upperBound = Object.class; 310 311 } else { 312 checkNotNull(upperBounds[0], "upperBound"); 313 checkNotPrimitive(upperBounds[0], "wildcard bounds"); 314 this.lowerBound = null; 315 this.upperBound = canonicalize(upperBounds[0]); 316 } 317 } 318 319 public Type[] getUpperBounds() { 320 return new Type[] { upperBound }; 321 } 322 323 public Type[] getLowerBounds() { 324 return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY; 325 } 326 327 public boolean isFullySpecified() { 328 return MoreTypes.isFullySpecified(upperBound) 329 && (lowerBound == null || MoreTypes.isFullySpecified(lowerBound)); 330 } 331 332 @Override public boolean equals(Object other) { 333 return other instanceof WildcardType 334 && MoreTypes.equals(this, (WildcardType) other); 335 } 336 337 @Override public int hashCode() { 338 // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds()); 339 return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) 340 ^ (31 + upperBound.hashCode()); 341 } 342 343 @Override public String toString() { 344 if (lowerBound != null) { 345 return "? super " + typeToString(lowerBound); 346 } else if (upperBound == Object.class) { 347 return "?"; 348 } else { 349 return "? extends " + typeToString(upperBound); 350 } 351 } 352 353 private static final long serialVersionUID = 0; 354 } 355 356 private static void checkNotPrimitive(Type type, String use) { 357 checkArgument(!(type instanceof Class<?>) || !((Class) type).isPrimitive(), 358 "Primitive types are not allowed in %s: %s", use, type); 359 } 360 361 /** A type formed from other types, such as arrays, parameterized types or wildcard types */ 362 private interface CompositeType { 363 /** Returns true if there are no type variables in this type. */ 364 boolean isFullySpecified(); 365 } 366}