Swap out GSON's type mechanics with the Type resolution code from Guice.

The most significant impact of this change is that fields whose types are type parameters should now GSONify just fine. For example, consider the class below.

abstract class Foo<A, B> {
   A a;
   B b;
   List<A> list;
   Map<A, List<B>> map;
}
class RealFoo extends Foo<String, Integer> {...}

This is a reasonable checkpoint but some work still needs to be done for this. In particular, the level of visibility of methods in TypeToken and Type should be reconsidered; we're exposing more than we need to!
This commit is contained in:
Jesse Wilson 2010-12-03 08:07:13 +00:00
parent faa5464e84
commit d1ddab2e6f
22 changed files with 876 additions and 1337 deletions

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
@ -517,7 +518,7 @@ final class DefaultTypeAdapters {
JsonArray array = new JsonArray();
Type childGenericType = null;
if (typeOfSrc instanceof ParameterizedType) {
childGenericType = new TypeInfoCollection(typeOfSrc).getElementType();
childGenericType = TypeToken.get(typeOfSrc).getCollectionElementType();
}
for (Object child : src) {
if (child == null) {
@ -540,7 +541,7 @@ final class DefaultTypeAdapters {
// Use ObjectConstructor to create instance instead of hard-coding a specific type.
// This handles cases where users are using their own subclass of Collection.
Collection collection = constructCollectionType(typeOfT, context);
Type childType = new TypeInfoCollection(typeOfT).getElementType();
Type childType = TypeToken.get(typeOfT).getCollectionElementType();
for (JsonElement childElement : json.getAsJsonArray()) {
if (childElement == null || childElement.isJsonNull()) {
collection.add(null);
@ -578,7 +579,7 @@ final class DefaultTypeAdapters {
JsonObject map = new JsonObject();
Type childGenericType = null;
if (typeOfSrc instanceof ParameterizedType) {
childGenericType = new TypeInfoMap(typeOfSrc).getValueType();
childGenericType = TypeToken.get(typeOfSrc).getMapKeyAndValueTypes()[1];
}
for (Map.Entry entry : (Set<Map.Entry>) src.entrySet()) {
@ -602,10 +603,10 @@ final class DefaultTypeAdapters {
// Use ObjectConstructor to create instance instead of hard-coding a specific type.
// This handles cases where users are using their own subclass of Map.
Map<Object, Object> map = constructMapType(typeOfT, context);
TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT);
Type[] keyAndValueTypes = TypeToken.get(typeOfT).getMapKeyAndValueTypes();
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
Object key = context.deserialize(new JsonPrimitive(entry.getKey()), mapTypeInfo.getKeyType());
Object value = context.deserialize(entry.getValue(), mapTypeInfo.getValueType());
Object key = context.deserialize(new JsonPrimitive(entry.getKey()), keyAndValueTypes[0]);
Object value = context.deserialize(entry.getValue(), keyAndValueTypes[1]);
map.put(key, value);
}
return map;

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
@ -39,21 +40,20 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
@SuppressWarnings("unchecked")
protected T constructTarget() {
TypeInfo typeInfo = new TypeInfo(targetType);
TypeToken typeToken = TypeToken.get(targetType);
if (!json.isJsonArray()) {
throw new JsonParseException("Expecting array found: " + json);
}
JsonArray jsonArray = json.getAsJsonArray();
if (typeInfo.isArray()) {
TypeInfoArray arrayTypeInfo = TypeInfoFactory.getTypeInfoForArray(targetType);
if (typeToken.isArray()) {
// We know that we are getting back an array of the required type, so
// this typecasting is safe.
return (T) objectConstructor.constructArray(arrayTypeInfo.getSecondLevelType(),
return (T) objectConstructor.constructArray(typeToken.getArrayComponentType(),
jsonArray.size());
}
// is a collection
return (T) objectConstructor.construct(typeInfo.getRawClass());
return (T) objectConstructor.construct(typeToken.getRawType());
}
public void visitArray(Object array, Type arrayType) {
@ -61,7 +61,7 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
throw new JsonParseException("Expecting array found: " + json);
}
JsonArray jsonArray = json.getAsJsonArray();
TypeInfoArray arrayTypeInfo = TypeInfoFactory.getTypeInfoForArray(arrayType);
TypeToken typeToken = TypeToken.get(arrayType);
for (int i = 0; i < jsonArray.size(); i++) {
JsonElement jsonChild = jsonArray.get(i);
Object child;
@ -69,11 +69,11 @@ final class JsonArrayDeserializationVisitor<T> extends JsonDeserializationVisito
if (jsonChild == null || jsonChild.isJsonNull()) {
child = null;
} else if (jsonChild instanceof JsonObject) {
child = visitChildAsObject(arrayTypeInfo.getComponentRawType(), jsonChild);
child = visitChildAsObject(typeToken.getArrayComponentType(), jsonChild);
} else if (jsonChild instanceof JsonArray) {
child = visitChildAsArray(arrayTypeInfo.getSecondLevelType(), jsonChild.getAsJsonArray());
child = visitChildAsArray(typeToken.getArrayComponentType(), jsonChild.getAsJsonArray());
} else if (jsonChild instanceof JsonPrimitive) {
child = visitChildAsObject(arrayTypeInfo.getComponentRawType(),
child = visitChildAsObject(typeToken.getArrayComponentType(),
jsonChild.getAsJsonPrimitive());
} else {
throw new IllegalStateException();

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
/**
@ -99,11 +100,11 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
throw new JsonParseException("Expecting object found: " + json);
}
JsonElement child = json.getAsJsonObject().get(fName);
TypeInfo typeInfo = new TypeInfo(declaredTypeOfField);
boolean isPrimitive = TypeToken.get(declaredTypeOfField).isPrimitive();
if (child == null) { // Child will be null if the field wasn't present in Json
return true;
} else if (child.isJsonNull()) {
if (!typeInfo.isPrimitive()) {
if (!isPrimitive) {
f.set(parent, null);
}
return true;
@ -114,7 +115,7 @@ final class JsonObjectDeserializationVisitor<T> extends JsonDeserializationVisit
return false;
}
Object value = invokeCustomDeserializer(child, pair);
if (value != null || !typeInfo.isPrimitive()) {
if (value != null || !isPrimitive) {
f.set(parent, value);
}
return true;

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
@ -71,15 +72,12 @@ final class JsonSerializationVisitor implements ObjectNavigator.Visitor {
public void visitArray(Object array, Type arrayType) {
assignToRoot(new JsonArray());
int length = Array.getLength(array);
TypeInfoArray fieldTypeInfo = TypeInfoFactory.getTypeInfoForArray(arrayType);
Type componentType = fieldTypeInfo.getSecondLevelType();
Type componentType = TypeToken.get(arrayType).getArrayComponentType();
for (int i = 0; i < length; ++i) {
Object child = Array.get(array, i);
Type childType = componentType;
// we should not get more specific component type yet since it is possible
// that a custom
// serializer is registered for the componentType
addAsArrayElement(new ObjectTypePair(child, childType, false));
// that a custom serializer is registered for the componentType
addAsArrayElement(new ObjectTypePair(child, componentType, false));
}
}

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
@ -54,7 +55,7 @@ final class MappedObjectConstructor implements ObjectConstructor {
}
public Object constructArray(Type type, int length) {
return Array.newInstance(TypeUtils.toRawClass(type), length);
return Array.newInstance(TypeToken.get(type).getRawType(), length);
}
private <T> T constructWithNoArgConstructor(Type typeOfT) {
@ -79,8 +80,7 @@ final class MappedObjectConstructor implements ObjectConstructor {
@SuppressWarnings({"unchecked", "cast"})
private <T> Constructor<T> getNoArgsConstructor(Type typeOfT) {
TypeInfo typeInfo = new TypeInfo(typeOfT);
Class<T> clazz = (Class<T>) typeInfo.getRawClass();
Class<?> clazz = TypeToken.get(typeOfT).getRawType();
Constructor<T>[] declaredConstructors = (Constructor<T>[]) clazz.getDeclaredConstructors();
AccessibleObject.setAccessible(declaredConstructors, true);
for (Constructor<T> constructor : declaredConstructors) {

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
@ -99,8 +100,8 @@ final class ObjectNavigator {
* does not get visited.
*/
public void accept(Visitor visitor) {
TypeInfo objTypeInfo = new TypeInfo(objTypePair.type);
if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawClass())) {
TypeToken<?> objTypeInfo = TypeToken.get(objTypePair.type);
if (exclusionStrategy.shouldSkipClass(objTypeInfo.getRawType())) {
return;
}
boolean visitedWithCustomHandler = visitor.visitUsingCustomHandler(objTypePair);
@ -115,7 +116,7 @@ final class ObjectNavigator {
try {
if (objTypeInfo.isArray()) {
visitor.visitArray(objectToVisit, objTypePair.type);
} else if (objTypeInfo.getActualType() == Object.class
} else if (objTypeInfo.getType() == Object.class
&& isPrimitiveOrString(objectToVisit)) {
// TODO(Joel): this is only used for deserialization of "primitives"
// we should rethink this!!!
@ -124,7 +125,7 @@ final class ObjectNavigator {
} else {
visitor.startVisitingObject(objectToVisit);
ObjectTypePair currObjTypePair = objTypePair.toMoreSpecificType();
Class<?> topLevelClass = new TypeInfo(currObjTypePair.type).getRawClass();
Class<?> topLevelClass = TypeToken.get(currObjTypePair.type).getRawType();
for (Class<?> curr = topLevelClass; curr != null && !curr.equals(Object.class); curr =
curr.getSuperclass()) {
if (!curr.isSynthetic()) {
@ -153,12 +154,12 @@ final class ObjectNavigator {
|| exclusionStrategy.shouldSkipClass(fieldAttributes.getDeclaredClass())) {
continue; // skip
}
TypeInfo fieldTypeInfo = TypeInfoFactory.getTypeInfoForField(f, objTypePair.type);
Type declaredTypeOfField = fieldTypeInfo.getActualType();
TypeToken<?> fieldTypeToken = getTypeInfoForField(f, objTypePair.type);
Type declaredTypeOfField = fieldTypeToken.getType();
boolean visitedWithCustomHandler =
visitor.visitFieldUsingCustomHandler(fieldAttributes, declaredTypeOfField, obj);
if (!visitedWithCustomHandler) {
if (fieldTypeInfo.isArray()) {
if (fieldTypeToken.isArray()) {
visitor.visitArrayField(fieldAttributes, declaredTypeOfField, obj);
} else {
visitor.visitObjectField(fieldAttributes, declaredTypeOfField, obj);
@ -166,4 +167,22 @@ final class ObjectNavigator {
}
}
}
/**
* Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a
* "TypeVariable" in a parameterized type then it evaluates the real type.
*
* @param f the actual field object to retrieve the type from
* @param typeDefiningF the type that contains the field {@code f}
* @return the type information for the field
*/
public static TypeToken<?> getTypeInfoForField(Field f, Type typeDefiningF) {
TypeToken<?> typeToken = TypeToken.get(typeDefiningF);
if (!f.getDeclaringClass().isAssignableFrom(typeToken.getRawType())) {
// this field is unrelated to the type; the user probably omitted type information
return TypeToken.get(f.getGenericType());
}
return typeToken.getFieldType(f);
}
}

View File

@ -16,6 +16,7 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
@ -121,7 +122,7 @@ final class ParameterizedTypeHandlerMap<T> {
public synchronized T getHandlerFor(Type type) {
T handler = map.get(type);
if (handler == null) {
Class<?> rawClass = TypeUtils.toRawClass(type);
Class<?> rawClass = TypeToken.get(type).getRawType();
if (rawClass != type) {
handler = getHandlerFor(rawClass);
}
@ -195,6 +196,6 @@ final class ParameterizedTypeHandlerMap<T> {
}
private String typeToString(Type type) {
return TypeUtils.toRawClass(type).getSimpleName();
return TypeToken.get(type).getRawType().getSimpleName();
}
}

View File

@ -1,35 +0,0 @@
/*
* 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;
/**
* This class is responsible for adapting/converting an particular "from"
* instance to an instance of type "to".
*
* @author Joel Leitch
*/
interface TypeAdapter {
/**
* Adapts an object instance "from" to and instance of type "to".
*
* @param from the object to adapt
* @param to the Type/Class which this will convert to
* @return the converted "from" instance to type "to"
*/
public <T> T adaptType(Object from, Class<T> to);
}

View File

@ -1,76 +0,0 @@
/*
* 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;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* Class that provides information relevant to different parts of a type.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
class TypeInfo {
protected final Type actualType;
protected final Class<?> rawClass;
TypeInfo(Type actualType) {
this.actualType = actualType;
rawClass = TypeUtils.toRawClass(actualType);
}
public final Type getActualType() {
return actualType;
}
/**
* Returns the corresponding wrapper type of {@code type} if it is a primitive
* type; otherwise returns {@code type} itself. Idempotent.
* <pre>
* wrap(int.class) == Integer.class
* wrap(Integer.class) == Integer.class
* wrap(String.class) == String.class
* </pre>
*/
public final Class<?> getWrappedClass() {
return Primitives.wrap(rawClass);
}
/**
* @return the raw class associated with this type
*/
public final Class<?> getRawClass() {
return rawClass;
}
public final boolean isCollectionOrArray() {
return Collection.class.isAssignableFrom(rawClass) || isArray();
}
public final boolean isArray() {
return TypeUtils.isArray(rawClass);
}
public final boolean isEnum() {
return rawClass.isEnum();
}
public final boolean isPrimitive() {
return Primitives.isWrapperType(Primitives.wrap(rawClass));
}
}

View File

@ -1,69 +0,0 @@
/*
* 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;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
/**
* Class to extract information about types used to define a generic array.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class TypeInfoArray extends TypeInfo {
private final Class<?> componentRawType;
private final Type secondLevel;
TypeInfoArray(Type type) {
super(type);
Class<?> rootComponentType = rawClass;
while (rootComponentType.isArray()) {
rootComponentType = rootComponentType.getComponentType();
}
this.componentRawType = rootComponentType;
this.secondLevel = extractSecondLevelType(actualType, rawClass);
}
private static Type extractSecondLevelType(Type actualType, Class<?> rawClass) {
return actualType instanceof GenericArrayType ?
((GenericArrayType) actualType).getGenericComponentType() : rawClass.getComponentType();
}
/**
* @return the raw type unwrapped of the second level of array.
* If the object is (single-dimensional or multi-dimensional) array, it is the class of the
* elements of the array. For example, this method returns Foo.class for Foo[].
* It will return Foo[].class for Foo[][]. For Foo&lt;String&gt;[][] types, it will return the
* type representing Foo&lt;String&gt;[]
* (i.e. <code>new TypeToken<Foo<String>[]>() {}.getType()</code>).
*/
public Type getSecondLevelType() {
return secondLevel;
}
/**
* @return the raw type of the root component.
* If the object is a single-dimensional array then the component type is the class of an
* element of the array.
* If the object is a multi-dimensional array then the component type is the class of the
* inner-most array element. For example, the This method will return Foo.class for Foo[][][].
*/
public Class<?> getComponentRawType() {
return componentRawType;
}
}

View File

@ -1,46 +0,0 @@
/*
* 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;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
/**
* A convenience object for retrieving the map type information.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class TypeInfoCollection {
private final ParameterizedType collectionType;
public TypeInfoCollection(Type collectionType) {
if (!(collectionType instanceof ParameterizedType)) {
throw new IllegalArgumentException(
"Collection objects need to be parameterized unless you use a custom serializer. "
+ "Use the com.google.gson.reflect.TypeToken to extract the ParameterizedType.");
}
TypeInfo rawType = new TypeInfo(collectionType);
Preconditions.checkArgument(Collection.class.isAssignableFrom(rawType.getRawClass()));
this.collectionType = (ParameterizedType) collectionType;
}
public Type getElementType() {
return collectionType.getActualTypeArguments()[0];
}
}

View File

@ -1,175 +0,0 @@
/*
* 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;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
/**
* A static factory class used to construct the "TypeInfo" objects.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class TypeInfoFactory {
private TypeInfoFactory() {
// Not instantiable since it provides factory methods only.
}
public static TypeInfoArray getTypeInfoForArray(Type type) {
Preconditions.checkArgument(TypeUtils.isArray(type));
return new TypeInfoArray(type);
}
/**
* Evaluates the "actual" type for the field. If the field is a "TypeVariable" or has a
* "TypeVariable" in a parameterized type then it evaluates the real type.
*
* @param f the actual field object to retrieve the type from
* @param typeDefiningF the type that contains the field {@code f}
* @return the type information for the field
*/
public static TypeInfo getTypeInfoForField(Field f, Type typeDefiningF) {
Class<?> classDefiningF = TypeUtils.toRawClass(typeDefiningF);
Type type = f.getGenericType();
Type actualType = getActualType(type, typeDefiningF, classDefiningF);
return new TypeInfo(actualType);
}
private static Type getActualType(
Type typeToEvaluate, Type parentType, Class<?> rawParentClass) {
if (typeToEvaluate instanceof Class<?>) {
return typeToEvaluate;
} else if (typeToEvaluate instanceof ParameterizedType) {
ParameterizedType castedType = (ParameterizedType) typeToEvaluate;
Type owner = castedType.getOwnerType();
Type[] actualTypeParameters =
extractRealTypes(castedType.getActualTypeArguments(), parentType, rawParentClass);
Type rawType = castedType.getRawType();
return new ParameterizedTypeImpl(rawType, actualTypeParameters, owner);
} else if (typeToEvaluate instanceof GenericArrayType) {
GenericArrayType castedType = (GenericArrayType) typeToEvaluate;
Type componentType = castedType.getGenericComponentType();
Type actualType = getActualType(componentType, parentType, rawParentClass);
if (componentType.equals(actualType)) {
return castedType;
}
return actualType instanceof Class<?> ?
TypeUtils.wrapWithArray(TypeUtils.toRawClass(actualType))
: new GenericArrayTypeImpl(actualType);
} else if (typeToEvaluate instanceof TypeVariable<?>) {
if (parentType instanceof ParameterizedType) {
// The class definition has the actual types used for the type variables.
// Find the matching actual type for the Type Variable used for the field.
// For example, class Foo<A> { A a; }
// new Foo<Integer>(); defines the actual type of A to be Integer.
// So, to find the type of the field a, we will have to look at the class'
// actual type arguments.
TypeVariable<?> fieldTypeVariable = (TypeVariable<?>) typeToEvaluate;
TypeVariable<?>[] classTypeVariables = rawParentClass.getTypeParameters();
ParameterizedType objParameterizedType = (ParameterizedType) parentType;
int indexOfActualTypeArgument = getIndex(classTypeVariables, fieldTypeVariable);
Type[] actualTypeArguments = objParameterizedType.getActualTypeArguments();
return actualTypeArguments[indexOfActualTypeArgument];
} else if (typeToEvaluate instanceof TypeVariable<?>) {
Type theSearchedType = null;
do {
theSearchedType = extractTypeForHierarchy(parentType, (TypeVariable<?>) typeToEvaluate);
} while ((theSearchedType != null) && (theSearchedType instanceof TypeVariable<?>));
if (theSearchedType != null) {
return theSearchedType;
}
}
throw new UnsupportedOperationException("Expecting parameterized type, got " + parentType
+ ".\n Are you missing the use of TypeToken idiom?\n See "
+ "http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener");
} else if (typeToEvaluate instanceof WildcardType) {
WildcardType castedType = (WildcardType) typeToEvaluate;
return getActualType(castedType.getUpperBounds()[0], parentType, rawParentClass);
} else {
throw new IllegalArgumentException("Type \'" + typeToEvaluate + "\' is not a Class, "
+ "ParameterizedType, GenericArrayType or TypeVariable. Can't extract type.");
}
}
private static Type extractTypeForHierarchy(Type parentType, TypeVariable<?> typeToEvaluate) {
Class<?> rawParentType = null;
if (parentType instanceof Class<?>) {
rawParentType = (Class<?>) parentType;
} else if (parentType instanceof ParameterizedType) {
ParameterizedType parentTypeAsPT = (ParameterizedType) parentType;
rawParentType = (Class<?>) parentTypeAsPT.getRawType();
} else {
return null;
}
Type superClass = rawParentType.getGenericSuperclass();
if (superClass instanceof ParameterizedType
&& ((ParameterizedType) superClass).getRawType() == typeToEvaluate.getGenericDeclaration()) {
// Evaluate type on this type
TypeVariable<?>[] classTypeVariables =
((Class<?>) ((ParameterizedType) superClass).getRawType()).getTypeParameters();
int indexOfActualTypeArgument = getIndex(classTypeVariables, typeToEvaluate);
Type[] actualTypeArguments = null;
if (parentType instanceof Class<?>) {
actualTypeArguments = ((ParameterizedType) superClass).getActualTypeArguments();
} else if (parentType instanceof ParameterizedType) {
actualTypeArguments = ((ParameterizedType) parentType).getActualTypeArguments();
} else {
return null;
}
return actualTypeArguments[indexOfActualTypeArgument];
}
Type searchedType = null;
if (superClass != null) {
searchedType = extractTypeForHierarchy(superClass, typeToEvaluate);
}
return searchedType;
}
private static Type[] extractRealTypes(
Type[] actualTypeArguments, Type parentType, Class<?> rawParentClass) {
Preconditions.checkNotNull(actualTypeArguments);
Type[] retTypes = new Type[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; ++i) {
retTypes[i] = getActualType(actualTypeArguments[i], parentType, rawParentClass);
}
return retTypes;
}
private static int getIndex(TypeVariable<?>[] types, TypeVariable<?> type) {
for (int i = 0; i < types.length; ++i) {
if (type.equals(types[i])) {
return i;
}
}
throw new IllegalStateException(
"How can the type variable not be present in the class declaration!");
}
}

View File

@ -1,58 +0,0 @@
/*
* 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;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Properties;
/**
* A convenience object for retrieving the map type information.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class TypeInfoMap {
private final Type keyType;
private final Type valueType;
public TypeInfoMap(Type mapType) {
if (mapType instanceof Class<?> && Properties.class.isAssignableFrom((Class<?>) mapType)) {
keyType = String.class;
valueType = String.class;
} else if (mapType instanceof ParameterizedType) {
TypeInfo rawType = new TypeInfo(mapType);
Preconditions.checkArgument(Map.class.isAssignableFrom(rawType.getRawClass()));
ParameterizedType paramType = (ParameterizedType) mapType;
keyType = paramType.getActualTypeArguments()[0];
valueType = paramType.getActualTypeArguments()[1];
} else {
throw new IllegalArgumentException(
"Map objects need to be parameterized unless you use a custom serializer. "
+ "Use the com.google.gson.reflect.TypeToken to extract the ParameterizedType.");
}
}
public Type getKeyType() {
return keyType;
}
public Type getValueType() {
return valueType;
}
}

View File

@ -1,95 +0,0 @@
/*
* 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;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
/**
* Utility class containing some methods for obtaining information on types.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
final class TypeUtils {
/**
* Returns the actual type matching up with the first type variable.
* So, for a {@code typeInfo} instance defined as:
* <pre>
* class Foo<A, B> {
* }
* Type fooType = new TypeToken<Foo<Integer, String>>() {}.getType();
* </pre>
* <code>TypeUtils.getActualTypeForFirstTypeVariable(fooType)</code> will return Integer.class.
*/
static Type getActualTypeForFirstTypeVariable(Type type) {
if (type instanceof Class<?>) {
return Object.class;
} else if (type instanceof ParameterizedType) {
return ((ParameterizedType)type).getActualTypeArguments()[0];
} else if (type instanceof GenericArrayType) {
return getActualTypeForFirstTypeVariable(((GenericArrayType)type).getGenericComponentType());
} else {
throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, "
+ "ParameterizedType, or GenericArrayType. Can't extract class.");
}
}
static boolean isArray(Type type) {
if (type instanceof Class<?>) {
return ((Class<?>)type).isArray();
} else if (type instanceof GenericArrayType) {
return true;
} else {
return false;
}
}
/**
* This method returns the actual raw class associated with the specified type.
*/
static Class<?> toRawClass(Type type) {
if (type instanceof Class<?>) {
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType actualType = (ParameterizedType)type;
return toRawClass(actualType.getRawType());
} else if (type instanceof GenericArrayType) {
GenericArrayType actualType = (GenericArrayType) type;
Class<?> rawClass = toRawClass(actualType.getGenericComponentType());
return wrapWithArray(rawClass);
} else if (type instanceof WildcardType) {
WildcardType castedType = (WildcardType) type;
return toRawClass(castedType.getUpperBounds()[0]);
} else {
throw new IllegalArgumentException("Type \'" + type + "\' is not a Class, "
+ "ParameterizedType, or GenericArrayType. Can't extract class.");
}
}
static Class<?> wrapWithArray(Class<?> rawClass) {
return Array.newInstance(rawClass, 0).getClass();
}
private TypeUtils() {
// Class with just some static utility methods, should not be instantiated
}
}

View File

@ -16,131 +16,118 @@
package com.google.gson.reflect;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* Represents a generic type {@code T}.
* 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:
*
* You can use this class to get the generic type for a class. For example,
* to get the generic type for <code>Collection&lt;Foo&gt;</code>, you can use:
* <p>
* <code>Type typeOfCollectionOfFoo = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType()
* </code>
*
* <p>Assumes {@code Type} implements {@code equals()} and {@code hashCode()}
* as a value (as opposed to identity) comparison.
* {@code TypeLiteral<List<String>> list = new TypeToken<List<String>>() {};}
*
* Also implements {@link #isAssignableFrom(Type)} to check type-safe
* assignability.
* <p>This syntax cannot be used to create type literals that have wildcard
* parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
* Such type literals must be constructed programatically, either by {@link
* Method#getGenericReturnType extracting types from members} or by using the
* {@link Types} factory class.
*
* <p>Along with modeling generic types, this class can resolve type parameters.
* For example, to figure out what type {@code keySet()} returns on a {@code
* Map<Integer, String>}, use this code:<pre> {@code
*
* TypeLiteral<Map<Integer, String>> mapType
* = new TypeToken<Map<Integer, String>>() {};
* TypeToken<?> keySetType
* = mapType.getReturnType(Map.class.getMethod("keySet"));
* System.out.println(keySetType); // prints "Set<Integer>"}</pre>
*
* @author Bob Lee
* @author Sven Mawson
* @author Jesse Wilson
*/
public abstract class TypeToken<T> {
public class TypeToken<T> {
final Class<? super T> rawType;
final Type type;
final int hashCode;
/**
* Constructs a new type token. Derives represented class from type
* 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.
*
* <p>For example:
* <code>
* {@literal TypeToken<List<String>> t = new TypeToken<List<String>>}(){}
* </code>
* 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>) getRawType(type);
this.rawType = (Class<? super T>) Types.getRawType(type);
this.hashCode = type.hashCode();
}
/**
* Unsafe. Constructs a type token manually.
* Unsafe. Constructs a type literal manually.
*/
@SuppressWarnings({"unchecked"})
private TypeToken(Type type) {
this.rawType = (Class<? super T>) getRawType(nonNull(type, "type"));
this.type = type;
@SuppressWarnings("unchecked")
TypeToken(Type type) {
checkNotNull(type);
this.type = Types.canonicalize(type);
this.rawType = (Class<? super T>) Types.getRawType(this.type);
this.hashCode = this.type.hashCode();
}
private static <T> T nonNull(T o, String message) {
if (o == null) {
throw new NullPointerException(message);
}
return o;
}
/**
* Gets type from super class's type parameter.
* Returns the type from super class's type parameter in {@link Types#canonicalize(java.lang.reflect.Type)
* canonical form}.
*/
static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class<?>) {
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
return ((ParameterizedType) superclass).getActualTypeArguments()[0];
ParameterizedType parameterized = (ParameterizedType) superclass;
return Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
/**
* Gets type token from super class's type parameter.
* Gets type literal from super class's type parameter.
*/
static TypeToken<?> fromSuperclassTypeParameter(Class<?> subclass) {
return new SimpleTypeToken<Object>(subclass);
}
private static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// type is a normal class.
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class.
// Neal isn't either but suspects some pathological case related
// to nested classes exists.
Type rawType = parameterizedType.getRawType();
if (rawType instanceof Class<?>) {
return (Class<?>) rawType;
}
throw buildUnexpectedTypeError(rawType, Class.class);
} else if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) type;
// TODO(jleitch): This is not the most efficient way to handle generic
// arrays, but is there another way to extract the array class in a
// non-hacky way (i.e. using String value class names- "[L...")?
Object rawArrayType = Array.newInstance(
getRawType(genericArrayType.getGenericComponentType()), 0);
return rawArrayType.getClass();
} else {
throw buildUnexpectedTypeError(
type, ParameterizedType.class, GenericArrayType.class);
}
return new TypeToken<Object>(getSuperclassTypeParameter(subclass));
}
/**
* Gets the raw type.
* Returns the raw (non-generic) type for this type.
*/
public Class<? super T> getRawType() {
public final Class<? super T> getRawType() {
return rawType;
}
/**
* Gets underlying {@code Type} instance.
*/
public Type getType() {
public final Type getType() {
return type;
}
@ -155,6 +142,8 @@ public abstract class TypeToken<T> {
* Check if this type is assignable from the given Type.
*/
public boolean isAssignableFrom(Type from) {
// TODO: resolve from first, then do something lightweight?
if (from == null) {
return false;
}
@ -164,12 +153,12 @@ public abstract class TypeToken<T> {
}
if (type instanceof Class<?>) {
return rawType.isAssignableFrom(getRawType(from));
return rawType.isAssignableFrom(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(getRawType(from))
return rawType.isAssignableFrom(Types.getRawType(from))
&& isAssignableFrom(from, (GenericArrayType) type);
} else {
throw buildUnexpectedTypeError(
@ -225,7 +214,7 @@ public abstract class TypeToken<T> {
}
// First figure out the class and any type information.
Class<?> clazz = getRawType(from);
Class<?> clazz = Types.getRawType(from);
ParameterizedType ptype = null;
if (from instanceof ParameterizedType) {
ptype = (ParameterizedType) from;
@ -285,55 +274,6 @@ public abstract class TypeToken<T> {
return false;
}
/**
* 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) {
if (to.equals(from)) return true;
if (from instanceof TypeVariable<?>) {
return to.equals(typeMap.get(((TypeVariable<?>)from).getName()));
}
return false;
}
/**
* Hashcode for this object.
* @return hashcode for this object.
*/
@Override public int hashCode() {
return type.hashCode();
}
/**
* Method to test equality.
*
* @return true if this object is logically equal to the specified object, false otherwise.
*/
@Override public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof TypeToken<?>)) {
return false;
}
TypeToken<?> t = (TypeToken<?>) o;
return type.equals(t.type);
}
/**
* Returns a string representation of this object.
* @return a string representation of this object.
*/
@Override public String toString() {
return type instanceof Class<?>
? ((Class<?>) type).getName()
: type.toString();
}
private static AssertionError buildUnexpectedTypeError(
Type token, Class<?>... expected) {
@ -350,26 +290,279 @@ public abstract class TypeToken<T> {
}
/**
* Gets type token for the given {@code Type} instance.
* 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) {
if (to.equals(from)) return true;
if (from instanceof TypeVariable<?>) {
return to.equals(typeMap.get(((TypeVariable<?>)from).getName()));
}
return false;
}
@Override public final int hashCode() {
return this.hashCode;
}
@Override public final boolean equals(Object o) {
return o instanceof TypeToken<?>
&& Types.equals(type, ((TypeToken) o).type);
}
@Override public final String toString() {
return Types.typeToString(type);
}
/**
* Gets type literal for the given {@code Type} instance.
*/
public static TypeToken<?> get(Type type) {
return new SimpleTypeToken<Object>(type);
return new TypeToken<Object>(type);
}
/**
* Gets type token for the given {@code Class} instance.
* Gets type literal for the given {@code Class} instance.
*/
public static <T> TypeToken<T> get(Class<T> type) {
return new SimpleTypeToken<T>(type);
return new TypeToken<T>(type);
}
/** Returns an immutable list of the resolved types. */
private List<TypeToken<?>> resolveAll(Type[] types) {
TypeToken<?>[] result = new TypeToken<?>[types.length];
for (int t = 0; t < types.length; t++) {
result[t] = resolve(types[t]);
}
return Arrays.asList(result);
}
/**
* Private static class to not create more anonymous classes than
* necessary.
* Resolves known type parameters in {@code toResolve} and returns the result.
*/
private static class SimpleTypeToken<T> extends TypeToken<T> {
public SimpleTypeToken(Type type) {
super(type);
TypeToken<?> resolve(Type toResolve) {
return TypeToken.get(resolveType(toResolve));
}
Type resolveType(Type toResolve) {
// this implementation is made a little more complicated in an attempt to avoid object-creation
while (true) {
if (toResolve instanceof TypeVariable) {
TypeVariable original = (TypeVariable) toResolve;
toResolve = Types.resolveTypeVariable(type, rawType, original);
if (toResolve == original) {
return toResolve;
}
} else if (toResolve instanceof GenericArrayType) {
GenericArrayType original = (GenericArrayType) toResolve;
Type componentType = original.getGenericComponentType();
Type newComponentType = resolveType(componentType);
return componentType == newComponentType
? original
: Types.arrayOf(newComponentType);
} else if (toResolve instanceof ParameterizedType) {
ParameterizedType original = (ParameterizedType) toResolve;
Type ownerType = original.getOwnerType();
Type newOwnerType = resolveType(ownerType);
boolean changed = newOwnerType != ownerType;
Type[] args = original.getActualTypeArguments();
for (int t = 0, length = args.length; t < length; t++) {
Type resolvedTypeArgument = resolveType(args[t]);
if (resolvedTypeArgument != args[t]) {
if (!changed) {
args = args.clone();
changed = true;
}
args[t] = resolvedTypeArgument;
}
}
return changed
? Types.newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
: original;
} else if (toResolve instanceof WildcardType) {
WildcardType original = (WildcardType) toResolve;
Type[] originalLowerBound = original.getLowerBounds();
Type[] originalUpperBound = original.getUpperBounds();
if (originalLowerBound.length == 1) {
Type lowerBound = resolveType(originalLowerBound[0]);
if (lowerBound != originalLowerBound[0]) {
return Types.supertypeOf(lowerBound);
}
} else if (originalUpperBound.length == 1) {
Type upperBound = resolveType(originalUpperBound[0]);
if (upperBound != originalUpperBound[0]) {
return Types.subtypeOf(upperBound);
}
}
return original;
} else {
return toResolve;
}
}
}
/**
* Returns the generic form of {@code supertype}. For example, if this is {@code
* ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
* Iterable.class}.
*
* @param supertype a superclass of, or interface implemented by, this.
*/
public TypeToken<?> getSupertype(Class<?> supertype) {
checkArgument(supertype.isAssignableFrom(rawType));
return resolve(Types.getGenericSupertype(type, rawType, supertype));
}
/**
* Returns the resolved generic type of {@code field}.
*
* @param field a field defined by this or any superclass.
*/
public TypeToken<?> getFieldType(Field field) {
if (!field.getDeclaringClass().isAssignableFrom(rawType)) {
throw new IllegalArgumentException(rawType.getName() + " does not declare field " + field);
}
return resolve(field.getGenericType());
}
/**
* Returns the resolved generic parameter types of {@code methodOrConstructor}.
*
* @param methodOrConstructor a method or constructor defined by this or any supertype.
*/
public List<TypeToken<?>> getParameterTypes(Member methodOrConstructor) {
Type[] genericParameterTypes;
if (methodOrConstructor instanceof Method) {
Method method = (Method) methodOrConstructor;
checkArgument(method.getDeclaringClass().isAssignableFrom(rawType));
genericParameterTypes = method.getGenericParameterTypes();
} else if (methodOrConstructor instanceof Constructor) {
Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType));
genericParameterTypes = constructor.getGenericParameterTypes();
} else {
throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
}
return resolveAll(genericParameterTypes);
}
/**
* Returns the resolved generic exception types thrown by {@code constructor}.
*
* @param methodOrConstructor a method or constructor defined by this or any supertype.
*/
public List<TypeToken<?>> getExceptionTypes(Member methodOrConstructor) {
Type[] genericExceptionTypes;
if (methodOrConstructor instanceof Method) {
Method method = (Method) methodOrConstructor;
checkArgument(method.getDeclaringClass().isAssignableFrom(rawType));
genericExceptionTypes = method.getGenericExceptionTypes();
} else if (methodOrConstructor instanceof Constructor) {
Constructor<?> constructor = (Constructor<?>) methodOrConstructor;
checkArgument(constructor.getDeclaringClass().isAssignableFrom(rawType));
genericExceptionTypes = constructor.getGenericExceptionTypes();
} else {
throw new IllegalArgumentException("Not a method or a constructor: " + methodOrConstructor);
}
return resolveAll(genericExceptionTypes);
}
/**
* Returns the resolved generic return type of {@code method}.
*
* @param method a method defined by this or any supertype.
*/
public TypeToken<?> getReturnType(Method method) {
checkArgument(method.getDeclaringClass().isAssignableFrom(rawType));
return resolve(method.getGenericReturnType());
}
static void checkNotNull(Object obj) {
checkArgument(obj != null);
}
static void checkArgument(boolean condition) {
if (!condition) {
throw new IllegalArgumentException("condition failed: " + condition);
}
}
// TODO: these methods are required by GSON but don't need to be public. Remove?
/**
* Returns true if this type is an array.
*/
public boolean isArray() {
return type instanceof GenericArrayType;
}
/**
* Returns true if this type is a primitive.
*/
public boolean isPrimitive() {
return type == boolean.class
|| type == byte.class
|| type == char.class
|| type == double.class
|| type == float.class
|| type == int.class
|| type == long.class
|| type == short.class
|| type == void.class;
}
/**
* Returns the component type of this array type.
* @throws ClassCastException if this type is not an array.
*/
public Type getArrayComponentType() {
return ((GenericArrayType) type).getGenericComponentType();
}
/**
* Returns the element type of this collection type.
* @throws IllegalArgumentException if this type is not a collection.
*/
public Type getCollectionElementType() {
TypeToken<?> collectionType = getSupertype(Collection.class);
return ((ParameterizedType) collectionType.getType()).getActualTypeArguments()[0];
}
/**
* Returns a two element array containing this map's key and value types in
* positions 0 and 1 respectively.
*/
public Type[] getMapKeyAndValueTypes() {
/*
* Work around a problem with the declaration of java.util.Properties. That
* class should extend Hashtable<String, String>, but it's declared to
* extend Hashtable<Object, Object>.
*/
if (type == Properties.class) {
return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties!
}
TypeToken<?> mapTypeToken = TypeToken.get(type).getSupertype(Map.class);
ParameterizedType mapParameterizedType = (ParameterizedType) mapTypeToken.getType();
return mapParameterizedType.getActualTypeArguments();
}
}

View File

@ -0,0 +1,488 @@
/**
* 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 static com.google.gson.reflect.TypeToken.checkArgument;
import static com.google.gson.reflect.TypeToken.checkNotNull;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* Static methods for working with types.
*
* @author Bob Lee
* @author Jesse Wilson
*/
public final class Types {
static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
private Types() {}
/**
* Returns a new parameterized type, applying {@code typeArguments} to
* {@code rawType}. The returned type does not have an owner type.
*
* @return a {@link java.io.Serializable serializable} parameterized type.
*/
public static ParameterizedType newParameterizedType(Type rawType, Type... typeArguments) {
return newParameterizedTypeWithOwner(null, rawType, typeArguments);
}
/**
* Returns a new parameterized type, applying {@code typeArguments} to
* {@code rawType} and enclosed by {@code ownerType}.
*
* @return a {@link java.io.Serializable serializable} parameterized type.
*/
public static ParameterizedType newParameterizedTypeWithOwner(
Type ownerType, Type rawType, Type... typeArguments) {
return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
}
/**
* Returns an array type whose elements are all instances of
* {@code componentType}.
*
* @return a {@link java.io.Serializable serializable} generic array type.
*/
public static GenericArrayType arrayOf(Type componentType) {
return new GenericArrayTypeImpl(componentType);
}
/**
* Returns a type that represents an unknown type that extends {@code bound}.
* For example, if {@code bound} is {@code CharSequence.class}, this returns
* {@code ? extends CharSequence}. If {@code bound} is {@code Object.class},
* this returns {@code ?}, which is shorthand for {@code ? extends Object}.
*/
public static WildcardType subtypeOf(Type bound) {
return new WildcardTypeImpl(new Type[] { bound }, EMPTY_TYPE_ARRAY);
}
/**
* Returns a type that represents an unknown supertype of {@code bound}. For
* example, if {@code bound} is {@code String.class}, this returns {@code ?
* super String}.
*/
public static WildcardType supertypeOf(Type bound) {
return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound });
}
/**
* Returns a type modelling a {@link List} whose elements are of type
* {@code elementType}.
*
* @return a {@link java.io.Serializable serializable} parameterized type.
*/
public static ParameterizedType listOf(Type elementType) {
return newParameterizedType(List.class, elementType);
}
/**
* Returns a type modelling a {@link Set} whose elements are of type
* {@code elementType}.
*
* @return a {@link java.io.Serializable serializable} parameterized type.
*/
public static ParameterizedType setOf(Type elementType) {
return newParameterizedType(Set.class, elementType);
}
/**
* Returns a type modelling a {@link Map} whose keys are of type
* {@code keyType} and whose values are of type {@code valueType}.
*
* @return a {@link java.io.Serializable serializable} parameterized type.
*/
public static ParameterizedType mapOf(Type keyType, Type valueType) {
return newParameterizedType(Map.class, keyType, valueType);
}
/**
* Returns a type that is functionally equal but not necessarily equal
* according to {@link Object#equals(Object) Object.equals()}. The returned
* type is {@link java.io.Serializable}.
*/
static Type canonicalize(Type type) {
if (type instanceof Class) {
Class<?> c = (Class<?>) type;
return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
} else if (type instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) type;
return new ParameterizedTypeImpl(p.getOwnerType(),
p.getRawType(), p.getActualTypeArguments());
} else if (type instanceof GenericArrayType) {
GenericArrayType g = (GenericArrayType) type;
return new GenericArrayTypeImpl(g.getGenericComponentType());
} else if (type instanceof WildcardType) {
WildcardType w = (WildcardType) type;
return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
} else {
// type is either serializable as-is or unsupported
return type;
}
}
static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// type is a normal class.
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class.
// Neal isn't either but suspects some pathological case related
// to nested classes exists.
Type rawType = parameterizedType.getRawType();
checkArgument(rawType instanceof Class);
return (Class<?>) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType)type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
// we could use the variable's bounds, but that won't work if there are multiple.
// having a raw type that's more general than necessary is okay
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName());
}
}
static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
/**
* Returns true if {@code a} and {@code b} are equal.
*/
static boolean equals(Type a, Type b) {
if (a == b) {
// also handles (a == null && b == null)
return true;
} else if (a instanceof Class) {
// Class already specifies equals().
return a.equals(b);
} else if (a instanceof ParameterizedType) {
if (!(b instanceof ParameterizedType)) {
return false;
}
// TODO: save a .clone() call
ParameterizedType pa = (ParameterizedType) a;
ParameterizedType pb = (ParameterizedType) b;
return equal(pa.getOwnerType(), pb.getOwnerType())
&& pa.getRawType().equals(pb.getRawType())
&& Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
} else if (a instanceof GenericArrayType) {
if (!(b instanceof GenericArrayType)) {
return false;
}
GenericArrayType ga = (GenericArrayType) a;
GenericArrayType gb = (GenericArrayType) b;
return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
} else if (a instanceof WildcardType) {
if (!(b instanceof WildcardType)) {
return false;
}
WildcardType wa = (WildcardType) a;
WildcardType wb = (WildcardType) b;
return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
&& Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
} else if (a instanceof TypeVariable) {
if (!(b instanceof TypeVariable)) {
return false;
}
TypeVariable<?> va = (TypeVariable) a;
TypeVariable<?> vb = (TypeVariable) b;
return va.getGenericDeclaration() == vb.getGenericDeclaration()
&& va.getName().equals(vb.getName());
} else {
// This isn't a type we support. Could be a generic array type, wildcard type, etc.
return false;
}
}
private static int hashCodeOrZero(Object o) {
return o != null ? o.hashCode() : 0;
}
static String typeToString(Type type) {
return type instanceof Class ? ((Class) type).getName() : type.toString();
}
/**
* Returns the generic supertype for {@code supertype}. For example, given a class {@code
* IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
* result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
*/
static Type getGenericSupertype(Type type, Class<?> rawType, Class<?> toResolve) {
if (toResolve == rawType) {
return type;
}
// we skip searching through interfaces if unknown is an interface
if (toResolve.isInterface()) {
Class[] interfaces = rawType.getInterfaces();
for (int i = 0, length = interfaces.length; i < length; i++) {
if (interfaces[i] == toResolve) {
return rawType.getGenericInterfaces()[i];
} else if (toResolve.isAssignableFrom(interfaces[i])) {
return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
}
}
}
// check our supertypes
if (!rawType.isInterface()) {
while (rawType != Object.class) {
Class<?> rawSupertype = rawType.getSuperclass();
if (rawSupertype == toResolve) {
return rawType.getGenericSuperclass();
} else if (toResolve.isAssignableFrom(rawSupertype)) {
return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
}
rawType = rawSupertype;
}
}
// we can't resolve this further
return toResolve;
}
static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) {
Class<?> declaredByRaw = declaringClassOf(unknown);
// we can't reduce this further
if (declaredByRaw == null) {
return unknown;
}
Type declaredBy = getGenericSupertype(type, rawType, declaredByRaw);
if (declaredBy instanceof ParameterizedType) {
int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
}
return unknown;
}
private static int indexOf(Object[] array, Object toFind) {
for (int i = 0; i < array.length; i++) {
if (toFind.equals(array[i])) {
return i;
}
}
throw new NoSuchElementException();
}
/**
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
* a class.
*/
private static Class<?> declaringClassOf(TypeVariable typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class
? (Class<?>) genericDeclaration
: null;
}
private static void checkNotPrimitive(Type type) {
checkArgument(!(type instanceof Class<?>) || !((Class) type).isPrimitive());
}
private static class ParameterizedTypeImpl implements ParameterizedType, Serializable {
private final Type ownerType;
private final Type rawType;
private final Type[] typeArguments;
public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
// require an owner type if the raw type needs it
if (rawType instanceof Class<?>) {
Class rawTypeAsClass = (Class) rawType;
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null);
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null);
}
this.ownerType = ownerType == null ? null : canonicalize(ownerType);
this.rawType = canonicalize(rawType);
this.typeArguments = typeArguments.clone();
for (int t = 0; t < this.typeArguments.length; t++) {
checkNotNull(this.typeArguments[t]);
checkNotPrimitive(this.typeArguments[t]);
this.typeArguments[t] = canonicalize(this.typeArguments[t]);
}
}
public Type[] getActualTypeArguments() {
return typeArguments.clone();
}
public Type getRawType() {
return rawType;
}
public Type getOwnerType() {
return ownerType;
}
@Override public boolean equals(Object other) {
return other instanceof ParameterizedType
&& Types.equals(this, (ParameterizedType) other);
}
@Override public int hashCode() {
return Arrays.hashCode(typeArguments)
^ rawType.hashCode()
^ hashCodeOrZero(ownerType);
}
@Override public String toString() {
StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
stringBuilder.append(typeToString(rawType));
if (typeArguments.length == 0) {
return stringBuilder.toString();
}
stringBuilder.append("<").append(typeToString(typeArguments[0]));
for (int i = 1; i < typeArguments.length; i++) {
stringBuilder.append(", ").append(typeToString(typeArguments[i]));
}
return stringBuilder.append(">").toString();
}
private static final long serialVersionUID = 0;
}
private static class GenericArrayTypeImpl implements GenericArrayType, Serializable {
private final Type componentType;
public GenericArrayTypeImpl(Type componentType) {
this.componentType = canonicalize(componentType);
}
public Type getGenericComponentType() {
return componentType;
}
@Override public boolean equals(Object o) {
return o instanceof GenericArrayType
&& Types.equals(this, (GenericArrayType) o);
}
@Override public int hashCode() {
return componentType.hashCode();
}
@Override public String toString() {
return typeToString(componentType) + "[]";
}
private static final long serialVersionUID = 0;
}
/**
* The WildcardType interface supports multiple upper bounds and multiple
* lower bounds. We only support what the Java 6 language needs - at most one
* bound. If a lower bound is set, the upper bound must be Object.class.
*/
private static class WildcardTypeImpl implements WildcardType, Serializable {
private final Type upperBound;
private final Type lowerBound;
public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
checkArgument(lowerBounds.length <= 1);
checkArgument(upperBounds.length == 1);
if (lowerBounds.length == 1) {
checkNotNull(lowerBounds[0]);
checkNotPrimitive(lowerBounds[0]);
checkArgument(upperBounds[0] == Object.class);
this.lowerBound = canonicalize(lowerBounds[0]);
this.upperBound = Object.class;
} else {
checkNotNull(upperBounds[0]);
checkNotPrimitive(upperBounds[0]);
this.lowerBound = null;
this.upperBound = canonicalize(upperBounds[0]);
}
}
public Type[] getUpperBounds() {
return new Type[] { upperBound };
}
public Type[] getLowerBounds() {
return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
}
@Override public boolean equals(Object other) {
return other instanceof WildcardType
&& Types.equals(this, (WildcardType) other);
}
@Override public int hashCode() {
// this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
return (lowerBound != null ? 31 + lowerBound.hashCode() : 1)
^ (31 + upperBound.hashCode());
}
@Override public String toString() {
if (lowerBound != null) {
return "? super " + typeToString(lowerBound);
} else if (upperBound == Object.class) {
return "?";
} else {
return "? extends " + typeToString(upperBound);
}
}
private static final long serialVersionUID = 0;
}
}

View File

@ -16,8 +16,10 @@
package com.google.gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
@ -148,13 +150,13 @@ public class ParamterizedTypeFixtures {
@SuppressWarnings("unchecked")
public MyParameterizedType<T> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Type genericClass = TypeUtils.getActualTypeForFirstTypeVariable(typeOfT);
TypeInfo typeInfo = new TypeInfo(genericClass);
String className = typeInfo.getRawClass().getSimpleName();
Type genericClass = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
TypeToken<?> typeToken = TypeToken.get(genericClass);
String className = typeToken.getRawType().getSimpleName();
T value = (T) json.getAsJsonObject().get(className).getAsObject();
if (typeInfo.isPrimitive()) {
if (typeToken.isPrimitive()) {
PrimitiveTypeAdapter typeAdapter = new PrimitiveTypeAdapter();
value = (T) typeAdapter.adaptType(value, typeInfo.getRawClass());
value = (T) typeAdapter.adaptType(value, typeToken.getRawType());
}
return new MyParameterizedType<T>(value);
}

View File

@ -1,123 +0,0 @@
/*
* 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;
import com.google.gson.reflect.TypeToken;
import junit.framework.TestCase;
import java.lang.reflect.Type;
import java.util.List;
/**
* Small test for the {@link TypeInfoArray}.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class TypeInfoArrayTest extends TestCase {
public void testArray() {
String[] a = {"a", "b", "c"};
TypeInfoArray typeInfo = new TypeInfoArray(a.getClass());
assertEquals(a.getClass(), typeInfo.getRawClass());
assertEquals(String.class, typeInfo.getComponentRawType());
}
public void testArrayOfArrays() {
String[][] a = {
new String[]{"a1", "a2", "a3"},
new String[]{"b1", "b2", "b3"},
new String[]{"c1", "c2", "c3"}};
TypeInfoArray typeInfo = new TypeInfoArray(a.getClass());
assertEquals(a.getClass(), typeInfo.getRawClass());
assertEquals(String.class, typeInfo.getComponentRawType());
assertEquals(String[].class, typeInfo.getSecondLevelType());
}
public void testParameterizedArray() {
Type type = new TypeToken<List<String>[]>() {}.getType();
TypeInfoArray typeInfo = new TypeInfoArray(type);
assertEquals(List[].class, typeInfo.getRawClass());
assertEquals(List.class, typeInfo.getComponentRawType());
}
public void testParameterizedArrayOfArrays() {
Type type = new TypeToken<List<String>[][]>() {}.getType();
Type secondLevelType = new TypeToken<List<String>[]>() {}.getType();
TypeInfoArray typeInfo = new TypeInfoArray(type);
assertEquals(List[][].class, typeInfo.getRawClass());
assertEquals(secondLevelType, typeInfo.getSecondLevelType());
assertEquals(List.class, typeInfo.getComponentRawType());
}
public void testNestedParameterizedArray() {
Type type = new TypeToken<List<List<String>>[]>() {}.getType();
Type secondLevelType = new TypeToken<List<List<String>>>() {}.getType();
TypeInfoArray typeInfo = new TypeInfoArray(type);
assertEquals(List[].class, typeInfo.getRawClass());
assertEquals(secondLevelType, typeInfo.getSecondLevelType());
assertEquals(List.class, typeInfo.getComponentRawType());
}
public void testPrimitiveArray() throws Exception {
TypeInfoArray arrayTypeInfo = new TypeInfoArray(int[].class);
assertTrue(arrayTypeInfo.isArray());
assertFalse(arrayTypeInfo.isPrimitive());
assertEquals(int.class, arrayTypeInfo.getSecondLevelType());
}
public void testStringArray() throws Exception {
TypeInfoArray arrayTypeInfo = new TypeInfoArray(String[].class);
assertTrue(arrayTypeInfo.isArray());
assertEquals(String.class, arrayTypeInfo.getSecondLevelType());
assertEquals(String[].class, arrayTypeInfo.getRawClass());
}
public void testPrimitiveArrayType() throws Exception {
TypeInfoArray typeInfo = new TypeInfoArray(long[].class);
assertTrue(typeInfo.isArray());
assertEquals(long.class, typeInfo.getSecondLevelType());
assertEquals(long[].class, typeInfo.getRawClass());
}
public void testStringArrayType() throws Exception {
TypeInfoArray typeInfo = new TypeInfoArray(String[].class);
assertTrue(typeInfo.isArray());
assertEquals(String[].class, typeInfo.getRawClass());
assertEquals(String.class, typeInfo.getSecondLevelType());
}
public void testArrayAsParameterizedTypes() throws Exception {
Type type = new TypeToken<List<String>[]>() {}.getType();
Type secondLevelType = new TypeToken<List<String>>() {}.getType();
TypeInfoArray typeInfo = new TypeInfoArray(type);
assertTrue(typeInfo.isArray());
assertEquals(List[].class, typeInfo.getRawClass());
assertEquals(secondLevelType, typeInfo.getSecondLevelType());
Type actualType = typeInfo.getActualType();
assertEquals(type, actualType);
Type actualTypeForFirstTypeVariable = TypeUtils.getActualTypeForFirstTypeVariable(actualType);
assertEquals(String.class, actualTypeForFirstTypeVariable);
}
}

View File

@ -1,253 +0,0 @@
/*
* 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;
import com.google.gson.reflect.TypeToken;
import junit.framework.TestCase;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.List;
/**
* Small test to ensure that the TypeInfoFactory is return the object that we expect.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public class TypeInfoFactoryTest extends TestCase {
private static Type OBJ_TYPE = new TypeToken<ObjectWithDifferentFields<Integer>>() {}.getType();
private ObjectWithDifferentFields<Integer> obj;
@Override
protected void setUp() throws Exception {
super.setUp();
obj = new ObjectWithDifferentFields<Integer>();
}
public void testSimpleField() throws Exception {
Field f = obj.getClass().getField("simpleField");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(String.class, typeInfo.getActualType());
assertEquals(String.class, typeInfo.getRawClass());
}
public void testEnumField() throws Exception {
Field f = obj.getClass().getField("enumField");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertTrue(typeInfo.isEnum());
assertEquals(ObjectWithDifferentFields.TestEnum.class, typeInfo.getActualType());
assertEquals(ObjectWithDifferentFields.TestEnum.class, typeInfo.getRawClass());
}
public void testParameterizedTypeField() throws Exception {
Type listType = new TypeToken<List<String>>() {}.getType();
Field f = obj.getClass().getField("simpleParameterizedType");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testNestedParameterizedTypeField() throws Exception {
Type listType = new TypeToken<List<List<String>>>() {}.getType();
Field f = obj.getClass().getField("simpleNestedParameterizedType");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testGenericArrayTypeField() throws Exception {
Type listType = new TypeToken<List<String>[]>() {}.getType();
Field f = obj.getClass().getField("simpleGenericArray");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List[].class, typeInfo.getRawClass());
}
public void testTypeVariableField() throws Exception {
Field f = obj.getClass().getField("typeVariableObj");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(Integer.class, typeInfo.getActualType());
assertEquals(Integer.class, typeInfo.getRawClass());
}
public void testTypeVariableArrayField() throws Exception {
Field f = obj.getClass().getField("typeVariableArray");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(Integer[].class, typeInfo.getActualType());
assertEquals(Integer[].class, typeInfo.getRawClass());
}
public void testMutliDimensionalTypeVariableArrayField() throws Exception {
Field f = obj.getClass().getField("mutliDimensionalTypeVariableArray");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(Integer[][][].class, typeInfo.getActualType());
assertEquals(Integer[][][].class, typeInfo.getRawClass());
}
public void testParameterizedTypeVariableField() throws Exception {
Type listType = new TypeToken<List<Integer>>() {}.getType();
Field f = obj.getClass().getField("listOfTypeVariables");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testNestedParameterizedTypeVariableField() throws Exception {
Type listType = new TypeToken<List<List<Integer>>>() {}.getType();
Field f = obj.getClass().getField("listOfListsOfTypeVariables");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testParameterizedTypeVariableArrayField() throws Exception {
Type listType = new TypeToken<List<Integer>[]>() {}.getType();
Field f = obj.getClass().getField("listOfTypeVariablesArray");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List[].class, typeInfo.getRawClass());
}
public void testWildcardField() throws Exception {
Type listType = new TypeToken<List<Object>>() {}.getType();
Field f = obj.getClass().getField("listWithWildcard");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testArrayOfWildcardField() throws Exception {
Type listType = new TypeToken<List<Object>[]>() {}.getType();
Field f = obj.getClass().getField("arrayOfListWithWildcard");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List[].class, typeInfo.getRawClass());
}
public void testListStringWildcardField() throws Exception {
Type listType = new TypeToken<List<String>>() {}.getType();
Field f = obj.getClass().getField("listWithStringWildcard");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testArrayOfListStringWildcardField() throws Exception {
Type listType = new TypeToken<List<String>[]>() {}.getType();
Field f = obj.getClass().getField("arrayOfListWithStringWildcard");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List[].class, typeInfo.getRawClass());
}
public void testListTypeVariableWildcardField() throws Exception {
Type listType = new TypeToken<List<Integer>>() {}.getType();
Field f = obj.getClass().getField("listWithTypeVariableWildcard");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List.class, typeInfo.getRawClass());
}
public void testArrayOfListTypeVariableWildcardField() throws Exception {
Type listType = new TypeToken<List<Integer>[]>() {}.getType();
Field f = obj.getClass().getField("arrayOfListWithTypeVariableWildcard");
TypeInfo typeInfo = TypeInfoFactory.getTypeInfoForField(f, OBJ_TYPE);
assertTrue(typeInfo.isArray());
assertFalse(typeInfo.isEnum());
assertEquals(listType, typeInfo.getActualType());
assertEquals(List[].class, typeInfo.getRawClass());
}
@SuppressWarnings("unused")
private static class ObjectWithDifferentFields<T> {
public static enum TestEnum {
TEST_1, TEST_2;
}
public String simpleField;
public TestEnum enumField;
public List<String> simpleParameterizedType;
public List<List<String>> simpleNestedParameterizedType;
public List<String>[] simpleGenericArray;
public T typeVariableObj;
public T[] typeVariableArray;
public T[][][] mutliDimensionalTypeVariableArray;
public List<T> listOfTypeVariables;
public List<List<T>> listOfListsOfTypeVariables;
public List<T>[] listOfTypeVariablesArray;
public List<?> listWithWildcard;
public List<?>[] arrayOfListWithWildcard;
public List<? extends String> listWithStringWildcard;
public List<? extends String>[] arrayOfListWithStringWildcard;
public List<? extends T> listWithTypeVariableWildcard;
public List<? extends T>[] arrayOfListWithTypeVariableWildcard;
}
}

View File

@ -1,66 +0,0 @@
/*
* 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;
import com.google.gson.reflect.TypeToken;
import junit.framework.TestCase;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Unit test for the default JSON map serialization object located in the
* {@link DefaultTypeAdapters} class.
*
* @author Joel Leitch
*/
public class TypeInfoMapTest extends TestCase {
public void testInvalidConstruction() throws Exception {
try {
new TypeInfoMap(String.class);
fail("Must be a ParameterizedType");
} catch (IllegalArgumentException expected) { }
}
public void testNonMapConstruction() throws Exception {
try {
Type parameterizedMapType = new TypeToken<List<String>>() {}.getType();
new TypeInfoMap(parameterizedMapType);
fail("The raw type must be a Map");
} catch (IllegalArgumentException expected) { }
}
public void testBasicGetters() throws Exception {
Type parameterizedMapType = new TypeToken<Map<String, Integer>>() {}.getType();
TypeInfoMap mapTypeInfo = new TypeInfoMap(parameterizedMapType);
assertEquals(String.class, mapTypeInfo.getKeyType());
assertEquals(Integer.class, mapTypeInfo.getValueType());
}
public void testMapImplementations() throws Exception {
Type parameterizedMapType = new TypeToken<HashMap<String, Integer>>() {}.getType();
TypeInfoMap mapTypeInfo = new TypeInfoMap(parameterizedMapType);
assertEquals(String.class, mapTypeInfo.getKeyType());
assertEquals(Integer.class, mapTypeInfo.getValueType());
}
}

View File

@ -1,111 +0,0 @@
/*
* 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;
import com.google.gson.reflect.TypeToken;
import junit.framework.TestCase;
import java.lang.reflect.Type;
import java.util.List;
/**
* Exercising the construction of the Parameter object and ensure the
* proper types are returned.
*
* @author Joel Leitch
*/
public class TypeInfoTest extends TestCase {
public void testPrimitive() throws Exception {
TypeInfo typeInfo = new TypeInfo(boolean.class);
assertFalse(typeInfo.isArray());
assertTrue(typeInfo.isPrimitive());
assertEquals(boolean.class, typeInfo.getRawClass());
assertEquals(Boolean.class, typeInfo.getWrappedClass());
}
public void testPrimitiveWrapper() throws Exception {
TypeInfo typeInfo = new TypeInfo(Integer.class);
assertEquals(Integer.class, typeInfo.getRawClass());
assertTrue(typeInfo.isPrimitive());
assertFalse(typeInfo.isArray());
}
public void testString() throws Exception {
TypeInfo typeInfo = new TypeInfo(String.class);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isPrimitive());
assertEquals(String.class, typeInfo.getRawClass());
}
public void testObject() throws Exception {
TypeInfo typeInfo = new TypeInfo(Object.class);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isPrimitive());
assertEquals(Object.class, typeInfo.getRawClass());
}
public void testPrimitiveType() throws Exception {
TypeInfo typeInfo = new TypeInfo(long.class);
assertFalse(typeInfo.isArray());
assertEquals(long.class, typeInfo.getRawClass());
}
public void testObjectType() throws Exception {
TypeInfo typeInfo = new TypeInfo(String.class);
assertFalse(typeInfo.isArray());
assertFalse(typeInfo.isPrimitive());
assertEquals(String.class, typeInfo.getRawClass());
}
public void testParameterizedTypes() throws Exception {
Type type = new TypeToken<List<String>>() {}.getType();
TypeInfo typeInfo = new TypeInfo(type);
assertFalse(typeInfo.isArray());
assertEquals(List.class, typeInfo.getRawClass());
assertEquals(type, typeInfo.getActualType());
}
public void testGenericizedGenericType() throws Exception {
Type type = new TypeToken<List<List<String>>>() {}.getType();
Type genericType = new TypeToken<List<String>>() {}.getType();
TypeInfo typeInfo = new TypeInfo(type);
assertFalse(typeInfo.isArray());
assertEquals(List.class, typeInfo.getRawClass());
Type actualTypeForFirstTypeVariable = TypeUtils.getActualTypeForFirstTypeVariable(type);
assertEquals(genericType, actualTypeForFirstTypeVariable);
typeInfo = new TypeInfo(genericType);
actualTypeForFirstTypeVariable = TypeUtils.getActualTypeForFirstTypeVariable(genericType);
assertEquals(String.class, actualTypeForFirstTypeVariable);
}
public void testStrangeTypeParameters() throws Exception {
try {
new TypeInfo(new Type() {});
fail("Should not be able to determine this unknown type");
} catch (IllegalArgumentException expected) {
}
}
}

View File

@ -1,57 +0,0 @@
/*
* 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;
import com.google.gson.reflect.TypeToken;
import junit.framework.TestCase;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
/**
* Unit tests for {@link TypeUtils}.
*
* @author Inderjeet Singh
*/
public class TypeUtilsTest extends TestCase {
private static final Type MAP_TYPE = new TypeToken<Map<String, Integer>>() {}.getType();
public void testGetActualTypeForFirstTypeVariable() {
assertEquals(String.class, TypeUtils.getActualTypeForFirstTypeVariable(MAP_TYPE));
}
public void testIsArrayForNonArrayClasses() {
assertFalse(TypeUtils.isArray(Boolean.class));
assertFalse(TypeUtils.isArray(MAP_TYPE));
}
public void testIsArrayForArrayClasses() {
assertTrue(TypeUtils.isArray(String[].class));
assertTrue(TypeUtils.isArray(Integer[][].class));
assertTrue(TypeUtils.isArray(Collection[].class));
}
public void testToRawClassForNonGenericClasses() {
assertEquals(String.class, TypeUtils.toRawClass(String.class));
}
public void testToRawClassForGenericClasses() {
assertEquals(Map.class, TypeUtils.toRawClass(MAP_TYPE));
}
}