/* * 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; } else { if (actualType instanceof Class) { return TypeUtils.wrapWithArray(TypeUtils.toRawClass(actualType)); } else { return new GenericArrayTypeImpl(actualType); } } } else if (typeToEvaluate instanceof TypeVariable) { // 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 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[] 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!"); } }