Introduction
In this tutorial i will show you how to define constraints, which could be checked before starting the generation process. If a constraint fails, we can stop the generation process. This provides us with more control over the generation. We will see how to define such constraints in the Check language. Then we will see how to introduce the checking step in the workflow.
Usually when dealing with XML files and if the reader finds a node or a text, that does not meet the XSD scheme requirements it will throw an error and stop the generation. However, the Check constraints give us the ability to define rules, which are not found in the XSD scheme. Such a rule for example would be for the values in the XML to be longer than 3 characters and shorter than 30. We can define this rule and then the Check component can look for such rules. You will find this very helpful.
Previously we saw how to create a runnable JAR file and with that we concluded the base EMF topics. With this tutorial and the upcoming ones I will show you some features of EMF, which you don’t meet everyday, but they are still useful and important.
Defining constraints
Creating a Check file
For the definition of the constraints we will use the declarative Check language. The language is very easy to understand. You can find some information about it in the Help Contents section of Eclipse. In order to start writing constraints in it, we need to create one new file with the extension .chk. Since it is important to be in the same classpath as the templates I will create one new folder inside the src folder and name it checks. Then right-click on it and select New >> Other… From the popup that appears find and select Check File. Give it some name and click Finish. Now we have the file. It is time to fill it up with some constraints.
Defining constraints
Following is the code and the explanation about it, which I use to define the constraints. The code has three checks which are performed on the input .xml files. First check is whether the value of the translation string is shorter than 30 characters. Second check is whether the translations contain one of the forbidden symbols. The last check is whether we have all unique ids for the translations or there are repeating ones.
import Translations; context TranslatedStringType WARNING "Translated value '" + value + "' too long!" : ((String)value).length < 30; context TranslatedStringType if ((String)value).length > 2 ERROR "Translated value contains forbidden characters!" : !((String)value).contains("$") && !((String)value).contains("&") && !((String)value).contains("@"); context TranslationsType WARNING "Not all translation ids are unique!" : translatedString.forAll(e1| translatedString.notExists(e2|e1 != e2 && e1.name == e2.name));
Constraint about text width
Firstly I start with importing the Translations package so I will avoid typing it every time. A rule is created using the context keyword. Following is the type for which we will be applying the rule. Here I want to check each TranslatedString node from the .xml file and check it’s value. This is why i am adding the TranslatedStringType after the context keyword. This is the type that is representing the TranslatedString nodes according to the .ecore model. Next I have WARNING – this means that if the constraint fails it will give a warning in the console and the generation will continue.
On the next line I put the text which I want to print as a warning. You can see that inside it I can have access to the member variables of the TranslatedStringType class and I can use them. Since these are Java objects I can apply also the methods, which they provide. Lastly I add column “:” at the end of the line.
The next line contains the condition under which the constraint will pass. Note that this constraint will not throw an error or warning as long as the condition is true. In our case I am checking whether the length of the translation value is less than 30 symbols. Once there is a value with greater width the check will fail and a warning will appear in the console. Note here also that I do a cast to String. The reason is that value is of java.lang.String type but in Check the strings are treated as if they are from String type (another string type). We need to end all with a semicolon “;”.
Constraint about forbidden characters
Next rule that I am defining is whether a translation contains a forbidden character. Again I start with the context keyword followed by the type I am applying the constraint to. After defining the type this rule applies to I am defining one condition. This is a guard condition and it means that we will apply the constraint only if this condition is true. We have the ability to define such conditions and skip the checking for some of the elements from the .xml file. In my case the checker object will check for the following constraint only if the length of the translation is greater than 2 characters.
I am finishing the line with the ERROR keyword. When this keyword is used the checker will throw an error and will stop the generation process. Following line contains the error message which the checker will print in the console and the mandatory column “:”.
Next I have the conditions under which the check should pass. Here I have added three different checks. You can add as many checks as you want and make the check as complex as you need. In my case I check a given translation whether it does not contain a “$”, “&” or “@” character. At the end I add the mandatory semicolon “;”.
Constraint about unique ids
The last constraint which I check is whether the ids I am using are unique. If there is an id, which appears more than once it will throw a warning. Again I create a context and this time it is for TranslationsType object. I am making this constraint to throw a warning. Next, I am adding the message to be printed in the console.
In the last line I am using some Java expression to check if all entries in a given list are unique. You can add such complex expressions to be checked. This is a demonstration of the flexibility of the Check language.
Updating the workflow to include the constraints checking
After we have developed the constraints checking rules it is time to update the workflow file in order to use them. We need to add the following step between the XML reading and the generation process:
<component class="org.eclipse.xtend.check.CheckComponent"> <metaModel idRef="mm_emf" /> <checkFile value="checks::Rules"/> <emfAllChildrenSlot value="model"/> </component>
In the checkFile element we should provide the path to the checks file without the extension. The rest can be the same as given above. This should be done for both languages. Finally it will look like this:
<!-- Parses the EN translations xml file and store all objects in slot 'model' --> <component class="org.eclipse.emf.mwe.utils.Reader"> <uri value="${modelFileEn}" /> <modelSlot value="model" /> </component> <component class="org.eclipse.xtend.check.CheckComponent"> <metaModel idRef="mm_emf" /> <checkFile value="checks::Rules"/> <emfAllChildrenSlot value="model"/> </component> <!-- 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('en') 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}"/> </component>
Testing the constraints
In order to see if the constraints work we should modify our input .xml file and run the generation process. Here is how my .xml file looks like.
<?xml version="1.0" encoding="utf-8"?> <Translations> <TranslatedString name="app_file">File</TranslatedString> <TranslatedString name="app_new_file">New</TranslatedString> <TranslatedString name="app_open_file">Open File...</TranslatedString> <TranslatedString name="app_close_file">Close</TranslatedString> <TranslatedString name="app_edit">Edit</TranslatedString> <TranslatedString name="app_cut">Cut</TranslatedString> <TranslatedString name="app_copy">Copy</TranslatedString> <TranslatedString name="app_paste">Paste</TranslatedString> <TranslatedString name="app_find">Find</TranslatedString> <TranslatedString name="app_replace">Replace</TranslatedString> <TranslatedString name="app_help">Help</TranslatedString> <TranslatedString name="app_about">About</TranslatedString> <TranslatedString name="app_about">About You</TranslatedString> <TranslatedString name="app_about1">About You@</TranslatedString> <TranslatedString name="app_about2">About YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout You</TranslatedString> </Translations>
When you run the generation you will get two warnings and one error and the generation will stop.
906 ERROR WorkflowEngine - Workflow interrupted. Reason: Errors during validation. 907 WARN WorkflowEngine - [WARNING]: Translated value 'About YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout YouAbout You' too long!(Element: app_about2; Reported by: -UNKNOWN-) 907 ERROR WorkflowEngine - [ERROR]: Translated value contains forbidden characters!(Element: app_about1; Reported by: -UNKNOWN-) 907 WARN WorkflowEngine - [WARNING]: Not all translation ids are unique!(Element: Translations.impl.TranslationsTypeImpl@247bddad; Reported by: -UNKNOWN-)
Conclusion
In this tutorial we saw how to add constraint checking step to our generation process. With this step we can add custom checking of the input files which is very useful. We had a brief overview of the Check language. Keep in mind that it has more feature than I showed. The problem is that they are not documented very well and it is hard to find any documentation about them (I couldn’t find any).
You can find the current source code attached to this tutorial. It is also available in GitHub.
Next we will see more Xpand features which we might use in our projects.
EMF Constraint Checking
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.