EMF Forms 1.6.0 Feature: Improved Rendering Architecture

EMF Forms 1.6.0 Feature: Improved Rendering Architecture

With the Mars Release, we released EMF Forms 1.6.0. EMF Forms makes it really simple to create forms, which edit your data based on an EMF model. To get started with EMF Forms please refer to our tutorial.

One of the major things we worked on for the 1.6 release was a refactoring of the rendering architecture of EMF Forms. The goal was to improve the testability, adaptability and reusability of the EMF Forms renderers. Before we go into details about the changes, let us quickly explain what a renderer is responsible for in EMF Forms and why it is such an important component for the framework.

Instead of manual and tedious UI coding, EMF Forms allows you to describe form-based UIs in simple language, the view model. Because it is focussed on forms, it is much more concise and easier to understand than manually written code, whether the UI technology is SWT, JavaFX or a web framework. A simple view model is shown in the following screenshot. The second screenshot shows how the view model would be rendered in SWT.

image05

image02

Translating the view model to a concrete usable UI is the responsibility of the renderers. Instead of generating code again, as most WYSIWYG-Editors do, EMF Forms interprets the view model at runtime. This allows for a lot of flexibility, e.g. to adapt renderers or even completely replace them without the need to change the view model at all.

To keep the rendering component as flexible as possible, it consists of single renderers, each responsible for a single view model element. As an example, there is a renderer for the element HorizontalLayout, another one for Group. For the control element in the view model, which represents a data field including label shown in the UI, there is a dedicated renderer for each attribute type, e.g. String, Integer or Enum. All renderers are registered in a central registry. When a certain element is rendered, a renderer factory is called to retrieve the correct renderer for an element. You can provide testers along with a renderer, which allows you to return different renderers for the same element based on certain rules, e.g. special renderers for certain domain model attributes.

image04

This architecture of dedicated elements and a factory is very flexible and allows for existing renderers to be replaced or adapted. It has been implemented since the beginning in EMF Forms. However, there are a few details that we improved with the 1.6.0 release.

In the previous release, renderers needed to implement a given interface, which was used to call rendering methods and to fill in required parameters. This does not sound bad on first glance, but this architecture has drawbacks. The set of parameter is fixed for every renderer. While most renderers share a common set of required parameters, there are others, especially custom adaptations, which required some very specific parameters. These might even be objects or services, which are project specific and not part of the framework. With the fixed set of parameters given by the defined interface, it was difficult to retrieve additional ones. Luckily, there is a well-known solution for this issue: dependency injection. It allows you to let the implementation of the renderer define itself, as well as which parameters it requires. EMF Forms will then take care to inject the required objects into the renderer on initialization. Further, renderers typically inherited from certain superclasses to share existing functionality. An example for such a reused feature is the creation of a data binding for a control. For the 1.6 release we have refactored these reusable features into independent services.

The architectural shift to small reusable components, which are wired using dependency injection has been pretty common pattern in recent years. Those components have typically much less dependencies and are therefore easier to test. Additionally, if you want to test renderers, it is easy to mock all external components. More importantly, this approach enables framework users to adapt, extend or exchange the implementation of these services.

image06

As you can see in the diagram, we still use a interface for the renderer to define methods, which are called by the framework, e.g. the render method. This could be removed by defining annotations to mark the respective methods and to call them via dependency injection. Eclipse 4 does that with the behavior annotations, e.g. @Focus or @Execute. However, we currently do not see a major advantage in introducing this into the rendering architecture. The affected methods have had a stable and very minimal parameter set since years, now (typically one or even no parameters). In addition, we sometimes expect pretty specific return values, which cannot be assured during compile time when using dependency injection, which is an advantage for keeping the interface. Finally, renderers are stateful, as they control the current state of the rendered UI. That means, there is currently no real issue with injecting services as fields or in the constructors. We might revise this and even introduce dependency injection for those methods, but the current solution already resolves the problem of differing parameter sets and support completely custom parameters.

We updated our code examples and tutorials to the new rendering architecture, please have a look at this tutorial to learn more about how to implement custom renderers for EMF Forms and how to adapt existing ones.