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;
	}
comments powered by Disqus