package tc.oc.commons.core.reflect;
import java.lang.reflect.TypeVariable;
import javax.annotation.Nullable;
import com.google.common.reflect.TypeToken;
/**
* Represents a type parameter of {@link D} that extends {@link T}, and allows the parameter to
* be resolved in the context of a given {@link D} subtype.
*
* @param <T> Upper bound of the type parameter
* @param <D> Class that the type parameter belongs to
*
* class Woot<T extends Number> {}
*
* class IntWoot extends Woot<Integer> {}
*
* new ResolvableTypeParameter<Number, Woot>("T"){}
* .resolveIn(IntWoot.class)
*
* => Integer.class
*
* Note: TypeToken assumes that its type is the first type parameter of the immediate superclass.
* It doesn't actually check if the superclass is TypeToken, so instead it will capture our first
* type parameter, which therefor must be the one passed to TypeToken.
*/
public abstract class ResolvableTypeParameter<T, D> extends TypeToken<T> {
private final TypeVariable<Class<D>> typeVariable;
protected ResolvableTypeParameter(String name) {
this(null, name);
}
protected ResolvableTypeParameter(@Nullable Class<D> decl, String name) {
if(decl == null) {
decl = (Class<D>) new TypeToken<D>(getClass()){}.getRawType();
}
this.typeVariable = Types.typeVariable(decl, name);
if(!isAssignableFrom(typeVariable)) {
throw new IllegalArgumentException("Type variable " + typeVariable + " is not assignable to " + this);
}
}
public TypeVariable<Class<D>> typeVariable() {
return typeVariable;
}
public TypeToken<? extends T> resolveIn(TypeToken<? extends D> context) {
return (TypeToken<? extends T>) context.resolveType(typeVariable);
}
}