I recently spent an hour figuring out why my Xtend/Xpand code did not produce what I expected. I'm writing a code generator that generates PL/I source code for call level interfaces based on abstract service specifications. A service has an input structure and an output structure.
simplified PL/I service signature:
- typeName: Field
- businessAttribute1: Field
- businessAttributeN: Field
- typeName: Field
- further attributes...
In each of them there is a structure field containing the type name among other business attributes. Both input and output structures should have this typeName field. During model transformation, first the input and then the output structure is created. Nevertheless, as soon as the output structure was created, the input structure kind of lost the typeName field.
After debugging (and thinking a little bit about the causes), I found out that it was due to some features of both Xtend and Ecore. I have an Xtend create expression for creating the typeName field:
create Field createTypeNameField():
// code omitted
In the Ecore model, there is a containment association between Structure and Field, i.e. a structure contains a list of fields.
And there we are, the create expression caches the Field instance, the key is "empty" since the create expression has no parameter (kind of singleton). Hence, during the second assignment of the typeName field (to the output structure), the first containment reference to the input structure gets "overwritten" (aka Ecore containment feature).
I solved the problem by assuring the creation of a separate instance of the typeName field each time. For this, the create expression has now a key parameter which is different for the input and for the output structures. Now the cache for the typeName field is parametrized with the key, even if the key is not used within the code of the expression:
create Field createTypeNameField(String key): // code omitted ;
As a summary, be aware of create expression with objects involved in containment associations.