How to force Acceleo to iterate over a collection in the same order on every template execution?

1.7k views Asked by At

I'm using an EMF model based on Modisco KDM metamodel. At some point of my Acceleo template I need to iterate over a collection, e.g.:

[for (e: AbstractCodeElement | action.codeElement) separator(', ')][e.generateCode() /]

The action.codeElement is a collection and modisco's kdm.ecore metamodel defines it as non-ordered.

Every time I run my generator, the output is generated on a different order. Cleary the serialized model xmi enforces a specific order, and every model editor (emf default editor, modisco editor) I open the model shows the same order always (matching the order the elements were serialized to the xmi file).

Since I cannot change the kdm.ecore metamodel to make the set ordered, would there be a workaround to get Acceleo to always iterate on the same order?

Thanks in advance

2

There are 2 answers

1
Laurent On BEST ANSWER

I'm afraid you can't. Try and cast it to a sequence:

action.codeElement->asSequence()

but I don't think anything guarantees that the sequence you get will always be sorted in the same order.

If the metamodel is made that way, there should be a reason, so either you can contact the metamodel authors to check this reason, or you should sort the result of action.codeElement with some stable criterion:

action.codeElement->sortedBy( some OCL expression)
0
jpcahoon On

I don't know of a clean way. I solved the problem by altering the name attribute of a child element so that it was sortable alphabetically in the way I wanted.

I wanted Slots in the same order every time, so I changed the name of each of their "value" child.

The names looked like: "01_id", "02_username", "03_city", ... "10_instructions", "11_contact". I didn't have to change what the "value" elements held, just their name, which I wasn't using for anything anyways. Hope this helps.

[for (s : Slot | instanceSpecification.slot->select(definingFeature.name = 'column')->sortedBy(value->asSequence()->first().name)]
    ... do work here ...
[/for]