1/**
2 * Copyright (C) 2006 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
17package com.google.inject;
18
19import static com.google.common.base.Preconditions.checkArgument;
20import static com.google.common.base.Preconditions.checkNotNull;
21import static com.google.inject.internal.MoreTypes.canonicalize;
22
23import com.google.common.collect.ImmutableList;
24import com.google.inject.internal.MoreTypes;
25import com.google.inject.util.Types;
26
27import java.lang.reflect.Constructor;
28import java.lang.reflect.Field;
29import java.lang.reflect.GenericArrayType;
30import java.lang.reflect.Member;
31import java.lang.reflect.Method;
32import java.lang.reflect.ParameterizedType;
33import java.lang.reflect.Type;
34import java.lang.reflect.TypeVariable;
35import java.lang.reflect.WildcardType;
36import java.util.List;
37
38/**
39 * Represents a generic type {@code T}. Java doesn't yet provide a way to
40 * represent generic types, so this class does. Forces clients to create a
41 * subclass of this class which enables retrieval the type information even at
42 * runtime.
43 *
44 * <p>For example, to create a type literal for {@code List<String>}, you can
45 * create an empty anonymous inner class:
46 *
47 * <p>
48 * {@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
49 *
50 * <p>Along with modeling generic types, this class can resolve type parameters.
51 * For example, to figure out what type {@code keySet()} returns on a {@code
52 * Map<Integer, String>}, use this code:<pre>   {@code
53 *
54 *   TypeLiteral<Map<Integer, String>> mapType
55 *       = new TypeLiteral<Map<Integer, String>>() {};
56 *   TypeLiteral<?> keySetType
57 *       = mapType.getReturnType(Map.class.getMethod("keySet"));
58 *   System.out.println(keySetType); // prints "Set<Integer>"}</pre>
59 *
60 * @author crazybob@google.com (Bob Lee)
61 * @author jessewilson@google.com (Jesse Wilson)
62 */
63public class TypeLiteral<T> {
64
65  final Class<? super T> rawType;
66  final Type type;
67  final int hashCode;
68
69  /**
70   * Constructs a new type literal. Derives represented class from type
71   * parameter.
72   *
73   * <p>Clients create an empty anonymous subclass. Doing so embeds the type
74   * parameter in the anonymous class's type hierarchy so we can reconstitute it
75   * at runtime despite erasure.
76   */
77  @SuppressWarnings("unchecked")
78  protected TypeLiteral() {
79    this.type = getSuperclassTypeParameter(getClass());
80    this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
81    this.hashCode = type.hashCode();
82  }
83
84  /**
85   * Unsafe. Constructs a type literal manually.
86   */
87  @SuppressWarnings("unchecked")
88  TypeLiteral(Type type) {
89    this.type = canonicalize(checkNotNull(type, "type"));
90    this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
91    this.hashCode = this.type.hashCode();
92  }
93
94  /**
95   * Returns the type from super class's type parameter in {@link MoreTypes#canonicalize(Type)
96   * canonical form}.
97   */
98  static Type getSuperclassTypeParameter(Class<?> subclass) {
99    Type superclass = subclass.getGenericSuperclass();
100    if (superclass instanceof Class) {
101      throw new RuntimeException("Missing type parameter.");
102    }
103    ParameterizedType parameterized = (ParameterizedType) superclass;
104    return canonicalize(parameterized.getActualTypeArguments()[0]);
105  }
106
107  /**
108   * Gets type literal from super class's type parameter.
109   */
110  static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
111    return new TypeLiteral<Object>(getSuperclassTypeParameter(subclass));
112  }
113
114  /**
115   * Returns the raw (non-generic) type for this type.
116   *
117   * @since 2.0
118   */
119  public final Class<? super T> getRawType() {
120    return rawType;
121  }
122
123  /**
124   * Gets underlying {@code Type} instance.
125   */
126  public final Type getType() {
127    return type;
128  }
129
130  /**
131   * Gets the type of this type's provider.
132   */
133  @SuppressWarnings("unchecked")
134  final TypeLiteral<Provider<T>> providerType() {
135    // This cast is safe and wouldn't generate a warning if Type had a type
136    // parameter.
137    return (TypeLiteral<Provider<T>>) get(Types.providerOf(getType()));
138  }
139
140  @Override public final int hashCode() {
141    return this.hashCode;
142  }
143
144  @Override public final boolean equals(Object o) {
145    return o instanceof TypeLiteral<?>
146        && MoreTypes.equals(type, ((TypeLiteral) o).type);
147  }
148
149  @Override public final String toString() {
150    return MoreTypes.typeToString(type);
151  }
152
153  /**
154   * Gets type literal for the given {@code Type} instance.
155   */
156  public static TypeLiteral<?> get(Type type) {
157    return new TypeLiteral<Object>(type);
158  }
159
160  /**
161   * Gets type literal for the given {@code Class} instance.
162   */
163  public static <T> TypeLiteral<T> get(Class<T> type) {
164    return new TypeLiteral<T>(type);
165  }
166
167
168  /** Returns an immutable list of the resolved types. */
169  private List<TypeLiteral<?>> resolveAll(Type[] types) {
170    TypeLiteral<?>[] result = new TypeLiteral<?>[types.length];
171    for (int t = 0; t < types.length; t++) {
172      result[t] = resolve(types[t]);
173    }
174    return ImmutableList.copyOf(result);
175  }
176
177  /**
178   * Resolves known type parameters in {@code toResolve} and returns the result.
179   */
180  TypeLiteral<?> resolve(Type toResolve) {
181    return TypeLiteral.get(resolveType(toResolve));
182  }
183
184  Type resolveType(Type toResolve) {
185    // this implementation is made a little more complicated in an attempt to avoid object-creation
186    while (true) {
187      if (toResolve instanceof TypeVariable) {
188        TypeVariable original = (TypeVariable) toResolve;
189        toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
190        if (toResolve == original) {
191          return toResolve;
192        }
193
194      } else if (toResolve instanceof GenericArrayType) {
195        GenericArrayType original = (GenericArrayType) toResolve;
196        Type componentType = original.getGenericComponentType();
197        Type newComponentType = resolveType(componentType);
198        return componentType == newComponentType
199            ? original
200            : Types.arrayOf(newComponentType);
201
202      } else if (toResolve instanceof ParameterizedType) {
203        ParameterizedType original = (ParameterizedType) toResolve;
204        Type ownerType = original.getOwnerType();
205        Type newOwnerType = resolveType(ownerType);
206        boolean changed = newOwnerType != ownerType;
207
208        Type[] args = original.getActualTypeArguments();
209        for (int t = 0, length = args.length; t < length; t++) {
210          Type resolvedTypeArgument = resolveType(args[t]);
211          if (resolvedTypeArgument != args[t]) {
212            if (!changed) {
213              args = args.clone();
214              changed = true;
215            }
216            args[t] = resolvedTypeArgument;
217          }
218        }
219
220        return changed
221            ? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
222            : original;
223
224      } else if (toResolve instanceof WildcardType) {
225        WildcardType original = (WildcardType) toResolve;
226        Type[] originalLowerBound = original.getLowerBounds();
227        Type[] originalUpperBound = original.getUpperBounds();
228
229        if (originalLowerBound.length == 1) {
230          Type lowerBound = resolveType(originalLowerBound[0]);
231          if (lowerBound != originalLowerBound[0]) {
232            return Types.supertypeOf(lowerBound);
233          }
234        } else if (originalUpperBound.length == 1) {
235          Type upperBound = resolveType(originalUpperBound[0]);
236          if (upperBound != originalUpperBound[0]) {
237            return Types.subtypeOf(upperBound);
238          }
239        }
240        return original;
241
242      } else {
243        return toResolve;
244      }
245    }
246  }
247
248  /**
249   * Returns the generic form of {@code supertype}. For example, if this is {@code
250   * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
251   * Iterable.class}.
252   *
253   * @param supertype a superclass of, or interface implemented by, this.
254   * @since 2.0
255   */
256  public TypeLiteral<?> getSupertype(Class<?> supertype) {
257    checkArgument(supertype.isAssignableFrom(rawType),
258        "%s is not a supertype of %s", supertype, this.type);
259    return resolve(MoreTypes.getGenericSupertype(type, rawType, supertype));
260  }
261
262  /**
263   * Returns the resolved generic type of {@code field}.
264   *
265   * @param field a field defined by this or any superclass.
266   * @since 2.0
267   */
268  public TypeLiteral<?> getFieldType(Field field) {
269    checkArgument(field.getDeclaringClass().isAssignableFrom(rawType),
270        "%s is not defined by a supertype of %s", field, type);
271    return resolve(field.getGenericType());
272  }
273
274  /**
275   * Returns the resolved generic parameter types of {@code methodOrConstructor}.
276   *
277   * @param methodOrConstructor a method or constructor defined by this or any supertype.
278   * @since 2.0
279   */
280  public List<TypeLiteral<?>> getParameterTypes(Member methodOrConstructor) {
281    Type[] genericParameterTypes;
282
283    if (methodOrConstructor instanceof Method) {
284      Method method = (Method) methodOrConstructor;
285      checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
286          "%s is not defined by a supertype of %s", method, type);
287      genericParameterTypes = method.getGenericParameterTypes();
288
289    } else if (methodOrConstructor instanceof Constructor) {
290      Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
291      checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType),
292          "%s does not construct a supertype of %s", constructor, type);
293      genericParameterTypes = constructor.getGenericParameterTypes();
294
295    } else {
296      throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
297    }
298
299    return resolveAll(genericParameterTypes);
300  }
301
302  /**
303   * Returns the resolved generic exception types thrown by {@code constructor}.
304   *
305   * @param methodOrConstructor a method or constructor defined by this or any supertype.
306   * @since 2.0
307   */
308  public List<TypeLiteral<?>> getExceptionTypes(Member methodOrConstructor) {
309    Type[] genericExceptionTypes;
310
311    if (methodOrConstructor instanceof Method) {
312      Method method = (Method) methodOrConstructor;
313      checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
314          "%s is not defined by a supertype of %s", method, type);
315      genericExceptionTypes = method.getGenericExceptionTypes();
316
317    } else if (methodOrConstructor instanceof Constructor) {
318      Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
319      checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType),
320          "%s does not construct a supertype of %s", constructor, type);
321      genericExceptionTypes = constructor.getGenericExceptionTypes();
322
323    } else {
324      throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
325    }
326
327    return resolveAll(genericExceptionTypes);
328  }
329
330  /**
331   * Returns the resolved generic return type of {@code method}.
332   *
333   * @param method a method defined by this or any supertype.
334   * @since 2.0
335   */
336  public TypeLiteral<?> getReturnType(Method method) {
337    checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
338        "%s is not defined by a supertype of %s", method, type);
339    return resolve(method.getGenericReturnType());
340  }
341}
342