Java Code Generation with Eclipse and AST

Posted by Mike Haller on Sunday, June 8. 2008 at 23:52 in Eclipse
As Mateusz asked for how to add Newlines to generated code in Eclipse, i looked at how Eclipse JDT is doing it. And of course, the Eclipse guys eat their own dog food and use the abstract syntax tree (AST) feature.

For a showcase, let's assume we've got an open Java Editor with an empty class and we want to add a new method. For the sake of simplicity for the demo, i'm going to implement it in an action and use the current editor as target. That's not nice, but pretty simple for now:
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
IWorkbenchPage page = window.getActivePage();
IEditorPart editor = page.getActiveEditor();
IEditorInput input = editor.getEditorInput();
IJavaElement element = JavaUI.getEditorInputJavaElement(input);

The central class is PlatformUI, which gives us access to a lot of functionality of the user interface of the Eclipse Platform. From there, we go our way through the active window (you can have multiple windows open for the same running instance of Eclipse, e.g. with Window > New Window), over the current page (I'm not sure if there can be multiple pages, but let's be sure here) and the currently active editor. There is no way to cast the editor part to something with a known interface - there is no ICompilationUnitEditor or IJavaEditor interface. And that's good because the correct way to get to the object representing the Java class currently being edited is to use the JavaUI utilities.

So, from the IEditorInput, which represents the open IFile instance, we get a IJavaElement instance, which is probably an ICompilationUnit.

From there, we instantiate a new AST so we have programmatic access to a Domain Object Model for the Java Source Code being edited:

ICompilationUnit cu = (ICompilationUnit) element;
AST ast = AST.newAST(AST.JLS3);


At first glance, it looks like an enormous effort to just add a new method with a single line in the body (took me 17 lines), when a simple StringBuffer and three calls to append() would do the job, too. Well, the bonus points come later and in the small hello-world demos the advantages of having a complex way of doing something do not become clear until you use them yourself.

Let's go on and create a new method in the class file using AST:
MethodDeclaration newMethod = ast.newMethodDeclaration();


Of course, this is not the only thing you need to do to add a new method. You also need to give it a name and set some additional modifiers:
newMethod.setName(ast.newSimpleName("doSomething"));
List newModifiers = ast.newModifiers(Modifier.PUBLIC);
newMethod.modifiers().add(newModifiers.get(0));
newMethod.setConstructor(false);


Okay, now set the name of the method to doSomething() and added the public keyword. Next, let's add a body to the method:

Block body = ast.newBlock();
newMethod.setBody(body);


And add the end of the method, let's set the return type of the method to String. Btw, that's one of the advantages of using AST, you can set the return type and after that, populate the body. You don't need to populate the method's structure in the right ordering.
Type stringType = ast.newSimpleType(ast.newSimpleName("String"));
newMethod.setReturnType2(stringType);


For demo purposes, we're going to print out something on the console using System.out.println:
MethodInvocation newMethodInvocation = ast.newMethodInvocation();
QualifiedName name = ast.newQualifiedName(ast
		.newSimpleName("System"), ast.newSimpleName("out"));
newMethodInvocation.setExpression(name);
newMethodInvocation.setName(ast.newSimpleName("println"));
ExpressionStatement expressionStatement = ast
.newExpressionStatement(newMethodInvocation);
body.statements().add(expressionStatement);


Yes, that last part is a bit complicated, but it can be extracted into factories and then it should be simpler to use.
The method invocation I created from the AST instance needs to have an expression and a name. The expression is the path to the method and the name of the method invocation is in fact the name of the method being invoked. The method invocation must be contained in an expression statement before you can add it to the list of statements in the body.

That's it.

Now you are sure excited on what's the output of all this effort, right? Here you go:

public String doSomething(){
  System.out.println();
}



Add Comment

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications
 
Submitted comments will be subject to moderation before being displayed.
 

About

My name is Mike Haller and I'm a software developer and architect at Bosch Software Innovations in Germany. I love programming, playing games and reading books. I like good food, making photos and learning and mentoring about the craftsmanship of commercial software development. Stack Overflow profile for mhaller

Quicksearch