février
2009
Avant de commencer à expliquer le code du projet DOM-SSE Shapes, je vais tenter d’expliquer les grands principes de SSE. Le projet WST fournit plusieurs éditeurs (XML, CSS…) qui permettent de gérer des documents structurés (DOM, CSS…) à partir d’éditeurs. WST fournit par exemple un Editeur XML :
Tout au long de ce billet je m’appuierais sur l’exemple de l’éditeur XML de WST pour illustrer mes explications (de ce que j’ai pu comprendre sur SSE).
Un document structuré (DOM, CSS..) est géré par SSE à l’aide d’un modèle SSE. Le contenu d’un document structuré est initialisé dans un premier temps avec le contenu d’une instance IFile (fichier du workspace) qui est ouvert à l’aide d’un éditeur (XML, CSS…). Ce contenu peut être ensuite modifiable via divers éditeurs. Dans le cas du projet DOM-SSE Shapes, le contenu du fichier XML pourra être modifié via l’editeur « DOM-SSE Shapes » et via l’éditeur XML de WST. Vous pourrez remarquer que ces 2 editeurs ouverts en même temps et qui éditent le même fichier XML seront synchronisés.
Model SSE – IStructuredModel
Pour illustrer ce que signifie modèle SSE, étudions l’éditeur XML de WST qui se trouve dans le plugin org.eclipse.wst.xml.ui. Cet éditeur multi-pages est constitué de 2 pages (Source/Design). Il est géré par la classe org.eclipse.wst.xml.ui.internal.tabletree.XMLMultiPageEditorPart (qui etend MultiPageEditorPart) et est constitué de 2 pages :
- une page Source qui utilise un EditorPart de type org.eclipse.wst.sse.ui.StructuredTextEditor.
- une page Design qui utilise un TreeViewer SWT de type org.eclipse.wst.xml.ui.internal.tabletree.XMLTableTreeViewer.
L’editor StructuredTextEditor étend la classe standard org.eclipse.ui.editors.text.TextEditor et gère un modèle SSE qui est représenté par l’interface org.eclipse.wst.sse.core.internal.provisional.IStructuredModel à partir du contenu de l’éditeur. Un modèle SSE est capable de gérer un document structuré (DOM, CSS…) en fonction du contenu de l’éditeur. WST fournit par exemple plusieurs éditeurs capable de gérer divers document structuré comme XML, CSS. Chacun de ces documents structurés sont gérés par un type de modèle SSE et étendent IStructuredModel :
- org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel est un modèle SSE de type DOM qui permet de gérer des documents XML. La méthode IDOMModel#getDocument() permet de retourner le DOM w3c courant associé au contenu XML saisi dans l’éditeur XML.
- org.eclipse.wst.css.core.internal.provisional.document.ICSSModel est un modèle SSE de type CSS qui permet de gérer des documents CSS. La méthode ICSSModel#getDocument() permet de retourner le document CSS courant associé au contenu CSS saisi dans l’éditeur CSS.
Dans le cas de l’editeur XML de WST, le modèle SSE est de type XML, appelé DOM-SSE (IDOMModel). Dans les billets EMF Shapes-WST, la classe EMF2DOMSSERenderer est utilisé pour sérializer le diagrame Shapes EMF en un XML de son choix et synchronizer l’instance EMF Shapes Diagram avec le DOM XML, qui permet au final de synchroniser le diagramme GEF avec d’autres éditeurs (XML, Texte…). EMF2DOMSSERenderer utilise en interne un DOM-SSE IDOMModel.
Voici les caractéristiques d’un modèle SSE que j’ai pu analyser :
- un modèle SSE est capable de maintenir une structure de donnée (dans le cas du XML, un document DOM) en cohérence avec le contenu d’un éditeur (XML, CSS…). Nous verrons dans la section DOM-SSE- IDOMModel de ce billet des exemples qui illustrent comment le DOM est maintenu.
- un modèle SSE donne la possibilités d’observer tout changement de la structure du document, à l’aide de listeners org.eclipse.wst.sse.core.internal.provisional.IModelStateListener (dans le cas du XML par exemple, l’ajout d’un Element w3c dans le DOM déclenchera un évenement), ce qui permet aux éditeurs d’observer les changements de structure et donc de se synchroniser entre eux.
- un modèle SSE gère la sauvegarde du document structuré.
- un modèle SSE gère l’état dirty du document structuré : vaut true si le document structuré à changé (et qu’il n’est pas sauvegardé) et false sinon.
- un modèle SSE gère le undo.
Toutes les structures communes au modèle SSE se trouve dans le plugin core de WST org.eclipse.wst.sse.core. Ensuite chaque implémentation de modèle SSE a son propre plugin. Par exemple le modèle SSE de type DOM se trouve dans le plugin org.eclipse.wst.xml.core.
IModelManager
Les éditeurs (XML, DOM-SSE Shapes…) doivent récupérer un modèle SSE à partir d’un IFile. Ceci s’effectue à l’aide de l’instance manager de modèle org.eclipse.wst.sse.core.internal.provisional.IModelManager comme ceci :
IModelManager manager = StructuredModelManager.getModelManager();
IStructuredModel model = manager.getExistingModelForRead(file);
if (model == null) {
model = manager.getModelForRead(file);
}
Ce code permet de récupérer un modèle SSE en lecture à partir d’un fichier du workspace :
- IModelManager#getExistingModelForRead(IFile file) : retourne une instance modèle SSE de file en testant si le modèle SSE a déja été récupéré (
par un autre éditeur par exemple). Cette méthode retourne null, si le modèle SSE n’a jamais été récupéré. - IModelManager#getModelForRead(IFile file) : retourne une (nouvelle) instance modèle SSE de file.
ATTENTION!!! Il se peut que le modèle SSE récupéré soit null si le content type du fichier est inconnu. Par exemple si vous modifiez l’extension d’un fichier *.xml en *.unknown et que vous ouvrez l’éditeur XML le message « Unsupported Content Type » s’affiche :
Dans le cas ou on souhaite récupérer un modèle SSE de type DOM, il faut caster IStructuredModel en IDOMModel :
IDOMModel domModel = (IDOMModel)model;
Ce qui donne :
IModelManager manager = StructuredModelManager.getModelManager();
IStructuredModel model = manager.getExistingModelForRead(file);
if (model == null) {
model = manager.getModelForRead(file);
}
IDOMModel domModel = (IDOMModel)model;
Il existe aussi les méthodes get*ModelForEdit qui permet de récupérer un modèle SSE en mode édition, qui doivent être utilisées si vous souhaitez modifier le DOM. Les méthodes get*ModelForRead qui permet de récupérer un modèle SSE en mode lecture, le permettent aussi, mais j’ai tenté d’expliquer les différences de mode (lecture, édition) dans la section getModel*ForRead OU getModel*ForEdit? du billet suivant.
Méthodes de IStructuredModel
L’interface IStructuredModel qui représente un modèle SSE fournit plusieurs méthodes :
- IStructuredModel#aboutToChangeModel() / IStructuredModel#changeModel() : qui permmettent de gérer la modification d’un document structuré d’un modèle SSE.
- IStructuredModel#isDirty() qui permet de gérer l’état dirty d’un modèle SSE.
- IStructuredModel#save(…) qui permet de gérer la sauvegarde d’un modèle SSE.
- IStructuredModel#releaseFrom*() qui permet de réinitialiser le document structuré d’un modèle SSE.
IStructuredModel#aboutToChangeModel() / IStructuredModel#changeModel()
Lorsque le document structuré d’un modèle SSE doit être modifié (plusieurs modifications doivent être effectuées), il est fortement conseillé pour des raisons de performances d’utiliser les méthodes IStructuredModel#aboutToChangeModel() et IStructuredModel#changeModel(). En effet un modèle SSE est observable et à chaque modification du document structuré, les listeners IModelStateListener branchés sur le modèle SSE se déclenchent.
L’utilisation de ces 2 méthodes s’effectue comme suit :
// 1. Indique que l'on souhaite modifier le document structuré
model.aboutToChangeModel();
// 2. Modification du document structuré
... // Ici aucun listener ne sera déclenché
// 3. Indique que l'on a fini de modifier le document structuré.
mode.changeModel();
// Ici les listeners se sont déclenchés
Par exemple si on souhaite ajouter 2 éléments XML à un DOM avec le modèle DOM-SSE, ceci donne :
IDOMModel domModel = (IDOMModel)model;
// 1. Indique que l'on souhaite modifier le DOM
domModel.aboutToChangeModel();
// 2. Modification du DOM en ajoutant 2 element
org.w3c.dom.Document document = domModel.getDocument();
Element elt1 = document.createElement("rectangle");
document.appendChild(elt1);
Element elt2 = document.createElement("ellipse");
document.appendChild(elt2); // Ici aucun listener ne sera déclenché
// 3. Indique que l'on a fini de modifier le DOM.
domModel.changeModel();
// Ici les listeners se sont déclenchés
Dans ce cas-ci UNE seule notification sera effectuée. Il est bien sur possible de modifier le DOM sans faire appel aux 2 méthodes aboutToChangeModel() et changedModel() du modèle SSE, mais si vous ne les utiliser pas, une notification se declenchera à chaque modification du DOM. Dans l’exemple ci-dessus, DEUX notifications s’effectueront (le DOM est modifiés 2 fois en ajoutant les 2 elements XML elt1 et elt2) si vous n’utilisez pas les 2 méthodes du modèle SSE.
IStructuredModel#isDirty()
Un modèle SSE est capable de gérer l’état dirty du document structuré. Lorsque le modèle SSE est récupéré pour la première fois, l’état dirty est positionné à false. Dès que le document structuré est modifié, l’état dirty est positionné à true. Dans le projet « DOM-SSE Shapes », nous allons implémenter la méthode EditorPart#isDirty() en utilisant l’état dirty du modèle SSE pour être en cohérence avec le document structuré DOM.
IStructuredModel#save(…)
Un modèle SSE est capable de sauvegarder le contenu d’un document structuré à l’aide des méthodes save :
- IStructuredModel#save() : sauve le contenu du document structuré (ex : DOM) dans le fichier IFile qui a été utilisé pour charger le modèle SSE.
- IStructuredModel#save(IFile iFile) : sauve le contenu du document structuré (ex : DOM) dans le fichier IFile fiel passé en paramètre.
- IStructuredModel#save(OutputStream outputStream) : sauve le contenu du document structuré (ex : DOM) dans le l’outputStream passé en paramètre.
ATTENTION!!! Tout appel des méthodes save, modifie l’etat dirty à true du modèle SSE. Je me suis fait avoir lorsque j’ai souhaité sauvegarder le modèle SSE dans l’outputStream System.out pour tracer les modifications du DOM.
IStructuredModel#releaseFrom*(…)
La méthode IStructuredModel#releaseFromRead() permet de reinitialiser le document structuré associé au modèle SSE. Autrement dit si on modifie le document structuré et que l’on appelle cette méthode, le document structuré se ré-initialise avec le contenu de départ. Cette méthode est utilisé dans le projet « DOM-SSE Shapes » pour gérer le cas ou l’utilisateur modifie le DOM via l’interface SWT et ferme l’éditeur sans sauvegarder.
IModelStateListener
Lorsque un document structuré est modifié, son modèle SSE associé est capable de notifier ces changements. Ces changements peuvent être observés en ajoutant au modèle SSE une implémentation un listener IModelStateListener :
- IStructuredModel#addModelStateListener(IModelStateListener listener) : permet d’ajouter un listener IModelStateListener au modèle SSE.
- IStructuredModel#removeModelStateListener(IModelStateListener listener) : permet supprimer un listener IModelStateListener au modèle SSE.
Voici le code qui permet d’ajouter un listener IModelStateListener :
model.addModelStateListener(new IModelStateListener() {
public void modelAboutToBeChanged(IStructuredModel model) {
}
public void modelAboutToBeReinitialized(
IStructuredModel structuredModel) {
}
public void modelChanged(IStructuredModel model) {
}
public void modelDirtyStateChanged(IStructuredModel model,
boolean isDirty) {
}
public void modelReinitialized(IStructuredModel structuredModel) {
}
public void modelResourceDeleted(IStructuredModel model) {
}
public void modelResourceMoved(IStructuredModel oldModel,
IStructuredModel newModel) {
}
});
Dans notre cas, les méthodes qui nous intéresserons seront :
- IModelStateListener#modelAboutToBeChanged(IStructuredModel model) : le document structuré va être modifié.
- IModelStateListener#modelChanged(IStructuredModel model) : le document structuré à changé.
- IModelStateListener#modelDirtyStateChanged(IStructuredModel model) : l’état dirty du document structuré a changé.
DOM-SSE- IDOMModel
L’éditeur XML utilise (via StructuredTextEditor) un modèle SSE de type XML, appelé DOM-SSE (IDOMModel) qui se trouve dans le plugin org.eclipse.wst.xml.core. Ce DOM-SSE maintient au mieux en interne un document w3c DOM en fonction du contenu XML saisi dans l’éditeur. Ce DOM peut être récupéré à partir du modèle SSE IStructuredModel obtenu par IModelManager que l’on caste en IDOMModel comme suit :
IDOMModel domModel = (IDOMModel)model;
org.w3c.dom.Document document = domModel.getDocument();
Il est alors possible de modifier directement ce DOM w3c (en ajoutant des élements, en modifiant des attributs….), et l’éditeur XML reste toujours synchronisé, c’est à dire que modifier la valeur d’un attribut d’un élément donnée via l’API DOM est tout de suite reflété dans l’éditeur (le texte change, et l’éditeur passe en était dirty).
Notez toutefois qu’il vous est possible de caster touts les types w3c (Node, Attr, Element, etc.) en leur version structurés IDOM* (IDOMNode, IDOMAttr, IDOMElement, etc.). Ces types là vous donnent la possibilité de faire le lien entre le Document DOM et la notion de Document JFace org.eclipse.jface.text.IDocument (les offsets par exemple). IDOMNode offre entre autre 2 méthodes supplémentatires par rapport au type Node w3c qui sont :
- IDOMNode#getStartOffset() qui retourne la position du début du nœud dans le document.
- IDOMNode#getEndOffset() qui retourne la position de fin du nœud dans le document.
XML non valide
Le contenu XML de l’éditeur peut en effet ne pas être valide, lorsque l’utilisateur est en train de saisir un contenu XML dans l’editeur.
Par exemple, si l’editeur XML a le contenu suivant :
Le DOM valide en interne sera :
Si l’editeur XML a le contenu suivant :
<a
Le DOM valide en interne sera :
<a />
</root/>
Un DOM-SSE est donc capable de construire un DOM avec un contenu XML qui peut ne pas être valide.
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