The JFace library offers some widgets, called viewers, that can represent a data model. What are they for? Let’s suppose you want to show a table, a tree or a combo in order to visualize a list of objects or a hierarchical structure. You could certainly make use of basic widgets like Table, Tree or Combo, but they are usually not a good choice.
Imagine you while filling a table, row by row, starting from a list of Orders and your focus is doing some action with the Order that the user selected.
With a Table widget you have to find a trick like counting on a key value on a certain column, or keep an Order list with the same sequence showed on the table and extract by index on a positional basis from the index of selected row.
Further, just populating the table would be quite a heavy task.

Thanks to JFace viewers, it is possible to set up some sort of “binding” between the widget and the model itself. This allows, for instance, retrieving directly the selected Order object (instead of the index of selected row). This binding is performed by using a couple of providers :

– Label provider and
– Content provider

Here are some notes about the usage of these providers with JFace Viewers. In particular we’ll focus on TreeViewer.
The Model is made of Company entity, which owns a collection of Departments, which in turn owns a collection of Employees.
The goal is to obtain this

The Viewer will receive a list of Company objects. The code that set up the input and that set the providers is the following:

 viewer.setContentProvider(new MyTreeContentProvider());
 viewer.setLabelProvider(new MyTreeLabelProvider());
 ...
 viewer.setInput(listCompany);
 

Let’s examine now these providers role:

  • LabelProvider: it is invoked to determine what to show (text and image) for each node that come into view. The method getText() receives the examined object and must return the text to show. The optional method getImage() can be used to supply an image. Here is a possible implementation for our use-case:

  ...  
 import org.eclipse.jface.viewers.LabelProvider;

 public class MyTreeLabelProvider extends LabelProvider {

   @Override
   public String getText(Object element) {
     if (element instanceof Company) {
       return ((Company) element).getName();
     } else
     if (element instanceof Department) {
       return ((Department) element).getDescription();
     } else
     if (element instanceof Employee) {
       return ((Employee) element).getEmployeeName();
     }
     return null;
   }
 }
  • ContentProvider: it handles the content of the widget. In particular it provides the implementation of the methods that are called when:
    1. when the tree viewer is first shown (with first-level nodes not expanded) (getElements)
    2. when it is required to decide if expansion symbol has to be shown (hasChildren)
    3. when the user choose to expand a node and its children has to be shown (getChildren)

Please note that hasChildren, but also especially getChildren, gets called with a lazy mechanism, that is only when that’s required. As a result, the Employee list of a given Department gets loaded just when the user choose to expand that Department node, not before, with a clear good impact on performances.

 ...  
 import org.eclipse.jface.viewers.ITreeContentProvider;

 public class MyTreeContentProvider implements ITreeContentProvider {

     private static final Object[] EMPTY_ARRAY = new Object[0];

     //Called just for the first-level objects.
     //Here we provide a list of objects
     @Override
     public Object[] getElements(Object inputElement) {
       if (inputElement instanceof List)
         return ((List) inputElement).toArray();
       else
         return EMPTY_ARRAY;
     }

     //Queried to know if the current node has children
     @Override
     public boolean hasChildren(Object element) {
       if (element instanceof Company || element instanceof Department) {
         return true;
       }
       return false;
     }

     //Queried to load the children of a given node
     @Override
     public Object[] getChildren(Object parentElement) {
       if (parentElement instanceof Company) {
         Company company = (Company) parentElement;
         return company.getListDepartment().toArray();
       } else
       if (parentElement instanceof Department) {
         Department department = (Department) parentElement;
         return department.getListEmployee().toArray();
       }
       return EMPTY_ARRAY;
     }

     @Override
     public void dispose() {
     }

     @Override
     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
     }

     @Override
     public Object getParent(Object element) {
       return null;
     }

   }
 

The tree shows then all three object’s level (Company, Deparment, Employee)

 

Note: should the viewer be fed with a single Company object instead of a list, the getElements() method has to be changed like this:

   if (inputElement instanceof Company)
       return ((Company) inputElement).getListDepartment().toArray();

In this case the tree will not show the Company object; instead it will show its children Department (at first level) and the Employee objects (at second level).