Xtext Tip: Add custom code to your generated model
If you are into EMF modeling, you know that you can define custom operations and fields in EMF models. Xtext allows to make use of this feature in an elegant way. Let us start with the block language with a separate EMF metamodel from a previous tip. The full code for this example can be found here.
Prepare the model
We want to include a new computed field that returns the number of fields in our Block
class. We call this attribute numberOfFields
. We create an attribute with that name, and set the attributes as follows:
- Derived to true
- Transient to true
- Volatile to true
- Changeable to false
Derived tells EMF that your attribute is computed from another; transient, that the attribute is omitted from the serialization; volatile, that there is no storage associated with the attribute; and changeable, that the feature cannot be changed. Setting these fields tells the code generator to generate empty getters for this attribute.
Configure the project
The next step is to implement these generators in a way that Xtext can use them. We need to perform the following actions:
- Create a new source folder and configure the project to use it.
- Configure the Ecore generator to use this new source folder
- Implement a subclass that overrides the derived methods
- Regenerate the code so that it uses the new subclass
To show how this is done
Create a new source folder
In the package explorer we go to the project, select it and right click and go to New ⟶ Source folder. Let us call it custom-src-gen
. This creates a new source folder, but we need to configure the build.properties
file to use it. We modify the build.properties
file, and modify the source as follows:
source.. = src/,\
src-gen/,\
custom-src-gen/,\
xtend-gen/
Configure the Ecore generator
The Ecore generator (in the MWE2
) needs to be configured to use this new folder, we modify it as follows:
component = org.eclipse.emf.mwe2.ecore.EcoreGenerator {
genModel = "platform:/resource/com.idiomaticsoft.dsl.block/model/Block.genmodel"
srcPath = "platform:/resource/com.idiomaticsoft.dsl.block/custom-src-gen"
}
Implement a subclass
We can now create a subclass that implements our method. This subclass needs to be named as the implementation of our generated class followed by the prefix Custom
. In our case, the name is BlockImplCustom
. Let us use the simple implementation below:
package com.idiomaticsoft.dsl.block.block.impl;
public class BlockImplCustom extends BlockImpl {
@Override
public int getNumberOfFields() {
return getMembers().size();
}
}
Regenerate the code
Once we have our custom class implemented and our project configured, we can regenerate the code. This will regenrate the code by modify the generated factories to use our new subclass. We can check that the code in BlockFactoryImpl
to create a block is now the following:
@Override
public Block createBlock()
{
BlockImplCustom block = new BlockImplCustom();
return block;
}