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); } }