Saber-Factions/src/org/mcteam/factions/gson/TypeInfoFactory.java

176 lines
7.2 KiB
Java

/*
* 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 a; }
// new Foo<Integer>(); defines the actual type of A to be Integer.
// So, to find the type of the field a, we will have to look at the class'
// actual type arguments.
TypeVariable<?> fieldTypeVariable = (TypeVariable<?>) typeToEvaluate;
TypeVariable<?>[] classTypeVariables = rawParentClass.getTypeParameters();
ParameterizedType objParameterizedType = (ParameterizedType) parentType;
int indexOfActualTypeArgument = getIndex(classTypeVariables, fieldTypeVariable);
Type[] actualTypeArguments = objParameterizedType.getActualTypeArguments();
return actualTypeArguments[indexOfActualTypeArgument];
} else if (typeToEvaluate instanceof TypeVariable<?>) {
Type theSearchedType = null;
do {
theSearchedType = extractTypeForHierarchy(parentType, (TypeVariable<?>) typeToEvaluate);
} while ((theSearchedType != null) && (theSearchedType instanceof TypeVariable<?>));
if (theSearchedType != null) {
return theSearchedType;
}
}
throw new UnsupportedOperationException("Expecting parameterized type, got " + parentType
+ ".\n Are you missing the use of TypeToken idiom?\n See "
+ "http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener");
} else if (typeToEvaluate instanceof WildcardType) {
WildcardType castedType = (WildcardType) typeToEvaluate;
return getActualType(castedType.getUpperBounds()[0], parentType, rawParentClass);
} else {
throw new IllegalArgumentException("Type \'" + typeToEvaluate + "\' is not a Class, "
+ "ParameterizedType, GenericArrayType or TypeVariable. Can't extract type.");
}
}
private static Type extractTypeForHierarchy(Type parentType, TypeVariable<?> typeToEvaluate) {
Class<?> rawParentType = null;
if (parentType instanceof Class<?>) {
rawParentType = (Class<?>) parentType;
} else if (parentType instanceof ParameterizedType) {
ParameterizedType parentTypeAsPT = (ParameterizedType) parentType;
rawParentType = (Class<?>) parentTypeAsPT.getRawType();
} else {
return null;
}
Type superClass = rawParentType.getGenericSuperclass();
if (superClass instanceof ParameterizedType
&& ((ParameterizedType) superClass).getRawType() == typeToEvaluate.getGenericDeclaration()) {
// Evaluate type on this type
TypeVariable<?>[] classTypeVariables =
((Class<?>) ((ParameterizedType) superClass).getRawType()).getTypeParameters();
int indexOfActualTypeArgument = getIndex(classTypeVariables, typeToEvaluate);
Type[] actualTypeArguments = null;
if (parentType instanceof Class<?>) {
actualTypeArguments = ((ParameterizedType) superClass).getActualTypeArguments();
} else if (parentType instanceof ParameterizedType) {
actualTypeArguments = ((ParameterizedType) parentType).getActualTypeArguments();
} else {
return null;
}
return actualTypeArguments[indexOfActualTypeArgument];
}
Type searchedType = null;
if (superClass != null) {
searchedType = extractTypeForHierarchy(superClass, typeToEvaluate);
}
return searchedType;
}
private static Type[] extractRealTypes(
Type[] actualTypeArguments, Type parentType, Class<?> rawParentClass) {
Preconditions.checkNotNull(actualTypeArguments);
Type[] retTypes = new Type[actualTypeArguments.length];
for (int i = 0; i < actualTypeArguments.length; ++i) {
retTypes[i] = getActualType(actualTypeArguments[i], parentType, rawParentClass);
}
return retTypes;
}
private static int getIndex(TypeVariable<?>[] types, TypeVariable<?> type) {
for (int i = 0; i < types.length; ++i) {
if (type.equals(types[i])) {
return i;
}
}
throw new IllegalStateException(
"How can the type variable not be present in the class declaration!");
}
}