package org.jdbi.v3.core.generic.internal;
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;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import static java.util.Collections.singletonMap;
import static org.jdbi.v3.core.generic.internal.Preconditions.checkNotNull;
import static org.jdbi.v3.core.generic.internal.Preconditions.checkState;
public class TypeToken<T> extends TypeCapture<T> {
private final Type runtimeType;
private transient TypeResolver typeResolver;
protected TypeToken() {
this.runtimeType = capture();
checkState(
!(runtimeType instanceof TypeVariable),
"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 = checkNotNull(type, "type");
}
public static TypeToken<?> of(Type type) {
return new SimpleTypeToken<>(type);
}
public final <X> TypeToken<T> where(TypeParameter<X> typeParam, TypeToken<X> typeArg) {
TypeResolver resolver = new TypeResolver()
.where(singletonMap(new TypeResolver.TypeVariableKey(typeParam.typeVariable), typeArg.runtimeType));
return new SimpleTypeToken<T>(resolver.resolveType(runtimeType));
}
public final TypeToken<?> resolveType(Type type) {
checkNotNull(type, "type");
TypeResolver resolver = typeResolver;
if (resolver == null) {
resolver = (typeResolver = TypeResolver.accordingTo(runtimeType));
}
return of(resolver.resolveType(type));
}
public final Type getType() {
return runtimeType;
}
public final Class<? super T> getRawType() {
Class<?> rawType = getRawTypes().iterator().next();
@SuppressWarnings("unchecked")
Class<? super T> result = (Class<? super T>) rawType;
return result;
}
private Set<Class<? super T>> getRawTypes() {
final Set<Class<?>> builder = new LinkedHashSet<>();
new TypeVisitor() {
@Override
void visitTypeVariable(TypeVariable<?> t) {
visit(t.getBounds());
}
@Override
void visitWildcardType(WildcardType t) {
visit(t.getUpperBounds());
}
@Override
void visitParameterizedType(ParameterizedType t) {
builder.add((Class<?>) t.getRawType());
}
@Override
void visitClass(Class<?> t) {
builder.add(t);
}
@Override
void visitGenericArrayType(GenericArrayType t) {
builder.add(Types.getArrayClass(of(t.getGenericComponentType()).getRawType()));
}
}.visit(runtimeType);
@SuppressWarnings({"unchecked", "rawtypes"})
Set<Class<? super T>> result = (Set) Collections.unmodifiableSet(builder);
return result;
}
private static final class SimpleTypeToken<T> extends TypeToken<T> {
SimpleTypeToken(Type type) {
super(type);
}
}
}