How to get an OrderedSet of OccurrenceSpecifications from a Lifeline in QVTo?

332 views Asked by At

From the diagram on page 570 of the UML spec I concluded that a Lifeline should have the events property, holding an OrderedSet(OcurrenceSpecification). Unfortunately it is not there, at least in the QVTo implementation that I use.

All I have is the coveredBy property, serving me with an (unordered) Set(InteractionFragment). Since my transformation relies on the correct order of MessageOcurrenceSpecification I somehow need to implement myself what I expected to be implemented by the missing events property.

This is what I have so far:

helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {

    return self.coveredBy->selectByKind(OccurrenceSpecification)->sortedBy(true);
}

Obviously sortedBy(true) does not get me far, but I don't know any further. Who can help?

All I could find so far were other people struggling with the same issue years ago, but no solution:

3

There are 3 answers

1
hielsnoppe On BEST ANSWER

Based on the answer by Vincent combined with input from a colleague of mine I came up with the following solution for QVTo:

-- -----------------------------------------------------------------------------
-- Polyfill for the missing Lifeline::events property

query Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {

    return self.interaction.fragment
            ->selectByKind(OccurrenceSpecification)
            ->select(os: OccurrenceSpecification | os.covered->includes(self))
            ->asOrderedSet();
}
1
Vincent Aranega On

I don't know if it is possible, using directly coveredBy to get an ordered collection. As coveredBy is unordered, if you access it directly by the feature, you will have an unpredictible order and if you try to access it using the eGet(...) stuff, the same result will occur.

However, if I understood correctly, there is a "trick" that could work. It relies on the assumption that each OccurrenceSpecification instance you need is held by the same Interaction that contains the Lifeline and uses the way EMF stores contained elements. Actually, each contained element is always kind of 'ordered' relatively to its parent (and for each collection so EMF can find elements back when XMI references are expressed using the element position in collections). So, the idea would be to access all the elements contained by the Interaction that owns the lifeline and filter the ones that are contained in coveredBy.

Expression with Acceleo

This is easy to write in MTL/Acceleo. In know you don't use it, but it illustrates what the expression does:

# In Acceleo:
# 'self' is the lifeline instance
self.interaction.eAllContents(OccurrenceSpecification)->select(e | self.coveredBy->includes(e))->asOrderedSet() 

with self.interaction we retrieve the Interaction, then we get all the contained elements with eAllContents(...) and we filter the ones that are in the self.coveredBy collection.

But it is way less intuitive in QVT as eAllContents(...) does not exist. Instead you have to gain access to eContents() which is defined on EObject and returns an EList which is transtyped to a Sequence (in QVT,eAllContents() returns a ETreeIterator which is not transtyped by the QVT engine).

So, how to gain access to eContents() in the helper? There is two solutions:

Solution 1: Using the emf.tools library

The emf.tools library give you the ability to use asEObject() which casts your object in a pure EObject and give you more methods to access to (as eClass() for example...etc).

import emf.tools;  -- we import the EMF tools library

modeltype UML ...; -- all your metamodel imports and stuffs
...
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
  return self.interaction.asEObject().eContents()[OccurrenceSpecification]->select(e | self.coveredBy->includes(e))->asOrderedSet();
}

Solution 2: Using oclAstype(...)

If for some reason emf.tools cannot be accessed, you can still cast to an EObject using oclAsType(...).

modeltype UML ...; -- all your metamodel imports and stuffs
modeltype ECORE "strict" uses ecore('http://www.eclipse.org/emf/2002/Ecore');  -- you also register the Ecore metamodel
...
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
  return self.interaction.oclAsType(EObject).eContents()[OccurrenceSpecification]->select(e | self.coveredBy->includes(e))->asOrderedSet();
}

Limitation

Ok, let's be honest here, this solution seems to work on the quick tests I performed, but I'm not a 100% sure that you will have all the elements you want as this code relies on the strong assumption that every OccurrenceSpecification you need are in the same Interaction as the Liteline instance. If you are sure that all the coveredBy elements you need are in the Interaction (I think they should be), then, that's not the sexiest solution, but it should do the work.

EDIT>

The solution proposed by hielsnoppe is more eleguant than the one I presented here and should be prefered.

0
Ed Willink On

You are correct. The Lifeline::events property is clearly shown on the diagram and appears in derived models such as UML.merged.uml.

Unfortunately Eclipse QVTo uses the Ecore projection of the UML metamodel to UML.ecore where unnavigable opposites are pruned. (A recent UML2Ecore enhancement allows the name to be persisted as an EAnnotation.) However once the true property name "events" has been pruned the implicit property name "OccurrenceSpecification" should work.

All associations are navigable in both directions in OCL, so this loss is a bug. (The new Pivot-based Eclipse OCL goes back to the primary UML model in order to avoid UML2Ecore losses. Once Eclipse QVTo migrates to the Pivot OCL you should see the behavior you expected.)