Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

ATL/Tutorials - Create a simple ATL transformation

< ATL

This tutorial shows you how to create your first simple transformation with ATL, through a well-known basic example: Families2Persons.

Note: This tutorial is followed step by step in the ATL cheatsheet under eclipse : ATL Development > Create a simple ATL transformation. Go to the Help > Cheat Sheets... menu to find it.

Objectives

The objectives of this tutorial are to perform a transformation from a list of families to a list of persons.

On one side (the source), we have a list of families. Each family has a last name, and contains a father, a mother and a number of sons and daughters (0, 1 or more) all with a first name. We want to transform this list into a new list of persons (the target), this means that each member of the family will be a person, without differentiating parents from children, and with no link between the members of a same family (except a part of their name). In the end, we must only have a person with its full name (first name & last name), male or female.

The Families

FamiliesMetamodel.jpg

The Persons

PersonsMetamodel.jpg

In order to manage to do this, there are a few requirements.

Requirements

First of all, you will need to install ATL on eclipse. If you already have it installed, just skip this task. Otherwise, follow the few steps bellow:

  • click on Help > Install New Software...

InstallNewSoftware Menu.jpg

  • then you have to select an update site, an search for ATL on the filter field when a list of software is available
  • once you have done this, you should see a line "ATL SDK - ATLAS Transformation Language SDK" on the list below, under the Modeling node

ATLInstallation.jpg

  • check it, click Next >, and follow the instructions

You can check if ATL is installed by going to Help > About Eclipse SDK, then clicking the Installation Details button, and under the Plug-ins tab you should see several lines with ATL.

AboutEclipse.jpg

InstallationDetails.jpg

ATLPlugins.jpg

If you have any problem, please refer to the User Guide for further information about ATL installation.

Create a new ATL project

After the theory, let's start creating the project.

To create a new ATL project, you need to go to File > New > Other...

FileNewOther Menu.jpg

and then select ATL > ATL Project....

ATLProject.jpg

Then, click the Next > button. Type a name for the project (say "Families2Persons" for our example).

Families2Persons Project.jpg

The project should now appear on your projects list.

The User Guide also provides a detailled section for the creation of a new ATL project.

The metamodels

Now that our project is ready to use, we can fill it. Our first files are the representation of a family and a person, that is to say how we want to symbolize then (like a map symbolize the real world). This is called a metamodel, and it corresponds to an Ecore file.

To create the Ecore file, go to File > New > Other..., and then select Eclipse Modeling Framework > Ecore Model and click Next >.

EcoreModel.jpg

Select your Families2Persons project on the list, enter a name for the file (Families.ecore for instance), and click Finish. An empty file is added to your project. Repeat this task for the Persons.ecore metamodel.

FamiliesEcore.jpg

Now we need to fill these files.

(Note that the User Guide shows other metamodels examples.)

The Families metamodel

As we saw in the Objective part, a family has a last name, and a father, a mother, sons and daughters with a first name. That is what we need to tell to the Families.ecore file.

Open it with the default editor (Sample Ecore Model Editor). We will also need the Properties view, so if it is not already opened, you can show it by going on Window > Show View > Other..., selecting General > Properties and clicking OK.

WindowViewOther Menu.jpg

GeneralProperties.jpg

The Families.ecore file comes in the form of a tree. The root should be: "platform:/resource/Families2Persons/Families.ecore". If you expand it, there is an empty node under it.

FamiliesEcore step1.jpg

Click on it, and in the Properties view, enter "Families" in the value of the "Name" property. This node is where we are going to put everything that makes a family.

So first we create a class "Family", by right clicking on the Families node, and clicking on New Child > EClass.

FamiliesEcore step2.jpg

You can name it the same way you named the node Families above.

Then we give it an attribute (New Child > EAttribute) and name it "lastName".

FamiliesEcore step3.jpg

We want to have one and only one last name per family, so we control its multiplicity: set 1 for the lower bound (that should be set to 0 by default), and 1 for the upper bound (that should already be 1). These bounds can be set the same way than the name, but on the Lower Bound and Upper Bound properties. We can specify a type for this attribute, and we want it to be a string. So in the EType property, search for the EString type.

FamiliesEcore step4.jpg

At this moment, we have a family with its last name. Now we need members for this family. Therefore we are going to create another class (as we created the Family class): "Member". This class will be a Families node's child, as the other Family class. These members have a first name, so we add an attribute "firstName" of type EString, and again a member has one and only one first name (see above if you don't remember how to create an attribute, name it, give it a type and change its multiplicity).

Now we have to make the links between the family and the members. For this purpose, you have to create children of the Family of the type EReference. Name these references "father", "mother", "sons" and "daughters". They will have the EType Member. About the multiplicity, we have one father and one mother for one family (so upper and lower bounds set to 1), but we can have as many sons and daughters as we want, even 0 (so lower bound set to 0, and upper bound set to -1, which means *). And at last, put their Containment property to true so that they can contain members.

Once these attributes are created and configured, we do the same for the Member class. It also needs references towards the Family class. Just add 4 EReferences to the Member class: "familyFather", "familyMother", "familySon" and "familyDaughter" with EType Family. This time, each reference should have its multiplicity set to 0..1 (it is by default), because a member is either a father, or a mother, or a son, or a daughter, so the reference that is defined for a member shows its role in the family. Then, in order to tell which member refer to which family member, set their EOpposite field to their reference in the Family class (for example, familyFather refers to the father reference of the Family class).

<?xml version="1.0" encoding="ISO-8859-1"?>
<xmi:XMI xmi:version="2.0"
    xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore">
  <ecore:EPackage name="Families">
    <eClassifiers xsi:type="ecore:EClass" name="Family">
      <eStructuralFeatures xsi:type="ecore:EAttribute" name="lastName" ordered="false"
          unique="false" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="father" ordered="false"
          lowerBound="1" eType="#/0/Member" containment="true" eOpposite="#/0/Member/familyFather"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="mother" ordered="false"
          lowerBound="1" eType="#/0/Member" containment="true" eOpposite="#/0/Member/familyMother"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="sons" ordered="false"
          upperBound="-1" eType="#/0/Member" containment="true" eOpposite="#/0/Member/familySon"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="daughters" ordered="false"
          upperBound="-1" eType="#/0/Member" containment="true" eOpposite="#/0/Member/familyDaughter"/>
    </eClassifiers>
    <eClassifiers xsi:type="ecore:EClass" name="Member">
      <eStructuralFeatures xsi:type="ecore:EAttribute" name="firstName" ordered="false"
          unique="false" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="familyFather" ordered="false"
          eType="#/0/Family" eOpposite="#/0/Family/father"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="familyMother" ordered="false"
          eType="#/0/Family" eOpposite="#/0/Family/mother"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="familySon" ordered="false"
          eType="#/0/Family" eOpposite="#/0/Family/sons"/>
      <eStructuralFeatures xsi:type="ecore:EReference" name="familyDaughter" ordered="false"
          eType="#/0/Family" eOpposite="#/0/Family/daughters"/>
    </eClassifiers>
  </ecore:EPackage>
</xmi:XMI>

FamiliesEcore step5.jpg

And here we are with the metamodel for our families!

The Persons metamodel

The principle is the same for the target metamodel, in less complicated. Open the Persons.ecore file, and name the root child node to "Persons". Then add it a class "Person", with one attribute: "fullName" of EType EString and multiplicity 1..1.

Then set the Abstract attribute of the Person class to "true". We need to do this because we won't directly implement this class, but two other subclasses: "Male" and "Female", according to who was the person in the family, a man or a woman. Create these two classes at the same level than Person. We make them subclasses of Person by setting their ESuper Types property to Person.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xmi:XMI xmi:version="2.0"
    xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore">
  <ecore:EPackage name="Persons">
    <eClassifiers xsi:type="ecore:EClass" name="Person" abstract="true">
      <eStructuralFeatures xsi:type="ecore:EAttribute" name="fullName" ordered="false"
          unique="false" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
    </eClassifiers>
    <eClassifiers xsi:type="ecore:EClass" name="Male" eSuperTypes="#/0/Person"/>
    <eClassifiers xsi:type="ecore:EClass" name="Female" eSuperTypes="#/0/Person"/>
  </ecore:EPackage>
</xmi:XMI>

PersonsEcore.jpg

And our second metamodel is ready!

The ATL transformation code

Now that we have represented what we have (Families, the source) and what we want to obtain (Persons, the target), we can concentrate on the core of the transformation: the ATL code. This code is going to match a part of the source with a part of the target.

What we want in our example, is to take each member of each family, and transform him into a person. That implies melting his first and last name to have a full name, defining whether it's a man or a woman, and copy these pieces of information into a Person object.

We first need a file to put this code into. So create a new ATL file, by going to File > New > Other..., and then ATL > ATL File.

ATLFile.jpg

Name it "Families2Persons.atl" for instance, don't forget to select your project, and then click Finish.

Families2PersonsATL.jpg

If you are asked to open the ATL perspective, click Yes.

When you open the file, an error is marked (we will see how to fix it below), and it contains a single line:

module Families2Persons;

First we add two lines at the top of the file, one for each metamodel, so that the editor can use the auto-completion and documentation when we type in some code concerning the two metamodels:

-- @path Families=/Families2Persons/Families.ecore
-- @path Persons=/Families2Persons/Persons.ecore

Then we tell ATL that we have families in and we want persons out (this should fix the error):

create OUT: Persons from IN: Families;

Now we must define some helpers:

helper context Families!Member def: isFemale(): Boolean =
	if not self.familyMother.oclIsUndefined() then
		true
	else
		if not self.familyDaughter.oclIsUndefined() then
			true
		else
			false
		endif
	endif;
helper context Families!Member def: familyName: String =
	if not self.familyFather.oclIsUndefined() then
		self.familyFather.lastName
	else
		if not self.familyMother.oclIsUndefined() then
			self.familyMother.lastName
		else
			if not self.familySon.oclIsUndefined() then
				self.familySon.lastName
			else
				self.familyDaughter.lastName
			endif
		endif
	endif;

These helpers will be used in the rules that we will see below.

  • The first one is called on a member of a family (context Families!Member), gives us a boolean (: Boolean), and tells us whether the member is a female or not, by verifying if the familyDaughter or familyMother reference is defined or not.
  • The second one is also called on a member of a family, this time gives us a string (: String) and returns the last name of the member. It must look for it in every reference to the family, to see which one is defined (familyFather, familyMother, familySon or familyDaughter)

And finally, we add two rules creating male and female persons from members of families:

rule Member2Male {
	from
		s: Families!Member (not s.isFemale())
	to
		t: Persons!Male (
			fullName <- s.firstName + ' ' + s.familyName
		)
}
rule Member2Female {
	from
		s: Families!Member (s.isFemale())
	to
		t: Persons!Female (
			fullName <- s.firstName + ' ' + s.familyName
		)
}

Each rule will be called on the object that respect the filter predicate in the from part. For instance, the first rule takes each member of each families (from s: Families!Member) that is not a female (using the helper we described above, not s.isFemale()). And then it creates a male person (to t: Persons!Male) and set its fullName attribute to the first name of the member followed by its last name (using the helper familyName we saw above). The principle is the same for the second rule, whereas this time it takes only the female members.


Note that the ATL editor provides syntax highlighting, and indentation much better than what you can see above. Besides, you can find help on what we saw above on the User Guide, here and here.

The sample families model file

The transformation is ready to be used, we just need a sample model to run it on. First create a file in your project in which we will put the code of the model. Go to File > New > File

FileNewFile Menu.jpg

name it "sample-Families.xmi" for instance

Sample-FamiliesXMI.jpg

and open it with a text editor. Here is some code sample:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns="Families">
	<Family lastName="March">
		<father firstName="Jim"/>
		<mother firstName="Cindy"/>
		<sons firstName="Brandon"/>
		<daughters firstName="Brenda"/>
	</Family>
	<Family lastName="Sailor">
		<father firstName="Peter"/>
		<mother firstName="Jackie"/>
		<sons firstName="David"/>
		<sons firstName="Dylan"/>
		<daughters firstName="Kelly"/>
	</Family>
</xmi:XMI>

The launch configuration

We have everything we need to make the transformation, but there is one more step before we launch it, at least the first time: we have to configure the launching.

When you are in the ATL file (Families2Persons.atl), click on Run > Run (or Ctrl+F11)

RunRun Menu.jpg

A dialog opens. Several pieces of information are already filled in: the ATL module (our transformation file, Families2Persons.atl), the metamodels (Families.ecore and Persons.ecore), but we need to complete the page.

RunConfiguration step1.jpg

The Source Models (IN:, conforms to Families) part is the model we want to transform, that is to say our sample-Families.xmi; browse the workspace to add it.

RunConfiguration step2.jpg

The Target Models (Out:, conforms to Persons) part is the model to be generated; browse the workspace to find your project and enter a name for the file (say "sample-Persons.xmi").

RunConfiguration step3.jpg

A useful option can be found in the Common tab of the page: we can save our configuration so that ATL can find it the next time we would want to run it or if the project is exported. If you check Shared file and browse within your project, you can save this configuration in a file ("Families2Persons.launch" for example).

RunConfiguration step4.jpg


You can found help on how to compile an ATL file on the User Guide, here.

Running the transformation

At last we can run the transformation by clicking Run on the configuration page. A file is then generated, named sample-Persons.xmi, and containing the list of your family members transformed into persons.

Here is what you should get if you open it with a text editor:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns="Persons">
	<Male fullName="Jim March"/>
	<Male fullName="Brandon March"/>
	<Male fullName="Peter Sailor"/>
	<Male fullName="David Sailor"/>
	<Male fullName="Dylan Sailor"/>
	<Female fullName="Cindy March"/>
	<Female fullName="Brenda March"/>
	<Female fullName="Jackie Sailor"/>
	<Female fullName="Kelly Sailor"/>
</xmi:XMI>

Running an ATL launch configuration is explained on the User Guide, here

This is the end of this basic example. Further documentation, examples, and help can be found on the ATL website: http://www.eclipse.org/atl/.

See Also

Back to the top