To use modTransf you will need:
And for this tutorial you will need:Our objective in this tutorial will be to transform a graph described in UML into our own metamodel while introducing modTransf concepts.
Here is the example of a graph metamodel, we will be using it though our
tutorial.
The following will be used for our example.
Some programs provide means to describe a profile and work with it. For instance Enterprise Architect allows one to create a profile and put it inside a toolbox. Since our example is rather simple, we will not be using such a program; we will simply apply the stereotypes by hand.
XMI is an XML DTD to save MOF-based metamodels. One consequence is that UML models can be saved in XMI format. The main idea of XMI is to put an identifier to every element (xmi.id attribute) which allows to put references to elements (xmi.idref attribute).
XMI is supposed to make it easy share models between applications. Still, there are so many possible combinations of XMI (1.0, 1.1, 1.2) and UML (1.1, 1.3, 1.4, 2.0) that sometimes importing a model does not work. When working with modTransf you should stick to XMI 1.2 and UML 1.4.
With all this modTransf will generate the output model. It can also
write a log file if you ask for this in the transformation rules.
In our case, we will use a MOF-based metamodel. The helper already exists and is based on MDR. MDR is an implementation of JMI, a specification for accessing metadata of MOF-based metamodels. To make it short, we don't have to write any helper, we can concentrate on writting the metamodels and the transformation rules.
A metamodel for UML is provided with modTransf. This will
greatly help us since we will only have to write the output metamodel.
Through our tutorial we will:
The metamodel must be exported to XMI 1.2 without any diagram data. We save it as model/graph-metamodel-uml.xmi.
The next step is to transform the UML into MOF using uml2mof. We do this by editing build.xml and creating a new target (copy-paste of uml2mof one):
<target name="graph-uml2mof" depends="compile.examples"
description="Generate mof from uml">
<java classname="org.netbeans.lib.jmi.uml2mof.Main" fork="yes">
<classpath path="."/>
<classpath refid="run.example.classpath"/>
<classpath refid="mdr.metamodels"/>
<arg line="model/graph-metamodel-uml.xmi"/>
<arg line="model/graph-metamodel-mof.xmi"/>
</java>
</target>
We run this target with ant:
ant graph-uml2mofThis should generate the mof file.
When this is done, we have to add the metamodel to the list in metamodel-config.xml:
<metamodel publicId="graph"
classname="modTransf.model.factory.MDRModelFactory"
xmiFile="model/graph-metamodel-mof.xmi"
mainExtent="Graph"
engineAdaptor="modTransf.model.jmi.JmiModelHelper"
/>
This states that the metamodel helper is JmiModelHelper
and that the name of the outermost package is Graph. The
publicId is the name of the metamodel as it will be
referenced by from build.xml.
Now that we have an input model, its metamodel (which is UML) and an output metamodel, we only lack the transformation rules.
The transformation is done by manipulating concepts, and properties inside the concepts.
Here are the transformation rules to be put inside rules/uml2graph.xml.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE transformation PUBLIC "-//Model Transformation XML Rules 3.0//EN"
"http://www.lifl.fr/~dumoulin/mdaTransf/resources/modtransf-rules_3_0.dtd">
<transformation models="uml14,graph" defaultTargets="graph">
<ruleSet>
<topRule name="start">
<domain model="uml14" varName="c1" type="Core.Class">
<primitiveProperty name="name" varName="n" type="String"/>
<collProperty name="associatedElements" varName="nodesSrc"/>
<collProperty name="stereotypes" varName="stereotypes"/>
<enterGuards>
<script script="stereotypes.contains('start')" language="groovy"/>
</enterGuards>
</domain>
<domain model="graph" varName="c2" type="Start">
<primitiveProperty name="name" varName="n" type="String"/>
<collProperty name="next" varName="nodesDst"/>
</domain>
<actions>
<call rule="node">
<arg expr="nodesSrc"/>
<arg expr="nodesDst"/>
</call>
</actions>
</topRule>
<rule name="node">
<domain model="uml14" varName="c1" type="Core.Class">
<primitiveProperty name="name" varName="n" type="String"/>
<collProperty name="stereotypes" varName="stereotypes"/>
<enterGuards>
<script script="stereotypes.contains('node')" language="groovy"/>
</enterGuards>
</domain>
<domain model="graph" varName="c2" type="Node">
<primitiveProperty name="name" varName="n" type="String"/>
</domain>
</rule>
</ruleSet>
</transformation>
The file is divided in a set of rules. Each one is responsible for transforming one concept. In our example we have two concepts (start and node) and therefore we have two rules.
Let us have a closer look at the second rule. The <domain> tags allow to select the concepts. The following means to select UML classes with the <<node>> stereotype. The value of the name attribute is stored inside a variable named n.
<domain model="uml14" varName="c1" type="Core.Class">
<primitiveProperty name="name" varName="n" type="String"/>
<collProperty name="stereotypes" varName="stereotypes"/>
<enterGuards>
<script script="stereotypes.contains('node')" language="groovy"/>
</enterGuards>
</domain>
Likewise, the following means to select Node objects and put the name in an n variable.
<domain model="graph" varName="c2" type="Node">
<primitiveProperty name="name" varName="n" type="String"/>
</domain>
When the same variable name is used for input and output concepts, the attribute value is to be copied from the input to the output concept. In our example, this is how the name from the UML model will be copied to the graph model.
<target name="graph" depends="compile.examples"
description="Transform an UML file into a graph">
<java classname="modTransf.example.engine.Transform" fork="yes">
<classpath path="."/>
<classpath refid="run.example.classpath"/>
<arg line="-mmFile metamodel-config.xml"/>
<arg line="-src model/graph-model-uml.xmi uml14"/>
<arg line="-dst model/graph-model.xmi graph"/>
<arg line="-r rules/uml2graph.xml"/>
</java>
</target>
First we do a clean-up. Run this command whenever you change a
metamodel. This is beacause MDR uses some cache files that depend
on the metamodels.
ant clean
Finally we can launch the transformation:
ant graph
This is the result we get (model/graph-model.xmi):
<?xml version = '1.0' encoding = 'ISO-8859-1' ?>
<XMI xmi.version = '1.2' timestamp = 'Wed Jun 22 16:50:54 CEST 2005'>
<XMI.header>
<XMI.documentation>
<XMI.exporter>Netbeans XMI Writer</XMI.exporter>
<XMI.exporterVersion>1.0</XMI.exporterVersion>
</XMI.documentation>
</XMI.header>
<XMI.content>
<Graph.Start xmi.id = 'a1' name = 'Node1'>
<Graph.Node.Next>
<Graph.Node xmi.idref = 'a2'/>
<Graph.Node xmi.idref = 'a3'/>
</Graph.Node.Next>
</Graph.Start>
<Graph.Node xmi.id = 'a2' name = 'Node2'/>
<Graph.Node xmi.id = 'a3' name = 'Node3'/>
</XMI.content>
</XMI>
As expected we have one Start object and two Node
objects. The Next collection of links is stored within the
Start object with references to the Node objects.
Some other elements which may generate such warnings are:
Do not forget to run ant clean whenever you change a metamodel file.
You might get this error when you have errors in the types definition. Your types must be defined as data types. In Poseidon, a <<dataType>> stereotyped class will not do, you have to use the appropriate icon.