Using Dependency Injection
As an image helper may keep some sort of cache, it is likely a singleton. Furthermore, there may be different implementations of the image helper; for example, one using the plug-in's image registry when run within Eclipse. So the question is: How do I get the currently valid implementation of the image helper?
Eclipse 3.x usually uses static methods or global singletons for these kinds of components. The disadvantages of this approach have been discussed often. You cannot easily provide another implementation of that service, as the default is hard wired. It is also impossible to have separate implementations for different usage scenarios (e.g., multiple Xtext languages within the same Eclipse workbench).
In Xtext, we use Dependency Injection (DI) to wire up all the components of the runtime infrastructure of a language. From the client's side, DI means that instead of instantiating or resolving a needed component yourself, you simply declare a dependency to its interface and leave the rest of the work to the framework.
How does that help us with the image helper? Xtext's DI is based on Google Guice. A dependency is declared by means of an @com.google.inject.Inject annotation. We just declare a field and annotate it. Then we use this injected instance in the usual way:
public class DomainmodelOutlineTreeProvider
extends DefaultOutlineTreeProvider {
...
@Inject
private IImageHelper imageHelper;
protected Image _image(Entity e) {
return imageHelper.getImage("Entity.gif");
}
protected Image _image(DataType d) {
return imageHelper.getImage("DataType.gif");
}
protected Image _image(PackageDeclaration p) {
return imageHelper.getImage("PackageDeclaration.gif");
}
protected Image _image(Feature f) {
return imageHelper.getImage("Feature.gif");
}
}
With some additional EMF magic, our outline tree provider reads as:
public class DomainmodelOutlineTreeProvider
extends DefaultOutlineTreeProvider {
protected void _createChildren(DocumentRootNode parentNode,
Domainmodel domainmodel) {
for(AbstractElement element: domainmodel.getElements()) {
createNode(parentNode, element);
}
}
protected void _createNode(IOutlineNode parentNode,
Import modelElement) {
}
@Inject
private IImageHelper imageHelper;
protected Image _image(EObject e) {
return imageHelper.getImage(e.eClass().getName() + ".gif");
}
}
If you run this code, you'll see that it produces the desired outline view.
Dependency Injection Configuration
Now that we've seen how simple DI looks from the client's side, let's have a look at how it is configured. In Guice, the component in charge of managing DI is called the Injector. Almost all objects in the Xtext runtime are created by an Injector, and thus are automatically wired. Rather than accessing the Injector directly, you should have the needed dependencies injected and make sure your own components are instantiated by the Injector only. Think twice before calling a constructor.
The Injector is configured with a Module, which defines the bindings of interfaces to implementing classes. Xtext puts a thin layer on top of the Guice modules to allow overriding bindings using the Generation Gap Pattern. As Xtext languages are also usable in non-Eclipse scenarios, we have two modules for each language: One for a for plain Java environments and another for Eclipse (Figure 2). Only the latter binds all the editor-related components.
Have a look at code of the DomainmodelUiModule: It is where you define your own custom bindings or override the generated or the default bindings. The class is generated once and will not be touched by further runs of the code generator. It inherits from the generated AbstractDomainmodelUiModule, which contains all the bindings that are regenerated when you run Xtext's code generator. Here, you will find a method:
// contributed by OutlineTreeProviderFragment
public Class<? extends IOutlineTreeProvider> bindIOutlineTreeProvider() {
return DomainmodelOutlineTreeProvider.class;
}
The OutlineTreeProviderFragment defined in your MWE2 workflow created the above binding and the skeleton of the DomainmodelOutlineTreeProvider we have just brought to life.


