Acceleo est un générateur de texte à partir de modèles (transformation "model to text") que l'on va utiliser pour générer du code Java à partir de nos modèles de diagrammes de classes simplifiés.
Acceleo utilise des templates qui permettent de parcourir le modèle et de générer du texte correspondant à du contenu dans le modèle. Pour cela, il se base sur OCL pour sélectionner des éléments du modèle ou calculer des valeurs à placer dans le texte.
La mise en place d'un projet Acceleo nécessite quelques configurations techniques pour faire le lien avec le projet EMF où se trouve le méta-modèle du DSL considéré avec son code Java/EMF généré. Voici les étapes à réaliser :
@generated
pour ne pas écraser à chaque
génération le code que vous avez modifié. Les deux bouts de code à
rajouter sont en bleu :
/**
* This can be used to update the resource set's package registry with all needed EPackages.
*
* @param resourceSet The resource set which registry has to be updated.
* @generated NOT
*/
@Override
public void registerPackages(ResourceSet resourceSet) {
super.registerPackages(resourceSet);
EPackage.Registry.INSTANCE.put("platform:/resource/ClassDiagram/model/ClassDiagram.ecore", ClassDiagramPackage.eINSTANCE);
...
org.eclipse.emf.ecore.EPackage
dans la
classe. Pour corriger la seconde erreur correspondant à l'accès au
package de ClassDiagram, il faut modifier les propriétés du projet
pour référencer les classes Java/EMF du méta-modèle de l'autre
projet. Dans le répertoire "META-INF" du projet Acceleo, ouvrez le
fichier "MANIFEST.MF", affichez l'onglet "Dependencies" et dans la
liste "Imported Packages", ajoutez, en cliquant sur "Add...", les
3 packages de ClassDiagram. Vous devez avoir ceci :ClassDiagram.ClassDiagramPackage
. Il n'y a
normalement plus d'erreurs dans votre projet.[comment @main/]
:
[for (cl : Class | aModelBase.allClasses)]
[file (cl.name.concat('.java'), false, 'UTF-8')]
package generate;
public class [ cl.name /] {
}
[/file]
[/for]
Vous avez désormais un projet Acceleo fonctionnel qui sait traiter des modèles de diagramme de classes mais ne fait pas grand chose. Vous devez rajouter les règles qui permettent de générer les interfaces, les attributs, les méthodes (avec une valeur de retour valide pour compiler), les constructeurs, les accesseurs ...
La documentation des fonctionnalités d'Acceleo se trouve sur cette page : https://wiki.eclipse.org/Acceleo/User_Guide.
Le template marqué par @main
est celui qui est exécuté
au lancement de la génération Acceleo. Ce template peut faire appel à
d'autres templates qui ont des paramètres ou à des requêtes (query)
écrites en OCL et qui retournent une valeur calculée sur le contenu du
modèle. Dans un template, il y a des constructeurs algorithmiques
comme la boucle [for]
pour parcourir une collection, les
tests [if]
ou la déclaration de variable comme en OCL
avec [let]
.
Voici par exemple une query OCL qui retourne le nom du type Java d'un type du méta-modèle de ClassDiagram :
[comment Retourne le type Java d'un type du modèle /]
[query public javaType(type : Type) : String =
if (type.oclIsTypeOf(VoidType)) then 'void'
else if (type.oclIsTypeOf(StringType)) then 'String'
else if (type.oclIsTypeOf(IntegerType)) then 'int'
else if (type.oclIsTypeOf(BooleanType)) then 'boolean'
else type.name
endif
endif
endif
endif
/]
Voici un template qui permet de définir la liste des interfaces
d'une classe via la construction implements
lors de la
définition d'une classe (notez le parcours de la collection qui
n'écrit le mot "implements" que si la collection n'est pas vide et qui
rajoute une virgule automatiquement entre les éléments sauf pour le
dernier de la collection) :
[comment Template pour générer la liste des interfaces implémentées par une classe /]
[template public interfaceList(cl : Class)]
[for (inter : Interface | cl.interfaces) before('implements ') separator (', ')][inter.name/][/for]
[/template]
En utilisant ce template et la query OCL, on peut enrichir la génération d'une classe pour y ajouter la liste des interfaces implémentées et la liste des attributs :
[for (cl : Class | aModelBase.allClasses)]
[file (cl.name.concat('.java'), false, 'UTF-8')]
package generate;
public class [ cl.name /] [interfaceList(cl)/] {
[for (att : Attribute | cl.attributes) ]
private [javaType(att.type)/] [att.name/];
[/for]
}
[/file]
[/for]
Eric Cariou, dernière modification : 10/02/24