001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.bm.ejb3guice.inject;
016:
017: import static com.bm.ejb3guice.internal.Objects.nonNull;
018:
019: import java.lang.reflect.GenericArrayType;
020: import java.lang.reflect.ParameterizedType;
021: import java.lang.reflect.Type;
022:
023: /**
024: * Represents a generic type {@code T}. Java doesn't yet provide a way to
025: * represent generic types, so this class does. Forces clients to create a
026: * subclass of this class which enables retrieval the type information even at
027: * runtime.
028: *
029: * <p>For example, to create a type literal for {@code List<String>}, you can
030: * create an empty anonymous inner class:
031: *
032: * <p>
033: * {@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
034: *
035: * <p>Assumes that type {@code T} implements {@link Object#equals} and
036: * {@link Object#hashCode()} as value (as opposed to identity) comparison.
037: *
038: * @author crazybob@google.com (Bob Lee)
039: */
040: public abstract class TypeLiteral<T> {
041:
042: final Class<? super T> rawType;
043: final Type type;
044: final int hashCode;
045:
046: /**
047: * Constructs a new type literal. Derives represented class from type
048: * parameter.
049: *
050: * <p>Clients create an empty anonymous subclass. Doing so embeds the type
051: * parameter in the anonymous class's type hierarchy so we can reconstitute it
052: * at runtime despite erasure.
053: */
054: @SuppressWarnings("unchecked")
055: protected TypeLiteral() {
056: this .type = getSuperclassTypeParameter(getClass());
057: this .rawType = (Class<? super T>) getRawType(type);
058: this .hashCode = hashCode(type);
059: }
060:
061: /**
062: * Unsafe. Constructs a type literal manually.
063: */
064: @SuppressWarnings("unchecked")
065: TypeLiteral(Type type) {
066: this .rawType = (Class<? super T>) getRawType(nonNull(type,
067: "type"));
068: this .type = type;
069: this .hashCode = hashCode(type);
070: }
071:
072: /**
073: * Gets type from super class's type parameter.
074: */
075: static Type getSuperclassTypeParameter(Class<?> subclass) {
076: Type super class = subclass.getGenericSuperclass();
077: if (super class instanceof Class) {
078: throw new RuntimeException("Missing type parameter.");
079: }
080: return ((ParameterizedType) super class)
081: .getActualTypeArguments()[0];
082: }
083:
084: /**
085: * Gets type literal from super class's type parameter.
086: */
087: static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
088: return new SimpleTypeLiteral<Object>(
089: getSuperclassTypeParameter(subclass));
090: }
091:
092: @SuppressWarnings({"unchecked"})
093: private static Class<?> getRawType(Type type) {
094: if (type instanceof Class<?>) {
095: // type is a normal class.
096: return (Class<?>) type;
097: } else {
098: if (type instanceof ParameterizedType) {
099: ParameterizedType parameterizedType = (ParameterizedType) type;
100:
101: // I'm not exactly sure why getRawType() returns Type instead of Class.
102: // Neal isn't either but suspects some pathological case related
103: // to nested classes exists.
104: Type rawType = parameterizedType.getRawType();
105: if (!(rawType instanceof Class<?>)) {
106: throw unexpectedType(rawType, Class.class);
107: }
108: return (Class<?>) rawType;
109: }
110:
111: if (type instanceof GenericArrayType) {
112: // TODO: Is this sufficient?
113: return Object[].class;
114: }
115:
116: // type is a parameterized type.
117: throw unexpectedType(type, ParameterizedType.class);
118: }
119: }
120:
121: /**
122: * Gets the raw type.
123: */
124: Class<? super T> getRawType() {
125: return rawType;
126: }
127:
128: /**
129: * Gets underlying {@code Type} instance.
130: */
131: public Type getType() {
132: return type;
133: }
134:
135: public int hashCode() {
136: return this .hashCode;
137: }
138:
139: public boolean equals(Object o) {
140: if (o == this ) {
141: return true;
142: }
143: if (!(o instanceof TypeLiteral<?>)) {
144: return false;
145: }
146: TypeLiteral<?> other = (TypeLiteral<?>) o;
147:
148: return equals(type, other.type);
149: }
150:
151: public String toString() {
152: return type instanceof Class<?> ? ((Class<?>) type).getName()
153: : type.toString();
154: }
155:
156: static AssertionError unexpectedType(Type type, Class<?> expected) {
157: return new AssertionError("Unexpected type. Expected: "
158: + expected.getName() + ", got: "
159: + type.getClass().getName() + ", for type literal: "
160: + type.toString() + ".");
161: }
162:
163: /**
164: * Gets type literal for the given {@code Type} instance.
165: */
166: public static TypeLiteral<?> get(Type type) {
167: return new SimpleTypeLiteral<Object>(type);
168: }
169:
170: /**
171: * Gets type literal for the given {@code Class} instance.
172: */
173: public static <T> TypeLiteral<T> get(Class<T> type) {
174: return new SimpleTypeLiteral<T>(type);
175: }
176:
177: private static class SimpleTypeLiteral<T> extends TypeLiteral<T> {
178: public SimpleTypeLiteral(Type type) {
179: super (type);
180: }
181: }
182:
183: static int hashCode(Type type) {
184: if (type instanceof ParameterizedType) {
185: ParameterizedType p = (ParameterizedType) type;
186: int h = p.getRawType().hashCode();
187: for (Type argument : p.getActualTypeArguments()) {
188: h = h * 31 + hashCode(argument);
189: }
190: return h;
191: }
192:
193: if (type instanceof Class) {
194: // Class specifies hashCode().
195: return type.hashCode();
196: }
197:
198: if (type instanceof GenericArrayType) {
199: return hashCode(((GenericArrayType) type)
200: .getGenericComponentType()) * 31;
201: }
202:
203: // This isn't a type we support. Could be a generic array type, wildcard
204: // type, etc.
205: return type.hashCode();
206: }
207:
208: static boolean equals(Type a, Type b) {
209: if (a instanceof Class) {
210: // Class already specifies equals().
211: return a.equals(b);
212: }
213:
214: if (a instanceof ParameterizedType) {
215: if (!(b instanceof ParameterizedType)) {
216: return false;
217: }
218:
219: ParameterizedType pa = (ParameterizedType) a;
220: ParameterizedType pb = (ParameterizedType) b;
221:
222: if (!pa.getRawType().equals(pb.getRawType())) {
223: return false;
224: }
225:
226: Type[] aa = pa.getActualTypeArguments();
227: Type[] ba = pb.getActualTypeArguments();
228: if (aa.length != ba.length) {
229: return false;
230: }
231:
232: for (int i = 0; i < aa.length; i++) {
233: if (!equals(aa[i], ba[i])) {
234: return false;
235: }
236: }
237:
238: return true;
239: }
240:
241: if (a instanceof GenericArrayType) {
242: if (!(b instanceof GenericArrayType)) {
243: return false;
244: }
245:
246: return equals(((GenericArrayType) a)
247: .getGenericComponentType(), ((GenericArrayType) b)
248: .getGenericComponentType());
249: }
250:
251: // This isn't a type we support. Could be a generic array type, wildcard
252: // type, etc.
253: return false;
254: }
255: }
|