/*
* Copyright 2014 mango.jfaster.org
*
* The Mango Project licenses this file to you 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.jfaster.mango.util.reflect;
import org.jfaster.mango.util.Primitives;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;
public abstract class TypeToken<T> extends TypeCapture<T> implements Serializable {
private final Type runtimeType;
private transient TypeResolver typeResolver;
protected TypeToken() {
this.runtimeType = capture();
if (runtimeType instanceof TypeVariable) {
throw new IllegalStateException(String.format("Cannot construct a TypeToken for a type variable.\n" +
"You probably meant to call new TypeToken<%s>(getClass()) " +
"that can resolve the type variable for you.\n" +
"If you do need to create a TypeToken of a type variable, " +
"please use TypeToken.of() instead.", runtimeType));
}
}
private TypeToken(Type type) {
this.runtimeType = type;
}
public static <T> TypeToken<T> of(Class<T> type) {
return new SimpleTypeToken<T>(type);
}
public static TypeToken<?> of(Type type) {
return new SimpleTypeToken<Object>(type);
}
public final Class<? super T> getRawType() {
Class<?> rawType = getRawType(runtimeType);
@SuppressWarnings("unchecked") // raw type is |T|
Class<? super T> result = (Class<? super T>) rawType;
return result;
}
public final Type getType() {
return runtimeType;
}
public final <X> TypeToken<T> where(TypeParameter<X> typeParam, TypeToken<X> typeArg) {
Map<TypeResolver.TypeVariableKey, Type> mappings = new HashMap<TypeResolver.TypeVariableKey, Type>();
mappings.put(new TypeResolver.TypeVariableKey(typeParam.typeVariable), typeArg.runtimeType);
TypeResolver resolver = new TypeResolver()
.where(mappings);
// If there's any type error, we'd report now rather than later.
return new SimpleTypeToken<T>(resolver.resolveType(runtimeType));
}
public final TypeToken<?> resolveType(Type type) {
TypeResolver resolver = typeResolver;
if (resolver == null) {
resolver = (typeResolver = TypeResolver.accordingTo(runtimeType));
}
return of(resolver.resolveType(type));
}
public final boolean isAssignableFrom(TypeToken<?> type) {
return isAssignableFrom(type.runtimeType);
}
public final boolean isAssignableFrom(Type type) {
return isAssignable(type, runtimeType);
}
public final boolean isArray() {
return getComponentType() != null;
}
public final boolean isPrimitive() {
return (runtimeType instanceof Class) && ((Class<?>) runtimeType).isPrimitive();
}
public final TypeToken<T> wrap() {
if (isPrimitive()) {
@SuppressWarnings("unchecked") // this is a primitive class
Class<T> type = (Class<T>) runtimeType;
return TypeToken.of(Primitives.wrap(type));
}
return this;
}
@Nullable
public final TypeToken<?> getComponentType() {
Type componentType = Types.getComponentType(runtimeType);
if (componentType == null) {
return null;
}
return of(componentType);
}
@Override
public boolean equals(@Nullable Object o) {
if (o instanceof TypeToken) {
TypeToken<?> that = (TypeToken<?>) o;
return runtimeType.equals(that.runtimeType);
}
return false;
}
@Override
public int hashCode() {
return runtimeType.hashCode();
}
@Override
public String toString() {
return Types.toString(runtimeType);
}
Set<TypeToken<?>> getTypes() {
Set<TypeToken<?>> tokens = new HashSet<TypeToken<?>>();
tokens.add(this);
TypeToken<?> superclass = getGenericSuperclass();
if (superclass != null) {
tokens.add(superclass);
tokens.addAll(superclass.getTypes());
}
List<TypeToken<?>> interfaces = getGenericInterfaces();
for (TypeToken<?> anInterface : interfaces) {
tokens.add(anInterface);
tokens.addAll(anInterface.getTypes());
}
return tokens;
}
public TypeToken<?> resolveFatherClass(Class<?> clazz) {
Set<TypeToken<?>> fathers = getTypes();
TypeToken<?> token = null;
for (TypeToken<?> father : fathers) {
if (clazz.equals(father.getRawType())) {
token = father.resolveType(clazz.getTypeParameters()[0]);
if (Object.class.equals(token.getRawType())) { // 处理范型T
token = TypeToken.of(Object.class);
}
}
}
if (token == null) {
throw new IllegalStateException();
}
return token;
}
public TokenTuple resolveFatherClassTuple(Class<?> clazz) {
Set<TypeToken<?>> fathers = getTypes();
TypeToken firstToken = null;
TypeToken secondToken = null;
for (TypeToken<?> father : fathers) {
if (clazz.equals(father.getRawType())) {
firstToken = father.resolveType(clazz.getTypeParameters()[0]);
if (Object.class.equals(firstToken.getRawType())) { // 处理范型T
firstToken = TypeToken.of(Object.class);
}
secondToken = father.resolveType(clazz.getTypeParameters()[1]);
if (Object.class.equals(secondToken.getRawType())) { // 处理范型T
secondToken = TypeToken.of(Object.class);
}
}
}
if (firstToken == null || secondToken == null) {
throw new IllegalStateException();
}
return new TokenTuple(firstToken, secondToken);
}
final List<TypeToken<?>> getGenericInterfaces() {
if (runtimeType instanceof TypeVariable) {
throw new IllegalStateException();
}
if (runtimeType instanceof WildcardType) {
throw new IllegalStateException();
}
List<TypeToken<?>> tokens = new ArrayList<TypeToken<?>>();
for (Type interfaceType : getRawType().getGenericInterfaces()) {
TypeToken<?> resolvedInterface = resolveSupertype(interfaceType);
tokens.add(resolvedInterface);
}
return tokens;
}
@Nullable
final TypeToken<?> getGenericSuperclass() {
if (runtimeType instanceof TypeVariable) {
throw new IllegalStateException();
}
if (runtimeType instanceof WildcardType) {
throw new IllegalStateException();
}
Type superclass = getRawType().getGenericSuperclass();
if (superclass == null) {
return null;
}
TypeToken<?> superToken = resolveSupertype(superclass);
return superToken;
}
private TypeToken<?> resolveSupertype(Type type) {
TypeToken<?> supertype = resolveType(type);
// super types' type mapping is a subset of type mapping of this type.
supertype.typeResolver = typeResolver;
return supertype;
}
private static boolean isAssignable(Type from, Type to) {
if (to.equals(from)) {
return true;
}
if (to instanceof WildcardType) {
return isAssignableToWildcardType(from, (WildcardType) to);
}
// if "from" is type variable, it's assignable if any of its "extends"
// bounds is assignable to "to".
if (from instanceof TypeVariable) {
return isAssignableFromAny(((TypeVariable<?>) from).getBounds(), to);
}
// if "from" is wildcard, it'a assignable to "to" if any of its "extends"
// bounds is assignable to "to".
if (from instanceof WildcardType) {
return isAssignableFromAny(((WildcardType) from).getUpperBounds(), to);
}
if (from instanceof GenericArrayType) {
return isAssignableFromGenericArrayType((GenericArrayType) from, to);
}
// Proceed to regular Type assignability check
if (to instanceof Class) {
return isAssignableToClass(from, (Class<?>) to);
} else if (to instanceof ParameterizedType) {
return isAssignableToParameterizedType(from, (ParameterizedType) to);
} else if (to instanceof GenericArrayType) {
return isAssignableToGenericArrayType(from, (GenericArrayType) to);
} else { // to instanceof TypeVariable
return false;
}
}
private static boolean isAssignableFromAny(Type[] fromTypes, Type to) {
for (Type from : fromTypes) {
if (isAssignable(from, to)) {
return true;
}
}
return false;
}
private static boolean isAssignableToClass(Type from, Class<?> to) {
return to.isAssignableFrom(getRawType(from));
}
private static boolean isAssignableToWildcardType(
Type from, WildcardType to) {
// if "to" is <? extends Foo>, "from" can be:
// Foo, SubFoo, <? extends Foo>, <? extends SubFoo>, <T extends Foo> or
// <T extends SubFoo>.
// if "to" is <? super Foo>, "from" can be:
// Foo, SuperFoo, <? super Foo> or <? super SuperFoo>.
return isAssignable(from, supertypeBound(to)) && isAssignableBySubtypeBound(from, to);
}
private static boolean isAssignableBySubtypeBound(Type from, WildcardType to) {
Type toSubtypeBound = subtypeBound(to);
if (toSubtypeBound == null) {
return true;
}
Type fromSubtypeBound = subtypeBound(from);
if (fromSubtypeBound == null) {
return false;
}
return isAssignable(toSubtypeBound, fromSubtypeBound);
}
private static boolean isAssignableToParameterizedType(Type from, ParameterizedType to) {
Class<?> matchedClass = getRawType(to);
if (!matchedClass.isAssignableFrom(getRawType(from))) {
return false;
}
Type[] typeParams = matchedClass.getTypeParameters();
Type[] toTypeArgs = to.getActualTypeArguments();
TypeToken<?> fromTypeToken = of(from);
for (int i = 0; i < typeParams.length; i++) {
// If "to" is "List<? extends CharSequence>"
// and "from" is StringArrayList,
// First step is to figure out StringArrayList "is-a" List<E> and <E> is
// String.
// typeParams[0] is E and fromTypeToken.get(typeParams[0]) will resolve to
// String.
// String is then matched against <? extends CharSequence>.
Type fromTypeArg = fromTypeToken.resolveType(typeParams[i]).runtimeType;
if (!matchTypeArgument(fromTypeArg, toTypeArgs[i])) {
return false;
}
}
return true;
}
private static boolean isAssignableToGenericArrayType(Type from, GenericArrayType to) {
if (from instanceof Class) {
Class<?> fromClass = (Class<?>) from;
if (!fromClass.isArray()) {
return false;
}
return isAssignable(fromClass.getComponentType(), to.getGenericComponentType());
} else if (from instanceof GenericArrayType) {
GenericArrayType fromArrayType = (GenericArrayType) from;
return isAssignable(fromArrayType.getGenericComponentType(), to.getGenericComponentType());
} else {
return false;
}
}
private static boolean isAssignableFromGenericArrayType(GenericArrayType from, Type to) {
if (to instanceof Class) {
Class<?> toClass = (Class<?>) to;
if (!toClass.isArray()) {
return toClass == Object.class; // any T[] is assignable to Object
}
return isAssignable(from.getGenericComponentType(), toClass.getComponentType());
} else if (to instanceof GenericArrayType) {
GenericArrayType toArrayType = (GenericArrayType) to;
return isAssignable(from.getGenericComponentType(), toArrayType.getGenericComponentType());
} else {
return false;
}
}
private static boolean matchTypeArgument(Type from, Type to) {
if (from.equals(to)) {
return true;
}
if (to instanceof WildcardType) {
return isAssignableToWildcardType(from, (WildcardType) to);
}
return false;
}
private static Type supertypeBound(Type type) {
if (type instanceof WildcardType) {
return supertypeBound((WildcardType) type);
}
return type;
}
private static Type supertypeBound(WildcardType type) {
Type[] upperBounds = type.getUpperBounds();
if (upperBounds.length == 1) {
return supertypeBound(upperBounds[0]);
} else if (upperBounds.length == 0) {
return Object.class;
} else {
throw new AssertionError(
"There should be at most one upper bound for wildcard type: " + type);
}
}
@Nullable
private static Type subtypeBound(Type type) {
if (type instanceof WildcardType) {
return subtypeBound((WildcardType) type);
} else {
return type;
}
}
@Nullable
private static Type subtypeBound(WildcardType type) {
Type[] lowerBounds = type.getLowerBounds();
if (lowerBounds.length == 1) {
return subtypeBound(lowerBounds[0]);
} else if (lowerBounds.length == 0) {
return null;
} else {
throw new AssertionError(
"Wildcard should have at most one lower bound: " + type);
}
}
static Class<?> getRawType(Type type) {
// For wildcard or type variable, the first bound determines the runtime type.
return getRawTypes(type).iterator().next();
}
static Set<Class<?>> getRawTypes(Type type) {
final Set<Class<?>> set = new HashSet<Class<?>>();
new TypeVisitor() {
@Override
void visitTypeVariable(TypeVariable<?> t) {
visit(t.getBounds());
}
@Override
void visitWildcardType(WildcardType t) {
visit(t.getUpperBounds());
}
@Override
void visitParameterizedType(ParameterizedType t) {
set.add((Class<?>) t.getRawType());
}
@Override
void visitClass(Class<?> t) {
set.add(t);
}
@Override
void visitGenericArrayType(GenericArrayType t) {
set.add(Types.getArrayClass(getRawType(t.getGenericComponentType())));
}
}.visit(type);
return set;
}
private static final class SimpleTypeToken<T> extends TypeToken<T> {
SimpleTypeToken(Type type) {
super(type);
}
private static final long serialVersionUID = 0;
}
}