Tree views in Eclipse plug-ins provide much elegant ways to present the recursive data. Perhaps they handle the parent-child relationships among the nodes more intuitively than any other view representation. When I wanted to put some 'kind' of model objects as nodes in a tree view, I wanted to create a small reusable group of classes which will make the process of putting an object in a tree
little simpler. Though it is not of any design pattern kind of stuff, I have benefitted rather significantly from this simple
interface layer later.
This interface layer constitutes of two interfaces,
TreeNode and
TreeInput representing each node and the input to the tree respectively.
TreeNode contract makes it possible for the tree viewer with all the data it requires such as name of the node, any associated image and whether it has any child nodes or not. The
TreeInput contract represents the input to the tree viewer. Following is the complete source code of these two main contracts.
TreeNode.java
import org.eclipse.swt.graphics.Image;
public interface TreeNode {
// Returns name of the node
public String getText();
// Returns image of the node
public Image getImage();
// Returns if this node has any child or not
public boolean hasChildren();
// Returns it's child nodes
public TreeNode[] getChildren();
// Returns it's parent node
public TreeNode getParent();
}
TreeInput.java
import java.util.List;
public interface TreeInput {
// Returns the list of top level nodes as input to the tree viewer
public List<TreeNode> getInput();
}
Having set the stage, let's take a use case as we want to create a simple tree view for a file system. That essentially two types nodes: directory and file. The root of the tree is the root of the file system (or any relative location) from where the top level directories, with no parent come as nodes. Only directory type nodes can have children nodes which are again either directory nodes or file nodes. And, let's consider someone called
FileViewInputProvider is what the tree view looks for input which actually provides all the top level nodes through
TreeInput.
Content and display to a tree viewer can be controlled by
ITreeContentProvider and
LabelProvider. A generic implementation to the content provider returns
nodes through the callbacks. For example, the
hasChildren() method checks the
TreeNode to see whether or not it has child nodes. The
getChildren(Object obj) is called when a tree node is expanded at the view which actually gets the child nodes from the
TreeNode#getChildren().
TreeContentProvider.java
import java.util.List;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
public class TreeContentProvider implements ITreeContentProvider {
@Override
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof TreeNode) {
return ((TreeNode) parentElement).getChildren();
}
if (parentElement instanceof TreeInput) {
return getElements(parentElement);
}
return new Object[0];
}
@Override
public Object getParent(Object element) {
if (element instanceof TreeNode) {
return ((TreeNode) element).getParent();
}
return null;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof TreeNode) {
return ((TreeNode) element).hasChildren();
}
return false;
}
@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof TreeInput) {
List<TreeNode> input = ((TreeInput)inputElement).getInput();
return input.toArray();
}
return new Object[0];
}
@Override
public void dispose() {
// Do nothing. Subclasses may override.
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// Do nothing. Subclasses may override.
}
}
Label provider is called once the content is set and ready to display in the viewer. It essentially provides a name to the node and an associated image. The generic label provider
TreeLableProvider extends
LabelProvider and overrides the
getText(Object obj) and
getImage(Object obj) methods. For these data, the label provider rely on the
TreeNode contract.
TreeLabelProvider.java
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.graphics.Image;
public class TreeLabelProvider extends LabelProvider {
/**
* Returns the text to be displayed in the tree view
*/
public String getText(Object obj) {
if (obj instanceof TreeNode) {
return ((TreeNode) obj).getText();
}
return (obj != null) ? obj.toString() : "???";
}
/**
* Returns the image to be displayed in the tree view
*/
public Image getImage(Object obj) {
if (obj instanceof TreeNode) {
return ((TreeNode) obj).getImage();
}
return null;
}
}
These simple interfaces would help creating tree views quickly by providing the required contract between the Eclipse tree viewer objects and the node input objects. This reusability provides speed and less maintenance while working with Eclipse tree views.