Introduction
In the previous post I showed you how to include a beautification step of the generated code. This post is about making the templates more readable and simple, by taking out some of the repeating and messy code to an Xpand extension. The Xpand extensions provide an option to create additional simple function, which will output something, when a template calls them. But their actual super power is the ability to create a connection to pure Java functions and use all the Java power inside your templates. In this post we will only see how to create additional functions to do some simple processing outside the templates.
Creating an Xpand extension
Let’s start with creating an Xpand extension. Again I would suggest you to create a seprate sub-folder in the src folder, where you can keep the extensions. I will call it extensions
Again, as with the templates and workflows I would prefer to keep my extension files in a separate folder. So let’s start with creating such a folder inside our src folder. Unfortunately, there is no specific file you can add from Eclipse. All you can do is right-click on the newly created folder >> New >> Other… >> and then from General select File. Give some name to that file (I named it Misc) and the .ext extension. Click Finish when you are ready. This will create the required Xpand extension.

Defining the code to be put
Defining the content to put in the extension
Let’s define what code we should put in the extension. The idea is to keep it simple. Choose code pieces, which are found multiple times and in multiple places. Let’s look at the code for the header template and the source template. You can see that we have a code, which checks whether the attribute or reference has multiple instances and if so it puts it inside a vector:
«IF attribute.upperBound == -1»std::vector<«ENDIF» «attribute.eType.name» «IF attribute.upperBound == -1»>«ENDIF»
Instead of having this logic multiple times, we can take it out to the Xpand extension and put it inside a function, which we can call instead. Let’s go ahead and do so.
Creating a function in the Xpand extension
I have written the following two functions in the Xpand extension:
String getType(ecore::EAttribute attribute) : if(attribute.upperBound == -1) then "std::vector< " + attribute.eType.name + " >" else attribute.eType.name ; String getType(ecore::EReference reference) : if(reference.upperBound == -1) then "std::vector< " + reference.eType.name + " >" else reference.eType.name ;
This is the way how we define a function in the extension. You put the return type, in our case String, give it a name and then provide some arguments. Also, there is a column at the end of the function declaration. The first arguments is also the type, which we can call the function for. As you can see there are two functions with the same name but different arguments. We call the first function on objects of EAttribute type and the second on objects of EReference type. You can use if…else and switch statements. There is no return statement, you just need to paste the value you want to return directly inside the function or inside the if…else/switch statement. Don’t forget the semicolon in the end.
In our functions we check if the upperBound is -1, which would mean that the attribute or reference has multiple instances so in that case we would use vector. If it isn’t, we just return the type as it is.
Calling the functions in the Xpand extension
It is time to use the functions, which we just wrote. First I am going to do so for the HeaderTemplate. You need to add the following line in the beginning of the HeaderTemplate file: «EXTENSION extensions::Misc». It is better to add it after this line: «IMPORT ecore».
Update the GetAllConstuctorArguments function to look like this:
«DEFINE GetAllConstructorArguments FOR EClass» «FOREACH this.eAllAttributes AS attribute ITERATOR iter» «attribute.getType()» «attribute.name»«IF iter.counter1 < this.eAttributes.size»,«ENDIF» «ENDFOREACH» «IF eAllAttributes.size > 0 && eAllReferences.size > 0»,«ENDIF» «FOREACH this.eAllReferences AS reference ITERATOR iter» «reference.getType()» «reference.name»«IF iter.counter1 < this.eAllReferences.size»,«ENDIF» «ENDFOREACH» «ENDDEFINE»
As you can see we are calling the functions in the same line where we are getting the attribute/reference names. With the following call – «attribute.getType()» – we call the first function. As you can see the object which calls it is of type EAttribute. With a reference type object we call the second function – «reference.getType()».
Update the rest of the functions in the HeaderTemplate as follow:
«REM»Generate all the getters and setters«ENDREM» «DEFINE CreateAllGettersAndSetters FOR EClass» «FOREACH this.eAllAttributes AS attribute» «attribute.getType()» get«attribute.name»() const; void set«attribute.name»(«attribute.getType()» value); «ENDFOREACH» «FOREACH this.eAllReferences AS reference» «reference.getType()» get«reference.name»() const; void set«reference.name»(«reference.getType()» value); «ENDFOREACH» «ENDDEFINE» «REM»Generate all the member variables«ENDREM» «DEFINE CreateAllMemberVariables FOR EClass» «FOREACH this.eAllAttributes AS attribute» «attribute.getType()» m_«attribute.name»; «ENDFOREACH» «FOREACH this.eAllReferences AS reference» «reference.getType()» m_«reference.name»; «ENDFOREACH» «ENDDEFINE»
Update the CreateAllGettersAndSettersImplementation function in the SourceTemplate the following way (Don’t forget to add this line – «EXTENSION extensions::Misc»)
«REM»Generate all the getters and setters implementations«ENDREM» «DEFINE CreateAllGettersAndSettersImplementation FOR EClass» «FOREACH this.eAllAttributes AS attribute» «attribute.getType()» «name»::get«attribute.name»() const { return m_«attribute.name»; } void «name»::set«attribute.name»«attribute.getType()» value) { m_«attribute.name» = value; } «ENDFOREACH» «FOREACH this.eAllReferences AS reference» «reference.getType()» «name»::get«reference.name»() const { return m_«reference.name»; } void «name»::set«reference.name»(«reference.getType()» value) { m_«reference.name» = value; } «ENDFOREACH» «ENDDEFINE»
As you can see we have saved some lines of code and now the template is more readable. Run the workflow file in order to verify that the generated files are the same as before.
Again, I have left the source code so you can download it from the link below.
Conclusion
In this post we saw how to create Xpand extensions and how to use them in order to add some helper functions. The extensions are also used to provide function calls to Java functions. We will see this functionality in the upcoming posts.
In the next post we will see how to use Xpand protected regions to preserve any changes that we do in the generated code.

Passionate developer, loving husband and caring father. As an introvert programming is my life. I work as a senior software engineer in a big international company. My hobbies include playing computer games (mostly World of Warcraft), watching TV series and hiking.