Introduction
In the previous post I showed you how to use Xpand extensions. Now I will introduce another Xpand concept – Xpand protected regions. This is a very powerful feature of Xpand and the one thing that made me fell in love with this framework. We will see how to use them in order to allow the user to update the generated code without losing the changes.
Xpand protected regions usage
The Xpand protected regions provide a way for the users to update the generated code and keep the changes. Often in the generated output appear pieces of code, which we want to modify manually after the generation. Since we can regenerate the code many times, we need to keep our changes and not do them again. The Xpand protected regions wrap the code we want to modify by hand in comments-like sections. These sections have some unique syntax and ID. When the code generator is running, it looks for such sections whether they are enabled. The generator does not overwrite their content if so. We have the options to wrap a code piece we want to update or to leave a blank region, which we can put additional code inside later.
When a user wants to make changes in the generated code, he/she should find the respective region. Then enable the region if it isn’t enabled by default and do the changes. Once all this done, the code generator can regenerate the code. It will preserve the user changes.
Xpand protected regions syntax
Following is the code piece we need to use in order to create a protected region:
«PROTECT CSTART expression CEND expression ID expression (DISABLE)?» a sequence of statements «ENDPROTECT»
The expressions after CSTART and CEND are the symbols which need to enclose the protected region in a comment section. In the case of C++ and Java they would be /* and */. ID should be an unique ID for the given region, which we don’t use in any other region. This means that if we create two or more files with the same template, we need to make sure that the ID of this region will be different in each file. The DISABLE expression is optional. If we don’t add it, the protected region will be enabled by default and everything we put inside it will be protected during the next code generation. If you want to manually enable/disable the protected region, add the DISABLE keyword.
Example Xpand protected regions
Here is an example of how the Xpand protected regions will look like for a C++ code
«PROTECT CSTART "/*" CEND "*/" ID "ThisClassIncludes" DISABLE» #include <vector> «ENDPROTECT»
Here I have chosen the /* and */ comments opening/closing symbols. For ID I have chosen some unique name. And then I have put DISABLED in order to not activate this region by default. A reason why I don’t want to do so is this: Inside the region I have some code, which I have written in my template directly. I would like to update it again in the template. If I do so and the protected regions is enabled by default, my change will not appear in the generated code. That is why I disable the protected region. Next time I want to do updates, I will enable the protected region and do my changes.
As you can see I have added some code inside the protected region and then closed it with «ENDPROTECT». This is all you need to do in order to have protected regions.
The generated output
Here is the code, which will be generated for the above protected region:
/*PROTECTED REGION ID(ThisClassIncludes) START*/ #include <vector> /*PROTECTED REGION END*/
In my case I have added some include statements. You can see how the generator have enclosed them in a protected region block. Now this block is not enabled and if I do a change like adding a new include statement, this change will disappear during the next generation. What I have to do now in order to enable it is to add the ENABLED keyword before START. Look at the first line and you will see where to add it.
My code after the updates will look like this:
/*PROTECTED REGION ID(ThisClassIncludes) ENABLED START*/ #include <vector> #include <string> /*PROTECTED REGION END*/
I can add whatever I want. During the next code generation my change will remain as it is.
Adding Xpand protected regions to the existing project
As a next step, let’s go ahead and update the header template and the source template with Xpand protected regions.
Updating the header template
In the header template I will add a protected regions which I will use for defining extra public and extra private class members. Here is how my CreateHeaderFile function will look like:
«DEFINE CreateHeaderFile FOR EClass» «FILE "include/" + name + ".hpp"» //================================================================================================================ // Copyright <2020> <Ahmed Karaibrahimov> // Permission is hereby granted, free of charge, to any person obtaining a copy of this software // and associated documentation files (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or substantial // portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //================================================================================================================ #ifndef XQ_GEN_«name.toUpperCase()»_HPP #define XQ_GEN_«name.toUpperCase()»_HPP #include <vector> «EXPAND GenerateNamespace(true) FOR this» class «name» { public: // Default constructor «name»() = default; // Argumented constructor «name»(«EXPAND GetAllConstructorArguments FOR this»); // Default destructor virtual ~«name»() = default; // Getters and Setters «EXPAND CreateAllGettersAndSetters FOR this» «PROTECT CSTART "/*" CEND "*/" ID name + "AdditionalPublicMembersDeclarations" DISABLE» // Add here additional class public members. // Add ENABLED before START in order to activate this region «ENDPROTECT» private: // Member variables «EXPAND CreateAllMemberVariables FOR this» «PROTECT CSTART "/*" CEND "*/" ID name + "AdditionalPrivateMembersDeclarations" DISABLE» // Add here additional class private members. // Add ENABLED before START in order to activate this region «ENDPROTECT» } «EXPAND GenerateNamespace(false) FOR this» #endif // XQ_GEN_«name.toUpperCase()»_HPP «ENDFILE» «ENDDEFINE»
My first protected region is on lines 44-47. Since the generator uses this file with multiple classes if I hardcode the ID it will appear on each header file, which is not allowed. In order to overcome this problem, I am adding the name of the class to the ID (as you can see on line 44). This way for each header file I will have unique ID. It will contain the class name + “AdditionalPublicMembersDeclarations”. I have just added some instructions for the users, who will be updating these files.
The other region is on line 53-56, but it is quite the same so I will not provide additional information.
Updating the source template
Here is how the updated CreateSourceFile function looks like on my side:
«DEFINE CreateSourceFile FOR EClass» «FILE "src/" + name + ".cpp"» //================================================================================================================ // Copyright <2020> <Ahmed Karaibrahimov> // Permission is hereby granted, free of charge, to any person obtaining a copy of this software // and associated documentation files (the "Software"), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or substantial // portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //================================================================================================================ #include «name».hpp «EXPAND templates::HeaderTemplate::GenerateNamespace(true) FOR this» // Argumented constructor «name»::«name»(«EXPAND templates::HeaderTemplate::GetAllConstructorArguments FOR this») : «EXPAND GetAllConstructorArgumentsInitialization FOR this» { } // Getters and setters «EXPAND CreateAllGettersAndSettersImplementation FOR this» «PROTECT CSTART "/*" CEND "*/" ID name + "AdditionalPublicMembersDefinitions" DISABLE» // Add here additional class methods definitions. // Add ENABLED before START in order to activate this region «ENDPROTECT» «EXPAND templates::HeaderTemplate::GenerateNamespace(false) FOR this» «ENDFILE» «ENDDEFINE»
The update is on lines 36-39. Again it is the same as in the previous section so I believe that explanation will not be necessary.
Updating the workflow to use Xpand protected regions
Once we have added the protected regions it is time to update the workflow file. It is necessary to provide information to the generator, that there are such regions. Firstly, we will provide information, where the generated files are. Then the generator will check them and save information what regions are available and what is their content. When the generator starts generating the files and it meets a region with a given ID, it will replace the text with the one, that was previously read from the files. Thus, it will preserve the user changes.
We update the following component from the workflow file: <component class=”org.eclipse.xpand2.Generator”>. The updated component will look like this:
<!-- generate code --> <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" /> <!-- Outlets are used to save the generated files on the file system--> <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}"/> </component>
You can see on lines 13-14 what should be added. The value of prSrcPaths should point to the folder, where we save the generated files.
Testing the Xpand protected regions
Now we have everything we need in order to use the protected region. Firstly, let’s run the workflow file in order to generated some code. You will see some new lines appearing in the console:
757 INFO ProtectedRegionResolverImpl - Source scan started ... 769 INFO ProtectedRegionResolverImpl - Source scan finished in 0.011s 769 INFO ProtectedRegionResolverImpl - Files scanned: 7 769 INFO ProtectedRegionResolverImpl - Regions found: 0
Open the generated code. You will find the following lines:
/*PROTECTED REGION ID(TranslatedStringTypeAdditionalPublicMembersDefinitions) START*/ // Add here additional class methods definitions. // Add ENABLED before START in order to activate this region /*PROTECTED REGION END*/
This is a protected region. Go ahead and update some of the protected regions. Add ENABLED before the START on the first line. Then add some code between the /*PROTECTED REGION ID(TranslatedStringTypeAdditionalPublicMembersDefinitions) START*/ and /*PROTECTED REGION END*/ . Should look something like this:
/*PROTECTED REGION ID(TranslatedStringTypeAdditionalPublicMembersDefinitions) ENABLED START*/ // Add here additional class methods definitions. // Add ENABLED before START in order to activate this region String TranslateStringType::toString() { return m_name + " " + m_value; } /*PROTECTED REGION END*/
After you have added the code, go ahead and run the workflow again. Open the generated files and verify that your changes are there. You will also see in the console that some number of regions were found.

Conclusion
In this post we saw how to add Xpand protected regions. We saw how to put user code inside them and how to enable them. Then we ran the code generator and verified that our code, written by hand was preserved. What we saw was how useful the protected regions are. Again, I have added my code so far for you to download it and check on your side if it works as expected.
Next we start with another Xpand project, which will generate files with translations for different languages.

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.