Manuel

publicité
Version
0.0
IRISA / INRIA
Model Transformation
Cahier des charges
Général
IRISA / INRIA - EQUIPE TRISKELL
Model Transformation : cahier des
charges général
 MTL : Model Transformation language from INRIA
Table des matières
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Chapitre
1
Les objectifs
Diverses fonctionnalités que ce nouveau langage doit permettre.
I
ntroduction
Pour préciser les objectifs à atteindre, il faut commencer par décrire l’environnement dans lequel on veut
créer ce nouveau langage. Cet environnement est basé sur l’approche MDA (Model Driven Architecture)
de l’OMG ; y figurent :

Quatre niveaux de modélisation

Des précisions sémantiques
Exprimées par des contraintes OCL, ou du texte ( notes attachées à un
modèle). On peut imaginer que d’autres expressions annotent les
diagrammes et puissent être vérifiées par des outils.
1
P E R S O N N A L I S A T I O N
F
D E
L A
P R E S E N T A T I O N
onctionnalités attendues de MTL
MTL doit pouvoir se connecter à un dépositaire de modèles de niveau M1, pour naviguer dans un
modèle présent dans le dépositaire et éventuellement le modifier.
MTL doit pouvoir être utilisé avec des dépositaires différents, et dialogue avec le dépositaire via une
API générique. Pour raccorder un nouveau dépositaire, il suffit d’écrire l’implémentation de l’API
générique qui lui est propre.
Le compilateur MTL doit pouvoir s’écrire en MTL de façon à rester indépendant du langage cible qui le
réalise concrètement.
Un interprète OCL devrait pouvoir s’écrire facilement en MTL, car MTL dispose des mêmes
possibilités de navigation et de test qu’OCL. MTL étend OCL en permettant des créations et
modifications.
MTL doit permettre d’appeler des « opérations externes » telles que l’appel d’un interprète OCL sur une
expression OCL isolée, un autre outil associé aux expressions présentes dans le métamodèle, ou encore
des fonctionnalités du langage cible.
MTL doit permettre de réaliser des modules ou librairies qui peuvent être réutilisés dans le cadre de
plusieurs transformations.
Fonctionnalités en termes de modèles :
MTL peut vérifier la cohérence d’un modèle vis-à-vis de son métamodèle : récupération de toutes les
contraintes OCL issues du métamodèle et attachées aux éléments du modèle, puis vérification de leur
validité par appel de l’interprète OCL sur de chacune d’elles.
MTL peut transformer un modèle en un autre, même lorsque ceux-ci sont basés sur des métamodèles
différents. Exemples : transformation d’un arbre quelconque en arbre binaire, transformation d’un arbre
ANTLR en arbre N-aire, transformation d’un modèle en UML1.4 en modèle d’UML2.
: MTL permet l’application d’une même transformation à tous les éléments de modèle satisfaisant une
propriété. Par exemple, l’application de contrat entre une interface requise et une fournie. MTL
recherche dans le modèle toutes les interfaces mises en correspondance et y ajoute systématiquement le
type de contrat ainsi que son implémentation. On peut également tisser des aspects sur le modèle.
MTL permet de dériver, à partir d’un groupe d’éléments du modèle représentant un produit générique,
une ligne de produits par introduction de caractéristiques spécifiques à chaque produit.
MTL doit-il être un langage orienté objet autorisant l’héritage multiple ?
Avantages : Les objets MTL manipulés représentent des éléments de modèle
(objets pouvant avoir un héritage multiple).
On peut utiliser la modélisation UML pour décrire des transformations.
2
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Chapitre
2
Connexion au(x) dépositaire(s)
Diverses analyses, divers dépositaires existants
A
pproche « Proxies »
Comme le modèle peut avoir une taille importante, et que le dépositaire est supposé assez lent, l’idée était de
fournir au compilateur MTL le code source de la transformation ainsi que le ou les métamodèles des
modèles manipulés.
Le compilateur générait alors le binaire de la transformation et autant de proxies que de métamodèles en
entrée afin que l’exécution de la transformation minimise les consultations du dépositaire.
3
P E R S O N N A L I S A T I O N
D E
SOURCE MTL
Transformation arbre ANTLR
=> arbre N-aire
L A
P R E S E N T A T I O N
METAMODELE
arbre ANTLR
METAMODELE
arbre N-aire
COMPILATEUR
MTL
METAMODELE
arbre ANTLR
Proxy de
modèle
d’arbre ANTLR
API
GENERIQUE
DEPOSIRAIRE
BINAIRE
Transformation
ANTLR => N-aire
Proxy de modèle
d’arbre N-aire
API
GENERIQUE
METAMODELE
arbre N-aire
DEPOSIRAIRE
Avantage : les éléments de modèle manipulés sont accessibles dans le proxy et donc en mémoire, ça va vite.
Inconvénients :
le compilateur n’est pas facile à construire ;
le métamodèle du dépositaire n’est pas toujours connu, et surtout il n’est pas du tout certain que l’on
puisse forcer le dépositaire à se baser sur un métamodèle dont on dispose, par exemple celui d’un arbre.
On veut pouvoir écrire des transformations de modèles qui sont applicables dans divers métamodèles.
Par exemple, un raffinement de diagramme de classes propre à une entreprise et qui resterait valable en
UML1.3, 1.4 et 2.
On envisage de déposer le métamodèle pour lequel on souhaite générer un proxy (en vue d’améliorer
les performances) comme simple modèle dans le dépositaire.
2
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
MTL permet de naviguer dans un modèle ( même si son métamodèle n’est pas accessible ) via l’API
générique. Donc MTL peut naviguer dans un métamodèle déposé comme modèle dans le dépositaire.
On peut écrire une transformation qui explore complètement un métamodèle et génère un proxy pour
naviguer facilement dans des modèles issus de ce métamodèle.
SOURCE MTL
Exploration de métamodèle
et génération d’un proxy
d’exploration de ses modèles
COMPILATEUR
MTL
METAMODELE
du dépositaire ???
BINAIRE
de génération de proxy
API
GENERIQUE
DEPOSIRAIRE
METAMODELE
arbre N-aire
( MODELE
Proxy de modèle
d’arbre N-aire
utilisable avec
l’API générique
A
pproche « Dépositaire MOF »
Dans un dépositaire tel que Netbeans-MDR, on se base sur le MOF pour y déposer un métamodèle
(UML ou CWM par exemple). Ensuite, on peut créer une interface d’accès et de modification de
modèles basée sur le métamodèle présent dans le dépositaire.
3
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
SOURCE MTL
Transformation de modèle
basée sur un métamodèle
DEPOSIRAIRE
MDR
COMPILATEUR
MTL
BINAIRE
de la transformation
API
MOF
METAMODELE
arbre N-aire
( MODELE MOF)
API générée par le
dépositaire, basée sur le
métamodèle d’arbre N-aire
Modèle
d’arbre N-aire
Avantage : Métamodèle et modèle sont présents dans le même dépositaire
Inconvénients :
MTL est très lié au dépositaire. Les dépositaires de ce type évoluent : NetBeans-MDR (Sun basé sur
MOF 1.4 ) Eclipse-EMF (IBM basé sur MOF 2 )
Il faut faire des échanges basés sur XMI entre « CASE tools » et dépositaire (sauf si l’on fait tout sous
Eclipse : EclipseUML + Eclipse + EMF lié à une base de données par exemple )
C
ONCLUSION ( ? )
Les règles d’or de l’accès à un dépositaire :
Le métamodèle utilisé par le dépositaire pour stocker des modèles n’est pas toujours complètement
connu, et évolue au rythme des RFP de l’OMG.
=> l’API d’accès au dépositaire doit demander et créer des instances de métaélément (métaélément
4
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
indiqué sous forme de chaîne de caractères car on ne connaît pas la structure du dépositaire). Si le
dépositaire ne peut satisfaire la requête, l’API le signale.
Le dépositaire peut faire des évaluations paresseuses.
=> lorsque l’API demande la collection des instances d’un métaélément particulier, il n’est pas certain
que le dépositaire construise réellement cette collection. Il est souvent plus judicieux qu’il fournisse un
nouvel élément de modèle pour chaque collection.at(i), i=1,2,…n en gardant l’uid des instances de
métaélément déjà fournies. ( à discuter ? )
Le programmeur de transformation ne doit pas à avoir à connaître par cœur le MOF1.4 ou le MOF 2
pour utiliser l’API de connexion au dépositaire.
=> l’API n’est basée sur aucun MOF particulier.
Le dépositaire risque à terme d’inclure la description de la transformation à effectuer : métamodèle
annoté par des expressions MTL et OCL. Les annotations MTL précisent la transformation à réaliser
dans les modèles basés sur ce métamodèle
=> l’API de connexion au dépositaire sera susceptible de servir pour obtenir le source MTL
D
ESCRIPTION SOMMAIRE DE L4API ENVISAGEE
L’API est organisée en une version de base et des modules d’extension. Selon les fonctionnalités du
dépositaire, et la sémantique attachée au métamodèle utilisé pour les modèles, une implémentation de
l’API de base seule, ou bien de celle-ci avec certains modules sera réalisée pour le dépositaire concerné.
Pour condenser, seules les opérations primordiales figurent dans le tableau ci-dessous.
API de base
Interface d’échange de
données
entre
applications et d’appel
fonctionnels extérieurs.
Possibilité de communication avec d’autres
langages de programmation ( Java, Python,
C++, C#) ; Conversion d’objets MTL en
« données extérieures » et vis versa.
( IDL, XDE, SOAP ???? )
Exemples : écriture de données dans un
fichier ;
Appel d’un interprète OCL sur une
expression
API de base
Opérations sur modèles :
uidm=NewModelEleme
nt (arguments[ ] )
Crée un élément de modèle en utilisant les
données passées en arguments, et en
renvoyant un uidm ( identificateur unique de
l’élément de modèle)
5
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
uidm.deleteModelEleme
nt ()
Enlève du modèle l’élément associé à l’uidm..
uidm.isTypeOf
(MetaType)
Détermine si l’élément de modèle a été créé
avec le métatype fourni
uidm.isKindOf(MetaTyp
e)
Détermine si l’élément de modèle est bien
une instance du métatype fourni.
uidm.getAttributeValue(
MetaAttribut)
uidm.setAttributeValue(
MetaAttribut,Valeur)
Donne ou affecte la valeur d’un attribut de
métaélément dont uidm est une instance.
uidm.getRelatedConstrai
nts()
Obtient l’ensemble des contraintes attachées
au métaélément dont uidm est une instance.
uidm.invokeOperation
(MetaOperation,argumen
ts[ ])
Fait appel à l’opération définie dans le
métaélément dont uidm est une instance.
associateModelElements
(Name,
ModelEndPoint[ ] )
Crée une association de nom (optionnel)
« Name » entre divers éléments de modèle.
Une spécification de liaison est fournie pour
chaque point de connexion à un élément, elle
comprend
dans
l’API
de
base :
- l’élément de modèle à raccorder ;
.
- un nom éventuel de rôle
;
( elle est étendue dans lez extensions d’API)
dissociateModelElements
(ModelEndPoint [ ])
Supprime l’association qui existe entre les
éléments de modèles, le dépositaire peut
retrouver cette association grâce aux specs de
liaison..
MetaType.allInstances ()
Fournit la collection d’instances du MetaType
6
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
présentes dans le modèle.
MetaType.selectInstances
(critère)
Fournit la collection d’instances du MetaType
présentes dans le modèle, qui de plus satisfont
le critère de sélection.
MetaType.getStaticAttrib
uteValue(MetaAttribut)
MetaType.setStaticAttrib
uteValue(MetaAttribut,V
aleur
API de base
Récupération ou modification de la valeur
d’un attribut statique.
Opérations propres aux
métamodèles :
MetaType.getAllParents()
Fournit la collection des métaéléments qui
sont issus d’une généralisation du MetaType
considéré.
MetaType.getAllChildren
()
Fournit la collection des métaéléments qui
sont issus d’une spécialisation du MetaType
considéré
MetaType.getAllAssociat
edElements()
Fournit la collection de collections des
métaéléments qui sont en association avec le
MetaType considéré.
uidm.getMetaType()
Fournit le Métaélément qui a permis de créer
l’instance de modèle considéré.
MetaType.getAllAttribute
sNames()
Donne la collection de noms de
MétaAttributs portés par le Métaélément
considéré.
Donne la collection de noms de
Métaopérations offertes par le Métaélément
considéré
MetaType.getAllOperatio
nsNames()
Extension
ModelEndPoint
contient
de
plus :
- un rang de l’élément à connecter ;
- un mode (insert/replace)
« lien ordonné »
Extension
ModelEndPoint
contient
de
plus :
un
tableau
selecteur[
]
chaque selecteur est un couple (nom de
qualificatif,valeur)
« liens qualifiés »
7
P E R S O N N A L I S A T I O N
Extension
« Multiplicité
des
valeurs
d’attributs »
D E
L A
P R E S E N T A T I O N
Valable pour un attribut
dont le
type
est
« Collection »
Les manipulations sur la collection attachée à
l’attribut sont réalisées par le dépositaire.
ou dont le type est un
type primitif avec une
multiplicité prédéfinie de
valeurs.
NB : il existe des fonctions similaires pour un
attribut statique ayant une multiplicité,
par exemple :
uidm.getAttributeCollecti
onSize(MetaAttribut)
MetaType.getStaticAttributeCollectionSize(M
etaAttribut)
uidm
getAttributeCollectionVa
lueAt(MetaAttribut,positi
on)
uidm
setAttributeCollectionVal
ueAt(MetaAttribut,positi
on)
… (autres opérations
telles
que
insertAt,
removeAt, etc. )
API : basée sur JMI, IDL, … ?
Avantage de JMI : devrait s’interfacer directement avec la plupart des « CASE tools » qui importent et
exportent du XMI. ;
Inconvénients de JMI :
Il faut une DTD identique pour relire un fichier xml issu d’un « CASE tool », il faut recréer en mémoire
un modèle identique à celui manipulé par le « CASE tool ».
Il existe plusieurs versions de XMI.
Avantages d’IDL,SOAP :
L’API correspond à une interface au travers de laquelle des données simples sont communiquées à
d’autres applications;
IDL ne présuppose rien sur l’implémentation de l’interface sœur : on peut utiliser un dépositaire écrit
dans un langage quelconque et disposant d’un driver compatible avec l’interface IDL correspondante à
l’API.
8
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Inconvénients :
le bus corba est lent ; IDL3 va remplacer IDL ? SOAP est basé sur un échange de données XML.
E
xemple d’utilisation de MTL et de son API avec les métamodèles d’arbre ANTLR et d’arbre Naire, ainsi que le modèle de transformation présentés en annexe.
COMPILATEUR
MTL
ANALYSE LEXICALE
ET SYNTAXIQUE
SOURCE MTL
Transformation de modèle
basée sur un métamodèle
BINAIRE
de la transformation
TRANSFORMATION
API
MTL
API
MTL
API
MTL
DEPOSIRAIRE
MDR
DEPOSIRAIRE
Eclipse-EMF
API générée par le
dépositaire, basée sur le
métamodèle d’arbre N-aire
API
MOF 2
API de
modèles ANTLR
METAMODELE
d’arbres
ANTLR et N-aire
annoté avec MTL
Modèle
d’arbre ANTLR
9
Modèle
d’arbre N-aire
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Chapitre
3
Compilation, ou interprétation de MTL ?
Après de longues discussions, l’état de l’art détaillé…
O
n va développer un prototype de langage MTL qu’il va falloir affiner pour arriver au langage définitif.
On a besoin d’un interprète OCL2 auquel on pourra éventuellement faire appel depuis MTL.
Pour les deux raisons précédentes, on préfère développer dans un premier temps un interprète qu’un
compilateur. Celui-ci ne verra le jour que lorsque le langage MTL commencera à se stabiliser.
La syntaxe concrète du langage MTL définitif n’est pas encore connue (voir le chapitre 4 sur l’utilisation) .
Elle pourra provenir directement d’un « CASE tool », d’un bus BOM via une interface normalisée de ce
bus, d’une description textuelle de la transformation.
Un parseur spécifique à chaque origine de transformation va produire un arbre concret basé sur la définition
de la syntaxe abstraite de MTL.
Analyse du source MTL
Source MTL
Diagramme annoté
dans un «CASE tool »
Parseur spécifque
Parseur de source texte MTL
Arbre concret annoté par les
éléments de syntaxe abstraite MTL
IFexpr
Basé sur l’API de connexion
à un dépositaire
+ syntaxe abstraite MTL
iCONDexpr
Données objets
venant du BOM
Basé sur l’IDL BOM
(IDL3 Corba ? )
+ syntaxe abstraite MTL
Basé sur la syntaxe abstraite MTL
THENclause
ELSEclause
Fichier texte
10
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
La syntaxe abstraite de MTL va évoluer en fonction de la mise au point du langage.
L’arbre concret issu du parseur va être compilé en utilisant un visiteur qui va réexprimer tout l’arbre en
fonction d’un langage MTL simplifié, le BasicMTL.
Par exemple, on peut exprimer un « FOR var IN collection DO expression » au moyen d’une boucle while
de BasicMTL qui va donner à var la valeur de chacun des membres de la collection et évaluer l’expression.
Cette transformation d’arbre peut être réalisée complètement par un compilateur MTL=>BasicMTL.
Compilation de MTL vers Basic MTL
Arbre concret MTL
Arbre concret BasicMTL
FORexpr
BasicWHILEexpr
IDENTIFIER
BasicNOTEMPTY
MTLexpr corps
BasicEVALSEQUENCE
COLLECTIONexpr
IDENTIFIER
BasicMTLexpr corps
BasicLET
IDENTIFIER
BasicNEXTELEMENT
BasicCOLLECTION
La dernière étape consiste à lancer un visiteur sur l’arbre concret basé sur la syntaxe abstraite de BasicMTL.
La syntaxe abstraite de BasicMTL est réduite à :
 La définition d’une classe (éventuellement abstraite) pouvant hériter d’une ou plusieurs autres
classes ;
2
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
 Une classe comprend des attributs dont les types sont soit prédéfinis soit des classes, et des
définitions de méthodes (éventuellement abstraites) ;
 Une définition de méthode comprend une séquence d’instructions. Chaque instruction est soit
la création d’un nouvel objet attaché à une variable, soit un appel de méthode d’un objet
existant avec d’éventuels paramètres.
 Une méthode particulière « main » permet d’initialiser et de lancer l’exécution de la
transformation.
U
ne syntaxe possible de BasicMTL pourrait être la suivante :
<BasicMTLpgme> ::= <definition_classe>* <main> <{> <decl_attributs>* <corps_méthode>+<}>
<definition_classe> ::= [ <abstract>] <class> identifier [ <extends> identifier+ ] <{> <corps_classe>
<}>
<corps_classe> ::= <decl_attribut>* <definition_methode>+
<decl_attribut> ::= <decl_vars>
<decl_vars> ::= { <type> | identifier } identifier < ;> // type var ;
<definition_methode> ::= [ <abstract> ] { <type> | identifier} identifier <(> <decl_vars>* <)> <{>
<corps_methode> <}>
//type nom_methode ( type1 paramètre1 type2 paramètre2 … ) { …}
<corps_methode> ::= <New> <(> identifier { <type> | identifier} <)> //New (var classe)
| identifier<.>identifier <(> {<valeur> | identifier}* <)>
//classe.méthode(…)
<type> ::= « integer » | « real » | « boolean » | “string” | « collection » | « tuple »
<valeur> ::= <nombre> | <chaine de caractères> | <valeur booléenne>
L
’héritage multiple va être réalisé en créant une interface de méthodes par classe parente. Un objet
héritant de plusieurs classes est une instance java réalisant toutes les interfaces correspondantes à ses
classes parentes.
Le visiteur d’arbre BasicMTL produit un fichier java lorsqu’il rencontre une définition de classe, puis le
compile avec javac. Il conserve en permanence le graphe des héritages de classes et la table des symboles
pour obtenir la correspondance « méthode MTL => méthode d’un fichier java.class généré ».
Le fichier java compilé peut faire référence au visiteur pour évaluer des morceaux d’arbres BasicMTL qui lui
sont fournis en arguments. Le « while » java fait appel au visiteur sur la partie condition pour savoir si celle-
3
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
ci est satisfaite ou pas :
Class BasicMTLinstructions { …
BasicMTLwhile( Object condition, Object CorpsBoucle)
{ boolean continueBoucle ;
do { Object condEvaluée = BasicMTL.Interprète( condition } ;
BasicMTL.CheckBoolean(condEvaluée); //ExceptionMTL si ! Bool
continueBoucle = BasicMtl.ToJava(condEvaluée);
if (continueBoucle) BasicMTL.Interprète( CorpsBoucle) ; }
while (continueBoucle) ;
}
Pour l’évaluation d’appels de méthodes, le visiteur d’arbre BasicMTL se contente de faire appel à la
méthode de la classe java correspondante. Il trouve cette classe en se basant sur son graphe de dépendances
entre classes et la table des symboles.
U
ne fois le langage stabilisé, on pourra compiler l’arbre BasicMTL et traduire directement le graphe
des héritages par des appels directs aux classes et méthodes java adéquates.
On prévoit de créer des librairies MTL (voir exemple en annexe) pour faciliter l’écriture modulaire des
transformations. Dans un premier temps, les sources MTL de librairies sont compilés pour produire des
« arbres concrets BasicMTL » que l’interprète MTL peut aller chercher et interpréter au besoin de façon
dynamique.
Lorsque le compilateur BasicMTL sera réalisé, les diverses librairies MTL seront alors disponibles sous
forme d’archives java (fichiers jar).
4
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Chapitre
4
Comment fera-t-on usage de MTL ?
D u modeleur à MTL…
M
odèles de transformation
On annexe, on donne un exemple de transformation d’arbre basée sur deux métamodèles distincts d’arbres.
La transformation elle-même peut être décrite dans le modeleur. Elle peut alors servir pour :
- constituer le texte de la transformation ; les éléments graphiques du modèle de transformation étant repris
un par un sous forme de texte déclarant des classes et des associations d’objets MTL ( voir le texte complet
de la transformation).
- être annotée par le contenu des méthodes MTL qui sont attachées aux classes. C’est exactement comme
pour la conception d’une application Java où le code source d’une méthode figurerait en note attachée.
Dans un modeleur, on choisit le langage vers lequel générer le modèle, java ou C++ par exemple.
On devrait pouvoir utiliser n’importe quel modeleur tel quel, du moment qu’on peut le connecter via l’API
générique. Au lieu d’être un dépositaire de modèles que la transformation va consulter et/ou modifier, le
modeleur est alors un « dépositaire de programme » qui contient le source MTL.
La « transformation » chargée de naviguer dans un modèle de transformation, de construire les classes
d’objets MTL nécessaires et d’y mettre les annotations MTL comme texte de méthodes pourra s’écrire en
MTL. Elle reviendra au même qu’une extension de génération MTL – réalisée comme une génération C++
ou java – effectuée par la société fabriquant le modeleur.
M
odèles d’objets annotés par du MTL
On croit que la façon la plus aisée de construire une ligne de produits, d’ajouter des contrats ou de faire du
« design pattern » sera sans doute d’annoter le modèle par des expressions MTL et d’y joindre un modèle
paramétrique de la transformation à effectuer comme dans l’exemple ci-dessous.
5
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Classe X
Classe X
{}
Op x::xop1 typeA->typeB
Op x::xop1 typeA->typeB
{MTL : T(x,y)}
Classe Y
Attrib y::yattrib1 typeB
Classe Y
-Fin1
Attrib y::yattrib1 typeB
1
-Fin2
Classe Z
*
Attrib z::zattrib1 typeA
Op z::zop1 typeB->typeA
Dans cet exemple, la transformation décrite par T s’applique dans tout modèle où l’on peut trouver une
portion de modélisation homomorphe à la partie située à gauche de la note MTL. La portion à droite de la
note MTL décrit la transformation réalisée pour chaque appariement effectué : une agrégation est ajoutée
avec une classe Z ayant un attribut et une opération (ceux-ci pouvant être choisis en fonction des classes x
et y et de leurs « features »).
MTL doit seulement garantir que tout le modèle cible sera parcouru, et que pour toute portion s’appariant
avec le motif de gauche, une et une seule transformation aura lieu. Cette « application de motif » devrait
pouvoir s’écrire également en MTL.
2
P E R S O N N A L I S A T I O N
D E
L A
P R E S E N T A T I O N
Annexes
Arbre ANTLR : l’arbre est parcouru avec FirstChild qui donne le premier fils d’un nœud, et
NextSibling qui donne le frère d’un nœud.
Un nœud comprend un entier correspondant au symbole non terminal parsé et une chaîne de
caractères pour la valeur concrète associée.
Arbre générique : au niveau de chaque nœud le père et les fils (dans l’ordre, de la gauche vers la droite)
sont conservés
3
Classes et associations (objets MTL) manipulés lors de la transformation d’un modèle d’arbre ANTLR
en un modèle d’arbre générique.
2
Texte source MTL de la transformation :
arbreANTLR.mtl
library ArbreANTLR;
model ModelArbreANTLR implements MetamodelArbreANTLR;
uses Arbres;
import MetamodelArbreANTLR:::*;
import Arbres:::*;
-- permet de consever la valeur de parsed
asscociation ParsedLocal extends ValeursLocales {
role:
+ valeurParsed [valeur] : 0..1 ValeurInteger;
unnavigable [arbre] : 0..1 ArbreANTLR;
}
-- permet de consever la valeur de text
asscociation TextLocal extends ValeursLocales {
role:
+ valeurText [valeur] : 0..1 ValeurString;
unnavigable [arbre] : 0..1 ArbreANTLR;
}
class ArbreANTLR specializes ArbreNAire {
attribute:
- referenceAST : AST;
- valeurPourParsed : ValeurInteger;
- valeurPourText : ValeurString;
- parsedName : String;
constructor:
# (reference : AST) {
if (self.reference = NULL)
throw new NullPointerException();
self.reference := reference;
self.valeurParsed := new ValeurInteger('parsed',
reference.parsed);
self.valeurText := new ValeurString('text',
reference.text);
self.parsedName :=
findRuleName(self.valeurParsed.valeur);
}
+ () {
constructor (newModelElement AST());
}
operation:
+ static arbreANTLRPour (reference : AST) : ArbreANTLR {
ArbreANTLR ret;
if reference = NULL
then ret := NULL;
else
3
ArbreANTLR ret := ArbreANTLR.selectInstances(ag|
ag.reference = reference)->getOne;
if ret = NULL
then ret := new ArbreANTLR(reference);
endif
endif
return ret;
}
# static frerePrecedentAST (ast : AST) : AST {
return AST.selectInstances(i| i.nextSibling = ast)>getOne;
}
# static frereAineAST (ast : AST) : AST {
AST frere = ast;
AST frerePrecedent = frerePrecedentAST(AST);
while frerePrecedent <> NULL
do
ast := frerePrecedent;
frerePrecedent = frerePrecedentAST(AST);
done
return ast;
}
# static pereAST (ast : AST) : AST {
-- cas ou ast represente un premier fils
AST referenceSurPere := AST.selectInstances(i|
i.firstChild = ast)->getOne;
-- cas ou ast represente un frere cadet
if referenceSurPere = NULL
then
AST frereAine := frereAineAST(ast);
if ast <> frereAine -- pour blocquer la recursivite
pour un noeud racine
then referenceSurPere := pereAST(frereAine);
endif
endif
return referenceSurPere;
}
# fabriquePere : ArbreSurProxie {
return arbreANTLRPour(pereAST(self.reference));
}
# fabriqueFils : Sequence(ArbreSurProxie) {
AST tmp := self.reference.firstChild;
Sequence(AST) filsAST := Sequence{};
while tmp <> NULL
do
filsAST->alterAppend(tmp);
tmp := tmp.nextSibling;
done
return filsAST->collect(i| arbreANTLRPour(i));
}
4
# insereFilsDistant (fils : ArbreNAireSurProxie, index :
Integer) {
if fils.oclIsTypeOf(ArbreANTLR).not
then throw new IllegalTypeException();
if i > self.fils->size + 1
then self.ajouteFils;
elsif i <= 0
then throw new IllegalArgumentException();
elsif index = 1 then
fils.reference.nextSibling :=
self.reference.firstChild;
self.reference.firstChild := fils.reference;
else -- une facon de faire un for...
avantInsert : AST;
apresInsert : AST := self.reference.firstChild;
for i in Sequence{1..index}
do
avantInsert := apresInsert;
apresInsert := apresInsert.nextSibling;
done
avantInsert.nextSibling := fils.reference;
fils.reference.nextSibling := apresInsert;
endif
}
# static findRuleName (ruleNumber : Integer) : String {
return WFR.selectInstances(number = ruleNumber)->getOne;
}
# static findRuleNumber (ruleName : Integer) : String {
return WFR.selectInstances(name = ruleName)->getOne;
}
+ fixeValeur (valeur : Valeur) {
if valeur.name = 'text' then
if not valeur.oclIsKindOf(ValeurString) then throw
new IllegalArgumentException(); endif
self.reference.text :=
valeur.oclAsType(ValeurString).valeur;
delete self.valeurText;
self.valeurText := valeur.oclAsType(ValeurString);
elsif valeur.name = 'parsed' then
if valeur.oclIsKindOf(ValeurString) then
ruleNumber : Integer :=
findRuleNumber(valeur.oclAsType(ValeurString).valeur);
if ruleNumber = NULL then throw new
IllegalArgumentException(); endif
self.parsedName :=
valeur.oclAsType(ValeurString).valeur;
valeur := new ValeurInteger('parsed',
ruleNumber);
elsif valeur.oclIsKindOf(ValeurInteger) then
ruleName : String :=
findRuleName(valeur.oclAsType(ValeurInteger));
if ruleName = NULL then
throw new IllegalArgumentException();
endif
5
self.parsedName := ruleName;
else
throw new IllegalArgumentException();
endif
self.reference.parsed :=
valeur.oclAsType(ValeurInteger).valeur;
delete self.valeurParsed;
self.valeurParsed := valeur;
else throw new IllegalArgumentException();
endif
}
+ = (ArbreANTLR rhs) : Boolean {
return rhs <> NULL and self.reference = rhs.reference;
}
}
class ArbreANTLRPratique specializes ArbreANTLR {
constructor:
# (reference : AST) {
superconstructor ArbreANTLR (reference);
}
+ () {
constructor (newModelElement AST());
}
operation:
+ parsedRuleNumber : Integer {
-- return self.reference.parsed; -- appel au modele
return self.valeurParsed.valeur; -- appel proxie
}
+ parsedRuleName : String {
return self.parsedName;
}
+ fixeParsedRuleNumber (parsed : Integer) {
self.fixeValeur(new ValeurInteger('parsed', parsed));
}
+ fixeParsedRuleName (parsed : String) {
self.fixeValeur(new ValeurString('parsed', parsed));
}
+ text : String {
return self.valeurText.valeur; -- appel proxie
}
+ fixeText (parsed : String) {
self.fixeValeur(new ValeurString('text', parsed));
}
}
6
arbregénérique.mtl
library ArbreGenerique;
model ModeleArbreGenerique implements MetamodeleArbreGenerique;
uses Arbres;
import MetamodeleArbreGenerique:::*;
import Arbres:::*;
class ArbreGenerique specializes ArbreNAire {
attribute:
- reference : Noeud; -- reference du proxy (objet reel du
modele)
constructor:
# (reference : Noeud) {
if (self.reference = NULL)
throw new NullPointerException();
self.reference := reference;
self.reference.attributs->collect(att| associate valeur
:= new ValeurString(att.nom, att.valeur), arbre := self);
//
self.reference.attributs->collect(att| self.valeur>alterInsert(new ValeurString(att.nom, att.valeur))); -- ancienne version
}
+ ()
constructor (newModelElement Noeud() in ModelArbreNAire);
-- optionnel car ModelArbreNAire est le seul modele implementant le
metamodele ArbreNAire
}
operation:
+ static arbreGeneriquePour (reference : Noeud) :
ArbreGenerique {
ArbreGenerique ret;
if reference = NULL
then ret := NULL;
else
ArbreGenerique ret :=
ArbreGenerique.allInstances()->select(ag| ag.reference = reference)>getOne;
// exemple en specifiant le modelea explorer a
allInstances; ici, c'est optionnel...
// ArbreGenerique ret :=
ArbreGenerique.allInstances(ModeleArbreGenerique)->select(ag| ag.reference
= reference)->getOne;
if ret = NULL
then ret := new ArbreGenerique(reference);
endif
endif
return ret;
}
# fabriquePere : ArbreSurProxie {
arbreGeneriquePour(self.reference.pere);
}
7
# fabriqueFils : Sequence(ArbreSurProxie) {
reference.fils->collect(i| arbreGeneriquePour(i));
}
# insereFilsDistant (fils : ArbreNAireSurProxie, index :
Integer) {
if (fils.oclIsTypeOf(ArbreGenerique).not)
throw new IllegalTypeException();
try {
associateModelElement pere := self.reference, fils
:= fils.reference insertAt index;
//
self.reference.fils->alterAt(fils.reference,
index); -- vieille version
} catch (IllegalArgumentException x) {
self.ajouteFils;
}
}
+ fixeValeur (valeur : Valeur) {
attributCorrespondant : Attribut :=
self.reference.attributs->select(nom = valeur.nom)->getOne;
if attributCorrespondant = NULL then
attributCorrespondant := newModelElement
Attribut();
attributCorrespondant.nom := valeur.getNom;
associateModelElement noeud := self.reference,
attributs := attributCorrespondant;
//
self.reference.atributs>alterInsert(attributCorrespondant); -- vieille version
endif
attributCorrespondant.valeur := valeur.valeurString;
self.oclAsType(Arbre).fixeValeur(valeur);
}
+ = (ArbreGenerique rhs) : Boolean {
return rhs <> NULL and self.reference = rhs.reference;
}
}
8
Transformation proprement dite : arbreANTLR2arbregénérique.mtl
transformation ANTLR2ArbreGenerique;
readonly model MonModeleANTLR implements MetamodelArbreANTLR;
model MonModeleArbreGenerique implements MetamodeleArbreGenerique;
uses ArbreGenerique with MonModeleANTLR as ModeleArbreGenerique;
uses ArbreANTLR with MonModeleArbreGenerique as ModeleArbreGenerique;
uses Arbres;
import Arbres:::ArbreNAire;
main () {
nouvelArbre : ArbreGenerique:::ArbreGenerique;
for racine in ArbreANTLR:::AST.selectInstances(pere->isEmpty) do
nouvelArbre := new ArbreGenerique:::ArbreGenerique();
copyFils(racine, nouvelArbre);
copyValeur(racine, nouvelArbre);
done
}
copyValeur (source : ArbreNAire, cible : ArbreNAire) {
for valeur in source.valeur do
cible.fixeValeur(valeur);
done
}
copyFils (source : ArbreANTLR:::AST, cible :
ArbreGenerique:::ArbreGenerique) {
ArbreGenerique:::ArbreGenerique nouveauFils;
for fils in source.fils do
nouveauFils := new ArbreGenerique:::ArbreGenerique();
cible.insereFils(nouveauFils);
copyValeur(fils, nouveauFils);
copyFils(fils, nouveauFils);
done
}
9
10
Téléchargement