package checkers.types;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.regex.Pattern;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeKind;
import checkers.basetype.BaseTypeChecker;
import checkers.quals.ImplicitFor;
import checkers.quals.TypeQualifiers;
import checkers.types.AnnotatedTypeMirror.AnnotatedArrayType;
import checkers.util.AnnotationUtils;
import com.sun.source.tree.*;
import com.sun.source.tree.Tree.Kind;
import com.sun.source.util.SimpleTreeVisitor;
/**
* Adds annotations to a type based on the contents of a tree. By default, this
* class honors the {@link ImplicitFor} annotation and applies implicit
* annotations specified by {@link ImplicitFor} for any tree 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 TreeAnnotator} does not traverse trees deeply by default.
*/
public class TreeAnnotator extends SimpleTreeVisitor<Void, AnnotatedTypeMirror> {
private final Map<Tree.Kind, AnnotationMirror> treeKinds;
private final Map<Class<?>, AnnotationMirror> treeClasses;
private final Map<Pattern, AnnotationMirror> stringPatterns;
private final QualifierHierarchy qualHierarchy;
private final AnnotatedTypeFactory typeFactory;
/**
* 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 TreeAnnotator(BaseTypeChecker checker, AnnotatedTypeFactory typeFactory) {
this.treeKinds = new EnumMap<Kind, AnnotationMirror>(Kind.class);
this.treeClasses = new HashMap<Class<?>, AnnotationMirror>();
this.stringPatterns = new IdentityHashMap<Pattern, AnnotationMirror>();
this.qualHierarchy = checker.getQualifierHierarchy();
this.typeFactory = typeFactory;
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 tree
// 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 Tree> treeClass : implicit.treeClasses())
treeClasses.put(treeClass, theQual);
for (Tree.Kind treeKind : implicit.trees())
treeKinds.put(treeKind, theQual);
for (String pattern : implicit.stringPatterns())
stringPatterns.put(Pattern.compile(pattern), theQual);
}
}
@Override
public Void defaultAction(Tree tree, AnnotatedTypeMirror type) {
if (type.isAnnotated())
return null;
// If this tree's kind is in treeKinds, annotate the type.
// If this tree's class or any of its interfaces are in treeClasses,
// annotate the type, and if it was an interface add a mapping for it to
// treeClasses.
if (treeKinds.containsKey(tree.getKind()))
type.addAnnotation(treeKinds.get(tree.getKind()));
else if (!treeClasses.isEmpty()) {
Class<? extends Tree> t = tree.getClass();
if (treeClasses.containsKey(t))
type.addAnnotation(treeClasses.get(t));
for (Class<?> c : t.getInterfaces()) {
if (treeClasses.containsKey(c)) {
type.addAnnotation(treeClasses.get(c));
treeClasses.put(t, treeClasses.get(c));
}
}
}
return null;
}
@Override
public Void visitLiteral(LiteralTree tree, AnnotatedTypeMirror type) {
if (!stringPatterns.isEmpty() && tree.getKind() == Tree.Kind.STRING_LITERAL) {
String string = (String)tree.getValue();
for (Pattern pattern: stringPatterns.keySet()) {
if (pattern.matcher(string).matches()) {
type.addAnnotation(stringPatterns.get(pattern));
break;
}
}
}
return super.visitLiteral(tree, type);
}
@Override
public Void visitNewArray(NewArrayTree tree, AnnotatedTypeMirror type) {
if (tree.getType() == null) {
Collection<AnnotationMirror> lub = null;
for (ExpressionTree init: tree.getInitializers()) {
AnnotatedTypeMirror iniType = typeFactory.getAnnotatedType(init);
Collection<AnnotationMirror> annos = iniType.getAnnotations();
lub = (lub == null) ? annos : qualHierarchy.leastUpperBound(lub, annos);
}
assert type.getKind() == TypeKind.ARRAY;
if (lub != null) {
((AnnotatedArrayType)type).getComponentType().addAnnotations(lub);
}
}
return super.visitNewArray(tree, type);
}
}