package checkers.types;
import java.lang.annotation.Annotation;
import java.util.*;
import com.sun.source.tree.Tree;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import checkers.basetype.BaseTypeChecker;
import checkers.quals.ImplicitFor;
import checkers.quals.TypeQualifiers;
import checkers.types.AnnotatedTypeMirror.AnnotatedExecutableType;
import checkers.types.visitors.AnnotatedTypeScanner;
import checkers.util.AnnotationUtils;
/**
* Adds annotations to a type based on the contents of a type. By default, this
* class honors the {@link ImplicitFor} annotation and applies implicit
* annotations specified by {@link ImplicitFor} for any type whose visitor is
* not overridden or does not call {@code super}; it is designed to be invoked
* from
* {@link AnnotatedTypeFactory#annotateImplicit(Element, AnnotatedTypeMirror)}
* and
* {@link AnnotatedTypeFactory#annotateImplicit(Tree, AnnotatedTypeMirror)}.
*
* <p>
*
* {@link TypeAnnotator} traverses types deeply by default, except that it skips
* the method receiver of executable types (for interoperability with
* {@link AnnotatedTypeFactory#annotateInheritedFromClass(AnnotatedTypeMirror)}).
*/
public class TypeAnnotator extends AnnotatedTypeScanner<Void, ElementKind> {
private final Map<TypeKind, AnnotationMirror> typeKinds;
private final Map<Class<?>, AnnotationMirror> typeClasses;
/**
* Creates a {@link TypeAnnotator} from the given checker, using that checker's
* {@link TypeQualifiers} annotation to determine the annotations that are
* in the type hierarchy.
*
* @param checker the type checker to which this annotator belongs
*/
public TypeAnnotator(BaseTypeChecker checker) {
this.typeKinds = new EnumMap<TypeKind, AnnotationMirror>(TypeKind.class);
this.typeClasses = new HashMap<Class<?>, AnnotationMirror>();
AnnotationUtils annoFactory = AnnotationUtils.getInstance(checker.getProcessingEnvironment());
// Get type qualifiers from the checker.
Set<Class<? extends Annotation>> quals
= checker.getSupportedTypeQualifiers();
// For each qualifier, read the @ImplicitFor annotation and put its type
// classes and kinds into maps.
for (Class<? extends Annotation> qual : quals) {
ImplicitFor implicit = qual.getAnnotation(ImplicitFor.class);
if (implicit == null) continue;
AnnotationMirror theQual = annoFactory.fromClass(qual);
for (Class<? extends AnnotatedTypeMirror> typeClass : implicit.typeClasses())
typeClasses.put(typeClass, theQual);
for (TypeKind typeKind : implicit.types())
typeKinds.put(typeKind, theQual);
}
}
@Override
protected Void scan(AnnotatedTypeMirror type, ElementKind p) {
if (type == null) // on bounds, etc.
return super.scan(type, p);
// If the type's kind or class is in the appropriate map, annotate the
// type.
if (!type.isAnnotated()) {
if (typeKinds.containsKey(type.getKind()))
type.addAnnotation(typeKinds.get(type.getKind()));
else if (!typeClasses.isEmpty()) {
Class<? extends AnnotatedTypeMirror> t = type.getClass();
if (typeClasses.containsKey(t))
type.addAnnotation(typeClasses.get(t));
}
}
return super.scan(type, p);
}
@Override
public Void visitExecutable(AnnotatedExecutableType t, ElementKind p) {
// skip the receiver
scan(t.getReturnType(), p);
scanAndReduce(t.getParameterTypes(), p, null);
scanAndReduce(t.getThrownTypes(), p, null);
scanAndReduce(t.getTypeVariables(), p, null);
return null;
}
}