ATL Transformations

OCL Expressions Deep Dive

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

In this tutorial, you’ll explore advanced OCL expressions including navigation, type operations, and sophisticated collection queries.

You’ll learn how to write precise, expressive transformations using OCL’s powerful expression language.

45 mins Estimated Time

Section 2

Advanced OCL Operations

OCL provides sophisticated operations for strings, numbers, and complex collection queries. Understanding these operations enables precise transformation logic.

Operations include string manipulation, numeric calculations, collection aggregation, and logical operations.

Step 1

Use string operations.

OCL string operations include concat, substring, toUpper, toLower, and size.

OrganisationTransform.atl
1-- @path Organisation=/Organisation/Organisation.ecore
2-- @path Report=/Report/Report.ecore
3
4module OrganisationTransform;
5create OUT: Report from IN: Organisation;
6
7-- Helper with string operations
8helper context Organisation!Employee def: formattedName(): String =
9 'Employee: ' + self.name.toUpper();
10
11-- Helper checking string content
12helper context Organisation!Developer def: isPythonDeveloper(): Boolean =
13 self.programmingLanguage = 'Python';

Step 2

Apply numeric operations and comparisons.

Numeric operations include arithmetic, comparison, and aggregate functions like sum, min, and max.

OrganisationTransform.atl
1-- @path Organisation=/Organisation/Organisation.ecore
2-- @path Report=/Report/Report.ecore
3
4module OrganisationTransform;
5create OUT: Report from IN: Organisation;
6
7-- Helper with numeric operations
8helper context Organisation!Manager def: isLargeTeam(): Boolean =
9 self.teamSize > 5;
10
11-- Helper with aggregate functions
12helper context Organisation!Department def: totalTeamSize(): Integer =
13 self.employees
14 ->select(e | e.oclIsTypeOf(Organisation!Manager))
15 ->collect(e | e.oclAsType(Organisation!Manager).teamSize)
16 ->sum();

Step 3

Combine complex collection operations.

Chain collection operations and use advanced queries like flatten, union, and intersection.

OrganisationTransform.atl
1-- @path Organisation=/Organisation/Organisation.ecore
2-- @path Report=/Report/Report.ecore
3
4module OrganisationTransform;
5create OUT: Report from IN: Organisation;
6
7-- Helper with complex collection operations
8helper context Organisation!Department def: managerNames: Sequence(String) =
9 self.employees
10 ->select(e | e.oclIsTypeOf(Organisation!Manager))
11 ->collect(e | e.name)
12 ->sortedBy(name | name);
13
14-- Helper checking collection properties
15helper context Organisation!Department def: hasSwiftDevelopers(): Boolean =
16 self.employees
17 ->select(e | e.oclIsTypeOf(Organisation!Developer))
18 ->exists(e | e.oclAsType(Organisation!Developer).programmingLanguage = 'Swift');

Step 4

Build a complete transformation with advanced OCL.

The complete transformation demonstrates sophisticated OCL expressions in a practical context.

OrganisationTransform.atl
1-- @path Organisation=/Organisation/Organisation.ecore
2-- @path Report=/Report/Report.ecore
3
4module OrganisationTransform;
5create OUT: Report from IN: Organisation;
6
7-- Helper: format employee information
8helper context Organisation!Employee def: formattedInfo(): String =
9 self.name + ' (ID: ' + self.id.toString() + ')';
10
11-- Helper: get manager-specific information
12helper context Organisation!Employee def: getManagerInfo(): String =
13 if self.oclIsTypeOf(Organisation!Manager) then
14 let mgr : Organisation!Manager = self.oclAsType(Organisation!Manager) in
15 'Manager of ' + mgr.teamSize.toString() + ' people'
16 else
17 'Not a manager'
18 endif;
19
20-- Helper: get developer-specific information
21helper context Organisation!Employee def: getDeveloperInfo(): String =
22 if self.oclIsTypeOf(Organisation!Developer) then
23 let dev : Organisation!Developer = self.oclAsType(Organisation!Developer) in
24 'Develops in ' + dev.programmingLanguage
25 else
26 'Not a developer'
27 endif;
28
29-- Helper: calculate department statistics
30helper context Organisation!Department def: statistics(): String =
31 let total : Integer = self.employees->size() in
32 let managers : Integer = self.employees
33 ->select(e | e.oclIsTypeOf(Organisation!Manager))
34 ->size() in
35 let developers : Integer = self.employees
36 ->select(e | e.oclIsTypeOf(Organisation!Developer))
37 ->size() in
38 'Total: ' + total.toString() +
39 ', Managers: ' + managers.toString() +
40 ', Developers: ' + developers.toString();
41
42-- Rule: Transform department to report
43rule DepartmentToReport {
44 from
45 s: Organisation!Department
46 to
47 t: Report!DepartmentReport (
48 name <- s.name,
49 statistics <- s.statistics(),
50 employees <- s.employees
51 ->collect(e | e.formattedInfo() + ' - ' +
52 if e.oclIsTypeOf(Organisation!Manager) then
53 e.getManagerInfo()
54 else
55 if e.oclIsTypeOf(Organisation!Developer) then
56 e.getDeveloperInfo()
57 else
58 'Employee'
59 endif
60 endif)
61 )
62}

Check Your Understanding

Question 1 of 3

What is the difference between . and -> in OCL navigation?

Question 2 of 3

When should you use oclIsKindOf instead of oclIsTypeOf?

Question 3 of 3

Can you use oclAsType without checking the type first?

Debugging Transformations

Learn techniques for debugging ATL transformations and diagnosing common transformation issues.