package tc.oc.commons.core.inject;
import javax.annotation.Nullable;
import com.google.common.reflect.TypeParameter;
import com.google.common.reflect.TypeResolver;
import com.google.common.reflect.TypeToken;
import com.google.inject.TypeLiteral;
import tc.oc.commons.core.reflect.TypeArgument;
import tc.oc.commons.core.reflect.Types;
/**
* Base for manifests that configure things related to a single generic type {@link T}
*
* This simply provides some commonly useful functionality to subclasses:
*
* - Ensures the type parameter is fully specified, resolving it automatically if possible
* - Provides a few helpful fields related to {@link T}
* - Provides {@link #resolve(TypeLiteral)} to conveniently resolve other types that refer to {@link T}
* - Uses the type as the equality key for the manifest (see {@link KeyedManifest})
*/
public class TypeManifest<T> extends KeyedManifest {
protected final TypeLiteral<T> type;
protected final TypeToken<T> typeToken;
protected final TypeParameter<T> typeParameter;
protected final TypeArgument<T> typeArg;
private final TypeResolver resolver;
protected TypeManifest() {
this(null);
}
public TypeManifest(@Nullable TypeLiteral<T> nullableType) {
this.typeToken = nullableType != null ? Types.toToken(nullableType)
: new TypeToken<T>(getClass()){};
this.type = nullableType != null ? nullableType
: Types.toLiteral(typeToken);
Types.assertFullySpecified(this.type);
this.typeParameter = new TypeParameter<T>(){};
this.typeArg = new TypeArgument<T>(this.type){};
TypeResolver resolver = new TypeResolver();
for(Class<?> cls = getClass(); cls != null; cls = cls.getSuperclass()) {
if(cls.getTypeParameters().length > 0) {
resolver = resolver.where(cls.getTypeParameters()[0], type.getType());
}
}
this.resolver = resolver;
}
@Override
protected Object manifestKey() {
return type;
}
/**
* Try to resolve variables in the given type using the known value of {@link T}
*
* TODO: This method makes the rather naive assumption that the first formal type
* parameter of every class descended from {@link TypeManifest} is the same type
* as {@link T}, which is typical but by no means guaranteed. The resolution could
* be much smarter with a bit more reflection work.
*/
protected <R> TypeLiteral<R> resolve(TypeLiteral<R> related) {
return (TypeLiteral<R>) TypeLiteral.get(Types.assertFullySpecified(resolver.resolveType(related.getType())));
}
}