-
Notifications
You must be signed in to change notification settings - Fork 0
QVT
Documents: http://redpanda.nl/BEP_P.J.Barendrecht.pdf https://help.eclipse.org/neon/topic/org.eclipse.m2m.qvt.oml.doc/references/M2M-QVTO.pdf http://software.imdea.org/OCL2014/papers/ocl2014_Kraas
QVT stands for "Query, View, Transformation", and a it's a set of languages made to describe transformation from one model to another. Is defined as a three-part structure: The first is named Query, because they can be applied to source model. The second is View, and it's a description of how the target model should look like. The third is transformation, and it's where the results of the queries are projected on the view of the target model.
QVT consists of three diferentes domain-specific languages. Two of them are declarative (Relations and Core), with a description of what to investigate and the implementation being left to and interpreter, and the other one is imperative (Operational Mappings), which is a implementation of transformation instructions.
In this set of languages, the Operational Mappings represent the most abstract layer. It can be programmed and then it will be transformed to Relations language and then to the Core language, or directly to the Core language. Also, It is possible to code complex algorithms in any supported programming language thanks to Blackbox implementation.
There are two implementations of QVTo (Operational Mappings for QVT): Eclipse M2M and SmartQVT.
There are papers with information about the language and complete description of how to code with it, and some other documents about QVTo and transformation of a model to a simpler one.
#QVTo
QVTo is an implementation of the QVT language in eclipse. It can be installed as a plugin, including useful examples with it.
The flow of programming with QVTO usually starts with a modeltype definition. This defines what meta-models are going to be used. These meta-models can be located locally, and thus the definition will be like this:
modeltype MM1 uses "platform:/resource/MM1ToMM2/transforms/MM1.ecore"
Or can be registered in Eclipse, in which case it will need to be defined with a URI, like this:
modeltype UML uses 'http://www.eclipse.org/uml2/5.0.0/UML';
In these cases, either MM1 or UML are alias to reference the metamodel. These alias are usually used in all the program.
After the modeltype definition, is necessary to define the transformation, with the following syntax:
transformation UML1toUML2(in uml1: UML1, out UML2);
The alias uml1 it's not stricly necessary, but can be pretty helpful to use it on the main function, which is the function that really does the transformation and it's defined like this:
main() {
uml1.rootObjects()[UML1::Model]->map model2Model();
//uml1.rootObjects()[UML1::Class]->map class2class();
uml1.objectsOfType(UML1::Class) -> map class2class();
}
The function rootObjects() return a set of the root objects. This is usually only the model, the first element in the hierarchy. It's a good way to start if the developer is planning to transform a known model with a lot of nested elements, because you can map the elements that depends of other elements, like in the example of the QVTo plugin, SimpleUMLtoRDB. In our case, we need to transform all the elements of a kind, so the function objectsOfType(T:Type) is better to us.
The elements are mapped into mapping functions that associate the source model elements with the target model elements. These functions are defined like this:
mapping UML1::Class::class2class(): UML2::Class {
name := self.name;
var relationships3 := self.getRelationships();
relationships3 -> forEach(r3){
//log(r3.toString());
};
}
UML1::Class is the type of element (Class of the metamodel UML1) of the input and UML2::Class represents the output of the function.
The code inside the function applies to every element of the source model and the related element of the target model, being the self the reference to the source object and being the reference to the target object implicit.
The mapping function can be applied with pre-conditions, where the mapping will only occur if the condition is conformed. Is defined like this:
mapping UML1::Class::class2class(): UML2::Class
when{self.name.startsWith("C")}
{
name := self.name;
var relationships3 := self.getRelationships();
relationships3 -> forEach(r3){
//log(r3.toString());
};
}
Query functions can be used as classic functions: It's called with an input and it returns an output, not doing any mapping, but doing an operation with the input. This is an example:
query UML1::Element::countRelationships() : Integer {
var i : Integer := 0;
self.getRelationships() -> forEach(relationship){
i := i+1;
};
return i;
}
This query function return the number of relationships that an element has.