KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > inject > TypeLiteral


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
17 package com.google.inject;
18
19 import static com.google.inject.util.Objects.nonNull;
20 import java.lang.reflect.ParameterizedType JavaDoc;
21 import java.lang.reflect.Type JavaDoc;
22 import java.lang.reflect.GenericArrayType JavaDoc;
23 import java.lang.reflect.Array JavaDoc;
24 import java.util.Arrays JavaDoc;
25
26 /**
27  * Represents a generic type {@code T}. Java doesn't yet provide a way to
28  * represent generic types, so this class does. Forces clients to create a
29  * subclass of this class which enables retrieval the type information even at
30  * runtime.
31  *
32  * <p>For example, to create a type literal for {@code List<String>}, you can
33  * create an empty anonymous inner class:
34  *
35  * <p>
36  * {@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
37  *
38  * <p>Assumes that type {@code T} implements {@link Object#equals} and
39  * {@link Object#hashCode()} as value (as opposed to identity) comparison.
40  *
41  * @author crazybob@google.com (Bob Lee)
42  */

43 public abstract class TypeLiteral<T> {
44
45   final Class JavaDoc<? super T> rawType;
46   final Type JavaDoc type;
47   final int hashCode;
48
49   /**
50    * Constructs a new type literal. Derives represented class from type
51    * parameter.
52    *
53    * <p>Clients create an empty anonymous subclass. Doing so embeds the type
54    * parameter in the anonymous class's type hierarchy so we can reconstitute it
55    * at runtime despite erasure.
56    */

57   @SuppressWarnings JavaDoc("unchecked")
58   protected TypeLiteral() {
59     this.type = getSuperclassTypeParameter(getClass());
60     this.rawType = (Class JavaDoc<? super T>) getRawType(type);
61     this.hashCode = hashCode(type);
62   }
63
64   /**
65    * Unsafe. Constructs a type literal manually.
66    */

67   @SuppressWarnings JavaDoc("unchecked")
68   TypeLiteral(Type JavaDoc type) {
69     this.rawType = (Class JavaDoc<? super T>) getRawType(nonNull(type, "type"));
70     this.type = type;
71     this.hashCode = hashCode(type);
72   }
73
74   /**
75    * Gets type from super class's type parameter.
76    */

77   static Type JavaDoc getSuperclassTypeParameter(Class JavaDoc<?> subclass) {
78     Type JavaDoc superclass = subclass.getGenericSuperclass();
79     if (superclass instanceof Class JavaDoc) {
80       throw new RuntimeException JavaDoc("Missing type parameter.");
81     }
82     return ((ParameterizedType JavaDoc) superclass).getActualTypeArguments()[0];
83   }
84
85   /**
86    * Gets type literal from super class's type parameter.
87    */

88   static TypeLiteral<?> fromSuperclassTypeParameter(Class JavaDoc<?> subclass) {
89     return new SimpleTypeLiteral<Object JavaDoc>(getSuperclassTypeParameter(subclass));
90   }
91
92   @SuppressWarnings JavaDoc({ "unchecked" })
93   private static Class JavaDoc<?> getRawType(Type JavaDoc type) {
94     if (type instanceof Class JavaDoc<?>) {
95       // type is a normal class.
96
return (Class JavaDoc<?>) type;
97     }
98     else {
99       if (type instanceof ParameterizedType JavaDoc) {
100         ParameterizedType JavaDoc parameterizedType = (ParameterizedType JavaDoc) type;
101
102         // I'm not exactly sure why getRawType() returns Type instead of Class.
103
// Neal isn't either but suspects some pathological case related
104
// to nested classes exists.
105
Type JavaDoc rawType = parameterizedType.getRawType();
106         if (!(rawType instanceof Class JavaDoc<?>)) {
107           throw unexpectedType(rawType, Class JavaDoc.class);
108         }
109         return (Class JavaDoc<?>) rawType;
110       }
111
112       if (type instanceof GenericArrayType JavaDoc) {
113         // TODO: Is this sufficient?
114
return Object JavaDoc[].class;
115       }
116
117       // type is a parameterized type.
118
throw unexpectedType(type, ParameterizedType JavaDoc.class);
119     }
120   }
121
122   /**
123    * Gets the raw type.
124    */

125   Class JavaDoc<? super T> getRawType() {
126     return rawType;
127   }
128
129   /**
130    * Gets underlying {@code Type} instance.
131    */

132   public Type JavaDoc getType() {
133     return type;
134   }
135
136   public int hashCode() {
137     return this.hashCode;
138   }
139
140   public boolean equals(Object JavaDoc o) {
141     if (o == this) {
142       return true;
143     }
144     if (!(o instanceof TypeLiteral<?>)) {
145       return false;
146     }
147     TypeLiteral<?> other = (TypeLiteral<?>) o;
148
149     return equals(type, other.type);
150   }
151
152   public String JavaDoc toString() {
153     return type instanceof Class JavaDoc<?>
154         ? ((Class JavaDoc<?>) type).getName()
155         : type.toString();
156   }
157
158   static AssertionError JavaDoc unexpectedType(Type JavaDoc type, Class JavaDoc<?> expected) {
159     return new AssertionError JavaDoc(
160         "Unexpected type. Expected: " + expected.getName()
161         + ", got: " + type.getClass().getName()
162         + ", for type literal: " + type.toString() + ".");
163   }
164
165   /**
166    * Gets type literal for the given {@code Type} instance.
167    */

168   public static TypeLiteral<?> get(Type JavaDoc type) {
169     return new SimpleTypeLiteral<Object JavaDoc>(type);
170   }
171
172   /**
173    * Gets type literal for the given {@code Class} instance.
174    */

175   public static <T> TypeLiteral<T> get(Class JavaDoc<T> type) {
176     return new SimpleTypeLiteral<T>(type);
177   }
178
179   private static class SimpleTypeLiteral<T> extends TypeLiteral<T> {
180     public SimpleTypeLiteral(Type JavaDoc type) {
181       super(type);
182     }
183   }
184
185   static int hashCode(Type JavaDoc type) {
186     if (type instanceof ParameterizedType JavaDoc) {
187       ParameterizedType JavaDoc p = (ParameterizedType JavaDoc) type;
188       int h = p.getRawType().hashCode();
189       for (Type JavaDoc argument : p.getActualTypeArguments()) {
190         h = h * 31 + hashCode(argument);
191       }
192       return h;
193     }
194
195     if (type instanceof Class JavaDoc) {
196       // Class specifies hashCode().
197
return type.hashCode();
198     }
199
200     if (type instanceof GenericArrayType JavaDoc) {
201       return hashCode(((GenericArrayType JavaDoc) type).getGenericComponentType()) * 31;
202     }
203
204     // This isn't a type we support. Could be a generic array type, wildcard
205
// type, etc.
206
return type.hashCode();
207   }
208
209   static boolean equals(Type JavaDoc a, Type JavaDoc b) {
210     if (a instanceof Class JavaDoc) {
211       // Class already specifies equals().
212
return a.equals(b);
213     }
214
215     if (a instanceof ParameterizedType JavaDoc) {
216       if (!(b instanceof ParameterizedType JavaDoc)) {
217         return false;
218       }
219
220       ParameterizedType JavaDoc pa = (ParameterizedType JavaDoc) a;
221       ParameterizedType JavaDoc pb = (ParameterizedType JavaDoc) b;
222
223       if (!pa.getRawType().equals(pb.getRawType())) {
224         return false;
225       }
226
227       Type JavaDoc[] aa = pa.getActualTypeArguments();
228       Type JavaDoc[] ba = pb.getActualTypeArguments();
229       if (aa.length != ba.length) {
230         return false;
231       }
232
233       for (int i = 0; i < aa.length; i++) {
234         if (!equals(aa[i], ba[i])) {
235           return false;
236         }
237       }
238
239       return true;
240     }
241
242     if (a instanceof GenericArrayType JavaDoc) {
243       if (!(b instanceof GenericArrayType JavaDoc)) {
244         return false;
245       }
246
247       return equals(
248           ((GenericArrayType JavaDoc) a).getGenericComponentType(),
249           ((GenericArrayType JavaDoc) b).getGenericComponentType()
250       );
251     }
252
253     // This isn't a type we support. Could be a generic array type, wildcard
254
// type, etc.
255
return false;
256   }
257 }
258
Popular Tags