package com.sora.util.akatsuki.analyzers;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import com.sora.util.akatsuki.TransformationContext;
import com.sora.util.akatsuki.analyzers.CascadingTypeAnalyzer.Analysis;
public class GenericTypeAnalyzer
extends CascadingTypeAnalyzer<GenericTypeAnalyzer, TypeMirror, Analysis> {
private TypeMirror resolvedMirror;
public GenericTypeAnalyzer(TransformationContext context) {
super(context);
}
@Override
protected GenericTypeAnalyzer createInstance(TransformationContext context) {
return new GenericTypeAnalyzer(context);
}
@Override
protected Analysis createAnalysis(InvocationContext<TypeMirror> context)
throws UnknownTypeException {
TypeVariable typeVariable = (TypeVariable) context.field.refinedMirror();
final TypeMirror upperBound = typeVariable.getUpperBound();
CascadingTypeAnalyzer<?, ?, ?> transformation = null;
if (upperBound instanceof DeclaredType) {
// we have a concrete type, good
resolvedMirror = upperBound;
transformation = resolve(context.field.refine(resolvedMirror));
} else if (upperBound instanceof IntersectionType) {
for (TypeMirror bound : ((IntersectionType) upperBound).getBounds()) {
// as long as the first bound matches anything, we use it
// TODO some bounds should have priority such as Parcelable and
// serializable. but how do we decide?
final CascadingTypeAnalyzer<?, ?, ?> found = resolve(
context.field.refine(resolvedMirror));
if (found != null) {
// we can probably do the following analysis to make this
// better:
// 1. Iterate through all strategies and save them as a list
// 2. Further filter out the working ones (ones that does
// not
// throw)
// 3. Do a super interface check on each of them and see
// which
// one is the most suitable
resolvedMirror = bound;
transformation = found;
break;
}
}
}
if (resolvedMirror == null || transformation == null)
throw new UnknownTypeException(context.field);
return cascade(transformation.target(resolvedMirror).cast(TypeCastStrategy.NO_CAST),
context, resolvedMirror);
}
}