I wrote about javac's APIs in my last blog entry. But, I did not mention an example for Javac's Tree API. How about a simple example that uses javac's tree API? Here it is... In the following example, we implement a checker for empty catch blocks (because "eating exceptions silently" is considered bad) and warn the user.
We implement EmptyCatchChecker as a JSR-269 processor. In the process method, we
get each ClassTree node and run it through a verifier visitor - which is a TreeScanner. In the TreeScanner subclass, whenever CatchTree node is visited, we check whether it's block has any statements or not. If not, we issue a warning message. To give a better warning message, we print the class and the method in which empty catch block occurs [To print source file and line number, we need CompilationUnitTree and I don't know how to get that!].
Update: I had missed HTML escaping < characters in source code [for generics] and so the code below was not compiling. Thanks to Jaya Hangal for notifying this issue. I've fixed it now.
import java.util.*;
// import all JSR 199, JSR 269 and Tree API stuff
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.tools.*;
import com.sun.source.tree.*;
import com.sun.source.util.*;
// An annotation processor that processes all classes
@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class EmptyCatchChecker extends AbstractProcessor {
private Trees treeUtils;
public void init(ProcessingEnvironment pe) {
super.init(pe);
treeUtils = Trees.instance(pe);
}
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
if (! roundEnv.processingOver()) {
Set<? extends Element> elements =
roundEnv.getRootElements();
for (Element e: elements) {
Tree tree = treeUtils.getTree(e);
if (tree.getKind().equals(Tree.Kind.CLASS)) {
verify((ClassTree)tree);
}
}
}
return true;
}
// verify each class tree
private void verify(ClassTree ct) {
ct.accept(new TreeScanner<Object, Void>() {
private String currentClass, currentMethod;
public Object visitClass(ClassTree node, Void v) {
currentClass = node.getSimpleName().toString();
return super.visitClass(node, v);
}
public Object visitMethod(MethodTree node, Void v) {
currentMethod = node.getName().toString();
return super.visitMethod(node, v);
}
public Object visitCatch(CatchTree node, Void v) {
BlockTree block = node.getBlock();
if (block.getStatements().isEmpty()) {
warn("empty catch block in " + currentMethod +
" method of " + currentClass);
}
return super.visitCatch(node, v);
}
}, null);
}
private void warn(String msg) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg);
}
}
To compile the above code, you can use the following command:
javac -classpath $JDK_HOME/lib/tools.jar EmptyCatchChecker.java
To use this empty catch checker, you can use the following command:
javac -processor EmptyCatchChecker YourClass.java
Note that the above checker can be fooled by adding a simple ';' in catch block - because ';' is a statement. How do we check for that? That is an exercise to the reader
[Hint: EmptyStatementTree]
Posted by anjan bacchu on November 15, 2006 at 11:01 AM IST #
Posted by Mehmood on November 15, 2006 at 12:25 PM IST #
Posted by A. Sundararajan on November 15, 2006 at 06:39 PM IST #