diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index da903aa0..77e3e7c4 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -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) 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 map = constructMapType(typeOfT, context); - TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT); + Type[] keyAndValueTypes = TypeToken.get(typeOfT).getMapKeyAndValueTypes(); for (Map.Entry 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; diff --git a/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java index e37aedf8..3f50d944 100644 --- a/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonArrayDeserializationVisitor.java @@ -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 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 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 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(); diff --git a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java index efdcbad2..25ba88e9 100644 --- a/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonObjectDeserializationVisitor.java @@ -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 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 extends JsonDeserializationVisit return false; } Object value = invokeCustomDeserializer(child, pair); - if (value != null || !typeInfo.isPrimitive()) { + if (value != null || !isPrimitive) { f.set(parent, value); } return true; diff --git a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java index c3edaf7d..f24e3429 100644 --- a/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java +++ b/gson/src/main/java/com/google/gson/JsonSerializationVisitor.java @@ -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)); } } diff --git a/gson/src/main/java/com/google/gson/MappedObjectConstructor.java b/gson/src/main/java/com/google/gson/MappedObjectConstructor.java index 9d8c91cc..0eb57b4c 100644 --- a/gson/src/main/java/com/google/gson/MappedObjectConstructor.java +++ b/gson/src/main/java/com/google/gson/MappedObjectConstructor.java @@ -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 constructWithNoArgConstructor(Type typeOfT) { @@ -79,8 +80,7 @@ final class MappedObjectConstructor implements ObjectConstructor { @SuppressWarnings({"unchecked", "cast"}) private Constructor getNoArgsConstructor(Type typeOfT) { - TypeInfo typeInfo = new TypeInfo(typeOfT); - Class clazz = (Class) typeInfo.getRawClass(); + Class clazz = TypeToken.get(typeOfT).getRawType(); Constructor[] declaredConstructors = (Constructor[]) clazz.getDeclaredConstructors(); AccessibleObject.setAccessible(declaredConstructors, true); for (Constructor constructor : declaredConstructors) { diff --git a/gson/src/main/java/com/google/gson/ObjectNavigator.java b/gson/src/main/java/com/google/gson/ObjectNavigator.java index f294eb1d..19c0e180 100644 --- a/gson/src/main/java/com/google/gson/ObjectNavigator.java +++ b/gson/src/main/java/com/google/gson/ObjectNavigator.java @@ -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); + } } diff --git a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java index db9f0b7d..06c95e9a 100644 --- a/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java +++ b/gson/src/main/java/com/google/gson/ParameterizedTypeHandlerMap.java @@ -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 { 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 { } private String typeToString(Type type) { - return TypeUtils.toRawClass(type).getSimpleName(); + return TypeToken.get(type).getRawType().getSimpleName(); } } diff --git a/gson/src/main/java/com/google/gson/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java deleted file mode 100644 index ae8d7a81..00000000 --- a/gson/src/main/java/com/google/gson/TypeAdapter.java +++ /dev/null @@ -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 adaptType(Object from, Class to); -} diff --git a/gson/src/main/java/com/google/gson/TypeInfo.java b/gson/src/main/java/com/google/gson/TypeInfo.java deleted file mode 100644 index e34b34cd..00000000 --- a/gson/src/main/java/com/google/gson/TypeInfo.java +++ /dev/null @@ -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. - *
-   *     wrap(int.class) == Integer.class
-   *     wrap(Integer.class) == Integer.class
-   *     wrap(String.class) == String.class
-   * 
- */ - 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)); - } -} \ No newline at end of file diff --git a/gson/src/main/java/com/google/gson/TypeInfoArray.java b/gson/src/main/java/com/google/gson/TypeInfoArray.java deleted file mode 100644 index a950a001..00000000 --- a/gson/src/main/java/com/google/gson/TypeInfoArray.java +++ /dev/null @@ -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<String>[][] types, it will return the - * type representing Foo<String>[] - * (i.e. new TypeToken[]>() {}.getType()). - */ - 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; - } -} diff --git a/gson/src/main/java/com/google/gson/TypeInfoCollection.java b/gson/src/main/java/com/google/gson/TypeInfoCollection.java deleted file mode 100644 index 50fe0350..00000000 --- a/gson/src/main/java/com/google/gson/TypeInfoCollection.java +++ /dev/null @@ -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]; - } -} diff --git a/gson/src/main/java/com/google/gson/TypeInfoFactory.java b/gson/src/main/java/com/google/gson/TypeInfoFactory.java deleted file mode 100644 index 3085e8a8..00000000 --- a/gson/src/main/java/com/google/gson/TypeInfoFactory.java +++ /dev/null @@ -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; } - // new Foo(); 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!"); - } -} diff --git a/gson/src/main/java/com/google/gson/TypeInfoMap.java b/gson/src/main/java/com/google/gson/TypeInfoMap.java deleted file mode 100644 index 12fb1077..00000000 --- a/gson/src/main/java/com/google/gson/TypeInfoMap.java +++ /dev/null @@ -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; - } -} diff --git a/gson/src/main/java/com/google/gson/TypeUtils.java b/gson/src/main/java/com/google/gson/TypeUtils.java deleted file mode 100644 index 701cf8f4..00000000 --- a/gson/src/main/java/com/google/gson/TypeUtils.java +++ /dev/null @@ -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: - *
-   *   class Foo {
-   *   }
-   *   Type fooType = new TypeToken>() {}.getType();
-   * 
- * TypeUtils.getActualTypeForFirstTypeVariable(fooType) 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 - } -} diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java index c044d672..6ae3d490 100644 --- a/gson/src/main/java/com/google/gson/reflect/TypeToken.java +++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java @@ -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. + * + *

For example, to create a type literal for {@code List}, 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 Collection<Foo>, you can use: *

- * Type typeOfCollectionOfFoo = new TypeToken<Collection<Foo>>(){}.getType() - * - * - *

Assumes {@code Type} implements {@code equals()} and {@code hashCode()} - * as a value (as opposed to identity) comparison. + * {@code TypeLiteral> list = new TypeToken>() {};} * - * Also implements {@link #isAssignableFrom(Type)} to check type-safe - * assignability. + *

This syntax cannot be used to create type literals that have wildcard + * parameters, such as {@code Class} or {@code List}. + * Such type literals must be constructed programatically, either by {@link + * Method#getGenericReturnType extracting types from members} or by using the + * {@link Types} factory class. + * + *

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}, use this code:

   {@code
+ *
+ *   TypeLiteral> mapType
+ *       = new TypeToken>() {};
+ *   TypeToken keySetType
+ *       = mapType.getReturnType(Map.class.getMethod("keySet"));
+ *   System.out.println(keySetType); // prints "Set"}
* * @author Bob Lee * @author Sven Mawson + * @author Jesse Wilson */ -public abstract class TypeToken { +public class TypeToken { final Class 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. * *

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. - * - *

For example: - * - * {@literal TypeToken> t = new TypeToken>}(){} - * + * 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) getRawType(type); + this.rawType = (Class) 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) getRawType(nonNull(type, "type")); - this.type = type; + @SuppressWarnings("unchecked") + TypeToken(Type type) { + checkNotNull(type); + this.type = Types.canonicalize(type); + this.rawType = (Class) Types.getRawType(this.type); + this.hashCode = this.type.hashCode(); } - private static 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(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(getSuperclassTypeParameter(subclass)); } /** - * Gets the raw type. + * Returns the raw (non-generic) type for this type. */ - public Class getRawType() { + public final Class 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 { * 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 { } 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()); } 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 { } // 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 { 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 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 { } /** - * 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 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(type); + return new TypeToken(type); } /** - * Gets type token for the given {@code Class} instance. + * Gets type literal for the given {@code Class} instance. */ public static TypeToken get(Class type) { - return new SimpleTypeToken(type); + return new TypeToken(type); + } + + + /** Returns an immutable list of the resolved types. */ + private List> 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 extends TypeToken { - 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}, this returns {@code Iterable} 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> 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> 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, but it's declared to + * extend Hashtable. + */ + 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(); + } } diff --git a/gson/src/main/java/com/google/gson/reflect/Types.java b/gson/src/main/java/com/google/gson/reflect/Types.java new file mode 100644 index 00000000..272f4dac --- /dev/null +++ b/gson/src/main/java/com/google/gson/reflect/Types.java @@ -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} and the + * result when the supertype is {@code Collection.class} is {@code Collection}. + */ + 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; + } +} diff --git a/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java b/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java index 897cc7c2..116ad199 100644 --- a/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java +++ b/gson/src/test/java/com/google/gson/ParamterizedTypeFixtures.java @@ -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 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(value); } diff --git a/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java b/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java deleted file mode 100644 index 92731f78..00000000 --- a/gson/src/test/java/com/google/gson/TypeInfoArrayTest.java +++ /dev/null @@ -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[]>() {}.getType(); - TypeInfoArray typeInfo = new TypeInfoArray(type); - assertEquals(List[].class, typeInfo.getRawClass()); - assertEquals(List.class, typeInfo.getComponentRawType()); - } - - public void testParameterizedArrayOfArrays() { - Type type = new TypeToken[][]>() {}.getType(); - Type secondLevelType = new TypeToken[]>() {}.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>[]>() {}.getType(); - Type secondLevelType = new TypeToken>>() {}.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[]>() {}.getType(); - Type secondLevelType = new TypeToken>() {}.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); - } -} diff --git a/gson/src/test/java/com/google/gson/TypeInfoFactoryTest.java b/gson/src/test/java/com/google/gson/TypeInfoFactoryTest.java deleted file mode 100644 index 26155307..00000000 --- a/gson/src/test/java/com/google/gson/TypeInfoFactoryTest.java +++ /dev/null @@ -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>() {}.getType(); - private ObjectWithDifferentFields obj; - - @Override - protected void setUp() throws Exception { - super.setUp(); - obj = new ObjectWithDifferentFields(); - } - - 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>() {}.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>>() {}.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[]>() {}.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>() {}.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>>() {}.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[]>() {}.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>() {}.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[]>() {}.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>() {}.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[]>() {}.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>() {}.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[]>() {}.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 { - public static enum TestEnum { - TEST_1, TEST_2; - } - - public String simpleField; - public TestEnum enumField; - public List simpleParameterizedType; - public List> simpleNestedParameterizedType; - public List[] simpleGenericArray; - - public T typeVariableObj; - public T[] typeVariableArray; - public T[][][] mutliDimensionalTypeVariableArray; - public List listOfTypeVariables; - public List> listOfListsOfTypeVariables; - public List[] listOfTypeVariablesArray; - - public List listWithWildcard; - public List[] arrayOfListWithWildcard; - public List listWithStringWildcard; - public List[] arrayOfListWithStringWildcard; - - public List listWithTypeVariableWildcard; - public List[] arrayOfListWithTypeVariableWildcard; - } -} diff --git a/gson/src/test/java/com/google/gson/TypeInfoMapTest.java b/gson/src/test/java/com/google/gson/TypeInfoMapTest.java deleted file mode 100644 index 1da4fb42..00000000 --- a/gson/src/test/java/com/google/gson/TypeInfoMapTest.java +++ /dev/null @@ -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>() {}.getType(); - new TypeInfoMap(parameterizedMapType); - fail("The raw type must be a Map"); - } catch (IllegalArgumentException expected) { } - } - - public void testBasicGetters() throws Exception { - Type parameterizedMapType = new TypeToken>() {}.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>() {}.getType(); - TypeInfoMap mapTypeInfo = new TypeInfoMap(parameterizedMapType); - - assertEquals(String.class, mapTypeInfo.getKeyType()); - assertEquals(Integer.class, mapTypeInfo.getValueType()); - } -} diff --git a/gson/src/test/java/com/google/gson/TypeInfoTest.java b/gson/src/test/java/com/google/gson/TypeInfoTest.java deleted file mode 100644 index 7fadd4ec..00000000 --- a/gson/src/test/java/com/google/gson/TypeInfoTest.java +++ /dev/null @@ -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>() {}.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>>() {}.getType(); - Type genericType = new TypeToken>() {}.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) { - } - } -} diff --git a/gson/src/test/java/com/google/gson/TypeUtilsTest.java b/gson/src/test/java/com/google/gson/TypeUtilsTest.java deleted file mode 100644 index edc7df04..00000000 --- a/gson/src/test/java/com/google/gson/TypeUtilsTest.java +++ /dev/null @@ -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>() {}.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)); - } -}