, azerr Dans ce billet nous allons enfin commencer a développer avec WST. Nous allons apprendre a sérialiser le fichier XML (format EMF) en un XML plus simple :
<?xml version="1.0" encoding="UTF-8"?>
<diagram>
<shapes>
<ellipse height="65" width="101" x="32" y="29"/>
<rectangle height="79" width="101" x="86" y="138"/>
<ellipse height="73" width="113" x="234" y="34"/>
</shapes>
</diagram>
Pour effectuer cette tâche, il faudra décrire un mapping entre le DOM XML et l'instance EMF ShapesDiagram en utilisant les Translators de WST. L'article Persisting EMF models with WTP traite l'utilisation des Translators WST avec EMF.
Vous pouvez télécharger le projet org.eclipse.gef.examples.shapes.emfwst_1.0.0.zip expliqué dans ce billet.
Pour rappel, la sérialisation d'une instance EMF, s'effectue à l'aide d'une implémentation org.eclipse.emf.ecore.resource.Resource. Dans le cas de Shapes EMF, le chargement de l'instance EMF ShapesDiagram à partir d'un fichier *shapesemfwst s'effectue dans la méthode ShapesEditor#setInput(IEditorInput input) :
...
URI uri = URI.createPlatformResourceURI(file.getFullPath().toString());
resource.setURI(uri);
...
resource.load(Collections.EMPTY_MAP);
...
diagram = (ShapesDiagram)resource.getContents().get(0);
...
Le code de sauvegarde de l'instance EMF ShapesDiagram s'effectue dans la méthode ShapesEditor#doSave(IProgressMonitor monitor)
...
resource.save(Collections.EMPTY_MAP);
...
La variable resource est initialisée comme suit :
private final Resource resource = new XMLResourceImpl();
XMLResourceImpl est une implémentation XML de l'interface Resource et permet de sérializer une instance EMF en un XML. EMF fournit aussi une sérialisation en un fichier XMI à l'aide de la classe XMIResourceImpl . Pour tester cela, modifier l'initialisation de la variable resource comme ceci :
private final Resource resource = new XMIResourceImpl();
Lorsque vous sauvegarderez le fichier *shapesemfwst et que vous l'éditerez vous pourrez remarquer que le contenu XML ne diffère pas excepté la déclaration du namespace XMI :
<org.eclipse.gef.examples.shapes.emf.model:ShapesDiagram
xmi:version="2.0"
xmlns:xmi="http://www.omg.org/XMI"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:org.eclipse.gef.examples.shapes.emf.model="http:///org/eclipse/gef/examples/shapes/emf/model.ecore">
....
Pour sérialiser dans un XML formé comme on le souhaite, il faut implémenter sa propre interface Resource. WST propose l'utilisation des Translators qui permettent de décrire un mapping entre le méta object (EClass) d'une instance EMF et le DOM XML a obtenir.
WST fournit une classe abstraite org.eclipse.wst.common.internal.emf.resource.TranslatorResourceImpl qui implémente Resource et qui fonctionne avec les Translators.
Pour utiliser les Translators, vous devez ajouter la dépendance org.eclipse.wst.common.emf dans le plugin. Pour effectuer cela, ouvrez le fichier MANIFEST.MF, cliquer sur l'onglet Dependencies, puis sur le bouton Add de la section Required Plug-ins.
Cette action ouvre la fenêtre de dialogue Plug-in Selection. Saisissez org.eclipse.wst.common.emf dans le champs Texte, puis Ok :

Chaque élément XML (diagram, rectangle, ellipse) devront être mappés à un méta objet de ShapesDiagram (ShapesDiagram, RectangularShape, EllipticalShape) à l'aide d'un Translator :
Les méta objets en EMF sont obtenus à l'aide de l'interface org.eclipse.gef.examples.shapes.emf.model.ModelPackage généré par EMF. Pour récupérer le méta objet ShapesDiagram par exemple, il suffit d'appeler le code suivant :
EClass eClass = ModelPackage.eINSTANCE.getShapesDiagram();
Dans cette section, nous coderons uniquement le mapping entre l'élément diagram et le méta objet ShapesDiagram. Pour cela créez la classe org.eclipse.gef.examples.shapes.emf.translator.ShapesTranslator
qui étend org.eclipse.wst.common.internal.emf.resource.RootTranslator comme suit :
package org.eclipse.gef.examples.shapes.emf.translator;
import org.eclipse.gef.examples.shapes.emf.model.ModelPackage;
import org.eclipse.wst.common.internal.emf.resource.RootTranslator;
import org.eclipse.wst.common.internal.emf.resource.Translator;
public class ShapesTranslator extends RootTranslator {
public static RootTranslator INSTANCE = new ShapesTranslator();
public ShapesTranslator() {
super("diagram", ModelPackage.eINSTANCE.getShapesDiagram());
}
protected Translator[] getChildren() {
return new Translator[] {};
}
}
Le constructeur de ShapesTranslator indique que l'élément XML diagram est mappé avec le meta object EClass EMF ShapesDiagram.
Il est important de définir la méthode Translator#getChildren() qui retourne un tableau de Translator vide, sinon on obtient un NullPointerException.
Ce code ne gère donc pas pour le moment la sérialisation des formes rectangle, ellipse qui ne seront donc pas sauvegardées. La sérialisation des formes rectangle, ellipse sera expliquée dans la suite de ce billet.
ShapesResourceImpl est notre implémentation EMF de Resource qui va utiliser notre Translator ShapesTranslator. Pour cela créer la classe org.eclipse.gef.examples.shapes.emf.translator.ShapesResourceImpl
qui étend l'implémentation WST de EMF Resource org.eclipse.wst.common.internal.emf.resource.TranslatorResourceImpl :
package org.eclipse.gef.examples.shapes.emf.translator;
import org.eclipse.wst.common.internal.emf.resource.EMF2DOMRendererFactoryDefaultHandler;
import org.eclipse.wst.common.internal.emf.resource.Translator;
import org.eclipse.wst.common.internal.emf.resource.TranslatorResourceImpl;
public class ShapesResourceImpl extends TranslatorResourceImpl {
public ShapesResourceImpl() {
super(EMF2DOMRendererFactoryDefaultHandler.INSTANCE
.getDefaultRendererFactory().createRenderer());
}
public Translator getRootTranslator() {
return ShapesTranslator.INSTANCE;
}
protected String getDefaultPublicId() {
return null;
}
protected String getDefaultSystemId() {
return null;
}
protected int getDefaultVersionID() {
return 0;
}
public String getDoctype() {
return null;
}
}
Voici quelques explications de ShapesResourceImpl :
EMF2DOMRendererFactoryDefaultHandler.INSTANCE.getDefaultRendererFactory().createRenderer()
La factory EMF2DOMRendererFactoryDefaultHandler.INSTANCE.getDefaultRendererFactory() retourne une instance WST Renderer EMF2DOMRenderer. L'interface Renderer définit comment un objet EMF doit être rendu en XML et inversement.
Le projet Shapes EMF utilise une instance Resource EMF à deux endroits :
Il faut donc modifier le code du projet pour utiliser notre ShapesResourceImpl et plus XMLResources.
Pour cela,
Modifier ShapesCreationWizard#getInitialContents() comme suit :
protected InputStream getInitialContents() {
ShapesDiagram diagram = (ShapesDiagram) createDefaultContent();
Resource resource = new ShapesResourceImpl()
...
Initialiser la variable resource de ShapesEditor comme suit :
private final Resource resource = new ShapesResourceImpl();
Relancer le plugin EMF Shapes, et regénérer un diagramme à l'aide du wizard Shapes EMF-WST Diagram.
Si vous éditer le fichier *shapesemfwst à l'aide d'un éditeur de Texte, vous verrez le contenu XML suivant :
<?xml version="1.0" encoding="UTF-8"?>
<diagram/>
L'instance EMF ShapesDiagram a été sérializé correctement.
A cette étape, si vous ajouter des formes rectangles ou ellipses dans l'editeur GEF et que vous sauvegardez, ces formes ne seront pas sauvegardées. En effet aucun Translator n'a été défini pour les formes rectangle et ellipse.
Pour effectuer cela, il faut implémenter la méthode ShapesTranslator#getChildren() correctement.
Modifier la classe ShapesTranslator, comme suit :
package org.eclipse.gef.examples.shapes.emf.translator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.examples.shapes.emf.model.ModelPackage;
import org.eclipse.wst.common.internal.emf.resource.GenericTranslator;
import org.eclipse.wst.common.internal.emf.resource.IDTranslator;
import org.eclipse.wst.common.internal.emf.resource.MultiObjectTranslator;
import org.eclipse.wst.common.internal.emf.resource.RootTranslator;
import org.eclipse.wst.common.internal.emf.resource.Translator;
public class ShapesTranslator extends RootTranslator {
public static RootTranslator INSTANCE = new ShapesTranslator();
private static final Translator RECTANGLE_TRANSLATOR = createRectangleTranslator();
private static final Translator ELLIPSE_TRANSLATOR = createEllipseTranslator();
public ShapesTranslator() {
// Mapping between XML diagram element and ShapesDiagram EMF Metadata
super("diagram", ModelPackage.eINSTANCE.getShapesDiagram());
}
protected Translator[] getChildren() {
return new Translator[] { IDTranslator.INSTANCE,
createShapesTranslator() };
}
private Translator createShapesTranslator() {
MultiObjectTranslator translator = new MultiObjectTranslator(
"diagram/ellipse,rectangle", ModelPackage.eINSTANCE
.getShapesDiagram_Shapes()) {
public Translator getDelegateFor(EObject o) {
switch (o.eClass().getClassifierID()) {
case ModelPackage.RECTANGULAR_SHAPE:
return RECTANGLE_TRANSLATOR;
case ModelPackage.ELLIPTICAL_SHAPE:
return ELLIPSE_TRANSLATOR;
}
throw new IllegalStateException("Shape type delegate expected"); //$NON-NLS-1$
}
public Translator getDelegateFor(String domName,
String readAheadName) {
if ("rectangle".equals(domName))
return RECTANGLE_TRANSLATOR;
if ("ellipse".equals(domName))
return ELLIPSE_TRANSLATOR;
return null;
}
protected Translator[] getChildren() {
return new Translator[] {};
}
};
return translator;
}
private static Translator createRectangleTranslator() {
GenericTranslator translator = new GenericTranslator("rectangle",
ModelPackage.eINSTANCE.getRectangularShape());
translator.setChildren(new Translator[] {
IDTranslator.INSTANCE,
new Translator("x", ModelPackage.eINSTANCE.getShape_X(),
Translator.DOM_ATTRIBUTE),
new Translator("y", ModelPackage.eINSTANCE.getShape_Y(),
Translator.DOM_ATTRIBUTE),
new Translator("width",
ModelPackage.eINSTANCE.getShape_Width(),
Translator.DOM_ATTRIBUTE),
new Translator("height", ModelPackage.eINSTANCE
.getShape_Height(), Translator.DOM_ATTRIBUTE) });
return translator;
}
private static Translator createEllipseTranslator() {
GenericTranslator translator = new GenericTranslator("ellipse",
ModelPackage.eINSTANCE.getEllipticalShape());
translator.setChildren(new Translator[] {
IDTranslator.INSTANCE,
new Translator("x", ModelPackage.eINSTANCE.getShape_X(),
Translator.DOM_ATTRIBUTE),
new Translator("y", ModelPackage.eINSTANCE.getShape_Y(),
Translator.DOM_ATTRIBUTE),
new Translator("width",
ModelPackage.eINSTANCE.getShape_Width(),
Translator.DOM_ATTRIBUTE),
new Translator("height", ModelPackage.eINSTANCE
.getShape_Height(), Translator.DOM_ATTRIBUTE) });
return translator;
}
}
Voici les explications de ce code :
Etudions dans un premier temps le mapping rectangle avec le meta object RectangularShape. Le mapping ellipse fonctionne de la même manière. Le mapping de rectangle est effectué dans la méthode createRectangleTranslator :
private static Translator createRectangleTranslator() {
GenericTranslator translator = new GenericTranslator("rectangle",
ModelPackage.eINSTANCE.getRectangularShape());
translator.setChildren(new Translator[] {
IDTranslator.INSTANCE,
new Translator("x", ModelPackage.eINSTANCE.getShape_X(),
Translator.DOM_ATTRIBUTE),
new Translator("y", ModelPackage.eINSTANCE.getShape_Y(),
Translator.DOM_ATTRIBUTE),
new Translator("width",
ModelPackage.eINSTANCE.getShape_Width(),
Translator.DOM_ATTRIBUTE),
new Translator("height", ModelPackage.eINSTANCE
.getShape_Height(), Translator.DOM_ATTRIBUTE) });
return translator;
}
GenericTranslator est une implementation de Translator qui gère un mapping simple entre un element XML et un meta object EClass. Le code :
GenericTranslator("rectangle",
ModelPackage.eINSTANCE.getRectangularShape());
permet de mapper l'element XML rectangle avec le meta object EClass RectangularShape. L'attribut XML width est mappé avec le meta object EAttribute getShape_Width. Ceci s'effectue à l'aide du code :
new Translator("width",ModelPackage.eINSTANCE.getShape_Width(),
Translator.DOM_ATTRIBUTE)
Les autres attributs XML sont mappés de la même manière.
Un diagrame ShapesDiagram est constitué d'une liste de Shape ou un Shape peut etre un RectangularShape OU EllipticalShape. J'ai passé un temps fou à découvrir comment gérer le cas des listes. Et j'ai miraculeusement trouvé un exemple avec EnterpriseBeansTranslator.
Pour gérer ceci, il faut utiliser MultiObjectTranslator qui permet de déleguer l'instance translator à utiliser :
Dans notre cas si on reçoit le nom XML rectangle on doit retourner le translator RECTANGLE_TRANSLATOR qui gere les rectangles et si on reçoit le EClass RectangularShape on doit retourner aussi le translator RECTANGLE_TRANSLATOR.
Les methodes delegate se base sur des instances static RECTANGLE_TRANSLATOR et ELLIPSE_TRANSLATOR pour éviter qu'à chaque appel des méthodes delegate, une instanciation d'un translator s'effectue.
Le mapping s'effectue comme suit :
new MultiObjectTranslator("shapes/ellipse,rectangle", ModelPackage.eINSTANCE.getShapesDiagram_Shapes())
Autrement dit on indique que l'élement XML shapes est mappé avec le méta object ShapesDiagram_Shapes. Cette liste (EList) peut contenir des instances de RectangularShape et de EllipticalShape. Pour gérer ceci, on écrit le path suivant :
shapes/ellipse,rectangle
Le chemin shapes indique que l'element XML shapes est mappé avec le meta object ShapesDiagram_Shapes. Il contient des element XML ellipse ou rectangle que l'on sépare avec le caractère ,.
Cet article n'a pas de Commentaires/Pingbacks pour le moment...
Vous devez être identifié pour poster un commentaire.
| Lun | Mar | Mer | Jeu | Ven | Sam | Dim |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 |
Copyright © 2000-2012 - www.developpez.com