如何通过EML结合复杂比较规则合并项目模型?
EML模型合并规则修改问题
问题描述
我正在使用EML和Epsilon Language Workbench,将两个基于元模型MM1和MM2的项目模型合并到第三个目标元模型中。目前可基于元素名称实现简单合并,但需要一套更复杂的规则:根据特定条件将第二个模型(M2)中的任务分配给第一个模型(M1)中的人员。
期望结果
对于M1中的每个Person实例p,仅当p在M1中已参与超过2个任务时,才将M2中的Task t分配给p。
当前尝试
已创建Epsilon程序(program.eml和program.ecl)执行合并,使用Epsilon playground测试,示例改编自游乐场标准示例。
元模型
- MM1(left.emf)
- MM2(right.emf)
- 目标元模型(target.emf)
模型
- M1 (left.flexmi) - M2 (right.flexmi)
Epsilon程序
- program.eml - program.ecl
元模型1 - MM1(left.emf)
@namespace(uri="psl", prefix="") package psl; class Project { attr String title; attr String description; val Task[*] tasks; @diagram(direction="right") val Person[*] people; } class Task { attr String title; attr int start; attr int duration; @diagram(direction="right") val Effort[*] effort; } class Person { attr String name; } class Effort { @diagram(direction="up") ref Person person; attr int percentage = 100; }
元模型2 - MM2(right.emf)
@namespace(uri="psl", prefix="") package psl; class Project { attr String title; attr String description; val Task[*] tasks; @diagram(direction="right") val Person[*] people; } class Task { attr String title; attr int start; attr int duration; @diagram(direction="right") val Effort[*] effort; } class Person { attr String name; } class Effort { @diagram(direction="up") ref Person person; attr int percentage = 100; }
模型1 - M1(left.flexmi)
<?nsuri psl?> <project title="ACME"> <person name="Alice"/> <person name="Bob"/> <task title="Analysis" start="1" dur="3"> <effort person="Alice"/> </task> <task title="Design" start="4" dur="6"> <effort person="Bob"/> </task> <task title="Implementation" start="7" dur="3"> <effort person="Bob" perc="50"/> <effort person="Alice" perc="50"/> </task> </project>
模型2 - M2(right.flexmi)
<?nsuri psl?> <project title="ACME"> <person name="Alice"/> <person name="Bob"/> <task title="Testing" start="10" dur="3"> <effort person="Alice" perc="50"/> </task> </project>
目标元模型(target.emf)
package psl; class Project { attr String title; attr String description; val Task[*] tasks; @diagram(direction="right") val Person[*] people; } class Task { attr String title; attr int start; attr int duration; @diagram(direction="right") val Effort[*] effort; } class Person { attr String name; } class Effort { @diagram(direction="up") ref Person person; attr int percentage = 100; }
program.eml
// This EML program merges two // project plan models as follows: // - Persons are merged based on name // - Tasks are not merged // Matched projects are merged // into a single project rule ProjectWithProject merge l : Left!Project with r : Right!Project into m : Merged!Project { m.title = l.title; m.people ::= l.people + r.people; m.tasks ::= l.tasks + r.tasks; } // Matched persons are merged // into a single person rule PersonWithPerson merge l : Left!Person with r : Right!Person into m : Merged!Person { m.name = l.name; } // Tasks are not merged // They are copied from the left // and the right model to the // merged model rule TaskWithTask transform s : Source!Task to t : Target!Task { t.title = s.title; t.start = s.start; t.duration = s.duration; t.effort ::= s.effort; } //merge efforts in the task ONLY when the people in the right worked in at least one task (i.e. has effort) in the left rule EffortWithEffort merge l : Left!Effort with r : Right!Effort into m : Merged!Effort { m.person ::= l.person; m.percentage = l.percentage; } // Persons and Efforts found in only one of the // two models are copied across // to the merged model rule Person2Person transform s : Source!Person to t : Target!Person { t.name = s.name; } rule Effort2Effort transform s : Source!Effort to t : Target!Effort { t.person ::= s.person; t.percentage = s.percentage; }
program.ecl
// We match persons by name rule PersonWithPerson match l : Left!Person with r : Right!Person { compare: l.name = r.name } rule EffortWithEffort match l : Left!Person with r : Right!Person { compare: l.tasks->collect(e | e.effort) ->flatten() ->excluding(l) ->collect(e | e.effort) ->flatten() ->count(r) >= 1 } // We expect only one project // in each model and therefore // we match them unconditionally rule ProjectWithProject match l : Left!Project with r : Right!Project { compare: true }
修改方案
1. 修正program.ecl中的Effort匹配规则
当前EffortWithEffort规则匹配逻辑错误,需改为匹配左右两边对应同一Person的Effort:
rule EffortWithEffort match l : Left!Effort with r : Right!Effort { compare: l.person.name = r.person.name }
2. 修改program.eml中的M2任务Effort过滤逻辑
通过修改TaskWithTask规则,对M2的Task进行Effort过滤,仅保留符合条件的分配:
rule TaskWithTask transform s : Source!Task to t : Target!Task { t.title = s.title; t.start = s.start; t.duration = s.duration; // 区分处理M1和M2的Task if (s.isTypeOf(Right!Task)) { // 仅保留M1中参与任务数>2的Person对应的Effort t.effort ::= s.effort->select(e | Left!Person.allInstances()->select(p | p.name = e.person.name) ->first().eContainer().tasks->select(task | task.effort.person.name = e.person.name)->size() > 2 ); } else { // M1的Task直接复制所有Effort t.effort ::= s.effort; } }
3. 优化Effort合并规则(可选)
若需合并同一Person在同一任务中的Effort,调整EffortWithEffort规则:
rule EffortWithEffort merge l : Left!Effort with r : Right!Effort into m : Merged!Effort { m.person ::= l.person; // 合并百分比(按需调整逻辑) m.percentage = l.percentage + r.percentage; }
说明
- 核心逻辑:通过
Left!Person.allInstances()定位M1中的对应Person,统计其参与的任务数量,只有当数量超过2时才保留M2中的Effort分配。 - 若需求为“至少参与2个任务”,将规则中的
>2改为>=2即可。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

