Declawer is a tool we use to strip the generics from Java source code.
It has some limitations:
no anonymous inner classes
no Java 5 syntactic sugar (i.e. enhanced for loop, autoboxing, etc.)
But generally it's an indispensable tool for supporting both Java 5 and Java 1.4 simultaneously on the same source base.
Generics Whitepaper (PDF) - How we get the best of Java 1.5 while continuing to support 1.4.
Varargs Support in Declawer
Recently, support for varargs was added to our Declawer tool. It serves as a good model for describing the manner in which all of the Declawer source code transformations have been implemented.
First, note that tools.jar is located in the \lib folder of your JDK installation. It contains the class com.sun.tools.javac.main.JavaCompiler. This is the Java compiler. More specifically, it is the class that can produce an Abstract Syntax Tree (AST) from Java source code. It contains the method:
that compiles the given Java files. Declawer simply constructs a new JavaCompiler object and then calls this method with a list of Java source files to convert from JDK 5.0 to JDK 1.4.
JavaCompiler has a brilliant secret embedded within. Not only can it construct ASTs from Java source code, but it can also write ASTs back out to disk as Java source code files. All that is required to do the latter is to pass a special argument into the constructor of the JavaCompiler that indicates it should output Java source code after compilation is complete.
So, Declawer is simply implemented as a custom version of the JavaCompiler. It uses the normal JavaCompiler facilities to construct ASTs from Java source code, fiddles around with those ASTs, and writes them back out to disk as Java source code.
Writing the code that fiddles around with the Java ASTs is also made trivial using classes available within tools.jar. Specifically, it contains com.sun.tools.javac.tree.TreeScanner which is a class that already knows how to visit each of the nodes in an AST. Nodes exist for each of the parts of Java source code, such as class definitions, method definitions, variable declarations, looping constructs, etc. So, all that is required is to create a custom TreeScanner that visits the AST nodes and modifies them to replace the incompatible JDK 5.0 constructs with reasonable JDK 1.4 equivalents.
In the case of varargs, a custom TreeScanner recognizes method declarations which contain varargs. Once detected, it modifies the AST node for the vararg in the method signature and replaces it with an AST node that defines an Object in place of the variable argument. In fact, in this case, since varargs truly are syntactic sugar, it is a simple flag in the AST node that indicates whether the declaration was a varargs declaration in the source code or a pedestrian Object. All that is needed by our custom TreeScanner is to clear the varargs flag, and when the AST is written back out, it is written as though it is the Object.
Adding features to Declawer is very difficult because of the way it is built on top of the existing Sun javac Java compiler. Regardless, it's nice to keep track of what we'd like if we can come up with a better strategy!
Remove asserts for limited use in Java 1.3.
Enhanced for loop