package checkers.util.debug; import checkers.source.*; import com.sun.source.tree.*; import com.sun.source.tree.Tree.Kind; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; import com.sun.tools.javac.tree.JCTree.JCNewArray; import java.util.*; import javax.annotation.processing.*; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; /** * A utility class for displaying the structure of the AST of a program. * * <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 view the structure of * the program. You may also wish to use the {@code -proc:only} javac option to * stop compilation after annotation processing. (But, in general * {@code -proc:only} causes type annotation processors not to be run.) * * <p> * * The utility will display the {@link Kind} of each * node it encounters while scanning the AST, indented according to its depth in * the tree. Additionally, the names of identifiers and member selection trees * are displayed (since these names are not tree nodes and therefore not * directly visited during AST traversal). * */ @SupportedAnnotationTypes("*") //@SupportedSourceVersion(SourceVersion.RELEASE_7) public class TreeDebug extends AbstractProcessor { protected Visitor createSourceVisitor(CompilationUnitTree root) { return new Visitor(); } private static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static class Visitor extends TreePathScanner<Void, Void> { private StringBuffer buf; public Visitor() { buf = new StringBuffer(); } @Override public Void scan(Tree node, Void p) { // Indent according to subtrees. if (getCurrentPath() != null) { for (TreePath tp = getCurrentPath(); tp != null; tp = tp.getParentPath()) buf.append(" "); } // Add node kind to the buffer. if (node == null) buf.append("null"); else buf.append(node.getKind()); buf.append(LINE_SEPARATOR); // Visit subtrees. super.scan(node, p); // Display and clear the buffer. System.out.print(buf.toString()); buf.setLength(0); return null; } /** * Splices additional information for a node into the buffer. * * @param text additional information for the node */ private final void insert(Object text) { buf.insert(buf.length() - 1, " "); buf.insert(buf.length() - 1, text); } @Override public Void visitIdentifier(IdentifierTree node, Void p) { insert(node); return super.visitIdentifier(node, p); } @Override public Void visitMemberSelect(MemberSelectTree node, Void p) { insert(node.getExpression() + "." + node.getIdentifier()); return super.visitMemberSelect(node, p); } @Override public Void visitNewArray(NewArrayTree node, Void p) { insert(((JCNewArray)node).annotations); insert("|"); insert(((JCNewArray)node).dimAnnotations); return super.visitNewArray(node, p); } @Override public Void visitLiteral(LiteralTree node, Void p) { insert(node.getValue()); return super.visitLiteral(node, p); } } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement element : ElementFilter.typesIn(roundEnv.getRootElements())) { TreePath path = Trees.instance(processingEnv).getPath(element); new Visitor().scan(path, null); } return false; } }