ATL Transformations

Advanced Rule Patterns

Explore advanced ATL rule patterns including lazy rules, called rules, and imperative sections.

In this tutorial, you’ll learn when and how to use different rule types for sophisticated transformation scenarios.

You’ll discover how lazy rules enable on-demand element creation and how called rules provide explicit control over transformation flow.

40 mins Estimated Time

Section 1

Lazy Rules

Lazy rules create target elements only when explicitly requested. Unlike matched rules that automatically apply to all matching source elements, lazy rules provide on-demand element creation.

Lazy rules are defined with the lazy rule keyword and invoked using thisModule.resolveLazy or through bindings.

Step 1

Create a metamodel with optional relationships.

This metamodel defines Project, Task, and Resource classes with various relationships.

Project.ecore
1<?xml version="1.0" encoding="UTF-8"?>
2<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="Project" nsURI="http://www.example.org/project" nsPrefix="proj">
4 <eClassifiers xsi:type="ecore:EClass" name="Project">
5 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
6 <eStructuralFeatures xsi:type="ecore:EReference" name="tasks" upperBound="-1" eType="#//Task" containment="true"/>
7 <eStructuralFeatures xsi:type="ecore:EReference" name="resources" upperBound="-1" eType="#//Resource" containment="true"/>
8 </eClassifiers>
9 <eClassifiers xsi:type="ecore:EClass" name="Task">
10 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
11 <eStructuralFeatures xsi:type="ecore:EAttribute" name="priority" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"/>
12 <eStructuralFeatures xsi:type="ecore:EAttribute" name="completed" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
13 </eClassifiers>
14 <eClassifiers xsi:type="ecore:EClass" name="Resource">
15 <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
16 <eStructuralFeatures xsi:type="ecore:EAttribute" name="type" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
17 </eClassifiers>
18</ecore:EPackage>

Step 2

Define a basic lazy rule.

Lazy rules only execute when explicitly called, useful for optional or conditional transformations.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Lazy rule: creates task summaries only when needed
8lazy rule TaskSummary {
9 from
10 s: Project!Task
11 to
12 t: Report!Summary (
13 title <- s.name,
14 status <- if s.completed then 'Done' else 'Pending' endif
15 )
16}

Step 3

Call a lazy rule from a matched rule.

Use thisModule.resolveLazy to invoke lazy rules selectively.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Lazy rule: creates task summaries only when needed
8lazy rule TaskSummary {
9 from
10 s: Project!Task
11 to
12 t: Report!Summary (
13 title <- s.name,
14 status <- if s.completed then 'Done' else 'Pending' endif
15 )
16}
17
18-- Matched rule that calls lazy rule for high-priority tasks
19rule HighPriorityProject {
20 from
21 s: Project!Project
22 to
23 t: Report!Document (
24 name <- s.name,
25 summaries <- s.tasks
26 ->select(task | task.priority > 5)
27 ->collect(task | thisModule.TaskSummary(task))
28 )
29}

Step 4

Create multiple lazy rules.

Different lazy rules handle different transformation scenarios on demand.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Lazy rule 1: creates brief task summaries
8lazy rule BriefTaskSummary {
9 from
10 s: Project!Task
11 to
12 t: Report!Summary (
13 title <- s.name,
14 status <- if s.completed then 'Done' else 'Pending' endif
15 )
16}
17
18-- Lazy rule 2: creates detailed task summaries
19lazy rule DetailedTaskSummary {
20 from
21 s: Project!Task
22 to
23 t: Report!DetailedSummary (
24 title <- s.name,
25 status <- if s.completed then 'Done' else 'Pending' endif,
26 priority <- s.priority,
27 description <- 'Priority ' + s.priority.toString() + ' task'
28 )
29}

Section 2

Called Rules and Imperative Sections

Called rules are explicitly invoked procedures that can create multiple target elements and execute imperative code. They provide procedural control flow within declarative transformations.

The do block enables imperative operations like assignments, loops, and conditional execution.

Step 1

Define a called rule.

Called rules are invoked explicitly and can accept parameters.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Called rule: explicitly invoked to create status reports
8rule CreateStatusReport(proj : Project!Project, taskCount : Integer) {
9 to
10 report: Report!StatusReport (
11 projectName <- proj.name,
12 totalTasks <- taskCount
13 )
14 do {
15 report;
16 }
17}

Step 2

Add an imperative do block.

The do block contains imperative code that executes after target element creation.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Rule with do block for imperative operations
8rule ProjectReport {
9 from
10 s: Project!Project
11 to
12 t: Report!Document (
13 name <- s.name
14 )
15 do {
16 -- Imperative code: calculate completion percentage
17 let completedCount : Integer = s.tasks->select(task | task.completed)->size() in
18 let totalCount : Integer = s.tasks->size() in
19 if totalCount > 0 then
20 t.completionRate <- (completedCount * 100) / totalCount
21 else
22 t.completionRate <- 0
23 endif;
24 }
25}

Step 3

Combine called rules with matched rules.

Matched rules invoke called rules to handle complex transformation scenarios.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Lazy rule: creates task summaries
8lazy rule TaskSummary {
9 from
10 s: Project!Task
11 to
12 t: Report!Summary (
13 title <- s.name,
14 status <- if s.completed then 'Done' else 'Pending' endif
15 )
16}
17
18-- Called rule: creates detailed reports
19rule CreateDetailedReport(proj : Project!Project) {
20 to
21 report: Report!DetailedDocument (
22 projectName <- proj.name,
23 taskCount <- proj.tasks->size()
24 )
25 do {
26 report;
27 }
28}
29
30-- Matched rule combining lazy and called rules
31rule MainProjectReport {
32 from
33 s: Project!Project
34 to
35 t: Report!Document (
36 name <- s.name,
37 summaries <- s.tasks->collect(task | thisModule.TaskSummary(task))
38 )
39 do {
40 -- Call the called rule to create additional report
41 thisModule.CreateDetailedReport(s);
42 }
43}

Step 4

Build a complete transformation with advanced patterns.

The complete transformation demonstrates lazy rules, called rules, and imperative sections working together.

ProjectTransform.atl
1-- @path Project=/Project/Project.ecore
2-- @path Report=/Report/Report.ecore
3
4module ProjectTransform;
5create OUT: Report from IN: Project;
6
7-- Helper: calculate completion percentage
8helper context Project!Project def: completionPercentage(): Integer =
9 let completedCount : Integer = self.tasks->select(t | t.completed)->size() in
10 let totalCount : Integer = self.tasks->size() in
11 if totalCount > 0 then
12 (completedCount * 100) / totalCount
13 else
14 0
15 endif;
16
17-- Lazy rule: creates task summaries on demand
18lazy rule TaskSummary {
19 from
20 s: Project!Task
21 to
22 t: Report!Summary (
23 title <- s.name,
24 status <- if s.completed then 'Done' else 'Pending' endif,
25 priorityLevel <- s.priority
26 )
27}
28
29-- Lazy rule: creates resource listings
30lazy rule ResourceListing {
31 from
32 s: Project!Resource
33 to
34 t: Report!ResourceItem (
35 name <- s.name,
36 category <- s.type
37 )
38}
39
40-- Called rule: creates executive summaries
41rule CreateExecutiveSummary(proj : Project!Project) {
42 to
43 summary: Report!ExecutiveSummary (
44 projectName <- proj.name,
45 completion <- proj.completionPercentage(),
46 taskCount <- proj.tasks->size(),
47 resourceCount <- proj.resources->size()
48 )
49 do {
50 summary;
51 }
52}
53
54-- Main matched rule combining all patterns
55rule ProjectToReport {
56 from
57 s: Project!Project
58 to
59 t: Report!Document (
60 name <- s.name,
61 completionRate <- s.completionPercentage(),
62 taskSummaries <- s.tasks
63 ->select(task | task.priority >= 3)
64 ->collect(task | thisModule.TaskSummary(task)),
65 resources <- s.resources
66 ->collect(res | thisModule.ResourceListing(res))
67 )
68 do {
69 -- Create executive summary via called rule
70 thisModule.CreateExecutiveSummary(s);
71
72 -- Add completion status message
73 if s.completionPercentage() = 100 then
74 t.statusMessage <- 'Project complete!'
75 else
76 t.statusMessage <- 'Project in progress: ' +
77 s.completionPercentage().toString() + '% complete'
78 endif;
79 }
80}

Check Your Understanding

Question 1 of 3

When should you use a lazy rule instead of a matched rule?

Question 2 of 3

What is the purpose of a called rule?

Question 3 of 3

What can you do in a do block that you can’t do in bindings?

OCL Expressions Deep Dive

Master the Object Constraint Language (OCL) for expressing complex queries and constraints in ATL transformations.