diff options
Diffstat (limited to 'gson/src/main/java/com/google/gson/reflect')
-rw-r--r-- | gson/src/main/java/com/google/gson/reflect/TypeToken.java | 305 | ||||
-rw-r--r-- | gson/src/main/java/com/google/gson/reflect/package-info.java | 6 |
2 files changed, 311 insertions, 0 deletions
diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java new file mode 100644 index 00000000..e16e8e6d --- /dev/null +++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gson.reflect; + +import com.google.gson.internal.$Gson$Types; +import com.google.gson.internal.$Gson$Preconditions; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.HashMap; +import java.util.Map; + +/** + * Represents a generic type {@code T}. Java doesn't yet provide a way to + * represent generic types, so this class does. Forces clients to create a + * subclass of this class which enables retrieval the type information even at + * runtime. + * + * <p>For example, to create a type literal for {@code List<String>}, you can + * create an empty anonymous inner class: + * + * <p> + * {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};} + * + * <p>This syntax cannot be used to create type literals that have wildcard + * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}. + * + * @author Bob Lee + * @author Sven Mawson + * @author Jesse Wilson + */ +public class TypeToken<T> { + final Class<? super T> rawType; + final Type type; + final int hashCode; + + /** + * Constructs a new type literal. Derives represented class from type + * parameter. + * + * <p>Clients create an empty anonymous subclass. Doing so embeds the type + * parameter in the anonymous class's type hierarchy so we can reconstitute it + * at runtime despite erasure. + */ + @SuppressWarnings("unchecked") + protected TypeToken() { + this.type = getSuperclassTypeParameter(getClass()); + this.rawType = (Class<? super T>) $Gson$Types.getRawType(type); + this.hashCode = type.hashCode(); + } + + /** + * Unsafe. Constructs a type literal manually. + */ + @SuppressWarnings("unchecked") + TypeToken(Type type) { + this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type)); + this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type); + this.hashCode = this.type.hashCode(); + } + + /** + * Returns the type from super class's type parameter in {@link $Gson$Types#canonicalize + * canonical form}. + */ + static Type getSuperclassTypeParameter(Class<?> subclass) { + Type superclass = subclass.getGenericSuperclass(); + if (superclass instanceof Class) { + throw new RuntimeException("Missing type parameter."); + } + ParameterizedType parameterized = (ParameterizedType) superclass; + return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); + } + + /** + * Returns the raw (non-generic) type for this type. + */ + public final Class<? super T> getRawType() { + return rawType; + } + + /** + * Gets underlying {@code Type} instance. + */ + public final Type getType() { + return type; + } + + /** + * Check if this type is assignable from the given class object. + * + * @deprecated this implementation may be inconsistent with javac for types + * with wildcards. + */ + @Deprecated + public boolean isAssignableFrom(Class<?> cls) { + return isAssignableFrom((Type) cls); + } + + /** + * Check if this type is assignable from the given Type. + * + * @deprecated this implementation may be inconsistent with javac for types + * with wildcards. + */ + @Deprecated + public boolean isAssignableFrom(Type from) { + if (from == null) { + return false; + } + + if (type.equals(from)) { + return true; + } + + if (type instanceof Class<?>) { + return rawType.isAssignableFrom($Gson$Types.getRawType(from)); + } else if (type instanceof ParameterizedType) { + return isAssignableFrom(from, (ParameterizedType) type, + new HashMap<String, Type>()); + } else if (type instanceof GenericArrayType) { + return rawType.isAssignableFrom($Gson$Types.getRawType(from)) + && isAssignableFrom(from, (GenericArrayType) type); + } else { + throw buildUnexpectedTypeError( + type, Class.class, ParameterizedType.class, GenericArrayType.class); + } + } + + /** + * Check if this type is assignable from the given type token. + * + * @deprecated this implementation may be inconsistent with javac for types + * with wildcards. + */ + @Deprecated + public boolean isAssignableFrom(TypeToken<?> token) { + return isAssignableFrom(token.getType()); + } + + /** + * Private helper function that performs some assignability checks for + * the provided GenericArrayType. + */ + private static boolean isAssignableFrom(Type from, GenericArrayType to) { + Type toGenericComponentType = to.getGenericComponentType(); + if (toGenericComponentType instanceof ParameterizedType) { + Type t = from; + if (from instanceof GenericArrayType) { + t = ((GenericArrayType) from).getGenericComponentType(); + } else if (from instanceof Class<?>) { + Class<?> classType = (Class<?>) from; + while (classType.isArray()) { + classType = classType.getComponentType(); + } + t = classType; + } + return isAssignableFrom(t, (ParameterizedType) toGenericComponentType, + new HashMap<String, Type>()); + } + // No generic defined on "to"; therefore, return true and let other + // checks determine assignability + return true; + } + + /** + * Private recursive helper function to actually do the type-safe checking + * of assignability. + */ + private static boolean isAssignableFrom(Type from, ParameterizedType to, + Map<String, Type> typeVarMap) { + + if (from == null) { + return false; + } + + if (to.equals(from)) { + return true; + } + + // First figure out the class and any type information. + Class<?> clazz = $Gson$Types.getRawType(from); + ParameterizedType ptype = null; + if (from instanceof ParameterizedType) { + ptype = (ParameterizedType) from; + } + + // Load up parameterized variable info if it was parameterized. + if (ptype != null) { + Type[] tArgs = ptype.getActualTypeArguments(); + TypeVariable<?>[] tParams = clazz.getTypeParameters(); + for (int i = 0; i < tArgs.length; i++) { + Type arg = tArgs[i]; + TypeVariable<?> var = tParams[i]; + while (arg instanceof TypeVariable<?>) { + TypeVariable<?> v = (TypeVariable<?>) arg; + arg = typeVarMap.get(v.getName()); + } + typeVarMap.put(var.getName(), arg); + } + + // check if they are equivalent under our current mapping. + if (typeEquals(ptype, to, typeVarMap)) { + return true; + } + } + + for (Type itype : clazz.getGenericInterfaces()) { + if (isAssignableFrom(itype, to, new HashMap<String, Type>(typeVarMap))) { + return true; + } + } + + // Interfaces didn't work, try the superclass. + Type sType = clazz.getGenericSuperclass(); + return isAssignableFrom(sType, to, new HashMap<String, Type>(typeVarMap)); + } + + /** + * Checks if two parameterized types are exactly equal, under the variable + * replacement described in the typeVarMap. + */ + private static boolean typeEquals(ParameterizedType from, + ParameterizedType to, Map<String, Type> typeVarMap) { + if (from.getRawType().equals(to.getRawType())) { + Type[] fromArgs = from.getActualTypeArguments(); + Type[] toArgs = to.getActualTypeArguments(); + for (int i = 0; i < fromArgs.length; i++) { + if (!matches(fromArgs[i], toArgs[i], typeVarMap)) { + return false; + } + } + return true; + } + return false; + } + + private static AssertionError buildUnexpectedTypeError( + Type token, Class<?>... expected) { + + // Build exception message + StringBuilder exceptionMessage = + new StringBuilder("Unexpected type. Expected one of: "); + for (Class<?> clazz : expected) { + exceptionMessage.append(clazz.getName()).append(", "); + } + exceptionMessage.append("but got: ").append(token.getClass().getName()) + .append(", for type token: ").append(token.toString()).append('.'); + + return new AssertionError(exceptionMessage.toString()); + } + + /** + * Checks if two types are the same or are equivalent under a variable mapping + * given in the type map that was provided. + */ + private static boolean matches(Type from, Type to, Map<String, Type> typeMap) { + return to.equals(from) + || (from instanceof TypeVariable + && to.equals(typeMap.get(((TypeVariable<?>) from).getName()))); + + } + + @Override public final int hashCode() { + return this.hashCode; + } + + @Override public final boolean equals(Object o) { + return o instanceof TypeToken<?> + && $Gson$Types.equals(type, ((TypeToken<?>) o).type); + } + + @Override public final String toString() { + return $Gson$Types.typeToString(type); + } + + /** + * Gets type literal for the given {@code Type} instance. + */ + public static TypeToken<?> get(Type type) { + return new TypeToken<Object>(type); + } + + /** + * Gets type literal for the given {@code Class} instance. + */ + public static <T> TypeToken<T> get(Class<T> type) { + return new TypeToken<T>(type); + } +} diff --git a/gson/src/main/java/com/google/gson/reflect/package-info.java b/gson/src/main/java/com/google/gson/reflect/package-info.java new file mode 100644 index 00000000..e666c431 --- /dev/null +++ b/gson/src/main/java/com/google/gson/reflect/package-info.java @@ -0,0 +1,6 @@ +/** + * This package provides utility classes for finding type information for generic types. + * + * @author Inderjeet Singh, Joel Leitch + */ +package com.google.gson.reflect;
\ No newline at end of file |