In this chapter we describe the main mechanism for customizing Xtext components: Google Guice, a Dependency Injection framework. With Google Guice we can easily and consistently inject custom implementations of specific components into Xtext. In the first section, we will briefly show some Java examples that use Google Guice. Then, we will show how Xtext uses this dependency injection framework. In particular, you will learn how to customize both the runtime and the UI aspects.
You're reading from Implementing Domain-Specific Languages with Xtext and Xtend
The Dependency Injection pattern (Fowler, 2004) is used to inject implementation classes into a class hierarchy in a consistent way. This is useful when classes delegate specific tasks to other classes: messages are simply forwarded to the objects referenced in fields (which abstract the actual behavior).
Let us consider a possible scenario: a Service
class that abstracts from the actual implementation of a Processor
class and a Logger
class (although in this section we show Java code, all the injection principles naturally apply to Xtend as well). The following is a possible implementation:
public class Service { private Logger logger; private Processor processor; public void execute(String command) { logger.log("executing " + command); processor.process(command); logger.log("executed " + command); } } public class Logger { public void log(String message) { System.out.println("LOG: " + message); } } public interface Processor { public...
All Xtext components rely on Google Guice dependency injection, even the classes that Xtext generates for your DSL. This means that in your classes, if you need to use a class from Xtext, you just have to declare a field of such type with the @Inject
annotation.
The injection mechanism allows a DSL developer to customize basically every component of the Xtext framework.
When running the MWE2 workflow, Xtext generates both a fully configured module and an empty module derived from the generated one. Customizations are added to the empty stub module. The generated module should not be touched. Xtext generates one runtime module that defines the non-user interface related parts of the configuration and one specific for usage in the Eclipse IDE. Guice provides a mechanism for composing modules that is used by Xtext: the module in the UI project uses the module in the runtime project and overrides some bindings.
Let us consider the Entities DSL example; you can find in the...
In this section we show typical concepts of the IDE for your DSL that you may want to customize. Xtext shows its usability in this context as well, since, as you will see, it reduces the customization effort.
Xtext UI classes make use of an ILabelProvider
interface to obtain textual labels and icons via its methods getText
and getImage
, respectively. ILabelProvider
is a standard component of Eclipse JFace-based viewers. You can see an ILabelProvider
interface in action in the Outline view and in content assist proposal pop-ups (as well as in various other places).
Xtext provides a default implementation of a label provider for all DSLs, which does its best to produce a sensible representation of the EMF model objects by using the name
feature, if it is found in the corresponding object class, and a default image. You can see that in the Outline view when editing an entities
file, refer to the following screenshot:
If you tried to apply one of the quickfixes we implemented in Chapter 4, Validation, you might have noticed that after the EMF model has changed, the editor immediately reflects this change; however, the resulting textual representation is not well formatted (as shown in the following screenshot, where we applied the quickfix which adds the missing entity FooBar
).
In general, the EMF model representing the AST does not contain any information about the textual representation, that is, all white space characters are not part of the EMF model (after all, the AST is an abstraction of the actual program).
Xtext keeps track of such information in another in-memory model called the node model . The node model carries the syntactical information, that is, offset and length in the textual document. However, when we manually change the EMF model, we do not provide any formatting directives, and Xtext uses the default formatter to get a textual representation of the modified or added...
All the customizations you have seen so far were based on modification of a generated stub class with accompanying generated Guice bindings in the module under the src-gen
directory.
However, since Xtext relies on injection everywhere, it is possible to inject a custom implementation for any mechanism, even if no stub class has been generated.
Tip
If you installed Xtext SDK in your Eclipse, the sources of Xtext are available for you to inspect. You should learn to inspect these sources by navigating to them and see what gets injected and how it is used. Then you are ready to provide a custom implementation and inject it. You can use the Eclipse Navigate menu. In particular, to quickly open a Java file (even from a library if it comes with sources), use Ctrl + Shift + T (Open Type...). This works only for Java classes, so if you want to quickly open another source file (for example, an Xtend class or the Xtext grammar file) use Ctrl + Shift + R (Open Resource...). Both...
In this chapter we introduced the Google Guice dependency injection framework on which Xtext relies. You should now be aware of how easy it is to inject custom implementations consistently throughout the framework. You also learned how to customize some basic runtime and IDE concepts for a DSL.
The next chapter shows how to perform unit testing for languages implemented in Xtext. Test driven development is an important programming technique which will make your implementations more reliable, resilient to changes of the libraries, and will allow you to program quickly.