如何从 QVTo 中的生命线获取一组有序的发生规范?

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

根据 UML spec 第 570 页的图表,我得出结论,Lifeline 应该有 events 属性,持有 OrderedSet(OcurrenceSpecification)。不幸的是,它不存在,至少在我使用的 QVTo 实现中是这样。

我只有 coveredBy 属性,为我提供(无序)Set(InteractionFragment)。由于我的转换依赖于 MessageOcurrenceSpecification 的正确顺序,因此我需要以某种方式自己实现我期望由缺失的 events 属性.

实现的内容

这是我目前拥有的:

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

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

显然 sortedBy(true) 并没有让我走得太远,但我不知道更多。谁能帮忙?

到目前为止,我所能找到的都是几年前其他人为同样的问题而苦苦挣扎,但没有解决方案:

不知道可不可以,直接用coveredBy得到一个有序的集合。由于 coveredBy 是无序的,如果您直接通过该功能访问它,您将获得不可预测的顺序,如果您尝试使用 eGet(...) 访问它,则会出现相同的结果。

但是,如果我理解正确的话,有一个 "trick" 可以工作。 它依赖于这样的假设,即您需要的每个 OccurrenceSpecification 实例都由包含 Lifeline 的同一个 Interaction 持有,并使用 EMF 存储包含元素的方式。实际上,每个包含的元素相对于其父元素总是有点 'ordered'(对于每个集合,因此当使用集合中的元素位置表示 XMI 引用时,EMF 可以找到元素)。因此,我们的想法是访问拥有生命线的 Interaction 包含的所有元素,并过滤 coveredBy 中包含的元素。

用 Acceleo 表达

这在MTL/Acceleo中很容易写。知道您不使用它,但它说明了表达式的作用:

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

使用 self.interaction 检索 Interaction,然后使用 eAllContents(...) 获取所有包含的元素,然后过滤 self.coveredBy 集合中的元素。

但在 QVT 中不太直观,因为 eAllContents(...) 不存在。相反,您必须访问在 EObject 和 returns 上定义的 eContents()EList,后者被转换为 Sequence(在 QVT 中,eAllContents() returns a ETreeIterator 未被 QVT 引擎转换)。

那么,如何在助手中获得对eContents()的访问权限呢?有两种解决方案:

解决方案 1:使用 emf.tools

emf.tools 库使您能够使用 asEObject() 将您的对象转换为纯 EObject 并为您提供更多访问方法(如 eClass()例如...等)。

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();
}

解决方案 2:使用 oclAstype(...)

如果由于某种原因无法访问 emf.tools,您仍然可以使用 oclAsType(...).

转换为 EObject
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();
}

限制

好吧,老实说,这个解决方案似乎适用于我执行的快速测试,但我不能 100% 确定你会拥有你想要的所有元素,因为这段代码依赖于强有力的假设您需要的每个 OccurrenceSpecification 都在与 Liteline 实例相同的 Interaction 中。如果您确定您需要的所有 coveredBy 元素都在 Interaction 中(我认为它们应该是),那么,这不是最性感的解决方案,但它应该可以完成工作。

编辑>

hielsnoppe 提出的 比我这里介绍的更优雅,应该优先考虑。

基于 并结合我一位同事的意见,我为 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();
}

你是对的。 Lifeline::events 属性 清楚地显示在图表上并出现在派生模型中,例如 UML.merged.uml.

不幸的是,Eclipse QVTo 使用 UML 元模型的 Ecore 投影到 UML.ecore,其中修剪了无法导航的对立面。 (最近的 UML2Ecore 增强功能允许名称作为 EAnnotation 保留。)但是,一旦真正的 属性 名称 "events" 被修剪,隐式 属性 名称 "OccurrenceSpecification" 应该可以工作。

所有关联在 OCL 中都可以双向导航,所以这个损失是一个错误。 (新的基于 Pivot 的 Eclipse OCL 返回到主要的 UML 模型以避免 UML2Ecore 损失。一旦 Eclipse QVTo 迁移到 Pivot OCL,您应该会看到您预期的行为。)