Ajout d'un diagramme à ArgoUML

Florent de LAMOTTE


Table des matières

Qu'est-ce qu'un diagramme
Le GraphModel
Le GraphRenderer
Les Layer
La toolbar
L'organisation des diagrammes dans Argo
Le package
La création d'un nouveau diagramme
Un premier choix
Création d'un module
Ajout d'une commande au menu
Le Diagramme
La création du diagramme

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 fichiers d'exemple, créés pendant l'élaboration de ce tutoriel sont disponibles ici. Vous pouvez tester le module directement en le décompactant dans le répertoire modules d'ArgoUML et en exécutant la commande ant run dans le répertoire test nouvellement créé (il faudra bien sur pour cela que Ant et le code source d'ArgoUML soient bien installés).

Qu'est-ce qu'un diagramme

Un diagramme est une entité composée de :

  • Un Layer

  • Un GraphModel

  • Une Toolbar

  • Un Title

La classe de base pour réaliser un diagramme UML Dans ArgoUML est la classe UMLDiagram, qui hérite de ArgoDiagram. Ces deux classes qui se trouvent respectivement dans org.argouml.uml.diagram.ui et org.argouml.ui.

On ajoute une figure au diagramme à l'aide de la methode add

Il est possible de récupérer les éléments d'un diagramme au travers de la méthode elements qui renvoie une Enumeration contenant les figures présentes dans le diagramme. La suppression d'une figure se fera à l'aide de la methode remove.

Le GraphModel

Un GraphModel stocke le modèle qui se cache derrière un diagramme. C'est au niveau du GraphModel que l'on va ajouter des éléments (si on peut ajouter des éléments à un GraphModel, on parle de MutableGraphModel). On peut ajouter un listener à un GraphModel, qui renverra alors un GraphEvent lorsqu'une modification est apportée au modèle.

Un des gros interêts du GraphModel est qu'il permet de gérer la connection entre plusieurs éléments il se charge en effet, au travers de la méthode connect de relier deux ports et de créer (s'il y en a besoin) la classe qui reliera ces deux ports. Une fois que la liaison est faite, le GraphModel enverra un Evenement edgeAdded pour que la partie graphique (le Layer en fait) sache qu'une liaison à été ajoutée.

Le GraphRenderer

Un GraphRenderer est une classe qui implémente deux interfaces : GraphNodeRenderer et GraphEdgeRenderer.

Ces deux interfaces fournissent un service permettant de créer une figure associée à un objet (un "node" ou une "edge" du diagramme). Les methodes permettant de faire cela sont getFigNodeFor et getFigEdgeFor.

Les Layer

Les Layers (calques en français) contiennent les différentes figures d'un diagramme et gèrent leur disposition. Comme ce sont eux qui gèrent les figures, ce sont vers eux qu'il faut se tourner lorsque l'on veut les manipuler.

On peut récupérer un calque lié à un diagramme à l'aide de la methode getLayer d'un diagramme.

Voici une liste de différentes opérations possibles sur une figure accessibles depuis le layer auquel elle appartient. Cette liste n'est pas exhaustive. Pour avoir une liste exhaustive mieux vaut aller voir la liste fournie par le JavaDoc.

  • add : ajoute une figure au Layer

  • remove : supprime une figure du Layer

  • sendToBack, sendToFront : place la figure devant ou derriere les autres figures du Layer

  • sendBackWard, sendForward : gère la position d'une figure dans un layer

  • bringInFrontOf : permet de mettre une figure devant une autre

  • presentationFor : permet de récupérer une figure correspondant à un modèle

Les calques utilisés dans les diagrammes d'Argo sont du type LayerPerspective. Un Layer du type LayerPerspective permet une meilleure liaison avec un GraphModel et un GraphRenderer. En effet, un LayerPerspective va récupérer les évènements en provenance du GraphModel. Lorsque celui-ci signale qu'il vient d'ajouter un node ou un edge au modèle le Layer demande au GraphRenderer de lui créer la figure qui va bien pour ensuite l'ajouter au diagramme.

Figure 1. Ajout d'une figure à un diagramme : connection de deux nodes

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

La toolbar

La barre d'outils est une barre de boutons dans laquelle chaque bouton est relié à une commande GEF. Les commandes GEF utilisées sont généralement CmdCreateNode ou CmdSetMode. CmdCreateNode crée un node et le rajoute au diagramme. CmdSetMode est généralement utilisé pour passer l'editeur dans un mode du type : ModeCreatePolyEdge qui permet de créer un lien entre deux nodes.

L'organisation des diagrammes dans Argo

Le package

Les diagrammes sont généralement placés dans le package : org.argouml.uml.diagram. Ce package comporte normalement un sous-package ui dans lequel sont regroupés toutes les classes qui se raportent à l'ui.

  • Les figures

  • Les Selections

  • La classe diagramme correspondante

  • Le GraphRenderer

Finalement ne reste généralement à la racine du package que le GraphModel et éventuellement des fichiers permettant la représentation du diagramme sous forme de table.

La création d'un nouveau diagramme

Un premier choix

Il vous faudra tout d'abord choisir comment vous voulez intégrer votre diagramme à Argo.

Deux solutions s'offrent à vous :

  1. Vous pouvez ajouter du code à ArgoUML, permettant de prendre en compte votre diagramme.

  2. Vous pouvez créer un module que vous pourrez distribuer indépendamment du code source d'ArgoUML.

Je recommande fortement la création d'un nouveau module implémentant votre diagramme. La raison principale est que vous pouvez ainsi rester totalement indépendant du code source d'ArgoUML et ne souffrirez pas d'une modification d'un fichier du source d'argo que vous aurez du modifier pour que l'utilisateur puisse accéder à votre diagramme.

Création d'un module

Configuration du module

Il vous faudra tout d'abord créer un répertoire dans argouml/modules dans lequel vous mettrez les fichiers correspondant à votre module.

Pour notre exemple nous allons appeller ce répertoire test.

Placez dans ce répertoire un fichier build.xml de configuration de ant et un fichier module.properties qui va paramétrer son fonctionnement. Vous pouvez récupérer ces fichiers dans le répertoire d'un autre module.

Si vous prenez par exemple le fichier build.xml provenant du module php, vous n'aurez normalement rien à modifier, tous les paramètres pour ce module se trouvant dans le fichier module.properties (Par contre le fichier build.xml du module junit est un peu différent puisqu'il a besoin de librairies externes.)

Le fichier module.properties comporte pour l'instant deux lignes (ce sont des propriétés ant).

  • argo.module.name : permet de spécifier le nom du module

  • argo.module.jarfile : permet de spécifier le nom de l'archive .jar

Pour notre module, le fichier de propriétés sera le suivant :

argo.module.name=test
argo.module.jarfile=argo_test
	  

Une fois que vous avez vos fichierz build.xml et module.properties vous pouvez tester la configuration de votre module en tapant ant.

Voici ce que ant devrait vous sortir :

	  
$ ant
Buildfile: build.xml

init:
     [echo] ------------------- ArgoModule-test 0.9.5-20020718-0858 [2001] ----------------

usage:
     [echo] 
     [echo] 
     [echo] ArgoModule-test Build file
     [echo] -------------------------------------------------------------
     [echo] 
     [echo]  available targets are:
     [echo] 
     [echo]    compile      --> compiles the source code to the tree under ../../modules/test/build
     [echo]    package      --> generates the argo_test.jar file
     [echo]    run          --> runs ArgoUML argo_test.jar
     [echo]    install      --> merges ./org into ../src_new/org tree
     [echo]    usage        --> show this message (default)
     [echo] 
     [echo]  See the comments inside the build.xml file for more details.
     [echo] -------------------------------------------------------------
     [echo] 
     [echo] 

BUILD SUCCESSFUL
Total time: 2 seconds
	  
	

Si vous obtenez quelque chose de semblable, c'est que la configuration de la compilation est correcte. Vérifiez surtout que le nom de l'archive est bien celui que vous aviez demandé, il est normalement visible sur les lignes package et run. De même, vérifiez le nom du module (vous devez voir s'afficher ArgoModule-test lors de l'init.

Mise en place de l'arborescence

Une fois que ant est configuré, vous pouvez créer l'arborescence des répertoires.

Créez donc un répertoire src dans lequel vous placerez les package c'est à dire une arborescence org/argouml.

Créez aussi un répertoire lib qui sera utilisé pour placer d'éventuelles librairies externes.

Le fichier manifest.mf pour les modules

Pour que le chargement du module s'effectue correctement, il vous faudra écrire un fichier manifest.mf indiquant les informations nécéssaires au chargement du module. Voici le manifest.mf pour notre module test.

	  
Manifest-Version: 2.0
Class-Path: test.jar
Created-By: 1.3.1 (Sun Microsystem Inc.)

Name: org/argouml/uml/ui/ActionTestDiagram.class
Extension-name: module.tools.test
Specification-Title: ArgoUML Dynamic Load Module
Specification-Version: 0.9.10
Specification-Vendor: University of California
Implementation-Title: Test Module
Implementation-Version: 0.9.10
Implementation-Vendor: test
	  
	

Une petite explication s'impose ...

  • Le champ Manifest-Version permet de spécifier la version du format utilisé, ici 2.0.

  • Le champ ClassPath nous donne le nom des classes à charger en même demps que le module

  • Le champ Created-By contient le nom du compilateur utilisé pour compiler les classes.

  • Le champ Name explicite le nom de la classe principale. Cette classe sera chargée par le classloader d'Argo en tant que module.

  • ...

Le fichier manifest.mf doit être placé à la racine de l'archive, c'est à dire dans le répertoire src/org.

Un petit test de l'installation

Nous allons maintenant pouvoir tester que tout fonctionne correctement avant de commencer.

Tout d'abord, nous allons essayer de créer le package à l'aide de la commande ant package que vous devez exécuter dans le répertoire de base de votre module.

	  
$ ant package
Buildfile: build.xml

init:
     [echo] ------------------- ArgoModule-test 0.9.5-20020718-0941 [2001] ----------------

prepare:
     [echo] Preparing the build directories

compile:
     [echo] Compiling the sources

package:
     [copy] Copying 1 file to /usr/src/argo/modules/test/build/classes
      [jar] Building jar: /usr/src/argo/modules/test/build/argo_test.jar

BUILD SUCCESSFUL

Total time: 3 seconds
	  
	

Vous devriez obtenir le même résultat. Après l'exécution de la commande, un fichier build/argo_test.jar devrait avoir été créé.

Bien sur l'archive argo_test.jar que vous avez générée ne devrait contenir que le fichier manifest.mf.

Vous pouvez même essayer la commande ant run qui devrait normalement lancer ArgoUML avec votre module. Bien sur comme la classe que vous avez spécifié dans le champ Name de votre manifest.mf n'existe pas, le chargement du module devrait échouer et vous devriez voir s'afficher :

     [java] Could not instantiate module org/argouml/uml/ui/ActionTestDiagram
	  

Si par contre ArgoUML ne se lance pas et que vous obtenez

	  
run:
     [echo] --- Executing ArgoModule-test ---
     [java] Exception in thread "main" java.lang.NoClassDefFoundError: org/argouml/application/Main
     [java] Java Result: 1
	  
	

C'est que vous n'avez pas créé le fichier argouml.jar qui doit être placé dans le répertoire build d'Argo.

Un simple ant package dans le répertoire src_new d'Argo devrait résoudre le problème.

Ajout d'une commande au menu

Pour pouvoir créer notre diagramme, in nous faut créer une entrée dans l'un des menus d'Argo. Pour cela, nous allons utiliser les fonctionnalités de PluggableMenu.

L'interface PluggableMenu

PluggableMenu est une interface permettant d'écrire des Plug-ins pour Argo. PluggableMenu dérive de Pluggable qui elle même dérive de Module.

La figure suivante présente le diagramme de classe correspondant aux modules.

Figure 2. Diagramme de classe des Modules

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

Les méthodes fournies par l'interface Module servent principalement à identifier le module, l'initialiser et finalement à le fermer proprement.

La méthode inContext de l'interface Pluggable sert au plugin à se ratacher à une partie d'Argo.

La méthode getMenuItem de l'interface PluggableMenu est le gros interêt de cette interface, c'est au travers de cette méthode qu'ArgoUML va récupérer le JMenuItem fourni par notre PluggableMenu et l'insérer dans le bon context (précédemment demandé).

La méthode buildContext de l'interface PluggableMenu permet quant à elle de créer le tableau d'object qui sera renvoyé a la méthode inContext. Cette méthode est utilisée par Argo.

Le diagramme de séquence suivant explique comment se fait l'insertion d'une entrée dans le menu d'ArgoUML.

Figure 3. Insertion d'un menu

Désolé, l'image n'a pas pu être affichée

Notre PluggableMenu

Nous allons maintenant écrire notre PluggableMenu. Que nous allons appeller ActionTestDiagram et qui sera un fils de la classe UMLAction tout en implémentant PluggableMenu (ceci nous permet d'avoir dans la même classe l'action qui créé le menu et celle qui est éxecutée lorsque l'item est séléctionné).

Notre classe devra bien sur implémenter toutes les méthodes de Module, de Pluggable et de PluggableMenu, ainsi que ActionPerformed qui sera l'action exécutée par notre menu (pour commencer ce sera un simple System.out.println("Hello World!!").

Elle sera placée dans le répertoire src/org/argouml/uml/ui que vous devez créer, et appartiendra donc au package org.argouml.uml.ui.

	  
package org.argouml.uml.ui;

import org.argouml.ui.*;
import org.argouml.application.api.*;
import org.argouml.uml.ui.*;
import org.argouml.uml.*;
import org.argouml.kernel.*;

import org.tigris.gef.base.*;
import org.tigris.gef.util.*;

import ru.novosoft.uml.foundation.core.*;
import ru.novosoft.uml.behavior.state_machines.*;
import ru.novosoft.uml.model_management.*;

import java.awt.event.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import java.beans.*;

/**
 * Notre classe ActionTestDiagram
 */
public class ActionTestDiagram extends UMLAction
     implements PluggableMenu {

     public ActionTestDiagram() { super ("TestDiagram"); }

     private static JMenuItem _menuItem = null;

     /////////////////////////////////////////
     // implementation de UMLAction

     /**
      * Action executée par la selection du menu
      */
     public void actionPerformed (ActionEvent ae) {
	  System.out.println("HelloWorld!!");
     }


     /////////////////////////////////////////////////////////////////
     // implementation de l'interface Module

     private boolean _initialized = false;

     public boolean initializeModule() {
	  Argo.log.info("*** Initialisation de TestDiagram");

	  _initialized = true;

	  return _initialized;
     }


     public void setModuleEnabled(boolean enabled) { }
     public boolean isModuleEnabled() { return true; }
     public Vector getModulePopUpActions(Vector v, Object o) { return null; }
     public boolean shutdownModule() { return true; }

     public String getModuleName() { return "TestDiagram"; }
     public String getModuleDescription() { return "TestDiagram"; }
     public String getModuleAuthor() { return "Florent de Lamotte"; }
     public String getModuleVersion() { return "0.9.10"; }
     public String getModuleKey() { return "module.tools.test"; }


     /////////////////////////////////////////////////////////////////
     // Implémentation de l'interface Pluggable

     /**
      * Permet au module d'insérer le menu ou il le veut
      * @param o l'objet décrivant le contexte le premier objet est un JMenuItem 
      * et le second une clé correspondant à ce JMenuItem et correspondant à une entrée
      * dans le menu
      * @return true si l'emplacement nous convient
      */
     public boolean inContext(Object[] o) {
	  if (o.length < 2) return false;
	  if ((o[0] instanceof JMenuItem) && ("Create Diagrams".equals(o[1]))) {
	       return true;
	  }
	  return false;
     }

     /////////////////////////////////////////////////////////////////
     // Implémentation de l'interface PluggableMenu

     /**
      * Retourne le JMenuItem que l'on veut ajouter au menu
      *
      * @param mi le JMenuItem dans lequel sera ajouté notre JMenuItem
      * @param s  La clé correspondante (celle qui avait été passée lors du inContext
      * @return Le JMenuItem correspondant à ce menu
      */
     public JMenuItem getMenuItem(JMenuItem mi, String s) {
	  if (_menuItem == null) {
	       _menuItem = new JMenuItem("Test Diagram",
					 ResourceLoader.lookupIconResource("TestDiagram"));
	  }
	  _menuItem.addActionListener(this);
	       
	  return _menuItem;
     }

     /**
      * Construit l'objet qui sera envoyé a inContext
      */
     public Object[] buildContext(JMenuItem a, String b) {
	  return new Object[] { a, b };
     }
}
	  
	

Une fois que la nouvelle classe est écrite, vous pouvez lancer Argo avec votre Nouveau module grâce à la commande ant run depuis le répertoire de votre module.

Votre module devrait être chargé par Argo, et vous devriez voir les message suivant s'afficher :

	    
     [java] *** Initialisation de TestDiagram
     [java] Loaded Module: TestDiagram
	    
	  
De plus, dans le menu "Créer Diagramme", un nouvel item est apparu : "Test Diagram" Si on selectionne cet item, HelloWorld!! s'affiche sur la console.

Au chargement du module, un message indique qu'il n'a pas pu trouver une Icône ... en effet, lorsque nous avons créé notre JMenuItem, nous lui avons associé l'icône TestDiagram que nous n'avons ni créée ni placée dans le jar.

Les icônes sont placées dans le répertoire src/org/argouml/Images vous pouvez donc y placer une icône portant le nom TestDiagram.gif au format .gif.

Il va nous falloir modifier un petit peu le fichier build.xml pour qu'il copie toutes les images du répertoire src/org/argouml/Images dans le répertoire build/classes/org/argouml/Images, ce qui permettra ensuite à GEF de retrouver l'icône (c'est en fait le RessourceLoader de GEF qui s'occupe de ça.

Les modifications sont à effectuer pour la cible prepare qui s'occupe de préparer les répertoires dans lesquels seront placés les fichiers à archiver.

	  
  <!-- =================================================================== -->
  <!-- Prepares the build directory                                        -->
  <!-- =================================================================== -->
  <target name="prepare" depends="init">
    <!-- create directories -->
    <echo message="Preparing the build directories"/>
    <copy todir="${module.build.dest}/org/argouml/Images">
       <fileset dir="${module.build.src}/org/argouml/Images"
              includes="**/**" excludes="CVS/**"/>
    </copy>               
  <!-- These must be there already -->
  </target>
	  
	

Si vous executez ant run, vous devriez maintenant voir votre icône à côté du texte pour votre JMenuItem.

Le Diagramme

Nous arrivons enfin au sujet de l'article ... la création d'un nouveau diagramme.

Les fichiers sources concernant notre diagramme seront placés dans src/org/argouml/uml/diagram/test/, le package sera donc org.argouml.uml.diagram.test et il vous faut créer ce répertoire ainsi que le répertoire src/org/Argouml/uml/diagram/test/ui dans lequel seront placées toutes les classes se rapportant à l'interface utilisateur.

Le diagramme que nous allons réaliser sera un diagramme de classe tout simple dans lequel les classes sont représentées par des rectangles et dans lequel il n'y a que des associations entre les classes.

Le GraphModel

Nous allons commencer par écrire un GraphModel pour notre diagramme.

Il s'agit juste de remplir les methodes du MutableGraphModel. Par contre, le nombre de methodes est assez important. Pour l'exemple, j'ai pris le GraphModel du Diagramme de Classe que j'ai simplifié en enlevant les généralisations, et les interfaces.

Le GraphModel est composé de deux vecteurs qui sont _nodes et _edges et qui contiennent les noeuds et les liaisons du modèle.

On peut remarquer dans le code du GraphModel qu'à chaque fois qu'un élément du modèle est ajouté ou enlevé, un évènement est envoyé. Il sera récupéré par le layer qui propagera les modifications.

Le GraphRenderer

Le GraphRenderer est encore plus simple à implémenter que le GraphModel puisqu'il suffit de renvoyer la figure qui va bien.

Nous allons appeller les figures qui représenteront les classes FigClass et celles qui représenteront les associations FigAssociation.

Lorsque l'on veut une figure pour une classe, on appelle getFigNodeFor cette fonction renverra une nouvelle FigClass correspondante. Pour une liaison, il faut en plus récupérer les extrémités et le connecter ce qui demande beaucoup plus de travail. Ci dessous l'implémentation de FigNodeFor pour notre diagramme.

	  
     public FigEdge getFigEdgeFor(GraphModel gm, Layer lay, Object edge) {
	  if (edge instanceof MAssociation) {
	       MAssociation a = (MAssociation) edge;
	       FigAssociation aFig = new FigAssociation(a);
	       Collection connections = a.getConnections();
	       if (connections == null) System.out.println("null connections....");
	       Object[] con = connections.toArray();
	       MAssociationEnd fromEnd = (MAssociationEnd) con[0];
	       MClassifier fromCls = (MClassifier) fromEnd.getType();
	       MAssociationEnd toEnd = (MAssociationEnd) con[1];
	       MClassifier toCls = (MClassifier) toEnd.getType();
	       FigNode fromFN = (FigNode) lay.presentationFor(fromCls);
	       FigNode toFN = (FigNode) lay.presentationFor(toCls);
	       aFig.setSourcePortFig(fromFN);
	       aFig.setSourceFigNode(fromFN);
	       aFig.setDestPortFig(toFN);
	       aFig.setDestFigNode(toFN);
	       aFig.getFig().setLayer(lay);
	       return aFig;
	  }
	  return null;
     }
	  
	

Les figures

Il nous allons maintenant créer les figures pour notre diagramme. Nous avons deux figures à créer une du type FigNodeModelElement et l'autre du type FigEdgeModelElement.

Les noeuds (FigNodeModelElement) dérivent de FigNode dans GEF. Elles sont constitué d'un ou de plusieurs ports ainsi que de figures qui permettent de dessiner l'element. La plupart des diagrammes dans Argo n'ont qu'un seul port qui s'appelle généralement _bigPort. Voici le constructeur de notre FigClass.

	  	  
     public FigClass() {
	  _bigPort = new FigRect(X+1, Y+1, W+2, H+2, null, null);
	  _bigPort.setLineWidth(0);

	  _rectangle = new FigRect(X, Y, W, H, Color.black, Color.yellow);
	  _rectangle.setFilled(true);

	  setBounds(X+1, Y+1, W+2, H+2);

	  addFig(_bigPort);
	  addFig(_rectangle);
     }

     public FigClass(GraphModel gm, Object node) {
	  this();
	  setOwner(node);
     }
	  
	

On construit les éléments de la figure et on les ajoute ensuite à notre figure. Le port (_bigPort) est un élément comme les autres. Ici il est invisible.

Le fait que le port par défaut soit _figPort est défini dans la classe FigNodeModelElement. Si on veut ajouter d'autres ports pour des types de liaisons différente, on peut utiliser la méthode bindPort qui associe un Object à une figure du dessin.

Il est nécéssaire de surcharger la fonction setBounds pour que le redimensionnement de la figure se fasse correctement.

	  
     public void setBounds(int x, int y, int w, int h) {
	  Rectangle oldBounds = getBounds();
	  _bigPort.setBounds(x, y, w, h);
	  _rectangle.setBounds(x,y,w,h);
	  calcBounds();
	  updateEdges();
	  firePropChange("bounds", oldBounds,getBounds());
     }
	  
	

Comme on peut le voir, il est nécessaire de lancer un événement PropChange pour propager des modification aux listeners.

Si maintenant, on veut ajouter le nom de la classe sur le diagramme, il nous faut ajouter une nouvelle figure. On va d'abord modifier le constructeur.

	  
     public FigClass() {
	  _bigPort = new FigRect(X+1, Y+1, W+2, H+2, null, null);
	  _bigPort.setLineWidth(0);

	  _rectangle = new FigRect(X, Y, W, H, Color.black, Color.yellow);
	  _rectangle.setFilled(true);

	  _name = new FigText(W, Y, W, H);
	  _name.setFilled(false);
	  _name.setLineWidth(0);
	  
	  Rectangle r = _name.getBounds();
	  setBounds(r);

	  addFig(_bigPort);
	  addFig(_rectangle);
	  addFig(_name);
     }
	  
	

On peut noter qu'il est important d'effectuer les appels à addFig dans le bon ordre puisque cet appel détermine la position des figures les unes par rapport aux autres.

Pour que le champ _name soit correctement initialisé lors de l'insertion d'une figure dans le diagramme, il faut surcharger la méthode modelChanged.

	  
     public void modelChanged() {
	  MClass owner = (MClass)getOwner();
	  String name = owner.getName();
	  _name.setText(name != null ? name : "");
     }
	  
	

Il nous faut aussi modifier l'appel à clone pour que _name soit correctement clonnée.

	  
     public Object clone() {
	  FigClass figClone = (FigClass) super.clone();
	  Vector v = figClone.getFigs();
	  figClone._bigPort = (FigRect) v.elementAt(0);
	  figClone._rectangle = (FigRect) v.elementAt(1);
	  figClone._name = (FigText) v.elementAt(2);
	  return figClone;
     }
	  
	

En ce qui concerne notre FigAssociation, son implémentation n'est pas très compliquée puisqu'elle ne comprends qu'un constructeur.

UMLTestDiagram

UMLTestDiagram est la classe qui représente notre diagramme.

Les premières définitions que l'on rencontre sont les définitions statiques des Cmd permettant de créer nos figures


     protected static Action _actionClass =
     new CmdCreateNode(MClassImpl.class, "Class");

     protected static Action _actionAssociation =
     new CmdSetMode(ModeCreatePolyEdge.class,
		    "edgeClass", MAssociationImpl.class,
		    "Association");

	

Ensuite vient notre constructeur qui est chargé de construire tout l'environnement du diagramme c'est-à-dire le TestDiagramGraphModel, LayerPerspective et TestDiagramRenderer et d'initialiser tout ça.


     public UMLTestDiagram() {
	  String name = "Test diagram " + _TestDiagramSerial;
	  _TestDiagramSerial++;

	  try { 
	       setName(name); 
	  } catch (PropertyVetoException pve) { }
     }

     public UMLTestDiagram(MNamespace m) {
	  this();
	  TestDiagramGraphModel gm = new TestDiagramGraphModel();
	  setNamespace(m);
	  setGraphModel(gm);
	  LayerPerspective lay = new LayerPerspectiveMutable(m.getName(), gm);
	  setLayer(lay);
	  TestDiagramRenderer rend = new TestDiagramRenderer();
	  lay.setGraphNodeRenderer(rend);
	  lay.setGraphEdgeRenderer(rend);
     }

	

Les méthodes getOwner et setNameSpace sont des accesseurs vers le GraphModel.

Finalement initToolBar initialise la toolbar en créant les boutons et en y associant des actions.

La création du diagramme

Maintenant que nous avons tous les éléments du diagramme, on aimerait bien pouvoir le tester. Pour cela il faut pouvoir le créer et cela se fait normalement à l'aide d'une séléction d'un élement dans un menu (comme par hasard nous avons déjà réservé de la place pour cela.

Il va donc nous falloir remplacer la méthode actionPerformed de ActionTestDiagram par une vraie actionPerformed qui fait quelque chose d'utile : instancier UMLTestDiagram.

Voilà le code de notre nouvelle ActionPerformed :


     /**
      * Action executée par la selection du menu
      */
     public void actionPerformed (ActionEvent ae) {
	Project p = ProjectBrowser.TheInstance.getProject();
	Object target = ProjectBrowser.TheInstance.getDetailsTarget();
	MNamespace ns = p.getCurrentNamespace();
	if (target instanceof MPackage) ns = (MNamespace) target;
	try {
	    ArgoDiagram d = new UMLTestDiagram(ns);
	    p.addMember(d);
	    ProjectBrowser.TheInstance.getNavPane().addToHistory(d);
	    ProjectBrowser.TheInstance.setTarget(d);
	}
	catch (PropertyVetoException pve) { }

     }

      

Comme on peut le voir, quelques enregistrements aux bons endroits et c'est gagné !!

Une fois que tout ceci est terminé, on peut normalement tester notre nouveau diagramme.