| Java, Eclipse and Web programming Tutorials |
Version 0.3
Copyright © 2009 Lars Vogel
24.05.2009
| Revision History | ||
|---|---|---|
| Revision 0.1 | 11.05.2009 | Lars Vogel |
| Created Article. | ||
| Revision 0.2 | 23.05.2009 | Lars Vogel |
| Described Java Model and AST | ||
| Revision 0.3 | 24.05.2009 | Lars Vogel |
| Added AST example | ||
Eclipse JDT
This article describes how the Eclipse JDT (Java Development Tools) and the Eclipse AST (Abstract Syntax Tree) can be used to access, change and read the elements of a Java program.
The article assumes that you are familiar with Eclipse plugin development. In this article Eclipse 3.4 is used.
Level of this tutorial: Advanced
Table of Contents
The Eclipse Java Development Tools (JDT) provide APIs to access and manipulate Java source code. It allows to access the existing projects in the workspace, create new projects and modify and read existing projects. It also allows to launch Java programs.
Java JDT allows to access Java source code via two different means. The Java Model and the Abstract Syntax Tree (AST).
which is a Document Object Model similar to the XML DOMEach Java project is represented in Eclipse as a Java model. The Eclipse Java model is a light-weight and fault tolerant representation of the Java project. It does not contain as many information as the Abstract Syntax Tree (AST) (for example it does not contain the method body of a method) but is fast to re-created in case of changes. For example the outline view is using the Java model for it representation; this way the information in the outline view can quickly get updated.
The Java model is defined in package and plugin "org.eclipse.jdt.core".
The Java model is represented as a tree structure which can be described via the following table.
Table 1.
| Project Element | Java Model element | Description |
|---|---|---|
| Java project | IJavaProject | The Java project which contains all other objects. |
| src folder / bin folder / or external library | IPackageFragmentRoot | Hold source or binary files, can be a folder or a library (zip / jar file ) |
| Each package | IPackageFragment | Each package is below the IPackageFragmentRoot, sub-packages are not leaves of the package, they are listed directed under IPackageFragmentRoot |
| Java Source File | ICompilationUnit | The Source file is always below the package node |
| Types / Fields / Methods | IType / IField / IMethod | Types, fields and methods |
The AST is a detailed tree representation of Java source code. The AST defines API to modify, create, read and delete source code.
The package for AST is org.eclipse.jdt.core.dom in the Eclipse org.eclipse.jdt.core plugin.
Each element in the Java source file is represented as a subclass of ASTNode. Each specific AST node provides specific information about the object it represents. For example you have MethodDeclaration (for methods), VariableDeclarationFragment (for variable declarations) and SimpleName (for any string which is not a Java keyword).
The AST is typically created based on a ICompilationUnit from the Java Model.
This article assume that you are familiar with Eclipse plugin development. See Eclipse Plugin Development for an introduction. For UI related activities, e.g. creating a view extension point please see Eclipse RCP tutorial.
The following will create a command which will read the project of the workspace, get all package and Java source files for these projects and read all methods for them by using the JDT Java Model API.
Create the plugin project "de.vogella.jdt.infos". Choose the "Hello World, command" as a template.
Add the following dependencies to your plugin "org.eclipse.core.resources", "org.eclipse.jdt", org.eclipse.jdt.core", "org.eclipse.core.runtime", "org.eclipse.jdt.ui", "org.eclipse.jface.text"
Change the handler to the following.
package de.vogella.jdt.infos.handlers;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.text.Document;
public class SampleHandler extends AbstractHandler {
public Object execute(ExecutionEvent event) throws ExecutionException {
// Get the root of the workspace
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
// Get all projects in the workspace
IProject[] projects = root.getProjects();
// Loop over all projects
for (IProject project : projects) {
try {
printProjectInfo(project);
} catch (CoreException e) {
e.printStackTrace();
}
}
return null;
}
private void printProjectInfo(IProject project) throws CoreException,
JavaModelException {
System.out.println("Working in project " + project.getName());
// Check if we have a Java project
if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")) {
IJavaProject javaProject = JavaCore.create(project);
printPackageInfos(javaProject);
}
}
private void printPackageInfos(IJavaProject javaProject)
throws JavaModelException {
IPackageFragment[] packages = javaProject.getPackageFragments();
for (IPackageFragment mypackage : packages) {
// Package fragments include all packages in the
// classpath
// We will only look at the package from the source
// folder
// K_BINARY would include also included JARS, e.g.
// rt.jar
if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) {
System.out.println("Package " + mypackage.getElementName());
printICompilationUnitInfo(mypackage);
}
}
}
private void printICompilationUnitInfo(IPackageFragment mypackage)
throws JavaModelException {
for (ICompilationUnit unit : mypackage.getCompilationUnits()) {
System.out.println("Source file " + unit.getElementName());
Document doc = new Document(unit.getSource());
System.out
.println("Has number of lines: " + doc.getNumberOfLines());
printIMethods(unit);
}
}
private void printIMethods(ICompilationUnit unit) throws JavaModelException {
IType[] allTypes = unit.getAllTypes();
for (IType type : allTypes) {
IMethod[] methods = type.getMethods();
for (IMethod method : methods) {
System.out.println("Method name " + method.getElementName());
System.out.println("Signature " + method.getSignature());
System.out.println("Return Type " + method.getReturnType());
}
}
}
}
Start your plugin. Create a few projects in your new workspace. Create a few packages for them and a few Java files. Press the menu entry which points to your sample command.
You should see the projects, package and source files listed in the console of the calling workbench.
The following will create a command which will create a new package to existing (and open) Java projects which have the same name as the Java project. For example if you have a project "de.vogella.test" in your workspace without any package this command will create the package "de.vogella.test" in this project.
Create a new plugin project "de.vogella.jdt.newelements".
Add a new command "de.vogella.jdt.newelements.AddPackage" and put it into the menu.
Create the following handler for this command.
package de.vogella.jdt.newelements.handler;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
public class AddPackage extends AbstractHandler implements IHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
// Get all projects in the workspace
IProject[] projects = root.getProjects();
// Loop over all projects
for (IProject project : projects) {
try {
// Only work on open projects with the Java nature
if (project.isOpen()
&& project
.isNatureEnabled("org.eclipse.jdt.core.javanature")) {
IJavaProject javaProject = JavaCore.create(project);
IFolder folder = project.getFolder("src");
// folder.create(true, true, null);
IPackageFragmentRoot srcFolder = javaProject
.getPackageFragmentRoot(folder);
IPackageFragment fragment = srcFolder
.createPackageFragment(project.getName(), true,
null);
System.out.println("Juhu");
}
} catch (CoreException e) {
e.printStackTrace();
}
}
return null;
}
}
An example can be found on the source page in project "de.vogella.jdt.packageexplorer". Have a look at the command handler "AddPackage.java".
The following will create an AST for each source file in your workspace and print out the name and the return type of each method using the AST.
Create a new plugin project "de.vogella.jdt.astsimple" using the "Hello world" template.
Maintain the following plugin dependencies.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Astsimple Plug-in Bundle-SymbolicName: de.vogella.jdt.astsimple;singleton:=true Bundle-Version: 1.0.0 Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Require-Bundle: org.eclipse.core.runtime;bundle-version="3.4.0", org.eclipse.ui;bundle-version="3.4.2", org.eclipse.jdt.core;bundle-version="3.4.4", org.eclipse.core.resources;bundle-version="3.4.2", org.eclipse.jdt.ui;bundle-version="3.4.2"
To get information about the AST you can use the Visitor Pattern. This allows you to add a visitor to the AST for a specific elements. In this visitor can you capture information about the object and return it after processing the AST. Create for this the following class.
package de.vogella.jdt.astsimple.handler;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.MethodDeclaration;
public class MethodVisitor extends ASTVisitor {
List<MethodDeclaration> methods = new ArrayList<MethodDeclaration>();
@Override
public boolean visit(MethodDeclaration node) {
methods.add(node);
return super.visit(node);
}
public List<MethodDeclaration> getMethods() {
return methods;
}
}
Add a new command "de.vogella.jdt.astsimple.GetInfo" and put it into the menu.
Create the following handler for this command.
package de.vogella.jdt.astsimple.handler;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.MethodDeclaration;
public class GetInfo extends AbstractHandler {
@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot root = workspace.getRoot();
// Get all projects in the workspace
IProject[] projects = root.getProjects();
// Loop over all projects
for (IProject project : projects) {
try {
if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")) {
IPackageFragment[] packages = JavaCore.create(project)
.getPackageFragments();
// parse(JavaCore.create(project));
for (IPackageFragment mypackage : packages) {
if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) {
for (ICompilationUnit unit : mypackage
.getCompilationUnits()) {
// Now create the AST for the ICompilationUnits
CompilationUnit parse = parse(unit);
MethodVisitor visitor = new MethodVisitor();
parse.accept(visitor);
for (MethodDeclaration method : visitor
.getMethods()) {
System.out.print("Method name: "
+ method.getName()
+ " Return type: "
+ method.getReturnType2());
}
}
}
}
}
} catch (CoreException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Reads a ICompilationUnit and creates the AST DOM for manipulating the
* Java source file
*
* @param unit
* @return
*/
private static CompilationUnit parse(ICompilationUnit unit) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setKind(ASTParser.K_COMPILATION_UNIT);
parser.setSource(unit);
parser.setResolveBindings(true);
return (CompilationUnit) parser.createAST(null); // parse
}
}
Add the command to your menu. Run your new plugin, create a new Java project and select the command. It should print out the information about your methods in the IDE from which you started your new plugin
Thank you for practicing with this tutorial.
Please note that I maintain this website in my private time. If you like the information I'm providing please help me by donating.For questions and discussion around this article please use the www.vogella.de Google Group. Also if you note an error in this article please post the error and if possible the correction to the Group.
I believe the following is a very good guideline for asking questions in general and also for the Google group How To Ask Questions The Smart Way.
http://www.vogella.de/code/codeeclipse.html Source Code of Examples
http://www.eclipsecon.org/2005/presentations/EclipseCON2005_Tutorial29.pdf Fantastic Presentation about using Eclipse JDT
http://www.eclipse.org/articles/article.php?file=Article-JavaCodeManipulation_AST/index.html Java Code Manipulation AST
http://www.eclipse.org/articles/article.php?file=Article-Unleashing-the-Power-of-Refactoring/index.html Article about using the JDT Refactoring API
http://www.ibm.com/developerworks/opensource/library/os-ast/ IBM Article about the AST