Create an Eclipse Database Client with Hibernate

The purpose of the article is to demonstrate how fast and easy it can be to build an Eclipse client to a database using the Fishbolt technology.

There is a complete sample of building a hibernate model with a couple of data objects and several of the Eclipse views, editors, wizards and actions. The technology assumes a unique kind of data object (not unlike a Javabean). Such original kinds of data objects allows you to evaluate data structure errors at the compilation stage.

Table of contents

Eclipse based database client with the hibernate technology
   Data Model plug-in
     Plug-in creation
     Data object classes and its managers creation
     Data model class declaration
     Configuring hibernate
     Calculated fields
     Changes in the plug-in manifest
     The resulting plug-in
   Database structure creation
   UI plug-in
     Data object wizards
     Data object editor
     Change data field value dialog
     Department-Employee tree
     Employees table
     Employee search panel
   Application UI
   Additional resources

The example sources like other examples are prepared for download here. The required plug-ins update site URL is http://fishbolt.org/eclipse/update. A zipped archive of the update site can be found here.

Data Model plug-in

The separated Eclipse plug-in creation is described in the following section. The plug-in will contain the data model declaration. It is a good practice to separate data model from user interface.

Plug-in creation

Start by adding a dependency after the plug-in creation:


Add the following line to the MANIFEST.MF file (to use single class loader for work with hibernate libraries):

Eclipse-RegisterBuddy: org.fishbolt.model.hibernate.annotations

Data object classes and its managers creation

/**
* Data object presenting Department
*/
@DisplayLabel("Department")
@ImageResource("Department.gif")
@Entity
@Tuplizer(impl = CompanyModelTuplizer.class)
@AccessType("org.fishbolt.model.hibernate.FieldAccessor")
public class Department extends HDataObject<DepartmentManager> {

  @Id
  @GeneratedValue
  @DisplayLabel("Identity")
  public final static FieldDeclaration<Integer> id = 
     new FieldDeclaration<Integer>(Department.class);

  @ObjectPresentationField
  @Column(unique=true, nullable=false)
  @DisplayLabel("Name")
  @Width(250)
  public final static FieldDeclaration<String> name = 
     new FieldDeclaration<String>(Department.class);


  @DisplayLabel("Employees")
  @Relation(inverse="department")
  @OneToMany(targetEntity=Employee.class)
  @JoinColumn(name= "department") 
  public final static FieldDeclaration<Set<Employee>> employees = 
     new FieldDeclaration<Set<Employee>>(Department.class);
  
  public Department(DepartmentManager manager) {
    super(manager);
  }
  
  public Department() {
    super();
  }
}

"Department" data object manager:

/** 
 * Manager class for {@link Department} data object. 
 */ 
public class DepartmentManager extends HDataObjectManager<Department,Integer>{

  /** 
   * Constructor inherited from the super class
   */ 
  public DepartmentManager(IDataModel dataModel, ObjectDescriptor<?,?> objectDescriptor){ 
    super(dataModel, objectDescriptor);
  }

  /**
   * Searches for a Department by its name 
   * @param name - the name of the Department to search for
   * @return Department r<code>null</code>
   */
  public Department findDepartment(String name){
    String hql = "from Department where name=?";
    QueryUniqueResultCommand<Department> cmd = 
	new QueryUniqueResultCommand<Department>(hql);
    cmd.setParameters(new Parameter(name));
    return this.getModel().processCommand(cmd);
  }
}

"Employee" data object:

/**
 * Data object presenting Employee
 */ 
@DisplayLabel("Employee")
@ImageResource("Employee.gif")
@Entity
@Tuplizer(impl = CompanyModelTuplizer.class)
@AccessType("org.fishbolt.model.hibernate.FieldAccessor")
public class Employee extends HDataObject<EmployeeManager> {

  @Id
  @GeneratedValue
  @DisplayLabel("Identity")
  public final static FieldDeclaration<Integer> id = 
     new FieldDeclaration<Integer>(Employee.class);

  @Transient
  @ObjectPresentationField
  @DisplayLabel("Name")
  @Width(250)
  public final static FieldDeclaration<String> name = 
     new FieldDeclaration<String>(Employee.class);

  @Column(nullable=false)
  @DisplayLabel("First Name")
  public final static FieldDeclaration<String> firstName = 
     new FieldDeclaration<String>(Employee.class);
  
  @Column(nullable=false)
  @DisplayLabel("Last Name")
  public final static FieldDeclaration<String> lastName = 
     new FieldDeclaration<String>(Employee.class);
  
  @DisplayLabel("Phone Number")
  public final static FieldDeclaration<String> phoneNumber = 
     new FieldDeclaration<String>(Employee.class);
  
  @SimpleDateFormatPresentation("dd/MM/yyyy")
  @DisplayLabel("Birthday")
  public final static FieldDeclaration<Date> birthday = 
     new FieldDeclaration<Date>(Employee.class);

  @DisplayLabel("Salary")
  @Column(nullable=false)
  @DecimalFormatPresentation("#,##0.00")
  @PresentationDecorator(prefix="$ ")
  public final static FieldDeclaration<BigDecimal> salary = 
     new FieldDeclaration<BigDecimal>(Employee.class);
  
  @DisplayLabel("Health")
  @NumberFormatPresentation(NumberFormatInstance.percent)
  public final static FieldDeclaration<Double> health = 
     new FieldDeclaration<Double>(Employee.class);
  
  @DisplayLabel("Department")
  @Relation(inverse="employees")
  @ManyToOne(optional=false)
  @JoinColumn(name="department")
  public final static FieldDeclaration<Department> department = 
     new FieldDeclaration<Department>(Employee.class);
  
  public Employee(EmployeeManager manager) {
    super(manager);
  }
  
  public Employee() {
    super();
  }
}

"Employee" data object manager:

/**
 * Manager class for {@link Employee} data object. 
 */ 
public class EmployeeManager extends HDataObjectManager<Employee,Integer>{

  /** 
   * Constructor inherited from the super class
   */ 
  public EmployeeManager(IDataModel dataModel, ObjectDescriptor<?,?> objectDescriptor){ 
	super(dataModel, objectDescriptor);
  }

  /**
   * Searches for an employee among all employees of a company
   * @param firstName - employee's first name
   * @param lastName - employee's last name
   * @return<code>List</code> of employees
   */
  public List<Employee> findEmployee(String firstName,String lastName){ 
    String hql = "from Employee where firstName=? and lastName = ?";
    QueryListCommand<Employee> cmd =  
      new QueryListCommand<Employee>(hql);
    cmd.setParameters( new Parameter(firstName), new Parameter(lastName)); 
    return this.getModel().processCommand(cmd);
  }

  /** 
   * Searches for an employee in a Department
   * @param firstName - employee's first name
   * @param lastName - employee's last name
   * @param department
   * @return<code>List</code> of employees
   */
  public List<Employee> findEmployee(String firstName,String lastName, Department department){    
    String hql = "from Employee where firstName=? and lastName=? and department=?";
    QueryListCommand<Employee> cmd =  newQueryListCommand<Employee>(hql);
    cmd.setParameters(
      new Parameter(firstName),
      new Parameter(lastName),
      new Parameter(department));
    return this.getModel().processCommand(cmd);
  }
}

Data model class declaration

/**
 * Company model class
 */

public class CompanyModel extends DataModel {
  
  public final static 
    ObjectDescriptor<EmployeeManager,Employee> employee = 
       newEObjectDescriptor<EmployeeManager,Employee>(CompanyModel.class);
  
  public final static 
    ObjectDescriptor<DepartmentManager,Department> department = 
       newEObjectDescriptor<DepartmentManager,Department>(CompanyModel.class);
  
  public static ModelDescriptor getDescriptor(){
    return CompanyModel.Department.getModelDescriptor();
  }
  
  private static CompanyModel instance;
  
  public static CompanyModel getInstance(){
    return (instance == null) ? 
        instance = new CompanyModel(getDescriptor()) : instance;
  }

  private CompanyModel(ModelDescriptor modelDescriptor) {
    super(modelDescriptor);
  }
}

Configuring hibernate

Let's create the Tuplizer-class (mentioned at co-named annotation in data objects)

public class CompanyModelTuplizer extends DataObjectTuplizer {

  /**
   * Constructor inherited from the super class
   */
  public CompanyModelTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
    super(entityMetamodel, mappedEntity);
  }

  @Override
  protected ModelDescriptor getModelDescriptor() {
    return CompanyModel.getDescriptor();
  }
}

hibernate.cfg.xml creation

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD//EN"
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
      <property name="hibernate.dialect">
        org.hibernate.dialect.PostgreSQLDialect
      </property>
      <property name="hibernate.connection.driver_class">
        org.postgresql.Driver
      </property>
      <property name="hibernate.connection.url">
        jdbc:postgresql://localhost/company
      </property>
      <property name="hibernate.connection.username">
        postgres
      </property>
      <property name="hibernate.connection.password">
        ********
      </property>
    <mapping class="example.model.objects.Department"/>
    <mapping class="example.model.objects.Employee"/>
    </session-factory>
</hibernate-configuration>

Calculated fields

The "Employee" data object contains name filed. The filed is calculated and doesn't mapped to the database as a result. The algorithm of the filed value evaluation implemented bellow:

public class EmployeeFullNameCalculation implements IFieldCalculation<String> {
  
  public FieldPath[] getInfluencingPaths(FieldDescriptor<String> dependent) {
    return new FieldPath[]{
        new FieldPath(true, CompanyModel.employee,Employee.firstName),
        new FieldPath(true, CompanyModel.employee,Employee.lastName)
      };
  }

  public String evaluateValue(IDataField<String> toEvaluate) {
    Employee employee = (Employee)toEvaluate.getDataObject();
    return ModelUtil.getValue(employee,Employee.firstName)+
      " " +  ModelUtil.getValue(employee,Employee.lastName);
  }
}

Let's bind in code the algorithm and data field

public class CompanyCalculationProviderFactory extends FieldCalculationProviderFactorySafe{ 

  /** 
   * Binds calculated fields with calculation classes
   */ 
  @Override 
  protected void bindCalculations() { 
    //binding <code>Employee.name</code> with <code>EmployeeFullNameCalculation</code>
    bindCalculation(CompanyModel.employee,Employee.name, new EmployeeFullNameCalculation());
  }
}

Changes in the plug-in manifest

Let's register factory providers - declare extension of the ModelAdapterFactory point:

Hence the plugin.xml looks in the following way:

<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.2"?>
<plugin>
  <extension
     point="org.fishbolt.model.ModelAdapterFactory">
     <model class="example.model.CompanyModel">
      <factory class="example.model.calculations.CompanyCalculationProviderFactory"/>
      <factory class="org.fishbolt.model.memory.ext.compare.ComparatorProviderFactory"/>
      <factory class="org.fishbolt.model.memory.ext.listener.ListenerRegistryAdapterFactory"/>
      <factory class="org.fishbolt.model.ext.width.WidthProviderFactory"/>
      <factory class="org.fishbolt.model.ext.image.ObjectImageProviderFactory"/>
      <factory class="org.fishbolt.model.hibernate.ext.presenter.HPresenterProviderFactory"/>
      <factory class="org.fishbolt.model.hibernate.ext.calculate.HInfluencingFieldProviderFactory"/>
      <factory class="org.fishbolt.model.memory.ext.state.FieldStateFactory"/>
      <factory class="org.fishbolt.model.hibernate.ext.state.HObjectStateFactory"/>
      <factory class="org.fishbolt.model.hibernate.ext.state.HFieldDescriptorStateFactory"/>
      <factory class="org.fishbolt.model.hibernate.ext.state.HObjectDescriptorStateFactory"/>
      <factory class="org.fishbolt.model.hibernate.provider.field.HFieldManagerProviderFactory"/>
      <factory class="org.fishbolt.model.hibernate.provider.object.HObjectManagerProviderFactory"/>
      <factory class="org.fishbolt.model.memory.ext.label.LabelProviderFactory"/>
      <factory class="org.fishbolt.model.hibernate.provider.HModelProviderFactory"/>
      <factory class="org.fishbolt.model.hibernate.annotations.provider.AnnotationConfigurationProviderFactory"/>
      <factory class="org.fishbolt.model.hibernate.ext.lo.HLargeObjectProviderFactory"/>
     </model>
  </extension>
</plugin>

Let's export packages:

And add the driver library:


The resulting plug-in

Database structure creation

Let's create the "company" database with the help of the Data Base Admin console. Database server (Postgres), database name, login and password are defined in the hibernate.cfg.xml file

The class bellow generates database structure. It's necessary to change temporary the type of the last field descriptor (CompanyModel.department) from EObjectDescriptor to ObjectDescriptor before the class execution.

/** 
 * Use the class to generate database structure. The call 
 * erases data from database (drops tables with data and creates empty tables). 
 */ 
public class CompanyDBSchema { 

  /** 
   * To make it working you need to change <code>
   * new <b>E</b>ObjectDescriptor</code> with 
   * the<code>new ObjectDescriptor</code> for the last 
   * static field - {@link CompanyModel#department}.
   * (don't forget to return it back)
   */
  public static void main(String[] args){
    ModelDescriptor descriptor = CompanyModel.getDescriptor();
    // Explicit adapter registration is necessary because 
    // extension points don't work for plain java application
    AdapterFactoryManager manager = descriptor.getAdapterFactoryManager();
    
    // the adapter classes are taken from the 
    // ModelAdapterFactory extension point (see plugin.xml)
    manager.register(new ComparatorProviderFactory());
    manager.register(new ListenerRegistryAdapterFactory());
    manager.register(new WidthProviderFactory());
    manager.register(new ObjectImageProviderFactory());
    manager.register(new HPresenterProviderFactory());
    manager.register(new HInfluencingFieldProviderFactory());
    manager.register(new FieldStateFactory());
    manager.register(new HObjectStateFactory());
    manager.register(new HFieldDescriptorStateFactory());
    manager.register(new HObjectDescriptorStateFactory());
    manager.register(new HFieldManagerProviderFactory());
    manager.register(new HObjectManagerProviderFactory());
    manager.register(new LabelProviderFactory());
    manager.register(new HModelProviderFactory());
    manager.register(new CompanyCalculationProviderFactory());
    manager.register(new AnnotationConfigurationProviderFactory());
    manager.register(new HLargeObjectProviderFactory());
       
    // executing of the SchemaExport-hibernate tool
    ConfigurationProvider provider = descriptor.getAdapter(ConfigurationProvider.class);
    SchemaExport task = new SchemaExport(provider.getConfiguration());

    task.create(true, true);
  }
}

The following SQL statemets were generated and executed by the class:

alter table Employee drop constraint FK4AFD4ACE8385E2C7

drop table Department

drop table Employee

drop sequence hibernate_sequence

create table Department (id int4 not null, name varchar(255) not null unique, primary key (id))

create table Employee (id int4 not null, birthday timestamp, firstName varchar(255) not null,
    health float8, lastName varchar(255) not null, phoneNumber varchar(255), 
    salary numeric(19, 2) not null, department int4 not null, primary key (id))

alter table Employee add constraint FK4AFD4ACE8385E2C7 foreign key (department) references Department

create sequence hibernate_sequence

UI plug-in

Let's create UI plug-in and add the dependencies

As it shown, the created data model plug-in (example.org.fishbolt.model.hibernate.annotations) is among the dependencies.

Data object wizards

Let's create wizard clasess. The "Employe" data object wizard listing is bellow:

/**
 * Wizard for adding new employees
 */
public class NewEmployeeWizard extends ObjectNewWizard {

  public NewEmployeeWizard() {
    // indicating the descriptor of a data object
    // whose instances will be created by the wizard
    super(CompanyModel.employee);
  }
  
  @Override
  protected IDataModel getDataModel(IWorkbench workbench, 
      IStructuredSelection selection) {
    // providing a reference to the data model
    // to which new Employee instances will be added
    return CompanyModel.getInstance();
  }

  @SuppressWarnings("unchecked")
  @Override
  public void init(IWorkbench workbench, IStructuredSelection selection){
    super.init(workbench, selection);
    // Setting a default value to Employee.department
    Object selectionObject = selection.getFirstElement();
    if (selectionObject instanceof Department)
      ModelUtil.setValue(this.wizardObject, Employee.department,(Department)selectionObject);
    String name = LabelProvider.getDisplayLabel(CompanyModel.employee);
    // adding pages for step-by-step creation of Employee instances
    this.addPage(
        new ObjectSimpleWizardPage(name,name,null,
        Employee.firstName,
        Employee.lastName,
        Employee.department,
        Employee.phoneNumber));
    this.addPage(
        new ObjectSimpleWizardPage(name,name,null,
        Employee.birthday,
        Employee.salary,
        Employee.health));
  }
}

Let's register corresponding extension

The first page of the "Employee" wizard:

Let's add UI action which opens wizard to create new "Employee" data object

public class OpenNewEmployeeWizardAction extends OpenNewWizardAction {

  @SuppressWarnings("unchecked")
  public OpenNewEmployeeWizardAction(ViewerController controller) {
    super(controller, "New Employee...","Creates a new Employee");
  }

  @Override
  public INewWizard getNewWizard() {
    return new NewEmployeeWizard();
  }

  @Override
  public void updateEnable() {
    this.setEnabled(getActionObject() instanceof Department);
  }
}

Data object editor

Let's create editor factories for the data objects

/**
 * EditorFactory for <code>Employee</code>s 
 */ 
public class EmployeeEditorFactory extends EditorFactory {

  EmployeeEditorFactory() { 
    // Indicating an Employee object via its descriptor 
    // and creating editorpages 
    super(CompanyModel.employee,
       new SimpleEditorPageFactory( 
          "Personal information",
          Employee.firstName,
          Employee.lastName,
          Employee.phoneNumber,
          Employee.birthday, 
          Employee.health),
       new SimpleEditorPageFactory(
          "Department information",
          Employee.department,
          Employee.salary)
     );
  }
} 


/** 
 * EditorFactoryfor<code>Department</code>s
 */
public class DepartmentEditorFactory extends EditorFactory {

  DepartmentEditorFactory() {
    // Indicating a Department object via its descriptor
    // and creating editor pages    
    super(CompanyModel.department,
      new SimpleEditorPageFactory(
          LabelProvider.getDisplayLabel(CompanyModel.department), 
          Department.name)
    );
  }
}

It's also necessary to create editor provider class and register it:

public class CompanyEditorProvider extends EditorProvider {
  
  public CompanyEditorProvider(){
    register(new DepartmentEditorFactory());
    register(new EmployeeEditorFactory());
  }
}

And to declare editor:

Created Employee editor is shown on the picture:

Let's add UI action which opens editor for the data objects

public class OpenInEditorAction extends OpenEditorAction {

  @SuppressWarnings("unchecked")
  public OpenInEditorAction(ViewerController controller) {
    super("example.model.eclipse.DataObjectEditor", controller);
  }
}

Let's add UI action which opens editor for the data objects in a separate perspective for example

public class OpenInEditorPerspectiveAction extends OpenEditorPerspectiveAction {

  @SuppressWarnings("unchecked")
  public OpenInEditorPerspectiveAction(ViewerController controller) {  
    super("example.model.eclipse.perspectives.ObjectEditorPerspective",
        "example.model.eclipse.DataObjectEditor", controller);
  }
}

Change data field value dialog

Let's look at the class of a dialog that allows a user to move an Employee from one Department to another:

/**
 * Dialog that moves an Employee from one Department to another
 */

public class ChangeDepartmentDialog extends Dialog implements IWidgetsContext{
  
  private Employee employee;
  private ContextControllerBinder contextBinder;

  public ChangeDepartmentDialog(Shell parentShell,Employee employee) {
    super(parentShell);
    this.employee = employee;
  }

  /**
   * Returns ContextControllerBinder that binds
   * user interface components with data model components
   * through controllers
   */

  public ContextControllerBinder getContextBinder() {
    return contextBinder == null ? 
        contextBinder = new ContextControllerBinder(this) : contextBinder;
  }

  /**
   * Creates dialog's contents 
   * and binds user interface components with data model components
   */
  @Override  
  protected Control createDialogArea(Composite parent) 
  {
    // dialog title
    parent.getShell().setText(PresenterProvider.getPresentation(employee));
  
    Composite result = new Composite(parent, SWT.NONE);
    result.setLayout(new GridLayout(2, true));

    Label decription = new Label(result, SWT.NULL);
    decription.setLayoutData(new GridData(GridData.FILL,GridData.FILL,true,true,2,1));
    decription.setText("To move the employee to another Department, " +
        "select the Department and press OK.");
    
    // Label presenting the data object field
    Label label = new Label(result, SWT.NULL);
    label.setLayoutData(new GridData());
    
    // Combo presenting data field values
    Combo combo = new Combo(result, SWT.BORDER | SWT.DROP_DOWN);
    combo.setLayoutData(new GridData(GridData.FILL,GridData.CENTER,true,false));
  
    ContextControllerBinder binder = getContextBinder();

    // binding Label to FieldDescriptor 
    // (the controller that is responsible for interaction
    // between Label and FieldDescriptor is created automatically)
    binder.bind(label, CompanyModel.employee.getFieldDescriptor(Employee.department));

    // binding data field to Combo 
    // (the controller that is responsible for interaction
    // between data field and Combo is created automatically)
    binder.bind(combo, employee.getDataField(Employee.department));    
    return result;
  }

  /**
   * Reacts to changes resulted from user's actions on the UI
   * components (controls) that were bound to data model
   * components using ContextControllerBinder
   */
  @SuppressWarnings("unchecked")
  public void contextWidgetChanged(EditableWidgetController<Widget, Object, Object> controller,
      PresentationException exception) {
    if (exception!= null){
      MessageDialog.openError(this.getShell(),"The value is incorrect.","Please specify a correct value.");
      return;
    }

    Collection<ValidationMessage<IDataObject, Severity>> problems = employee.validate(null);
    if (problems.isEmpty()) return;  

    showProblems(problems); 
    employee.refresh();
  }
  
  /**
   * Shows information about the data object problems
   * @param problems
   */
  protected void showProblems(Collection<ValidationMessage<IDataObject, Severity>> problems){
    StringBuffer msg = new StringBuffer();
    for (ValidationMessage<IDataObject, Severity> problem : problems){
      msg.append(problem.getDescription());
      msg.append("\\;n");
    }
    
    MessageDialog.openError(this.getShell(),"The value is incorrect.",msg.toString());
  }
  
  /**
   * Cancel button handler
   */
  @Override
  protected void cancelPressed() {
    employee.refresh();
    super.cancelPressed();
  }
  
  /**
   * OK button handler  
   */
  @Override
  protected void okPressed() {
    employee.store();
    super.okPressed();
  }
}

The dialog for moving an employee to another Departments looks something like this:

Department-Employee tree

Let's create the UI-tree element which shows all the Departments with employees:

/**
 * Creates a view that contains
 * the tree of Departments and employees
 */
public class DepartmentEmployeesView extends ViewPart {

  private TreeViewerController treeController = null;

  @Override
  public void createPartControl(Composite parent) {
    // creating a controller for TreeViewer
    treeController = new TreeViewerController(parent);
    
    // Setting layout
    GridData data = new GridData(GridData.FILL,GridData.FILL,true,true);
    treeController.getViewer().getControl().setLayoutData(data);
    
    // Using ColumnData to wrap up data objects to be represented in tree nodes.
    // Data objects are specified through their descriptors
    treeController.setColumnProvider(
        new ColumnProvider(
            new ColumnData(CompanyModel.department).add(CompanyModel.employee)));

    // Wrapping data in TreeDataObjects and passing it to the controller.
    // To put it in more details:
    // - indicating data objects to be represented in parent nodes.
    //   In this case, these are objects of the Deparment type.
    //   The Department objects are indicated through their manager (DepartmentManager).
    // - indicating data objects to be represented in child nodes.
    //   They are indicated through a bidirectional association.
    //   In this case, the bidirectional association is expressed by Department.employees
    CompanyModel model = CompanyModel.getInstance();
    DepartmentManager manager = model.getDataObjectManager(CompanyModel.department);
    treeController.setDataObjects(
        new TreeDataObjects(
            new ManagerDataObjects(manager),
            new ChildRelation(CompanyModel.department,Department.employees)));

    // building a context menu for the tree
    ContextMenu menu = new ContextMenu();
    menu.add(new OpenInEditorAction(treeController));
    menu.add(new OpenInEditorPerspectiveAction(treeController));
    menu.add(new OpenNewDepartmentWizardAction(treeController));
    menu.add(new OpenNewEmployeeWizardAction(treeController));
    menu.add(new ChangeEmployeeDepartmentAction(treeController));
    menu.add(new ShowDepartmentEmployeesAction(treeController));
    menu.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    menu.add(new RemoveDataObjectAction(treeController));
    treeController.setContextMenu(this.getSite(),menu);
  }

  @Override
  public void setFocus() {
    this.treeController.getViewer().getControl().setFocus();
  }
}

Let's register the corresponding extension (org.eclipse.ui.views)

the result:

Employees table

/**
 * Creates a view that contains
 * a table displaying information about Employees
 */
public class EmployeesView extends ViewPart{
  
  private TableViewerController tableController = null;

  /**
   * Opens the view and passes to the controller 
   * a collectionof<code>Employee</code>s to be represented
   * in a table within the view.
   */
  public static void setEmployees(List<? extends Employee> employess){
    EmployeesView view = getView();
    view.tableController.setDataObjects( new CollectionDataObjects(employess)); 
  } 
 
  /**
   * Opens the view and passes to the controller
   * a collection of <code>Employee</code>s to be represented
   * in a table within the view. 
   * The collectionof <code>Employee</code>s is specified via 
   * the association with the<code>Department</code> object (Department.employees)
   */  
  public static void setDepartment(Department department){
    EmployeesView view = getView();
    IDataField<Set<Employee>> field = department.getDataField(Department.employees);
    view.tableController.setDataObjects(new RelationDataObjects(field));
  }
  
  @Override
  public void createPartControl(Composite parent) {
    // Creating a controller
    tableController = new TableViewerController(parent);
    // Setting layout
    GridData data = new GridData(GridData.FILL,GridData.FILL,true,true);
    tableController.getViewer().getControl().setLayoutData(data); 
	
    // Specifying data presentation. 
    // Passing to the controller an OddBackgroundColorPresentation instance 
    // that pastes the background of even table cells 
    // with the color specified. Default is RGB(230,230,255)
    tableController.setDataPresenation(    
        new EvenBackgroundColorPresentation(tableController)); 

    // Configuring table columns. 
    // Using ColumnData to wrap up data object fields to be represented in tree nodes. 
    // (Data object fields are indicatedthrough<code>FieldDeclaration</code>s)
    tableController.setColumnProvider(
        new ColumnProvider(
            new ColumnData(CompanyModel.employee,Employee.firstName),
            new ColumnData(CompanyModel.employee,Employee.lastName),
            new ColumnData(CompanyModel.employee,Employee.phoneNumber),
            new ColumnData(CompanyModel.employee,Employee.salary)));

    // Adding actions that open a dialog for column selection
    ChangeColumnsAction.registerInMenu(this, tableController);
    ChangeColumnsAction.registerInToolBar(this, tableController);
    // adding a context menu

    ContextMenu menu = new ContextMenu();
    menu.add(new OpenInEditorAction(tableController));

    menu.add(new OpenInEditorPerspectiveAction(tableController));

    menu.add(new ChangeEmployeeDepartmentAction(tableController));
    menu.add(new RemoveDataObjectAction(tableController));
    tableController.setContextMenu(this.getSite(),menu);
  }

  @Override
  public void setFocus() {
    this.tableController.getViewer().getControl().setFocus();
  }

  /**
   * Opens EmployeesView
   */  
  private static EmployeesView getView(){
    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
    EmployeesView result = null;
    try {
      for (IViewReference ref : page.getViewReferences()) {
        if ("example.model.eclipse.views.EmployeesView".equals(ref.getId())) {
          result = (EmployeesView) ref.getView(true);
          break;
        }
      }
      if (result == null) 
        result = (EmployeesView) page.showView("example.model.eclipse.views.EmployeesView");
    } catch (PartInitException e) {
      throw new RuntimeException(e);
    }
    return result;
  }
}

Let's add an extension

the result:

Employee search panel

Let's create a panel. The panel will give an ability to search Employees by input parameters and show them in the table.

public class SearchEmployeeView extends ViewPart implements IWidgetsContext{ 
 
  private Label firstNameLabel; 
  private Text firstName; 
  private Label secondNameLabel; 
  private Text secondName; 

  private Label DepartmentLabel; 
  private DialogChooser<Department> department; 
  private Button searchButton; 
  private ContextControllerBinder contextBinder; 

  private SelectionAdapter doSearch = new SelectionAdapter() { 

    @Override 
    public void widgetSelected(SelectionEvent e) { 
      CompanyModel model = CompanyModel.getInstance();
      EmployeeManager manager = model.getDataObjectManager(CompanyModel.employee);
      String first = firstName.getText();
      String second = secondName.getText();
      Department d = department.getObject();
      List<Employee> list = (d == null) ?
        manager.findEmployee(first,second) : manager.findEmployee(first, second, d);
      EmployeesView.setEmployees(list);
    }
  };

  @Override
  public void createPartControl(Composite parent) {
    parent.setLayout(new GridLayout(2,false));
    ContextControllerBinder binder = getContextBinder();
   
    firstNameLabel = new Label(parent, SWT.NULL);
    firstNameLabel.setLayoutData(new GridData());
    binder.bind(firstNameLabel,CompanyModel.employee,Employee.firstName);
    
    firstName = new Text(parent, SWT.BORDER | SWT.SINGLE);
    firstName.setLayoutData(new GridData(GridData.FILL,GridData.FILL,true,false));
    
    secondNameLabel = new Label(parent, SWT.NULL);
    secondNameLabel.setLayoutData(new GridData());

    binder.bind(secondNameLabel,CompanyModel.employee,Employee.lastName);
    
    secondName = new Text(parent, SWT.BORDER | SWT.SINGLE);
    secondName.setLayoutData(new GridData(GridData.FILL,GridData.FILL,true,false));
    
    departmentLabel = new Label(parent, SWT.NULL);
    departmentLabel.setLayoutData(new GridData());
    binder.bind(departmentLabel,CompanyModel.department);
    
    department =  newDialogChooser<Department>(parent);

    department.setLayoutData(new GridData(GridData.FILL,GridData.FILL,true,false));
    CompanyModel model = CompanyModel.getInstance();
    binder.bind(department,model.getDataObjectManager(CompanyModel.department));
    
    searchButton = new Button(parent,SWT.PUSH);
    searchButton.setText("Search");
    ImageDescriptor id = AbstractUIPlugin.imageDescriptorFromPlugin(
          "example.org.fishbolt.model.eclipse","icons/search.gif");
    searchButton.setImage(id.createImage());

    searchButton.setLayoutData(new GridData(GridData.END,GridData.FILL,false,false,2,1));
    searchButton.addSelectionListener(doSearch);
  }

  @Override
  public void setFocus() {
    this.firstName.setFocus();
  }

  public void contextWidgetChanged(EditableWidgetController<Widget, Object, Object> controller,
      PresentationException exception) {
  }

  public ContextControllerBinder getContextBinder() {
    return (contextBinder == null) ? 
        contextBinder = new ContextControllerBinder(this) : contextBinder ;
  }
}

Let's add an extension

Application UI

As a result we got a an Eclipse based application. The application allows to create view edit and delete the database data. The UI is user friendly and responsive - changing a data object in editor or deleting it a view automaticaly causes a refresing the corresponding data in other views.

It's easy to change or extend the data due to the data structure errors evaluation at the compilation stage. It's because the UI dependency mostly presented by data object's java constants references.

Additional resources

The additional information about abilities of the Eclipse UI for the data model see at the User interface for the data model in Eclipse IDE. See also libraries:



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

Most Popular Programming Stories

More for Developers

RSS Feeds