- Implementing Domain-Specific Languages with Xtext and Xtend(Second Edition)
- Lorenzo Bettini
- 709字
- 2021-07-14 10:06:36
The Eclipse Modeling Framework (EMF)
The EMF (Eclipse Modeling Framework) (Steinberg et al, 2008), since it simplifies the development of complex software applications with its mechanisms. The model specification (metamodel) can be described in XMI, XML Schema, Unified Modeling Language (UML), Rational Rose, or annotated Java. It is also possible to specify the metamodel programmatically using Xcore , which was implemented in Xtext. Typically, a metamodel is defined in the Ecore format, which is similar to an implementation of a subset of UML class diagrams.
Note
Pay attention to the meta levels in this context—an Ecore
model is a metamodel, since it is a model describing a model. Using the metamodel EMF produces a set of Java classes for the model. If you are not familiar with modeling technologies, you can think of a metamodel as a way of defining Java classes, that is, hierarchy relations, fields, method signatures, and so on. All Java classes generated by EMF are subclasses of EObject
, which can be seen as the EMF equivalent of java.lang.Object
. Similarly, EClass
corresponds to java.lang.Class
for dealing with introspection and reflection mechanisms. The relationship between a metamodel and a model is instantiation.
Xtext relies on EMF for creating the AST, Abstract Syntax Tree, which we talked about in Chapter 1, Implementing a DSL. From your grammar specification, Xtext will automatically infer the EMF metamodel for your language. You can refer to the Xtext documentation for all the details about metamodel inference. For the moment, you can consider this simplified scenario—for each rule in your grammar, an EMF Java interface and the corresponding implementation class will be created with a field for each feature in the rule, together with a getter
and setter
. For instance, for the Entity
rule, we will have the corresponding Java interface (and the corresponding implementation Java class):
public interface Entity extends EObject { String getName(); void setName(String value); Entity getSuperType(); void setSuperType(Entity value); EList<Attribute> getAttributes(); }
Since these Java artifacts are generated, they are placed in the corresponding package in the src-gen
folder. Refer to the following screenshot, where you can also see some expanded Java interfaces generated by EMF:
The generated EMF metamodel is placed in the directory model/generated
. You can have a look at the generated Entities.ecore
by opening it with the default EMF Ecore editor. Although you may not know the details of the description of a metamodel in EMF, it should be quite straightforward to grasp the meaning of it. Refer to the following screenshot:
The inference of the metamodel and the corresponding EMF code generation is handled transparently and automatically by Xtext. However, Xtext can also use an existing metamodel that you maintain yourself. We will show an example of a DSL with a manually maintained metamodel in Chapter 13, Advanced Topics.
Since the model of your DSL programs are generated as instances of these generated EMF Java classes, a basic knowledge of EMF is required. As soon as you have to perform additional constraint checks for your DSL and to generate code, you will need to inspect this model and traverse it.
It is easy to use the generated Java classes since they follow conventions. In particular, instances of EMF classes must be created through a static factory, which results from EMF generation itself, thus, there is no constructor to use. Initialization of fields, that is, features, is performed using getters and setters. A collection in EMF implements the EList
interface, which is an extension of the standard library java.util.List
. With only these notions in mind, it is easy to programmatically manipulate the model of your program. For instance, this Java snippet programmatically creates an Entities
model that corresponds to an Entities
DSL program:
import org.example.entities.entities.Attribute; import org.example.entities.entities.EntitiesFactory; import org.example.entities.entities.Entity; import org.example.entities.entities.Model; public class EntitiesEMFExample { public static void main(String[] args) { EntitiesFactory factory = EntitiesFactory.eINSTANCE; Entity superEntity = factory.createEntity(); superEntity.setName("MySuperEntity"); Entity entity = factory.createEntity(); entity.setName("MyEntity"); entity.setSuperType(superEntity); Attribute attribute = factory.createAttribute(); attribute.setName("myattribute"); attribute.setArray(false); attribute.setType(superEntity); entity.getAttributes().add(attribute); Model model = factory.createModel(); model.getEntities().add(superEntity); model.getEntities().add(entity); } }
EMF is easy to to learn, but as with any powerful tool, there is much to learn to fully master it. As hinted previously, EMF is widely used in the Eclipse world; thus you can consider it as an investment.