September 16, 2010

Visualizing Eclipse Project Dependencies with Zest

I wonder how many Java developers that are using Eclipse to develop systems the old-fashioned way without using OSGi? It feels awkward and strange not to use OSGi if you are used doing that, but sometimes you don't have a choice when working with legacy systems.


It is well known that when the system grows it gets harder to maintain a clear separation between different parts of the system. Dividing the system into Eclipse projects does not help much since the tool lacks functionality for managing project dependencies. When developing applications using plug-ins and OSGi I have used the PDE Dependency Visualization tool. However, it only works for plug-in projects and does not show project dependencies for standard Java projects.


Googling for an existing solution didn't help much. The only discussion about this was a suggestion to parse all .classpath files and build a DOT file for GraphViz to display. Oh well, I did some hacking for a while, forgetting everything I read in the Clean Code book. The result was a standalone Java program that generated the needed DOT file. It was working ok and the GraphViz tooling could create the graph as an image for me. What I really wanted, though, was to have a view in Eclipse that showed my dependencies just as the PDE Dependency tool does.

The PDE Dependency tool is using Zest: The Eclipse Visualization Toolkit, so I decided to take a look at it. To my surprise it turned out to have exactly what I needed, namely Zest Viewers modeled after JFace.

With that I could create what I needed with just these few steps:

1. Install the Graphical Editing Framework Zest Visualization Toolkit SDK in Eclipse.

2. Create a Plug-In Project using the "Plug-in with a view" template. Change the default names to be more suited, for example the view class name to "ProjectDependencies".

3. Add the following plug-in dependencies:
  • org.eclipse.zest.core
  • org.eclipse.zest.layouts
  • org.eclipse.core.resources

4. In the view class, ProjectDependencies.java, change the TableViewer to a GraphViewer and use the following code in createPartControl :

public void createPartControl(Composite parent) {
  viewer = new GraphViewer(parent, SWT.NONE);
  viewer.setContentProvider(new ViewContentProvider());
  viewer.setLabelProvider(new ViewLabelProvider());
  viewer.setLayoutAlgorithm(new TreeLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING));
  viewer.setConnectionStyle(ZestStyles.CONNECTIONS_DIRECTED | ZestStyles.CONNECTIONS_DASH);
  viewer.setInput(null);


5. Change the content provider to implement the IGraphEntityContentProvider interface. Since the Resources plug-in already has the model of all Eclipse projects and its dependencies I can easily use it to return what is needed for implementing the content provider:
class ViewContentProvider implements IGraphEntityContentProvider {
  public Object[] getElements(Object parent) {
    // Return all projects in the workspace
    return ResourcesPlugin.getWorkspace().getRoot().getProjects();
  }

  public Object[] getConnectedTo(Object entity) {
    IProject project = (IProject) entity;
    try {
      // Return the project dependencies
      return project.getReferencedProjects();
    } catch (CoreException e) { }
    return null;
  }

  public void dispose() { }
  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { }
}
6.The last step is to change the ViewLabelProvider to return the project name.
class ViewLabelProvider extends LabelProvider {
  public String getText(Object element) {
    if (!(element instanceof IProject))
      return null;

    IProject project = (IProject) element;
    return project.getName();
  }

  public Image getImage(Object obj) {
    return null;
  }
}

Running the plug-in project as an Eclipse application and showing the view shows the project dependency graph:



Zest contains a lot more that could be utilized, such as a number of different layout algorithms, but this simple solution was exactly what I needed. I was surprised to see how easily I could achieve it, once I found the right component to reuse. Reusing existing software feels great, and the Eclipse platform has a lot of it!