Transformation Modèle vers Text - univ-lille.fr
Transcript of Transformation Modèle vers Text - univ-lille.fr
Cedric Dumoulin
Presentation basé sur : Model-to-Text Transformations Mr M.LotfiNia
https://fr.slideshare.net/majidln/modeltotext-transformation-language-chapter-9-j-cabot-model-driven-engineering
MODEL-TO-TEXT TRANSFORMATIONS Teaching material for the book Model-Driven Software
Engineering in Practice by Marco Brambilla, Jordi Cabot, Manuel Wimmer. Morgan & Claypool, USA, 2012
https://edisciplinas.usp.br/pluginfile.php/4138138/mod_resource/content/1/Aula12-MDSE-book-slides-chapter9.pdf
Génération de texte avec Acceleo Cedric Dumoulin
Présentation de PJE
Plan Bases de la génération de code Dirigé par les Modèles
Génération de code avec des langages de programmation
Génération de code avec des langages de transformation M2T
Langages de transformation basé sur des templates
Avantages des langages de transformation M2T
Outils
Acceleo
Maitriser la génération de code
Génération de code La génération de code existe depuis toujours en génie logiciel
Objectif principal de l’IDM:
Modéliser le système, puis , puis l’exécuter hors du modèle
dans un compilateur, la génération de code est le processus de transformation de code source en code machine
Ex, de code Java en Bytecode
En IDM, la génération de code est la transformation de Modèles vers du texte (code, documents, XMI…)
Arbre syntaxique
abstraitcodeGénérateur de code
Génération de code et IDM En IDM, la génération de code est le processus de transformer des
modèles vers du code source
La génération de code IDM
produit du code pour les compilateurs existants pour les langages de programmation
Génération de code et IDM
s’effectue généralement sur les modèles de bas niveau d’abstraction
pour produire du code exécutable
Modèle 1.1 Modèle 1.2
codeJava
code.net
codeC++
Modèle 2
Modèle 2
ModèleJava
Modèle.net
ModèleC++
transfo
transfotransfo
transfotransfotransfo
générationgénérationgénération
vérificationsimulations, …
haut niveau
basniveau
Questions Basiques Quelle quantité de code est générée ?
Quelles parties générer ?
Total ou partiel ?
Qu’est-ce qui est généré ?
Quel type de code source ?
Comment générer ?
Quels langages et quels outils utiliser pour développer un générateur de code?
Langage Généraliste ? DSL ?
Quelle quantité de code est générée ? Quelles parties du code peuvent être générées automatiquement à partir
des modèles ?
Une partie de l'application peut être complètement générée
Une autre partie peut être complètement développée manuellement.
Une partie peut être partiellement générée
et certaines parties manquantes doivent être complétées manuellement
Il n’y a pas de règles. Cela dépend de votre approche
Génération complète ou partielle ?
Génération partielle Seules certaines parties du code sont générées
Les autres parties ne sont pas touchées par le générateur de code
elles doivent être implémentées à la main
En cas de re-génération :
le générateur ne doit pas modifier les parties écrites à la main
Il faut « protéger » ou identifier ces parties
Existe souvent un tag « protected » ou « not generated »
Quel type de code source générer ? M2T = Model To Text
Code de programmation
Documentation
Tests (code de tests)
Sérialisation des modèles (XMI)
Générer de préférence vers un langage de haut niveau Réutiliser les générateurs de code correspondant
Réutiliser les langages et les compilateurs existant
Moins on génère de code, le mieux s’est !
Pas de bytecode, pas d’assembleur
Il est possible de générer du code machine (bytecode, assembleur), mais : risques d’erreurs, difficile à optimiser (les compilateurs le font pour nous)
Quel type de code source générer ?
(extrait de Model-Driven Software Engineering In Practice)
Comment générer ?Implémentation d’un générateur de code Deux approches possibles :
Utiliser un langage généraliste
Java, C++…
Utiliser un langage dédié (Domain Specific Language - DSL)
approche plus IDM
Le langage dédié est un modèle conforme à un métamodèle
Exemple de code généré(extrait de Model-Driven Software Engineering In Practice) MiniUML vers MiniJava
M2
M1
Générateur de code écrit avec des langages généralistes Le générateur de code
peut être implémenté dans un Langage de programmation généraliste Java, C++, Kotlin, C #,…
On utilise l’API de manipulation du modèle
pour charger le modèle d’entré
pour manipuler le modèle, et générer le code
L’API du modèle facilite sa manipulation
Elle est généré automatiquement à partir des métamodèles Dans EMF: .ecore -> .genmodel -> code Java
Sérialisation/dé-sérialisation prédéfini pour le modèle
Instanciation des concept du métamodèle
Prise en charge prête à l'emploi dans EMF
API de manipulation du modèle
(extrait de Model-Driven Software Engineering In Practice)
API de manipulation du modèle Pour chaque concept du métamodèle :
une interface et son implémentation sont générées
Pour chaque attributs et références :
Un getter et un setter sont générés
L’API permet :
De lire et écrire des instances du modèle (sérialisation/dé-sérialisation)
De manipuler le modèle
De créer des instances des concepts
Phases de la génération de codeavec Java Lire les modèles
Charger le fichier XMI et créer le graphe d’objet en mémoire
Traiter les modèles et produire du code
Traiter les modèles en parcourant la structure du modèle (avec l’API)
Utiliser les informations du modèle pour produire du code
Enregistrer le code dans une chaine de caractères
Ecrire le code
Sauvegarder la chaine de caractères dans un fichier
Générateur de code écrit avec des langages généralistes
(extrait de Model-Driven Software Engineering In Practice)
Générateur de code écrit avec des langages généralistes Avantages
Le langage est connu.
Il n’est pas nécessaire d’apprendre un nouveau langage
Pas de dépendance vers des outils ou frameworks supplémentaires
Inconvénients
Mélange du code statique et du code dynamique
Pas de vue globale de la structure du code à générer
Pas de langage de requête pour interroger le modèle
Pas de fonctionnalités de base dédiées à la génération
Langages Dédiés basés sur des templates
En francais : ‘patron’
Un templates sert à générer tout type de texte
xml, code, texte, …
C’est une ébauche du texte à générer
contient des ‘trous’ à remplacer par les vrais valeurs
On ‘instancie’ un template
on spécifie les valeurs à utiliser
on obtient le texte final
TemplatesPrincipe générale
un template (fichier texte) avec des « trous » des données le moteur rempli les trous avec les données et génère le fichier de
sortie
Bonjour [name]birthday : [birthday]
Bonjour alicebirthday : 1 janvier 2000
name = ‘alice’birthday = ‘1 janvier 2000’
Mtemplate
données
résultatsname = ‘Bob’birthday = ‘6 juin 2000’
Bonjour Bobbirthday : 6 juin 2000
moteur
TemplatesPrincipe générale Le template comprend des parties
statiques – texte qui ne change pas
dynamiques – parties variables, à remplacer par une valeur venant du modèle
Le moteur
lit le template,
interroge le modèle pour extraire les données
remplace au runtime les parties dynamique par les valeurs
produit le fichier de sortie
Bonjour [name]birthday : [birthday]
statiquedynamique(méta markers)
Les templates sont typées par le métamodèle Les templates fonctionnenent pour tous les modèles conformes au
métamodèle
produitlit
métamodèled’entrée
métamodèled’entrée
conforme à
typé par templates
lit
templates
modèled’entréemodèled’entrée
codecode
code
M2
M1
Avantages des langages dédiés Bonne séparation du code statique et du code dynamique
Les templates séparent le code statique, c'est-à-dire le texte normal, du code dynamique décrit par des méta-marqueurs
Structure de sortie explicite
La structure principale du template est la structure de sortie
La logique de calcul est intégrée dans cette structure
Langage de requête déclaratif
En général, OCL est utilisé pour interroger les modèles d'entrée
Fonctionnalités de bases réutilisables
Prise en charge de la lecture dans les modèles, sérialisation du texte en fichiers,…
Moteurs de templates existants Eclipse
Jet
XPand
Acceleo – pour générer du texte à partir de modèles
JSP – pour pages web dynamiques
PHP – pour pages web dynamiques
XSLT
…
JET Java Emitter Template
Premier moteur utilisé par EMF pour générer les classes supports
JET n’est pas limité aux modèles basés sur EMF
Dans JET, chaque objet basé sur Java est transformable en texte
Les modèles JET sont transformés en code Java pur pour afin d’être exécuté
Xpand Ce langage de transformation M2T a émergé dans le projet
openArchitectureWare
Fait partie de Eclipse M2T
Propose un langage d'interrogation de modèles qui est un mélange de Java et OCL
MOFScript MOFScript is a tool for model to text transformation,
e.g., to support generation of implementation code or documentation from models.
Simple model to model transformation functionality is also supported.
Provides a metamodel-independent language
It allows to use any kind of metamodel and its instances for text generation or model to model transformation.
Traceability links can be automatically generated.
The MOFScript tool is based on EMF and Ecore as metamodelframework.
Acceleo Implémentation du standard M2T de l'OMG pour les modèles EMF
Acceleo website: http://www.eclipse.org/acceleo/
M2T Transformation standard: http://www.omg.org/spec/MOFM2T
Langage basé sur des templates
support OCL complet pour l'interrogation de modèles
outil mature utilisé dans l'industrie
Outils de développement : editeur, complétion de code, mise en couleur
debuggeur, traçabilité …
Principe générale Notion de Module
Correspond à un fichier .mtl
Déclare les métamodèle pour les modèles d’entrée
Contient un ensemble de templates
Template
Correspond au texte avec des « trous » à compléter
Un template est associé à une metaclasse particulière
Les templates peuvent s’appeler les uns les autres, ou étendre d’autres templates
Un des templates est annoté par[comment @main/]
Indique le template de départ
Concepts du langage [template] tag : Pour déclarer un template
[file] tag : Pour ouvrir et fermer un fichier dans lequel le texte est généré
[for/if] tags : structures de controles
[query] tag : pour écrire une fonction qui va interroger le modèle
[expression] tag : Calcul des valeurs qui seront insérées dans le fichier de sortie
[protected] tag : déclare une zone protégée, qui ne sera pas écrasée lors de prochaine génération de texte.
Exemple
[module generate('http://www.example.org/simpleuml')]
[query public getter(att : Attribute) : String= 'get'+att.name.toUpperFirst() /]
[query public returnStatement(type: PrimitiveType) : String= if type = 'Boolean‘ then 'return true;' else '...' endif /]
[template public javaClass(aClass : Class)][comment @main/]
[file (aClass.name.toUpperFirst()+'.java', false, 'UTF-8')]package entities;
import java.io.Serializable;
public class [aClass.name/] implements Serializable {
[for (att : Attribute | aClass.attribute) separator ('\n')][javaAttribute(att)/][/for]
[for (op : Operation | aClass.operation) separator ('\n')][javaMethod(op)/][/for]}[/file][/template]
[template public javaAttribute(att : Attribute)]private [att.type/] [att.name/];public [att.type/] [att.getter()/]() {return [att.name/];}...[/template]
[template public javaMethod(op : Operation)]public [op.returnType/] [op.name/]() {// [protected (op.name)]// Fill in the operation implementation here![returnStatement(op.returnType)/]// [/protected]}[/template]
import du MM
Query
Open output file
Static text
appel
Template
loop
Definition Template Meta class
Expression
zone
protégée
close output file
Definition Template
Zones protégées Les zones protégées ne sont pas écrasées lors des génération successives
Elles sont délimitées par des commentaires
Le contenu est fusionné avec le nouveau code généré
Pratique pour généré le squelette d’une méthode
le corps de la méthode est écrit ‘à la main’
Il n’est pas écrasé lors des prochaines générations
Structure d’un module[comment encoding = UTF-8 /][*** The documentation of the module generate2.*/]
[module generate2('http:///document.ecore')]
[*** The documentation of the template generateElement.* @param aDocument*/]
[template public generateElement(aDocument : Document)][comment @main/][file (aDocument.name, false, 'UTF-8')]
[/file][/template]
Module Contient des templates et des queries
Correspond à un fichier ‘.mtl’ un module == un fichier ‘.mtl’
Sert d’espace de nommage
Le fichier doit commencer déclarer le MM utilisé [module <module_name>('metamodel URI')/]
Spécifie : Le nom du module
Le type du MM utilisé
[comment encoding = UTF-8 /]
[module generate('http:///mjava.ecore')/]
. . .
generate.mtl
Template Spécifie un template avec des trous
Permet de générer le texte
Est délimité par les balises
[template]...[/template]
Contient le texte à générer et des instructions à remplacer par des données
Tous le texte entre les balises est généré dans le fichier de sortie
Sauf les instructions qui sont remplacées par le texte qu’elles génèrent
[comment encoding = UTF-8 /]
[module generate('http:///mjava.ecore')/]
[template public generateClass(c : Class)]
name = [c.name/]
[/template]
generate.mtl
Template Peut être vue comme une méthode avec des paramètres
A au moins un paramètre
Type de l’élément du métamodèle qui est utilisé pour obtenir les données
Instructions
délimités par [ … /]
ex:[c.name/] – retourne la valeur de la variable
ex::[c.generateProperty()/] - appel la méthode
Sont remplacés par le texte qu’elles génèrent
[comment encoding = UTF-8 /]
[module generate('http:///mjava.ecore')/]
[template public generateClass(c : Class)]
name = [c.name/]
[/template]
generate.mtl
Fichier de sortie [file]
Il faut spécifier le fichier de sortie Lors d’une génération, on peut utiliser plusieurs fichiers de sortie (les uns après les autres) [file (<uri_expression>, <append_mode>, '<output_encoding>')] (...) [/file]
uri_expression nom du fichier à générer peut contenir un chemin d’accès – les répertoires sont alors créés
append_mode optionnel – indique si le texte doit être remplacé ou ajouté
output_encoding Optionnel
A faire dans un template
[comment encoding = UTF-8 /]
[module generate('http:///mjava.ecore')/]
[template public generateClass(c : Class)]
[comment @main /]
[file (c.name, false, 'UTF-8')]
name = [c.name/]
[/file]
[/template]
Lancement du moteur Il faut spécifier:
Le module a utiliser
Le modèle d’entré
Il doit correspondre au type de MM spécifié dans le module
Le répertoire de sortie
Les fichiers seront générés dans répertoire
Runner ‘Java Application’
Fonctionement Le moteur recherche le template annoté
[comment @main /]
Il est préférable de n’en avoir qu’un
Le moteur regarde le type de paramètre du premier argument de ce template
Le modèle recherche dans le modèle TOUS les éléments de ce type
Le moteur appelle le template pour chaque élément trouvé
Point d’entré (template ‘main’) Le moteur exécute:
les templates contenant une annotation [comment @main /]
[module generate('http:///mjava.ecore')/]
[template public generateClass(c : Class)]
[comment @main /]
[file (c.name, false, 'UTF-8')]
class [c.name/]
[/file]
[/template]
[template public generateName(ele : NamedElement)]
[ele.name/]
[/template]
ici, seul le premier template est executépar le moteur
Relation avec OCL Acceleo utilise les expressions OCL
Pour les expressions booléennes
Pour faire des requêtes dans le modèle
Ex: Rechercher un groupe d’objet selon des critères
Pour écrire des (nouvelles) opérations (query) associées aux types du MM
Expressions Les expressions sont écrite en OCL
Syntaxe similaire a Java
Accès à une propriétés
Notation pointé
Appel d’une méthode
Notation pointés
[aClass.name/]
[aClass.getAllAttributes()/]
Expressions Possibilités d’utiliser les librairies OCL
Des opérations sur les chaines
[if (aClass.name.startsWith('A'))]block_expression[/if][/file]
Expressions Possibilités d’utiliser les librairies OCL
Des operations puissantes sur les ensembles select, forAll, … http://wiki.eclipse.org/Acceleo/OCL_Operations_Reference
Pour les operation sur les Collection Utiliser ‘->’
// Test si le nom commence par une certaine lettre[let allProp : Set(Property) =
aClass.getAllAttributes()->select(e |e.name.toString().startsWith('A') )]block_expression[/let]
// Test la taille de la collection[if (aClass.getAllAttributes()->size() <> 0)]block_expression[/if]
Types de bases (types OCL) String, Integer, Boolean
Any
Représente tout les types
Les types Java sont transformé dans le type OCL correspondant
Transformation en général transparente
// Declarer une chaine[let str : String = 'une chaine'] [/let]
Types de bases (types OCL) Collection
Collection, Bag, Set, Sequence, OrderedSet
Les Collection Java sont transformé dans la collection OCL correspondante Transformation en général transparente
Possibilité de « caster »
// Declarer une variable de type collection[let var : Set(Class) = Classifier.allInstances()][/let]// declarer une collection[let var : Set(String) = Set{'a', 'b'}] [/let]// Transfo auto des types java->OCL[let ele : Set(Property) = aClass.getAllAttributes()][/let]
Comment appeler un template existant
Template exécution Un template peut être exécuté de deux façons:
soit par appel par le moteur
le moteur recherche dans le modèle tout les éléments du type requis par le paramètre du template
Pour chaque élément, le template est appelé
C’est ce qui se passe avec le template ‘main’
soit par appel explicite, à partir d’un autre template
équivalent à un appel de méthode
Templateappel explicite
Un template peut appeler d’autres templates (~ appel de méthode)
l’appel se fait sur une instance du type du 1er paramètre du template à appeler [c.generateName()/]
ou en passant les paramètres
[generateName(c)/]
[template public generateClass(c : Class)]
[comment @main /]
[file (c.name, false, 'UTF-8')]
name = [c.generateName()/]
[/file]
[/template]
[template public generateName(ele : NamedElement)]
[ele.name/]
[/template]
Invocation de templates
[template multi( arg: Set(String) )]
[multi( Set(‘lundi’, ‘mardi, ‘mercredi’) )]
[multi( ‘lundi’ )] [multi( Set(‘lundi’) )]
[template single( arg : String )]
[single( Set(‘lundi’, ‘mardi, ‘mercredi’) )]
[single( ‘lundi’ )]
[single( ‘lundi’ )]
[single( ‘mardi’ )]
[single( ‘mercredi’ )]
[single( ‘lundi’ )]
[multi( Set(‘lundi’, ‘mardi, ‘mercredi’) )]
[templatexcross( arg1: String,
arg2: String) )]
[xcross(Set(‘lundi’, ‘mardi, ‘mercredi’), Set(‘mars’, ‘avril’) )]
[xcross( ‘lundi’, ‘mars’ )]
[xcross( ‘lundi’, ‘avril’ )]
[xcross( ‘mardi’, ‘avril’ )]
[xcross( ‘mercredi’, ‘avril’ )]
[xcross( ‘mardi’, ‘mars’ )]
[xcross( ‘mercredi’, ‘mars’ )]
Déclaration
Invocation appels
Paramètre singleton
Paramètre Set
Paramètre singleton
argument singleton
argument set
argument set
Acceleo ne supporte pas encore tous les cas !
Le problème Une hiérarchie de classes
Une collection d’éléments de la hiérarchie (ici Mammifère)
polymorphisme
Un template différent pour chaque classe effective
Comment appeler le template correspondant à la classe effective ?
Sans tester le type de la classe !
Polymorphisme : solution
[template public generateElement(mamifere : Mamifere)][comment @main/][file (mamifere.name, false, 'UTF-8')]
[toProfil(mamifere)/][/file][/template]
[template public toProfil(mamifere : Mamifere)]!!! A implementer !!! [/template]
[template public toProfil(cheval : Cheval)]nom : [cheval.name/]robe : [cheval.robe/][/template]
[template public toProfil(singe : Singe)]nom : [singe.name/]pelage : [singe.pelage/][/template]
•Templates avec le même nom•Mais signatures différentes•Utilise le type effectif
Polymorphisme
Lors de l’utilisation du polymorphisme:
écrivez un template pour le cas par défaut
faite lui écrire un message d’erreur
n’écrivez pas tous les cas à l’aide de [if]
faite un template pour chaque sous-cas
TemplatePre-condition
Un template peut avoir une pré condition
il ne sera exécuté que si la condition est vrai
[template public generateClass(c : Class) ? (isCompilationUnit = true)]
[comment @main /]
[file (c.name, false, 'UTF-8')]
name = [c.name/]
[/file]
[/template]
[template public generateClass(c : Class) ? (isCompilationUnit = false)]
[/template]
Conditions : [if]
Pour spécifier une partie conditionnel dans un [template]
condition – expression booléenne
Peut avoir un [else] , des [elseif]
[if (condition)]
(...)
[/if]
[if (condition)]
(...)
[else]
(...)
[/if]
[if (condition)]
(...)
[elseif (condition)]
(...)
[elseif (condition)]
(...)
[/if]
IterationsBoucles [for]
Le bloc for Déclare une variable ‘a’ de type Attribute Produit le texte entre [for] et [/for]
pour chaque attribut de la collection c.attribute
[template public classToJava(c : Class)]
class [c.name/]
{
// Attribute declarations
[for(a : Attribute | c.attribute)]
[a.type.name/] [a.name/];
[/for]
}
[/template]