III. Réalisation du plug-out▲
III-A. Créer un plug-out▲
Démarrez BOUML, et chargez le plug-out appelé empty, celui-ci faisant partie de la distribution. BOUML vous demande immédiatement de sauver le projet, en effet vous ne pouvez pas modifier le plug-out empty, il s'agit donc d'un save-as forcé, appelons ce plug-out sort :
Comme vous pouvez le voir, le projet contient deux packages principaux :
- le premier (API BASE) contient des classes système et leurs artefacts. Ces classes sont interfacées avec l'API du modeleur. Vous ne pouvez pas modifier ces classes, et la plupart d'entre elles ne sont pas instanciables ;
- le second package (API USER) contient la prédéfinition des classes utilisateur et de leur artefacts, généralement ces classes héritent d'une classe système.
De façon générale, ces classes sont une vision des éléments du browser, sont également définies des classes auxiliaires permettant d'accéder aux autres informations, par exemple les différents settings. Ainsi, un package est représenté par la classe utilisateur UmlPackage qui hérite de la classe système UmlBasePackage, etc. Par exemple, les classes relatives aux packages et vues sont les suivantes :
III-B. Début d'exécution▲
Un plug-out est toujours appliqué à un élément du browser, et comme tout programme qui se respecte son exécution débute par le main. Une définition par défaut est donnée via l'artefact main, celui-ci est défini dans le Deployment view API USER :
Notre but est de trier les sous-éléments de l'élément du browser sur lequel le plug-out est appliqué, par exemple de trier les éléments se trouvant dans une class view. L'élément sur lequel est appliqué le plug-out est obtenu en appelant l'opération targetItem définie pour UmlCom, le résultat est un UmlItem qui est la classe de base de toutes les classes associées aux éléments du browser. Comme vous le voyez ci-dessus, cette opération est appelée dans la définition par défaut de main.
III-C. Traiter les erreurs de configuration du plug-out▲
Le plug-out de tri n'a d'intérêt que s'il est appliqué à un package, une vue ou un use case, et normalement il sera configuré dans BOUML pour n'être appliqué qu'a ceux-ci. Mais parce que la configuration du plug-out peut être erronée, il faut prendre en compte les autres cas. Pour se faire, définissons l'opération sort au niveau UmlItem pour indiquer l'erreur, et redéfinissons cette opération pour les classes UmlPackage, UmlUseCaseView, UmlClassView, UmlComponentView, UmlDeploymentView et UmlUseCase. Dans le main vous avez donc juste à appeler sort sur le résultat de targetItem :
Cette façon de faire n'est pas la seule possible, mais c'est la plus propre. L'opération kind définie pour chaque sous-classe système terminale de UmlItem permet de connaître le type réel d'un élément, mais faire un switch dessus n'est vraiment pas joli joli !
Maintenant, définissez sort pour UmlItem. Pour indiquer l'erreur à l'utilisateur, vous disposez de l'opération message définie sur UmlCom. Le message envoyé est une chaîne de caractères qui sera affichée dans la fenêtre de trace de BOUML. Cette chaîne doit être un bloc HTML valide, attention donc aux caractères &, < ou > !
L'ensemble des capacités d'HTML n'est pas accessible, mais vous pouvez par exemple écrire en italique ou en gras, et changer la couleur de la police de caractères.
L'opération sort ne retourne pas de valeur, et ne prend pas de paramètres. En C++ cette opération doit bien sûr être déclarée virtuelle :
En C++, l'artefact doit être modifié pour ajouter #include « UmlCom.h » dans le source.
Pour rapidement retrouver cet artefact, appelez le menu de la classe UmlItem et choisissez select associated artefact, puis, pour revenir vers la classe une fois l'artefact édité, appelez le menu de l'artefact et choisissez select the associated class.
III-D. Trier les sous-éléments▲
Dans tous les cas le but est le même : trier les sous-éléments, faisons faire cela par l'opération sortChildren définie sur UmlItem. Ainsi il suffira d'appeler sortChildren à partir de l'opération sort définie sur UmlPackage, UmlUseCaseView, UmlClassView, UmlComponentView, UmlDeploymentView et UmlUseCase. Donc, définissions sort pour UmlPackage appelant simplement sortChildren et, pour rapidement avoir la même définition pour les autres classes, utilisons les marques : clic gauche avec la touche Control enfoncée sur l'opération sort de UmlPackage dans le browser, puis utilisez l'entrée de menu duplicate marked into sur UmlUseCaseView, etc. dans le browser.
Pour réaliser le tri, il y a deux possibilités : écrire l'algorithme de tri, ou utiliser une bibliothèque. Même si implémenter quicksort est trivial, utilisons une bibliothèque, car c'est plus intéressant pour ce tutoriel. En Java une opération de tri est définie sur les tableaux dont les éléments implémentent Comparable. En C++ vous pouvez utiliser l'opération de tri définie pour les QVector via une sous-classe implémentant compareItems. Dans les deux cas, le tri est d'abord fait dans la mémoire du plug-out, puis les éléments du browser seront déplacés pour suivre le même ordre. Remarquez que l'implémentation du tri met en jeu des classes différentes dans les deux langages.
III-E. Trier en Java▲
Parce que le tri porte sur des instances de plusieurs classes différentes, je propose de définir Comparable sur UmlItem. Étant donné que seules une classe et une opération doivent être définies, il est inutile d'utiliser Java catalog et tout sera fait à la main :
- créez un package appelé aux sous le projet ;
- dans aux, créez une class view également appelée aux ;
- dans cette class view, créez la classe Comparable. Changez son stéréotype en interface. Dites que la classe est externe en Java, grâce à cela le générateur de code ne produira pas de message d'avertissement parce qu'elle n'a pas de définition. Enfin, même si ce n'est pas obligatoire, définissez l'opération compareTo (avec un corps vide) ;
- créez un class diagram pour vous permettre de dire que UmlItem réalise Comparable, et dites que cette réalisation n'existe pas en C++ ;
- définissez compareTo sur UmlItem, par exemple en appelant le menu de la classe et en choisissant add inherited operation. Dites que l'opération n'est pas définie en C++ ;
- la comparaison doit d'abord tenir compte du type de l'élément, puis de son nom. Pour cela, définissez l'opération orderWeight retournant un int valant 0 pour les UmllItem (la valeur n'est pas importante si vous prenez en compte tout les types d'éléments possibles) ;
- le nom d'un élément du browser est obtenu par l'opération name, la définition de int compareTo(Object o) est donc :
III-F. Trier en C++ avec l'aide de Qt▲
- Coté C++, dites que orderWeight est virtuelle au niveau UmlItem et qu'elle retourne aussi 0.
- Dans la class view aux créez la classe paramétrée (template) QVector, dites qu'elle n'est pas implémentée en Java, et dites qu'elle est externe en C++ avec la définition suivante :
- Même si cela n'est pas obligatoire, définissez l'opération int compareItems(QCollection::Item d1, Qcollection::Item d2).
- Définissez la classe VectorOfUmlItem héritant de Qvector et dites que cette classe n'est pas définie en Java. En C++ sa définition doit être (l'héritage doit être défini avant d'éditer la classe afin d'accéder aux actuals) :
- Sur VectorOfUmlItem définissez compareItems ainsi :
- Pour éviter de créer un artefact pour VectorOfUmlItem, éditez l'artefact UmlItem et ajoutez VectorOfUmlItem parmi les classes associées.
Finalement :
III-G. L'opération orderWeight▲
Maintenant il faut définir orderWeight, pour avoir l'ordre voulu la valeur retournée peut être :
- UmlPackage (package) : 1 ;
- UmlUseCaseView (use case view) : 2 ;
- UmlClassView (class view) : 3 ;
- UmlComponentView (component view): 4 ;
- UmlDeploymentView (deployment view) : 5 ;
- UmlUseCaseDiagram (use case diagram), UmlClassDiagram (class diagram), UmlComponentDiagram (components diagram), UmlDeploymentDiagram (deployment diagram) : 6 ;
- UmlSequenceDiagram (sequence diagram) : 7 ;
- UmlCollaborationDiagram (collaboration diagram) : 8 ;
- UmlUseCase (use case), UmlState (state machine), UmlComponent (component), UmlNode (deployment node) : 9 ;
- UmlClass (class), UmlArtifact (artifact) : 10 ;
- UmlRelation et UmlNcRelation de type généralisation (relationKind() retourne aGeneralisation) : 11 ;
- UmlRelation et UmlNcRelation de type dépendence (relationKind() retourne aDependency) : 12.
Définissez donc l'opération pour chaque classe, avec la bonne valeur retournée, pour C++ et pour Java.
Le plus rapide pour le faire est de dupliquer l'opération déjà définie sur UmlItem.
III-H. L'opération sortChildren▲
L'opération children définie sur UmlBaseItem retourne un vecteur d'UmlItems.
Pour changer l'ordre des éléments dans le browser, il faut utiliser l'opération moveAfter définie sur UmlBaseItem. Cette opération prend un paramètre, si celui-ci est nul, l'élément est déplacé pour être le premier sous-élément de l'élément qui le contient, sinon l'élément est déplacé pour être juste après l'élément donné en paramètre.
Il est seulement possible de changer l'ordre des éléments dans le browser, il n'est pas possible de déplacer un élément d'un conteneur à un autre (même si cela peut être fait dans certains cas à la main dans le browser). Donc l'opération moveAfter ne fait rien si l'élément sur laquelle elle est appliquée et le paramètre n'ont pas le même conteneur dans le browser.
La définition de sortChildren en C++ est donc :
La définition de sortChildren en Java est donc :
L'écriture du plug-out est terminée.
IV. Générer le code▲
Pour générer le code du plug-out vous devez dire où les sources seront placés, éditez les generation settings par exemple pour avoir :
Tout devant être généré, demandez la génération de code pour C++ et/ou Java via le menu Tools, ou en utilisant le menu au niveau du projet dans le browser.
Pour compiler les sources C++ sous Linux ou Mac OS X vous avez besoin d'un Makefile. Le plus simple est de produire le fichier .pro en appliquant le plug-out genpro sur l'artefact appelé executable. Comme sort n'utilise rien aucune information spécifique à un des langages (on n'accède qu'au nom des éléments) autant retirer la définition des defines (l'exécutable sera plus petit), et disons que le nom de l'exécutable à produire est browsersort :
Utilisez qmake pour produire le Makefile, puis make pour compiler.
En Java vous utilisez bien sûr javac pour compiler les sources, mais comme certains fichiers contiennent plusieurs classes de base vous ne devez pas compiler les sources un à un, mais tous ensemble par javac *.java dans une ligne de commande.
Voilà, tout est fini, mais n'oubliez pas de sauver votre projet !
V. Utiliser le plug-out▲
Pour pouvoir être utilisé afin de trier les éléments d'un projet, vous devez déclarer le plug-out dans le projet. Dans le menu Tools choisissez Tools settings, ci-dessous les déclarations sont faites pour les implémentations en C++ et Java (mais une seule est suffisante) :
Maintenant le tri est disponible dans le sous-menu tool du menu du projet, des packages, des vues et des use cases.
VI. Conclusion▲
VI-A. Épilogue▲
Facile, non ?
N'hésitez pas à écrire vos propres plug-outs, l'automatisation des actions vous rendra la vie plus agréable, et plus sûre en évitant les erreurs des opérations manuelles. Regardez la définition des plug-outs distribués avec BOUML pour vous aider si besoin.
Si vous pensez avoir développé un plug-out utile à d'autres, vous pouvez me proposer de le diffuser via le site de BOUML.
Il n'y a pas de problème de compatibilité des plug-outs d'une version de BOUML à une autre, de la même façon d'un projet défini avec une version de BOUML est utilisable avec les versions postérieures. Qui plus est, lorsque des nouveautés sont introduites dans BOUML, le plug-out upgrade vous permet de mettre à jour vos plug-outs existants pour bénéficier de ces nouveautés (mais ce n'est pas une obligation). C'est par exemple que je mets à jour le générateur de document HTML.
VI-B. Liens▲
- Le site de Bouml : http://bouml.free.frsite de Bouml
- Le tutoriel premiers pas avec Bouml est iciPremiers pas avec Bouml
- Le tutoriel les classes et la génération de code sous Bouml est iciLes classes et la génération de code sous Bouml
Merci à Miles pour la relecture de ce tutoriel.
Bonnes modélisations !