package annotator.scanner; import com.sun.source.tree.ClassTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; import com.sun.source.util.TreePath; import com.sun.source.util.TreePathScanner; /** * AnonymousClassScanner determine the index of a tree for an anonymous * class. If the index is i, it is the ith anonymous class in the file. * Thus, if i = 2, it will have a name of the form NamedClass$2. */ public class AnonymousClassScanner extends TreePathScanner<Void, Integer> { /** * Given an anonymous class, computes and returns its 1-based index in the given tree representing * an anonymous class. * * @param path the source path ending in the anonymous class * @param anonclass the anonymous class to search for * @return the index of the anonymous class in the source code */ public static int indexOfClassTree(TreePath path, Tree anonclass) { // Move up to the CLASS tree enclosing this CLASS tree and start the tree // traversal from there. This prevents us from counting anonymous classes // that are in a different part of the tree and therefore aren't included // in the index number. int classesFound = 0; boolean anonclassFound = false; while (path.getParentPath() != null && classesFound < 1) { if (path.getLeaf() == anonclass) { anonclassFound = true; } path = path.getParentPath(); if (anonclassFound && CommonScanner.hasClassKind(path.getLeaf())) { classesFound++; } } AnonymousClassScanner lvts = new AnonymousClassScanner(anonclass); lvts.scan(path, 0); if (lvts.found) { return lvts.index; } else { return -1; } } private int index; // top-level class doesn't count, so first index will be -1 private boolean found; private Tree anonclass; /** * Creates a new AnonymousClassScanner that searches for the index of the given * tree, representing an anonymous class. * * @param tree the anonymous class to search for */ private AnonymousClassScanner(Tree anonclass) { this.index = 1; // start counting at 1 this.found = false; this.anonclass = anonclass; } // Slightly tricky counting: if the target item is a CLASS, only count // CLASSes. If it is a NEW_CLASS, only count NEW_CLASSes // The level parameter keeps us from traversing too low in the tree and // counting classes that aren't included in the index number. @Override public Void visitClass(ClassTree node, Integer level) { if (level < 2) { if (!found && CommonScanner.hasClassKind(anonclass)) { if (anonclass == node) { found = true; } else if (node.getSimpleName().toString().trim().isEmpty()) { // don't count classes with given names in source index++; } } super.visitClass(node, level + 1); } return null; } @Override public Void visitNewClass(NewClassTree node, Integer level) { // if (level < 2) { if (!found && anonclass.getKind() == Tree.Kind.NEW_CLASS) { if (anonclass == node) { found = true; } else if (node.getClassBody() != null) { // Need to make sure you actually are creating anonymous inner class, // not just object creation. index++; } else { return null; } } super.visitNewClass(node, level + 1); // } return null; } }