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