Introduction
In this post I will describe some miscellaneous features which didn’t find a place in the other EMF tutorials. The last thing that we saw was the Check language. Now we will see some more Xpand features. These are some features, which have some specifics so you might not meet them in your everyday work. One such feature is the encoding, another one is the lazy evaluation and it goes on. Check this post as a reference if you ever need to use them.
The encoding feature
The first feature that I find important especially when working with texts of different languages is the encoding. In my case I was working with Bulgarian language in the previous tutorials. After the generation was over, the translated Bulgarian text was not displayed correctly – instead there were “?” symbols. In order to fix this you need to add the following line in the workflow file: <fileEncoding value=”THE_DESIRED_ENCODING”/>. It should be added between the <expand> and <outlet> sections of the Generator component. See below how it looks like on my side.
<!-- generate code for BG translations --> <component class="org.eclipse.xpand2.Generator"> <metaModel idRef="mm_emf" /> <!-- Call the Main function of the Main.xpt template--> <expand value="templates::MainTemplate::Main('bg') FOR model" /> <!-- Set the encoding --> <fileEncoding value="UTF-8"/> <!-- Files generated with this outlet will be beautified using the rules in the config file--> <outlet path="${outputPath}"> <postprocessor class="org.eclipse.xpand.support.cdt.CppBeautifier" configFile="${beautifierConfigFile}"/> </outlet> <!-- Path to the folder where the files are generated. Used when looking for protected regions--> <prSrcPaths value="${outputPath}"/> </component>
If you don’t see the change in the generated files in Eclipse you need to do the following. Right click on the generated file and select Properties. From Resource find Attributes and change the Text file encoding.

The GLOBALVARS feature
Next feature allows us to define global variables in the workflow file and access them from any place in the templates. Thus, there will be no need to pass data as arguments to the main function. We can have as many global variables as we want and we can access them directly. This is very helpful if you want to pass multiple parameters to the templates and you don’t want to have a main function with a bunch of arguments.
Defining a global variable
The global variables are defined in the workflow file. There is a special element, which I have added to the Generator component. The element’s name is <globalVarDef> and we should provide it with the name and the value of the variable. Here is how it looks like on my side. I have done it for both the English and Bulgarian language generators.
<!-- generate code for EN translations--> <component class="org.eclipse.xpand2.Generator"> <metaModel idRef="mm_emf" /> <!-- Call the Main function of the Main.xpt template--> <expand value="templates::MainTemplate::Main FOR model" /> <!-- Set the encoding --> <fileEncoding value="UTF-8"/> <!-- Files generated with this outlet will be beautified using the rules in the config file--> <outlet path="${outputPath}"> <postprocessor class="org.eclipse.xpand.support.cdt.CppBeautifier" configFile="${beautifierConfigFile}"/> </outlet> <!-- Path to the folder where the files are generated. Used when lookig for protected regions--> <prSrcPaths value="${outputPath}"/> <globalVarDef name="Language" value="'en'"/> </component>
As you can see I have added one global variable named Language. I have defined it at the end of the Generator component definition. The value is inside single quotation marks because it is a String variable. It is important to be inside the single quotation marks or else it will be parsed as some object and the generation will fail. Also, note that I have removed the argument from the <expand> function. I don’t need it anymore, because it is a global variable now.
Don’t forget to add this global variable to all Generator components!
Accessing the global variable
Once the global variable is defined, we can access it. For this purpose I define one Extension function, which is returning the value of the global variable. Then I can call this function from my templates and access the global variable value. Here is how I define this function.
String GetCurrentLanguage() : GLOBALVAR Language ;
It is defined as any other extension function. I use the GLOBALVAR keyword to access the value of the global variable.
Once I have this function I can call it from any template. In my case I will call it from the Main template but you can go ahead and do it from some other template. Here is how my Main template looks like now.
«REM» Name: MainTemplate.xpt Description: This template is the entry point for the code generation process Creation Date: 10/08/2020 Created By: Ahmed Karairbahimov Last Update: 10/10/2020 Updated By: Ahmed Karaibrahimov «ENDREM» «IMPORT Translations» «EXTENSION extensions::Helper» «DEFINE Main FOR DocumentRoot» «EXPAND templates::HeaderTemplate::CreateHeaderFile(GetCurrentLanguage()) FOR this» «EXPAND templates::SourceTemplate::CreateSourceFile(GetCurrentLanguage()) FOR this» «ENDDEFINE»
As you can see I am calling GetCurrentLanguage() which is my extension function. It will return the current language and then it will forward that value to the next function. As you can see it is very simple to use a global variable.
The lazy evaluation feature
This feature allows Xpand to execute a function and adds its result to the file at any place just before the file is closed and saved. How is this helpful? Usually when we generate a file we are filling its content from top to bottom. Now imagine that there is some information you want to add at the beginning of the file, but in order to get the information you need to process information which comes later. So you want your information on the top, but the information becomes available once you are at the bottom of the file. Using some Java code you could do some preprocessing and retrieve that information. But there is another way. Enter the lazy evaluation.
How to use lazy evaluation
By appending the ONFILECLOSE to the EXPAND routine you could force the generator to call the function before it closes the file and it will append the result at the place where the function was called. Let’s do one example – I want to add #include directives on top of the file. However, the files I will have to include will become obvious once I process some other information and this will happen below somewhere. Here is the code followed by some explanation.
«DEFINE LazyEvalDemo FOR Object» «LET (List[String]) {} AS includes» «FILE ...» ... «EXPAND ImportIncludes FOREACH includes ONFILECLOSE» ... «includes.add(someString)» ... «ENDFILE» «ENDLET» «ENDDEFINE» «DEFINE ImportIncludes FOR String» #include "«this»" «ENDDEFINE»
The first thing that I do is to define one variable of List<String> type. Using LET you can define variables. You have to surround the place where you will be using that variable with <LET><ENDLET>. Then I am opening a file for writing. I have some code and so on. Now following is the place where I want to add my includes. I am calling one function ImportIncludes which the generator will call for each String in the List includes. And I have added the ONFILECLOSE directive. The generator will not call the function now.
The generation will continue, it will add some content. At some point it will update the includes List with some values. Then will keep on generating code. Once it is over, it will come to the end of the file. Now, before it closes the file, it will go back at the place where we added that function and will call it. The generator will append the result of the function call in that same place. So in our case it will add all the includes that we need. Once this is over, the generator will save the file and close it. This is how to use the lazy evaluation.
The ERROR Xpand Feature
There is a way to throw an error from your template, if something is not OK with the models. However, this is discouraged. The way to throw an error is to use the ERROR tag with some expression in it. Nothing special in here. Check this example:
«DEFINE ThrowInvalidInputError FOR String» «ERROR this + " is an invalid input"» «ENDDEFINE»
Conclusion
In this tutorial we saw some fancy Xpand features, which we might not use extensively in our daily work. Such thing like global variables and lazy evaluation. Still, you might find them very useful.
The project that we have developed so far is again available in GitHub and it is attached at the end of the post.
Next we will see some more Xpand specifics.
EMF Misc Features
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.