blob: 80361130a7f788616cd0ac1851908a88fb7b1592 [file] [log] [blame]
crazyboblee41bc8522006-12-08 07:06:55 +00001/**
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
limpbizkit53664a72009-02-21 00:25:27 +000019import com.google.inject.internal.ImmutableList;
limpbizkit3beaaaf2008-06-10 06:37:12 +000020import com.google.inject.internal.MoreTypes;
21import static com.google.inject.internal.MoreTypes.canonicalize;
limpbizkit53664a72009-02-21 00:25:27 +000022import static com.google.inject.internal.Preconditions.checkArgument;
23import static com.google.inject.internal.Preconditions.checkNotNull;
limpbizkit3beaaaf2008-06-10 06:37:12 +000024import com.google.inject.util.Types;
limpbizkit4f5d1f72008-11-14 08:43:40 +000025import java.lang.reflect.Constructor;
26import java.lang.reflect.Field;
27import java.lang.reflect.GenericArrayType;
28import java.lang.reflect.Member;
29import java.lang.reflect.Method;
crazyboblee41bc8522006-12-08 07:06:55 +000030import java.lang.reflect.ParameterizedType;
kevinb9na99dca72007-02-11 04:48:57 +000031import java.lang.reflect.Type;
limpbizkit4f5d1f72008-11-14 08:43:40 +000032import java.lang.reflect.TypeVariable;
limpbizkit8d13d412008-11-14 09:06:10 +000033import java.lang.reflect.WildcardType;
limpbizkit4f5d1f72008-11-14 08:43:40 +000034import java.util.List;
crazyboblee41bc8522006-12-08 07:06:55 +000035
36/**
crazyboblee235d0682007-01-31 02:25:21 +000037 * Represents a generic type {@code T}. Java doesn't yet provide a way to
crazybobleee3adfd62007-02-02 21:30:08 +000038 * represent generic types, so this class does. Forces clients to create a
kevinb9na99dca72007-02-11 04:48:57 +000039 * subclass of this class which enables retrieval the type information even at
40 * runtime.
crazybobleee3adfd62007-02-02 21:30:08 +000041 *
kevinb9na99dca72007-02-11 04:48:57 +000042 * <p>For example, to create a type literal for {@code List<String>}, you can
43 * create an empty anonymous inner class:
crazybobleee3adfd62007-02-02 21:30:08 +000044 *
kevinb9na99dca72007-02-11 04:48:57 +000045 * <p>
46 * {@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
crazyboblee41bc8522006-12-08 07:06:55 +000047 *
limpbizkita2b241d2009-05-07 19:15:22 +000048 * <p>This syntax cannot be used to create type literals that have wildcard
49 * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
50 * Such type literals must be constructed programatically, either by {@link
51 * Method#getGenericReturnType extracting types from members} or by using the
52 * {@link Types} factory class.
53 *
54 * <p>Along with modeling generic types, this class can resolve type parameters.
55 * For example, to figure out what type {@code keySet()} returns on a {@code
56 * Map<Integer, String>}, use this code:<pre> {@code
57 *
58 * TypeLiteral<Map<Integer, String>> mapType
59 * = new TypeLiteral<Map<Integer, String>>() {};
60 * TypeLiteral<?> keySetType
61 * = mapType.getReturnType(Map.class.getMethod("keySet"));
62 * System.out.println(keySetType); // prints "Set<Integer>"}</pre>
crazyboblee41bc8522006-12-08 07:06:55 +000063 *
64 * @author crazybob@google.com (Bob Lee)
limpbizkit0de5e3e2008-12-07 08:28:31 +000065 * @author jessewilson@google.com (Jesse Wilson)
crazyboblee41bc8522006-12-08 07:06:55 +000066 */
limpbizkit8c4c71c2008-09-12 04:34:35 +000067public class TypeLiteral<T> {
crazyboblee41bc8522006-12-08 07:06:55 +000068
69 final Class<? super T> rawType;
70 final Type type;
crazyboblee1b54d6a2007-02-17 02:48:45 +000071 final int hashCode;
crazyboblee41bc8522006-12-08 07:06:55 +000072
73 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +000074 * Constructs a new type literal. Derives represented class from type
crazyboblee41bc8522006-12-08 07:06:55 +000075 * parameter.
76 *
77 * <p>Clients create an empty anonymous subclass. Doing so embeds the type
kevinb9na99dca72007-02-11 04:48:57 +000078 * parameter in the anonymous class's type hierarchy so we can reconstitute it
79 * at runtime despite erasure.
crazyboblee41bc8522006-12-08 07:06:55 +000080 */
kevinb9na99dca72007-02-11 04:48:57 +000081 @SuppressWarnings("unchecked")
crazyboblee0baa9fc2007-01-31 02:38:54 +000082 protected TypeLiteral() {
crazyboblee41bc8522006-12-08 07:06:55 +000083 this.type = getSuperclassTypeParameter(getClass());
limpbizkit3beaaaf2008-06-10 06:37:12 +000084 this.rawType = (Class<? super T>) MoreTypes.getRawType(type);
85 this.hashCode = MoreTypes.hashCode(type);
crazyboblee41bc8522006-12-08 07:06:55 +000086 }
87
88 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +000089 * Unsafe. Constructs a type literal manually.
crazyboblee41bc8522006-12-08 07:06:55 +000090 */
kevinb9na99dca72007-02-11 04:48:57 +000091 @SuppressWarnings("unchecked")
crazyboblee1b54d6a2007-02-17 02:48:45 +000092 TypeLiteral(Type type) {
kevinb9n1601ae52008-06-03 22:21:04 +000093 this.type = canonicalize(checkNotNull(type, "type"));
limpbizkit3beaaaf2008-06-10 06:37:12 +000094 this.rawType = (Class<? super T>) MoreTypes.getRawType(this.type);
95 this.hashCode = MoreTypes.hashCode(this.type);
crazyboblee41bc8522006-12-08 07:06:55 +000096 }
97
98 /**
limpbizkit4272aef2008-11-23 08:10:09 +000099 * Returns the type from super class's type parameter in {@link MoreTypes#canonicalize(Type)
100 * canonical form}.
crazyboblee41bc8522006-12-08 07:06:55 +0000101 */
102 static Type getSuperclassTypeParameter(Class<?> subclass) {
103 Type superclass = subclass.getGenericSuperclass();
104 if (superclass instanceof Class) {
105 throw new RuntimeException("Missing type parameter.");
106 }
limpbizkitf530b252008-05-27 23:03:42 +0000107 ParameterizedType parameterized = (ParameterizedType) superclass;
108 return canonicalize(parameterized.getActualTypeArguments()[0]);
crazyboblee41bc8522006-12-08 07:06:55 +0000109 }
110
111 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +0000112 * Gets type literal from super class's type parameter.
crazyboblee41bc8522006-12-08 07:06:55 +0000113 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000114 static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
limpbizkiteab76472008-05-26 19:45:12 +0000115 return new TypeLiteral<Object>(getSuperclassTypeParameter(subclass));
crazyboblee41bc8522006-12-08 07:06:55 +0000116 }
117
118 /**
limpbizkit4272aef2008-11-23 08:10:09 +0000119 * Returns the raw (non-generic) type for this type.
limpbizkitb92a84e2008-12-31 07:47:48 +0000120 *
121 * @since 2.0
crazyboblee41bc8522006-12-08 07:06:55 +0000122 */
limpbizkit4272aef2008-11-23 08:10:09 +0000123 public final Class<? super T> getRawType() {
crazyboblee41bc8522006-12-08 07:06:55 +0000124 return rawType;
125 }
126
127 /**
128 * Gets underlying {@code Type} instance.
129 */
limpbizkiteab76472008-05-26 19:45:12 +0000130 public final Type getType() {
crazyboblee41bc8522006-12-08 07:06:55 +0000131 return type;
132 }
133
crazyboblee552472f2007-09-07 16:52:39 +0000134 /**
135 * Gets the type of this type's provider.
136 */
137 @SuppressWarnings("unchecked")
limpbizkiteab76472008-05-26 19:45:12 +0000138 final TypeLiteral<Provider<T>> providerType() {
crazyboblee552472f2007-09-07 16:52:39 +0000139 // This cast is safe and wouldn't generate a warning if Type had a type
140 // parameter.
limpbizkit3beaaaf2008-06-10 06:37:12 +0000141 return (TypeLiteral<Provider<T>>) get(Types.providerOf(getType()));
crazyboblee552472f2007-09-07 16:52:39 +0000142 }
143
limpbizkiteab76472008-05-26 19:45:12 +0000144 @Override public final int hashCode() {
crazyboblee1b54d6a2007-02-17 02:48:45 +0000145 return this.hashCode;
crazyboblee41bc8522006-12-08 07:06:55 +0000146 }
147
limpbizkiteab76472008-05-26 19:45:12 +0000148 @Override public final boolean equals(Object o) {
149 return o instanceof TypeLiteral<?>
limpbizkit3beaaaf2008-06-10 06:37:12 +0000150 && MoreTypes.equals(type, ((TypeLiteral) o).type);
crazyboblee41bc8522006-12-08 07:06:55 +0000151 }
152
limpbizkiteab76472008-05-26 19:45:12 +0000153 @Override public final String toString() {
limpbizkit3beaaaf2008-06-10 06:37:12 +0000154 return MoreTypes.toString(type);
crazyboblee41bc8522006-12-08 07:06:55 +0000155 }
156
157 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +0000158 * Gets type literal for the given {@code Type} instance.
crazyboblee41bc8522006-12-08 07:06:55 +0000159 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000160 public static TypeLiteral<?> get(Type type) {
limpbizkiteab76472008-05-26 19:45:12 +0000161 return new TypeLiteral<Object>(type);
crazyboblee41bc8522006-12-08 07:06:55 +0000162 }
163
164 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +0000165 * Gets type literal for the given {@code Class} instance.
crazyboblee41bc8522006-12-08 07:06:55 +0000166 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000167 public static <T> TypeLiteral<T> get(Class<T> type) {
limpbizkiteab76472008-05-26 19:45:12 +0000168 return new TypeLiteral<T>(type);
crazyboblee41bc8522006-12-08 07:06:55 +0000169 }
limpbizkit4f5d1f72008-11-14 08:43:40 +0000170
171
172 /** Returns an immutable list of the resolved types. */
limpbizkit4272aef2008-11-23 08:10:09 +0000173 private List<TypeLiteral<?>> resolveAll(Type[] types) {
174 TypeLiteral<?>[] result = new TypeLiteral<?>[types.length];
limpbizkit4f5d1f72008-11-14 08:43:40 +0000175 for (int t = 0; t < types.length; t++) {
176 result[t] = resolve(types[t]);
177 }
178 return ImmutableList.of(result);
179 }
180
181 /**
182 * Resolves known type parameters in {@code toResolve} and returns the result.
183 */
limpbizkit4272aef2008-11-23 08:10:09 +0000184 TypeLiteral<?> resolve(Type toResolve) {
185 return TypeLiteral.get(resolveType(toResolve));
186 }
187
188 Type resolveType(Type toResolve) {
189 // this implementation is made a little more complicated in an attempt to avoid object-creation
limpbizkit4f5d1f72008-11-14 08:43:40 +0000190 while (true) {
191 if (toResolve instanceof TypeVariable) {
192 TypeVariable original = (TypeVariable) toResolve;
193 toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
194 if (toResolve == original) {
195 return toResolve;
196 }
197
198 } else if (toResolve instanceof GenericArrayType) {
199 GenericArrayType original = (GenericArrayType) toResolve;
200 Type componentType = original.getGenericComponentType();
limpbizkit4272aef2008-11-23 08:10:09 +0000201 Type newComponentType = resolveType(componentType);
limpbizkit4f5d1f72008-11-14 08:43:40 +0000202 return componentType == newComponentType
203 ? original
204 : Types.arrayOf(newComponentType);
205
206 } else if (toResolve instanceof ParameterizedType) {
207 ParameterizedType original = (ParameterizedType) toResolve;
208 Type ownerType = original.getOwnerType();
limpbizkit4272aef2008-11-23 08:10:09 +0000209 Type newOwnerType = resolveType(ownerType);
limpbizkit4f5d1f72008-11-14 08:43:40 +0000210 boolean changed = newOwnerType != ownerType;
211
212 Type[] args = original.getActualTypeArguments();
213 for (int t = 0, length = args.length; t < length; t++) {
limpbizkit4272aef2008-11-23 08:10:09 +0000214 Type resolvedTypeArgument = resolveType(args[t]);
limpbizkit4f5d1f72008-11-14 08:43:40 +0000215 if (resolvedTypeArgument != args[t]) {
216 if (!changed) {
217 args = args.clone();
218 changed = true;
219 }
220 args[t] = resolvedTypeArgument;
221 }
222 }
223
224 return changed
225 ? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
226 : original;
227
limpbizkit8d13d412008-11-14 09:06:10 +0000228 } else if (toResolve instanceof WildcardType) {
229 WildcardType original = (WildcardType) toResolve;
230 Type[] originalLowerBound = original.getLowerBounds();
231 Type[] originalUpperBound = original.getUpperBounds();
232
233 if (originalLowerBound.length == 1) {
limpbizkit4272aef2008-11-23 08:10:09 +0000234 Type lowerBound = resolveType(originalLowerBound[0]);
limpbizkit8d13d412008-11-14 09:06:10 +0000235 if (lowerBound != originalLowerBound[0]) {
236 return Types.supertypeOf(lowerBound);
237 }
238 } else if (originalUpperBound.length == 1) {
limpbizkit4272aef2008-11-23 08:10:09 +0000239 Type upperBound = resolveType(originalUpperBound[0]);
limpbizkit8d13d412008-11-14 09:06:10 +0000240 if (upperBound != originalUpperBound[0]) {
241 return Types.subtypeOf(upperBound);
242 }
243 }
244 return original;
245
limpbizkit4f5d1f72008-11-14 08:43:40 +0000246 } else {
247 return toResolve;
248 }
249 }
250 }
251
252 /**
253 * Returns the generic form of {@code supertype}. For example, if this is {@code
254 * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
255 * Iterable.class}.
256 *
257 * @param supertype a superclass of, or interface implemented by, this.
limpbizkitc489adf2008-11-18 07:01:33 +0000258 * @since 2.0
limpbizkit4f5d1f72008-11-14 08:43:40 +0000259 */
limpbizkit4272aef2008-11-23 08:10:09 +0000260 public TypeLiteral<?> getSupertype(Class<?> supertype) {
limpbizkit4f5d1f72008-11-14 08:43:40 +0000261 checkArgument(supertype.isAssignableFrom(rawType),
262 "%s is not a supertype of %s", supertype, this.type);
263 return resolve(MoreTypes.getGenericSupertype(type, rawType, supertype));
264 }
265
266 /**
267 * Returns the resolved generic type of {@code field}.
268 *
269 * @param field a field defined by this or any superclass.
limpbizkitc489adf2008-11-18 07:01:33 +0000270 * @since 2.0
limpbizkit4f5d1f72008-11-14 08:43:40 +0000271 */
limpbizkit4272aef2008-11-23 08:10:09 +0000272 public TypeLiteral<?> getFieldType(Field field) {
limpbizkit4f5d1f72008-11-14 08:43:40 +0000273 checkArgument(field.getDeclaringClass().isAssignableFrom(rawType),
274 "%s is not defined by a supertype of %s", field, type);
275 return resolve(field.getGenericType());
276 }
277
278 /**
279 * Returns the resolved generic parameter types of {@code methodOrConstructor}.
280 *
281 * @param methodOrConstructor a method or constructor defined by this or any supertype.
limpbizkitc489adf2008-11-18 07:01:33 +0000282 * @since 2.0
limpbizkit4f5d1f72008-11-14 08:43:40 +0000283 */
limpbizkit4272aef2008-11-23 08:10:09 +0000284 public List<TypeLiteral<?>> getParameterTypes(Member methodOrConstructor) {
limpbizkit4f5d1f72008-11-14 08:43:40 +0000285 Type[] genericParameterTypes;
286
287 if (methodOrConstructor instanceof Method) {
288 Method method = (Method) methodOrConstructor;
289 checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
290 "%s is not defined by a supertype of %s", method, type);
291 genericParameterTypes = method.getGenericParameterTypes();
292
293 } else if (methodOrConstructor instanceof Constructor) {
limpbizkit5ae41eb2009-06-06 17:51:27 +0000294 Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
limpbizkit4f5d1f72008-11-14 08:43:40 +0000295 checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType),
296 "%s does not construct a supertype of %s", constructor, type);
297 genericParameterTypes = constructor.getGenericParameterTypes();
298
299 } else {
300 throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
301 }
302
303 return resolveAll(genericParameterTypes);
304 }
305
306 /**
307 * Returns the resolved generic exception types thrown by {@code constructor}.
308 *
309 * @param methodOrConstructor a method or constructor defined by this or any supertype.
limpbizkitc489adf2008-11-18 07:01:33 +0000310 * @since 2.0
limpbizkit4f5d1f72008-11-14 08:43:40 +0000311 */
limpbizkit4272aef2008-11-23 08:10:09 +0000312 public List<TypeLiteral<?>> getExceptionTypes(Member methodOrConstructor) {
limpbizkit4f5d1f72008-11-14 08:43:40 +0000313 Type[] genericExceptionTypes;
314
315 if (methodOrConstructor instanceof Method) {
316 Method method = (Method) methodOrConstructor;
317 checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
318 "%s is not defined by a supertype of %s", method, type);
319 genericExceptionTypes = method.getGenericExceptionTypes();
320
321 } else if (methodOrConstructor instanceof Constructor) {
322 Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
323 checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType),
324 "%s does not construct a supertype of %s", constructor, type);
325 genericExceptionTypes = constructor.getGenericExceptionTypes();
326
327 } else {
328 throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
329 }
330
331 return resolveAll(genericExceptionTypes);
332 }
333
334 /**
335 * Returns the resolved generic return type of {@code method}.
336 *
337 * @param method a method defined by this or any supertype.
limpbizkitc489adf2008-11-18 07:01:33 +0000338 * @since 2.0
limpbizkit4f5d1f72008-11-14 08:43:40 +0000339 */
limpbizkit4272aef2008-11-23 08:10:09 +0000340 public TypeLiteral<?> getReturnType(Method method) {
limpbizkit4f5d1f72008-11-14 08:43:40 +0000341 checkArgument(method.getDeclaringClass().isAssignableFrom(rawType),
342 "%s is not defined by a supertype of %s", method, type);
343 return resolve(method.getGenericReturnType());
344 }
crazyboblee41bc8522006-12-08 07:06:55 +0000345}