Créer un méta modèle manipulable par ArgoUML

Florent de LAMOTTE


Table des matières

Le meta modèle d'ArgoUML
NSUML
Le système d'évènements de NSUML
L'utilisation des évènements NSUML dans ArgoUML
La création de notre méta modèle
Un méta modèle manipulable par ArgoUML
Ajouter le support des évènements à notre méta modèle

Résumé

Ce document est un complément au cookbook d'Argo. Il a pour ambition de guider le lecteur vers une meilleure compréhension du fonctionnement d'ArgoUML. Il est basé sur une expérience personnelle et ne constitue donc pas un document de référence.

Les différents points abordés dans ce document proviennent de l'écriture d'un méta modèle de réseaux de Petri pour le module ArgoPN, ce meta modèle devant ensuite être utilisé par le diagramme réseau de Petri.

Un méta modèle est un langage permettant de créer des modèles. Dans le cas d'UML, le méta modèle permet de créer des modèles UML, l'utilisateur ne voit que le modèle

Le meta modèle d'ArgoUML

ArgoUML utilise la librairie NSUML pour représenter le méta modèle UML. Cette bibliothèque est générée automatiquement à partir de la description en XMI d'UML fournie par l'OMG.

NSUML fournit un système d'évènements qui permettent d'"écouter" les modifications qui sont appliquées au modèle. Ce système est massivement utilisé par ArgoUML (dans les diagrammes et les Panneaux de propriété notamment) et il nous faudra l'étudier pour pouvoir en faire profiter notre nouveau méta modèle.

NSUML

Le système d'évènements de NSUML

Il est nécéssaire de comprendre le fonctionnement du méchanisme gérant les évènements NSUML et comment ceux-ci sont utilisés dans ArgoUML pour pouvoir éspèrer utiliser ce méchanisme.

Ces méchanismes mettent en jeu trois classes(interfaces) présentées ci-dessous :

Figure 1. Les trois classes mises en jeu dans la gestion des évènements.

L'image n'a pas pu être affichée :(

Toutes les classes du métamodèle UML implémentent l'interface MBase, au travers de la classe MBaseImpl dont elles descendent toutes.

Cette classe dispose de méthodes addMElementListener et removeMElementListener qui permettent à une classe implémentant MElementListener de s'abonner ou de se désabonner de la liste des classes qui "écoutent" les évènements en provenance de celle-ci.

Chaque évènement envoyé à un "listener" est décrit par un objet de type MElementEvent qui permet de récupérer l'objet sur lequel a été effectué la modification et des informations sur la transformation effectuée.

L'utilisation des évènements NSUML dans ArgoUML

Au début, chaque élément de l'interface graphique d'ArgoUML s'enregistrait auprès des objets du modèle UML qu'il représentait. Malheureusement cette solution n'était pas satisfaisante car il y avait énormément d'évènements envoyés par NSUML et énormément de classes écoutant ces évènements, certains de ces évènements n'étant même pas pris en compte par la suite.

La solution qui a été adoptée fut la création d'une "pompe d'évènements" appellée UmlModelEventPump qui s'enregistre auprès de tous les éléments du modèle UML et auprès de laquelle chaque élément de l'interface graphique s'enregistre pour recevoir seulement l'évènement qui le concerne ( par exemple, le champ name d'un PropertyPanel ne va écouter que l'évènement "name" envoyé par l'objet qu'il est en train d'écouter.

La classe UmlModelEventPump est présentée dans la figure ci-dessous.

Figure 2. UmlModelEventPump

La création de notre méta modèle

Nous avons vu dans la section précédente qu'ArgoUML manipulait des éléments provenant du méta modèle fourni par NSUML et que l'une des caractéristique importante de NSUML était la manière dont il gérait les évènements.

Nous allons maintenant voir comment on peut écrire notre méta modèle pour qu'il soit directement manipulable par ArgoUML et pour qu'il envoie des évènements qui pourront ensuite être traités par ArgoUML.

Un méta modèle manipulable par ArgoUML

Lorsque ArgoUML manipule des éléments du méta modèle, il considère qu'il vient de NSUML. Il nous faut donc absolument hériter de classes NSUML.

Il est possible d'hériter de la classe MBaseImpl (ou d'implémenter son interface MBase. cette classe fournit les méthodes de base pour la manipulation des évènements et est normalement reconnue par ArgoUML.

Nous avons pour notre part choisi d'hériter de MModelElementImpl qui correspond dans le méta modèle UML à la classe ModelElement qui se situe en haut de la hierarchie des classes UML.

Ajouter le support des évènements à notre méta modèle

Une fois que le meta modèle dérivé du méta modèle NSUML est écrit, il nous faut pouvoir lancer (fire) des évènements. Pour cela, nous utiliseront les méthodes présentes dans MBaseImpl.

Nous utiliseront pour cela les methode operationStarted et operationFinished, ainsi que les methodes fire*.

Description des différentes méthodes utilisées

operationStarted est une méthode statique qui permet de signifier que l'on débute une opération qui va modifier un élément du modèle. Cette méthode transmet le message à la factory (MFactoryImpl) qui maintient un compteur indiquant la profondeur d'imbrication des opérations.

operationFinishd est elle aussi une méthode statique qui permet de signaler que l'on vient de terminer une opération sur un élément du modèle. Cette méthode transmet elle aussi le message à la factory qui va décrémenter le compteur.

Lorsque le compteur atteind la valeur 0, les évènements qui avaient été placés en attente sont envoyés aux MElementListeners.

Les methodes de MBase commençant par le préfixe fire servent à envoyer les évènements. Ces évènements sont placés en attente et ne seront envoyés que lors de l'appel d'operationFinished.

Utilisation de ces méthodes dans le meta modèle

Il va nous faloir utiliser ces méthodes dans le méta modèle pour pouvoir signifier aux éléments qui écoutent ces évènements que le modèle a été modifié.

Les différentes méthodes sont généralement appellées à partir de "setters". Voici ci-dessous une trame à utiliser pour un setter.


/**
 * Sets the data value to the _value field of the class
 * We send a "value" event to all the listeners, telling them
 * that the _value field has been changed
 * @param data the new value of _value.
 */
public void setSomethingToBeSet(TheType data) {
    operationStarted();
    // first we keep the old value of the data we are changing
    TheType old = _value;

    // we make the modification
    _value = data;
    
    // and fire the event
    fireAttrSet("value", old, data);

    operationFinished();
}

	

Si on fait une modification sur (par exemple) un Vecteur, on utilisera une autre méthode fire parmi celles-ci :

  • fireBagSet

  • fireBagAdd

  • fireBagRemove