The generator model, aka genmodel, provides means for configuring the code generator for that generates Java code from Ecore models.

Introduction

There are many reasons to model data, but one of the main motivations is to generate code for representing and processing the data. EMF includes a Java code generator, and supports many ways of customising it. Instead of doing this programmatically, e.g. by subclass a generator class, you create a generator model (genmodel for short) for configuration the generator. The generator is template-driven, and certain customisations are better achieved by editing the templates.

Genmodel is a so-called decorator model, which means it essentially adds extra data to an existing model, in this case Ecore. This means there are genmodel classes corresponding to Ecore's, e.g. a GenPackage is used for configuring how the generator generates code at the package level, a GenClass does the same for an EClass. Since the generator supports generating code for domain classes, editing and testing, there are options covering many code generation aspects for each Ecore model element class.

The wizard and editor for genmodel automatically synchronises the structure of genmodel with that of Ecore, so you don't need to create the corresponding GenXYZ instances by hand. And all but one options have good defaults, to you quickly can create Java code to get started.

Classes for representing and creating domain objects

The following table describes what code is generated for representing and creating domain objects and some important configuration options. In general, to understand genmodel, the best is to use a sample Ecore model and se what happens when changing the options and generating.

Ecore

Genmodel

EPackage

Container for all EClassifiers.

EPackage and EFactory subclasses (interfaces and implementation classes) are generated. The former is for accessing the model (see below), the latter for creating EClass instances and converting EDataTypes to/from strings.

By default, these subclass will use the EPackage name as a prefix for "Package" and "Factory", respectively, e.g. "OrgPackage" and "OrgFactory". GenPackage supports specifying a different prefix.

All the classes are generated into a package, and the package name can be customised in several ways. First, a base prefix can (should be) be set, that is prepended the package prefix. E.g. if the EPackage's name is "org" and the base prefix is "no.hal", the package prefix for classes will be "no.hal.org". In addition, packages suffixes for implementation, utility and test classes can be set (but usually aren't).

EClassOrgUnitPerson

By default, an interface and an implementation class is generated, the interface will have the EClass name, and the implementation will have "Impl" appended, e.g. "OrgUnitImpl". Of course, abstract and interface flags are respected.

Genmodel supports generating only an implementation class, and the naming pattern can be changed.

The EFactory subclass will have a method for creating an instance, named by prefixing the EClass name with "create", e.g. createOrgUnit(). This method should be used instead of using the empty constructor.

EDataType

A non-modelled or "foreign" type, provided by the host language and runtime environment, like int, double, String, Date etc. Many such types are pre-defined by Ecore, e.g. EInt, EIntegerObject, EDouble, EDoubleObject, EBoolean, EBooleanObject, EString, EDate corresponding to Java's int, Integer, double, Double, boolean, Boolean, String and Date types. You can define your own, to be able to use other Java types in your model.

No classes are generated, as they already exist. Methods (stubs) for converting to/from String will be generated in the EFactory subclass. The names have the formats "convert%sToString" and "create%sFromString", respectively, where %s is the EDataType's name. By default, these methods delegate to EFactory's generic methods, which use toString() and valueOf(String) methods, but the method bodies can be changed. Remember to handle null and "" values!

EStructualFeature

Abstract superclass of EAttribute and EReference

The owning classes's interface will include a getter and possibly a setter, and the implementation will include an appropriate field.

Single valued attributes will get a field and a getter and setter.

Multi-valued attributes will get an EList field specialised to the feature's type. There will only be a getter for the list as a whole, as the list may be manipulated directly.

EAttribute

Has a name, type (an EDataType) and multiplicity (lower and upper bounds).

See EStructuralFeature.

EReference

Has a name, type (an EClass) and multiplicity. Corresponds to a one-way/directed association from the owning EClass to another EClass. Two EReferences may be each other's opposite, meaning that of one instance refers to the other, the other must refer back.

See EStructuralFeature.

In addition, there will be code for handling the opposite logic (often called handshake), which is complex to do by hand.

EOperationThe interface will include the operation and the implementation class will have a stub method (unless the body code is provided in an EAnnotation).

As indicated above, you can or need to change or fill inn code in some method bodies. In this case it's important to note @generated marker in the comment above the method, that indicates that the method body is untouched and will be overwritten if the code is regenerated, After hand-editing the method body, the marker must be changed, e.g. to @generated NOT.

Accessing the model

EPackage, EClass, EDataType, EAttribute, EReference, EOperation and EAnnotation are all ordinary Java classes, that are used to represent the Ecore model (when loaded into memory). Instances of these classes are often called meta-objects, since they are used to describe the structure of the domain classes, from which you create "ordinary" objects. You use (instances of) these classes if you load an Ecore model programmatically, e.g. for writing you own code generator. This will give you EPackage objects containing EClass and EDataType objects, and the EClass object will contain EDataTypeEAttributeEReference and EOperation objects. If you have generated code you may access the same structure of meta-objects directly, as the generated EPackage subclass includes code for instantiating and accessing all of them.

The EPackage instance can be retrieved using the eINSTANCE field of the generated interface, and by using its getters you can retrieve all the other meta-objects. E.g. OrgPackage.eINSTANCE.getEClassifiers() retrieves all EClass and EDataType objects. There are also getters for specific model elements, including those that are not directly contained e.g. OrgPackage.eINSTANCE.getOrgUnit() will give you the (directly contained) OrgUnit EClass, and OrgPackage.eINSTANCE.getOrgUnit_workers() will give you the workers EReference contained by the OrgUnit EClass.

The meta-objects are not that useful for making specific applications, but once you want to make more generic utilities and tools, they are absolutely necessary! For instance, suppose you need to create a form for a domain object. For each attribute you want a text field, and the widget's contents should be sync'ed with the domain object's attribute. This is easy, but tedious to do by hand, but pretty easy to do generically if you use the meta-objects. You get the EClass for a domain object using EObject's eClass() method, and then all EAttribute objects can be retrieved using the getEAllAttributes() method of EClass and their types (EDataType) using getEAttributeType(). Then you use EObject's eGet(EStructuralFeature) and eSet(EStructuralFeature, Object) methods to get/set the attribute values of an EObject in combination with the EFactory methods convertToString(EDataType) and EFactory.createFromString(String) to sync the text field contents and attribute values. If you want to add support for creating new objects, you can use the EFactory method create(EClass).

Serialising and de-serialising model instances

By default, no classes are generated for serialisation, but you can create and use an XMIResource to save and load model instances in the XMI format. However, you will get a better experience by setting the resources type to XMI in the genmodel. In this case, two implementation classes will be generated, for the Resource and Resource.Factory interfaces, respectively. The Resource.Factory is used to create the Resource, and the Resource is used to save and load model instances (see Serialization). Both the generated classes may be customised, e.g. to adjust save and load options or perform pre- or post-operations when saving and loading.

In addition to generating these classes, the Resource.Factory implementation is configured as the default in plugin.xml, so Eclipse will it internally whenever needed, e.g. by the generated editor. This requires that the file extension(s) is set in genmodel, which is done automatically. However, the default file extension is the EPackage name, which may not be what you want, so you should usually consider it when specifying the resource type.

Editing model instances

Once you can create, save and load models, you would like to be able to edit model instances using an editor that "understands" your domain. As described in Editing Ecore model instances, there are several generic alternatives, but it may be better in the long run to generate and customise an editor. E.g. you may want to change how the model instances are presented and edited and domain-specific actions.

The code generator can generate two kinds of support for editing instances. The so-called edit code is pretty generic and may be used in any kind of editor, also web-based ones, while the editor code utilities the edit code to provide an Eclipse-based editor. Out-of-the-box the editor has the same functionality and behaviour as the generic one, so the initial advantage is that it is configured as your model's default editor. The usability is low, but it's easy to incrementally improve it and for internal use it is a good option.

Dynamic templates

The code generator is template-driven and is pre-configured with templates for all the kinds of files it needs to generate. If the genmodel options are not sufficient for configuring the generator, you may set the dynamic templates flag and provide a folder with customised templates in the templates directory attribute of the root GenModel object. The easiest way to get started is to copy all the templates from the org.eclipse.emf.codegen.ecore plugin and modify them, rather than writing them from scratch. See http://blog.eclipse-tips.com/2008/08/using-dynamic-templates-in-emf.html for details.

 

 

  • No labels