package checkers.util.count; import checkers.source.*; import com.sun.source.tree.*; import com.sun.source.util.*; import java.util.*; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; /** * A utility class for listing the potential locations of annotations. * * <p> * * The class is actually an annotation processor; in order to use it, invoke * the compiler on the source file(s) for which you wish to count annotations * locations, and supply the argument: * <pre>-processor checkers.util.count.Locations</pre> * * * <p> * * Counting the number of lines of the processor's output yields the annotation * location count (e.g., by piping the output to {@code wc}). Because the * processor outputs a single line of text describing type of each annotation * location it encounters, you can obtain the count for specific annotation * location types (i.e., possible local variable annotations, or possible * method receiver annotations) by filtering the output accordingly (e.g., with * {@code grep}). * * <p> * * By default, this utility displays annotation locations only. The following * two options may be used to adjust the output: * * <ul> * <li>{@code -Anolocations}: suppresses location output</li> * <li>{@code -Aannotations}: enables annotation output</li> * </ul> */ @SupportedOptions({"nolocations", "annotations"}) @SupportedSourceVersion(SourceVersion.RELEASE_7) public class Locations extends SourceChecker { @Override protected SourceVisitor<?, ?> createSourceVisitor(CompilationUnitTree root) { return new Visitor(this, root); } static class Visitor extends SourceVisitor<Void, Void> { /** Whether annotation locations should be printed. */ private final boolean locations; /** Whether annotation details should be printed. */ private final boolean annotations; public Visitor(Locations l, CompilationUnitTree root) { super(l, root); // Get annotation processor "-A" options. Map<String, String> options = l.getProcessingEnvironment().getOptions(); locations = !options.containsKey("nolocations"); annotations = options.containsKey("annotations"); } private static final String LINE_SEPARATOR = System.getProperty("line.separator"); @Override public Void visitAnnotation(AnnotationTree tree, Void p) { if (annotations) { // An annotation is a body annotation if, while ascending the // AST from the annotation to the root, we find a block // immediately enclosed by a method. // // If an annotation is not a body annotation, it's a signature // (declaration) annotation. boolean isBodyAnnotation = false; TreePath path = getCurrentPath(); Tree prev = null; for (Tree t : path) { if (prev != null && prev.getKind() == Tree.Kind.BLOCK && t.getKind() == Tree.Kind.METHOD) { isBodyAnnotation = true; break; } prev = t; } System.out.printf(":annotation %s %s %s %s%s", tree.getAnnotationType(), tree, root.getSourceFile().getName(), (isBodyAnnotation ? "body" : "sig"), LINE_SEPARATOR); } return super.visitAnnotation(tree, p); } @Override public Void visitArrayType(ArrayTypeTree tree, Void p) { if (locations) System.out.println("array type"); return super.visitArrayType(tree, p); } @Override public Void visitClass(ClassTree tree, Void p) { if (locations) { System.out.println("class"); if (tree.getExtendsClause() != null) System.out.println("class extends"); for (Tree t : tree.getImplementsClause()) System.out.println("class implements"); } return super.visitClass(tree, p); } @Override public Void visitMethod(MethodTree tree, Void p) { if (locations) { System.out.println("method return"); System.out.println("method receiver"); for (Tree t : tree.getThrows()) System.out.println("method throws"); for (Tree t : tree.getParameters()) System.out.println("method param"); } return super.visitMethod(tree, p); } @Override public Void visitVariable(VariableTree tree, Void p) { if (locations) System.out.println("variable"); return super.visitVariable(tree, p); } @Override public Void visitMethodInvocation(MethodInvocationTree tree, Void p) { if (locations) { for (Tree t : tree.getTypeArguments()) System.out.println("method invocation type argument"); } return super.visitMethodInvocation(tree, p); } @Override public Void visitNewClass(NewClassTree tree, Void p) { if (locations) { System.out.println("new class"); for (Tree t : tree.getTypeArguments()) System.out.println("new class type argument"); } return super.visitNewClass(tree, p); } @Override public Void visitNewArray(NewArrayTree tree, Void p) { if (locations) { System.out.println("new array"); for (Tree t : tree.getDimensions()) System.out.println("new array dimension"); } return super.visitNewArray(tree, p); } @Override public Void visitTypeCast(TypeCastTree tree, Void p) { if (locations) System.out.println("typecast"); return super.visitTypeCast(tree, p); } @Override public Void visitInstanceOf(InstanceOfTree tree, Void p) { if (locations) System.out.println("instanceof"); return super.visitInstanceOf(tree, p); } @Override public Void visitParameterizedType(ParameterizedTypeTree tree, Void p) { if (locations) { for (Tree t : tree.getTypeArguments()) System.out.println("parameterized type"); } return super.visitParameterizedType(tree, p); } @Override public Void visitTypeParameter(TypeParameterTree tree, Void p) { if (locations) { for (Tree t : tree.getBounds()) System.out.println("type parameter bound"); } return super.visitTypeParameter(tree, p); } @Override public Void visitWildcard(WildcardTree tree, Void p) { if (locations) System.out.println("wildcard"); return super.visitWildcard(tree, p); } } }