/* * 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 org.mcteam.factions.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!"); } }