juillet
2009
A l’étape du billet précédant [step7] l’instance EMF WorkflowType (uniquement) est représentée graphiquement après avoir initialisé l’EditPart et la Figure Draw2d pour le modèle EMF WorkflowType. Dans ce billet nous allons nous occuper des instances EMF enfants ActionType et StateType et les représenter graphiquement :
- afficher un carré pour les instances EMF ActionType.
- afficher une ellipse pour les instances EMF StateType.
Vous pouvez télécharger le projet org.example.workflow_step8.zip présenté dans ce billet.
GEF – ActionType
Model – ActionTypeFigure
Ici la couche modèle est représentée par les instances EMF ActionType appartenant à l’instance EMF WorkflowType.
View – ActionTypeFigure
La vue d’une action ActionType doit être représentée par une figure carré. Elle suit le même principe que la vue WorkflowTypeFigure, figure Draw2d qui gère la vue de l’instance EMF WorkflowType. Draw2d propose des figures de base dont org.eclipse.draw2d.RectangleFigure que nous allons utiliser pour représenter une action par un carré.
Créer la classe org.example.workflow.presentation.graphical.figures.ActionTypeFigure comme suit :
import org.eclipse.draw2d.RectangleFigure;
public class ActionTypeFigure extends RectangleFigure {
}
Controlleur – ActionTypePart
Le contrôleur d’une instance EMF ActionType suit le même principe que le contrôleur WorkflowTypePart, EditPart qui gère l’instance EMF WorkflowType.
Créer la classe org.example.workflow.presentation.graphical.parts.ActionTypePart comme suit :
import org.eclipse.draw2d.IFigure;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.example.workflow.presentation.graphical.figures.ActionTypeFigure;
public class ActionTypePart extends AbstractGraphicalEditPart {
@Override
protected IFigure createFigure() {
return new ActionTypeFigure();
}
@Override
protected void createEditPolicies() {
}
}
EditPartFactory – ActionTypePart
Pour prendre en compte l’EditPart ActionTypePart, Il faut mettre à jour la factory d’EditPart WorkflowPartFactory pour gérer le modèle EMF ActionType, pour cela ajouter le code suivant dans la méthode WorkflowPartFactory#getPartForElement(Object modelElement) :
return new ActionTypePart();
}
GEF – StateType
Model – StateTypeFigure
Ici la couche modèle est représentée par les instances EMF StateType appartenant à l’instance EMF WorkflowType.
View – StateTypeFigure
La vue d’un etat StateType doit être représentée par une figure ellipse. Elle suit le même principe que la vue WorkflowTypeFigure, figure Draw2d qui gère la vue de l’instance EMF WorkflowType. Draw2d propose des figures de base dont org.eclipse.draw2d.Ellipse que nous allons utiliser pour représenter un StateType par une ellipse.
Créer la classe org.example.workflow.presentation.graphical.figures.StateTypeFigure comme suit :
import org.eclipse.draw2d.Ellipse;
public class StateTypeFigure extends Ellipse {
}
Controlleur – StateTypePart
Le contrôleur d’une instance EMF StateType suit le même principe que le contrôleur WorkflowTypePart, EditPart qui gère l’instance EMF WorkflowType.
Créer la classe org.example.workflow.presentation.graphical.parts.StateTypePart comme suit :
import org.eclipse.draw2d.IFigure;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.example.workflow.presentation.graphical.figures.StateTypeFigure;
public class StateTypePart extends AbstractGraphicalEditPart {
@Override
protected IFigure createFigure() {
return new StateTypeFigure();
}
@Override
protected void createEditPolicies() {
}
}
EditPartFactory – StateTypePart
Pour prendre en compte l’EditPart StateTypePart, Il faut mettre à jour la factory d’EditPart WorkflowPartFactory pour gérer le modèle EMF StateType, pour cela ajouter le code suivant dans la méthode WorkflowPartFactory#getPartForElement(Object modelElement) :
return new StateTypePart();
}
Voici le code en entier de WorkflowPartFactory :
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartFactory;
import org.example.workflow.model.ActionType;
import org.example.workflow.model.StateType;
import org.example.workflow.model.WorkflowType;
public class WorkflowPartFactory implements EditPartFactory {
public static final EditPartFactory INSTANCE = new WorkflowPartFactory();
public EditPart createEditPart(EditPart context, Object modelElement) {
// get EditPart for model element
EditPart part = getPartForElement(modelElement);
// store model element in EditPart
part.setModel(modelElement);
return part;
}
/**
* Maps an object to an EditPart.
*
* @throws RuntimeException
* if no match was found (programming error)
*/
private EditPart getPartForElement(Object modelElement) {
if (modelElement instanceof WorkflowType) {
return new WorkflowTypePart();
}
if (modelElement instanceof ActionType) {
return new ActionTypePart();
}
if (modelElement instanceof StateType) {
return new StateTypePart();
}
throw new RuntimeException("Can't create part for model element: "
+ ((modelElement != null) ? modelElement.getClass().getName()
: "null"));
}
}
Modification WorkflowTypePart
REMARQUE : les tests effectués dans ce billet sont fait en utilisant le modèle XML de workflow :
<workflow xmlns="http://www.example.org/workflow">
<state name="s1"/>
<state name="s2"/>
<action name="a1" fromState="s1" toState="s2"/>
</workflow>
Relancez le plugin et vous pourrez constater que la page Graphics est toujours blanche. La première chose à vérifier est si l’instanciation des EditPart ActionTypePart et StateTypePart fonctionne. Pour cela mettez un point d’arret dans la méthode WorkflowPartFactory#getPartForElement(Object modelElement) de la classe WorkflowPartFactory.
Relancer le plugin, vous pourrez constater que le debug s’arrete une seule fois sur le code :
return new WorkflowTypePart();
}
Autrement dit seul l’EditPart de l’instance EMF WorkflowType est instanciée. Ce comportement est normal car nous n’avons pas défini dans l’EditPart WorkflowTypePart les enfants modèles à représenter graphiquement avec GEF (nos instances EMF StateType et ActionType). Ceci s’effectue via la méthode AbstractEditPart#getModelChildren() classe abstraite d’EditPart. Par défaut cette méthode est implémentée comme ceci :
return Collections.EMPTY_LIST;
}
Elle retourne une Collection vide. Dans notre cas, nous allons surcharger cette méthode dans la classe WorkflowTypePart pour indiquer que les modèles EMF StateType et ActionType doivent être pris en compte par GEF. Pour cela surchargé la méthode WorkflowTypePart#getModelChildren() dans la classe WorkflowTypePart comme ceci :
@Override
protected List<EObject> getModelChildren() {
WorkflowType workflow = getWorkflow();
List<EObject> models = new ArrayList<EObject>(workflow.getState());
models.addAll(workflow.getAction());
return models;
}
public WorkflowType getWorkflow() {
return (WorkflowType) super.getModel();
}
...
La méthode WorkflowTypePart#getModelChildren() retourne la liste des StateType et ActionType EMF appartenant à l’instance EMF WorkflowType.
Relancez le plugin en mode debug. Vous pourrez constater que le debug s’arrête plusieurs fois dans la méthode WorkflowPartFactory#getPartForElement(Object modelElement), 4 fois plus précisement :
- une fois pour l’instance EMF WorkflowType.
- 2 fois pour les 2 instances EMF StateType.
- 1 fois pour l’instance EMF ActionType.
La page Graphics par contre est toujours blanche?
Positionnement figures – Modèle métier – Modèle graphique
Nos figures ne s’affichent pas car elles ne sont pas positionnées. Au début de la conception de mon éditeur de Workflow,je n’avais pas cette problématique en tête. Et pourtant c’est un point très important à penser avant de réaliser son éditeur graphique : comment positionner les figures?
Il existe plusieurs mode de positionnement des figures dans un éditeur graphique :
- positionnement manuel (x,y) : dans ce cas-ci les figures se positionnent manuellement. L’utilisateur positionne ses figures la ou il le souhaite.
- positionnement automatique : dans ce cas-ci les figures se positionnent dans l’éditeur à la bonne place selon un algorithme définit.
- positionnement manuel (x,y) ET automatique: combine les 2 modes. A tout moment on peut basculer d’un mode à un autre.
Le positionnement manuel est très séduisant mais il implique la gestion de la persistance des coordonnées des figures. En effet dans ce mode manuel, les coordonnées doivent être sauvegardé (à la fermeture de l’éditeur) pour être ensuite utilisé lors de la prochaine ouverture de l’éditeur pour repositionner les figures correctement.
Dans tous les exemples que j’ai vu dans GEF, le modèle contenait ces informations x,y. Dans ce cas-ci la persistance des coordonnées s’effectue très facilement.
Mais lorsque le modèle ne contient pas ces informations de coordonnées, ca se complique beaucoup plus (nous sommes dans ce cas-ci, notre modèle EMF WorkflowType ne contient pas les coordonnées de chacune des figures). On voit apparaître 2 modèles dans ce cas-ci :
- le modèle métier : c’est le modèle que l’on souhaite gérer à l’aide d’un éditeur graphique. Dans notre cas, c’est notre contenu XML workflow (qui est géré à l’aide de notre instance EMF WorkflowType).
- le modèle graphique (visuel) : c’est le modèle qui contient les informations visuelles de l’éditeur comme les coordonnées de chacune des figures.
JSF Webtools applique ce principe :
- le modèle métier est le contenu XML faces-config.xml géré par une instance EMF org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType :
<navigation-rule>
<display-name>page1.html</display-name>
<from-view-id>/page1.html</from-view-id>
<navigation-case>
<to-view-id>/page2.html</to-view-id>
</navigation-case>
</navigation-rule> - le modèle graphique est le fichier YOUR_PROJECT/.metadata/WebContent/WEB-INF/faces-config.pageflow géré par une instance EMF org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.Pageflow :
<?xml version="1.0" encoding="UTF-8"?>
<pageflow:Pageflow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:pageflow="http://www.sybase.com/suade/pageflow"
id="pf12399546824680"
configfile="/JSFProject/WebContent/WEB-INF/faces-config.xml">
<nodes xsi:type="pageflow:PFPage" name="page1.html" x="88" y="104" id="pf12399547449371" outlinks="pf12399547449372" path="/page1.html"/>
<nodes xsi:type="pageflow:PFPage" name="page2.html" x="284" y="192" id="pf12399547449373" inlinks="pf12399547449372" path="/page2.html"/>
<links id="pf12399547449372" target="pf12399547449373" source="pf12399547449371"/>
</pageflow:Pageflow>
La difficulté de travailler avec ces 2 modèles (métier et graphique) est de devoir les synchroniser. Dans le cas de JSF Webtools les instances EMF org.eclipse.jst.jsf.facesconfig.emf.FacesConfigType et org.eclipse.jst.jsf.facesconfig.ui.pageflow.model.Pageflow sont synchronisées en utilisant les Adapter EMF (listener EMF). J’ai fait plusieurs tests avec JSF Webtools ou j’ai trouvé plusieurs bugs lorsque les 2 fichiers ne sont plus en cohérences. Par exemple si vous vous amusez a ajouter/supprimer un fragment XML navigation-rule en dehors de l’editeur JSF (editeur XML, editeur Text….), le modèle graphique pageflow n’est plus en cohérence avec le modèle métier faces-config.xml et on perd tous les emplacements des figures.
C’est pourquoi j’ai décidé dans notre éditeur de Workflow de ne gérer que le mode automatique qui positionnera selon un algorithme les figures et les connections. GEF propose des examples comme l’exemple de flow qui gère ce mode automatique. Je reprendrais ces classes pour les adapter à notre modèle WorkflowType.
Dans un premier temps nous allons afficher aléatoirement les figures carrés et ellipses dans la page GEF. Pour cela, nous devons :
- affecter les coordonnées x, y, taille et hauteur des figures pour chacune des figures ActionTypeFigure et StateTypeFigure. Ces coordonnées seront calculés aléatoirement en utilisant la méthode Java classique Math.random()
- indiquer le layout à utiliser dans la figure WorkflowTypeFigure qui contiendra les figures ActionTypeFigure et StateTypeFigure. Le layout Draw2d que nous utiliserons dans un premier temps est org.eclipse.draw2d.XYLayout qui comme son nom l’indique permet de de positionner des figures en leur affectant des coordonnées.
Positionnement x,y – ActionTypePart – StateTypePart
La méthode AbstractEditPart#refreshVisual() est la méthode idéale à surcharger pour synchroniser le modèle avec la vue. Cette méthode est automatiquement appelée par GEF la première fois ou la vue est créée. Cette méthode devra ensuite être appelée manuellement lorsque notre modèle changera pour synchroniser la vue avec les données du modèle. Ici nous souhaitons positionner les coordonnées x,y et taille de la figure. Comme nous l’avons dit précédemment ces coordonnées et taille ne proviennent pas du modèle EMF. Elles sont calculés aléatoirement. Pour positionner les figurés représentant les instances EMF ActionType et StateTypele ajouter le code suivant dans les classes ActionTypePart et StateTypePart :
@Override
protected void refreshVisuals() {
super.refreshVisuals();
int x = new Double(Math.random() * 400).intValue();
int y = new Double(Math.random() * 400).intValue();
Rectangle bounds = new Rectangle(x, y, 50, 50);
((GraphicalEditPart) getParent()).setLayoutConstraint(this,
getFigure(), bounds);
}
Layout x,y – WorkflowTypeFigure
Si vous relancez le plugin, notre page Graphics est toujours blanche. En effet la figure racine WorkflowTypeFigure qui hébérge les figures StateTypeFigure et ActionTypeFigure n’a auncun layout de définit. Elle est donc incapable de savoir comment interpréter les contraintes de layout fournit par les figures StateTypeFigure et ActionTypeFigure. Pour y remédier nous allons indiquer le layout (manager) à utiliser dans le constructeur de WorkflowTypeFigure comme ceci :
super.setLayoutManager(new XYLayout());
Relancez le plugin, et vous pourrez constater que les figures s’affichent! :
Il y a 1 carré qui correspond à l’instance EMF ActionType et 2 ellipses qui correspondent aux 2 instances EMF StateType.
Model -> View
A cette étape nous avons pu représenter les states par des ellipses et les actions par des carrées. Le nom des states et actions apparait primordial a afficher dans la page Graphics. Dans cette section nous allons synchroniser le nom name des StateType et ActionType (Model) avec les figures StateTypeFigure et ActionTypeFigure (View).
La figure Draw2d ReactangleFigure ne permet pas de gérer du texte dans sa figure. Il existe une figure Draw2d org.eclipse.draw2d.Label qui permet d’afficher du texte. Nous allons dans un premier temps afficher les libéllés des action et state puis nous personaliserons les figures pour qu’elles ait une forme carrée et ellipse. Pour afficher le nom d’une ActionType, modifiez la classe ActionTypeFigure comme ceci :
import org.eclipse.draw2d.Label;
public class ActionTypeFigure extends Label {
public void setName(String name) {
super.setText(name);
}
}
Comme nous avons décrit ci-dessus, la méthode AbstractEditPart#refreshVisual() est la méthode prévue pour synchroniser le modèle avec la vue. Modifier la classe ActionTypePart pour synchroniser le nom name du modèle EMF ActionType avec la vue ActionTypeFigure dans la classe ActionTypePart :
@Override
protected void refreshVisuals() {
super.refreshVisuals();
int x = new Double(Math.random() * 400).intValue();
int y = new Double(Math.random() * 400).intValue();
Rectangle bounds = new Rectangle(x, y, 50, 50);
((GraphicalEditPart) getParent()).setLayoutConstraint(this,
getFigure(), bounds);
ActionType action = getAction();
ActionTypeFigure figure = (ActionTypeFigure)getFigure();
figure.setName(action.getName());
}
public ActionType getAction() {
return (ActionType)super.getModel();
}
...
Faisons la même chose pour la classe StateTypeFigure :
import org.eclipse.draw2d.Label;
public class StateTypeFigure extends Label {
public void setName(String name) {
super.setText(name);
}
}
Et pour la classe StateTypePart :
@Override
protected void refreshVisuals() {
super.refreshVisuals();
int x = new Double(Math.random() * 400).intValue();
int y = new Double(Math.random() * 400).intValue();
Rectangle bounds = new Rectangle(x, y, 50, 50);
((GraphicalEditPart) getParent()).setLayoutConstraint(this,
getFigure(), bounds);
StateType State = getState();
StateTypeFigure figure = (StateTypeFigure) getFigure();
figure.setName(State.getName());
}
public StateType getState() {
return (StateType) super.getModel();
}
...
Relancez le plugin, vous pourrez constater que les nom name des StateType et ActionType s’affichent : :
Maintenant nous souhaitons représenter par des carrées les ActionType et par des ellipses les StateType. Après avoir décortiqué le code de RectangleFigure et Ellipse, il apparaît que la gestion de la forme s’effectue dans la méthode
Figure#paintFigure(Graphics graphics).
Pour afficher un carré avec un fond orange et du texte noire pour représenter les actions, modifiez le code de ActionTypeFigure comme suit :
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
public class ActionTypeFigure extends Label {
public ActionTypeFigure() {
super.setForegroundColor(ColorConstants.black);
}
@Override
protected void paintFigure(Graphics graphics) {
graphics.setBackgroundColor(ColorConstants.orange);
graphics.fillRectangle(getBounds());
super.paintFigure(graphics);
}
public void setName(String name) {
super.setText(name);
}
}
Pour afficher une ellipse avec un fond bleu et du texte blanc pour représenter les states, modifiez le code de StateTypeFigure comme suit :
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
public class StateTypeFigure extends Label {
public StateTypeFigure() {
super.setForegroundColor(ColorConstants.white);
}
@Override
protected void paintFigure(Graphics graphics) {
graphics.setBackgroundColor(ColorConstants.darkBlue);
graphics.fillOval(getBounds());
super.paintFigure(graphics);
}
public void setName(String name) {
super.setText(name);
}
}
Enlever le code qui affiche la bordure rouge pour représenter l’instance EMF WorkflowType:
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.XYLayout;
public class WorkflowTypeFigure extends Figure {
public WorkflowTypeFigure() {
super.setLayoutManager(new XYLayout());
//super.setBorder(new LineBorder(ColorConstants.red, 2));
}
}
Relancez le plugin et vous pourrez visualiser nos actions et states :
8 Commentaires + Ajouter un commentaire
Articles récents
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step5]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step4]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step3]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step2]
- Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step1]
Commentaires récents
- Conception d’un Editeur Eclipse de workflow XML [step 0] dans
- Conception d’un Editeur Eclipse de workflow XML [step 19] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
- Conception d’un Editeur Eclipse de workflow XML [step 7] dans
>Oui Modeling tools ^^
Ok.
>Mais je crois que j’ai compris pourquoi ça marche pas, je vais essayer de voir tout ça à la maison
Ok, si tu peux faire un retour de ton probleme sur le blog, ca serait sympa pour les autres. merci.
>Demain je continue à te faire plein de questions, en tout cas c très sympa de ta part ^^
Ok, je t’ai envoye le step1. Mais stp prends le temps de lire les billets avant de me poser des questions (j’ai essaye au mieux de TOUT expliquer) car je n’ai pas beacoup de temps a consacrer. Merci.
Angelo
Oui Modeling tools ^^
Mais je crois que j’ai compris pourquoi ça marche pas, je vais essayer de voir tout ça à la maison
Demain je continue à te faire plein de questions, en tout cas c très sympa de ta part ^^
Encore merci Angelo
Shivitax
Non je ne le teste pas en local, car c’est pas mon serveur.
Tu utilises Eclipse Modelling ou une autre version? As tu le menu « Example Model Creation Wizards »?
Angelo
Oui, je teste dans le 2eme Eclipse, je suis pas si bête xD
Le FTP tu le fait en local ?? car si c’est en local c’est normal ^^
Sinon si ça te dérange pas tu pourrais me l’envoyer par mail à shivitax@gmail.com =)
Merci pour tout et désolé de te déranger ='(
Une question bete. Tu essaie de l’ouvrir a partir du 2eme Eclipse. C’est bien ca?
Je viens de retester le zip et ca marche de mon coté?
Angelo
Re salut Angelo
Effectivement c’est le step 1, j’ai suivi pas à pas le billet, complètement tout tout tout, j’ai même refait le billet 4 ou 5 fois, et j’arrive pas à le faire marcher ='(
Maintenant j’ai réussi à régler la partie du paramétrage du genmodel, mais maintenant je bloque à dans le lancement du workflow editor.
Je trouve pas le dossier indique dans la capture d’ecran http://blog.developpez.com/media/Workflow_Model_page1.png, je sais pas pourquoi ='(
Par contre pour le ftp, je continue à ne pas avoir accès au ZIP.
Shivitax
Bonjour,
Je suppose que tu parles du step1 http://blog.developpez.com/akrogen/p7532/plugin-eclipse/conception-d-un-editeur-eclipse-de-work-1/
As tu suivi pas a pas ce billet? La copie d’ecran http://blog.developpez.com/media/Workflow_Project_GenModel_Param2.png te montre comment parametrer le genmodel.
Pour le FTP je viens de tester l etelechargement de step1 et step8 et ca marche? C’est quel step qui pose probleme?
Merci pour tes encouragements.
Angelo
Salut, je suis en train de suivre ton tuto, et j’ai des petits problèmes pour générer le model Java EMF du workflow, en fait mes packages impl et util ne sont pas dans model ='(
Je comprend pas du tout.
Tu sais pourquoi il me fait ça ??
Existe t-il des solutions pour ça ?
Et aussi y a un petit problème avec le FTP, le lien pour le zip est mort.
Merci d’avance
Très bon tuto =)
Shivitax