Syndication : Atom 1.0  RSS 2.0
Blogs des développeurs   »   Akrogen Blog

Article complet: Tutorial WST- DOM SSE [Step 1]

23/02/2009

Permalink 15:27:58, Catégories: Plugin Eclipse, Récapitulatif Eclipse, Frameworks, 2187 mots   French (FR) , azerr

[Eclipse][Java] Tutorial WST- DOM SSE [Step 1]

Dans ce billet nous allons expliquer le code du projet DOM-SSE Shapes. Plus exactement nous détaillerons le code de la classe ShapesEditor qui est un EditortPart Eclipse qui utilise un DOM-SSE. Il est conseillé de bien lire le billet précedant pour comprendre comment fonctionne DOM-SSE.

Vous pouvez télécharger le projet org.eclipse.wst.xml.examples.shapes_1.0.0.zip expliqué dans ce billet.

[Suite:]

Structure du projet

Ce projet est constitué des classes suivantes :

  • ShapesDOMSSEPlugin : classe plugin du projet.
  • ShapesCreationWizard : classe qui permet de gérer le wizard de création d'un fichier XML Shapes shapesDiagram*.xml.
  • ShapesEditor EditorPart eclipse qui utilise le DOM-SSE pour gérer le fichier XML shapesDiagram*.xml.
  • DOMUtils : classe utilitaire de gestion d'un DOM w3c.
  • ShapesDiagramUtils : classe utilitaire de gestion du DOM w3c qui contient les informations (titre du diagramme, ...) d'un fichier XML shapesDiagram*.xml.

ShapesEditor

La classe ShapesEditor est un éditor Eclipse classique qui éténd la classe org.eclipse.ui.EditorPart.

Chargement DOM-SSE - IDOMModel

La classe ShapesEditor contient une variable model qui est le DOM-SSE :

// The DOM Model initialized with IFile Input Source 
private IDOMModel model; 

Cette variable doit être intialisée à l'aide du fichier sélectionné IFile lors de l'ouverture de l'éditor. Lorsqu'un fichier XML shapesDiagram*.xml est édité avec ShapesEditor, la méthode EditorPart#init(IEditorSite site, IEditorInput input) est appelée et vérifie que IEditorInput est un IFileEditorInput (qui permet de récupérer un IFile). Cette méthode appelle dans notre cas la méthode EditorPart#setInput(IEditorInput input) :

public void init(IEditorSite site, IEditorInput input) 
  throws PartInitException { 
  if (!(input instanceof IFileEditorInput)) 
  throw new PartInitException("Invalid Input: Must be IFileEditorInput"); 
  setSite(site); 
  setInput(input); 
  setPartName(input.getName()); 
  } 
}

C'est dans la méthode ShapesEditor#setInput(IEditorInput input) que nous allons récupérer le DOM-SSE à partir du IFile récupéré du IFileEditorInput et initiliser la variable IDOMModel model :

protected void setInput(IEditorInput input) { 
  super.setInput(input); 
  // Force the load of IDOMMOdel on Editor load 
  getDOMModel(); 

 
private IDOMModel getDOMModel() { 
  if (model == null) { 
  IFile file = ((IFileEditorInput) super.getEditorInput()).getFile(); 
  try { 
  model = getDOMModel(file); 
  } catch (Exception e) { 
  throw new RuntimeException("Invalid Input: Must be DOM", e); 
  } 
  } 
  return model; 
}

Cette méthode appelle la méthode privée ShapesEditor#getDOMModel(IFile file) qui retourne le DOM-SSE à partir du file IFile. :

private IDOMModel getDOMModel(IFile file) throws Exception { 
  IModelManager manager = StructuredModelManager.getModelManager(); 
  IStructuredModel model = manager.getExistingModelForEdit(file); 
  if (model == null) { 
  model = manager.getModelForEdit(file); 
  } 
  if (model == null) { 
  throw new Exception( 
"DOM Model is null, check the content type of your file (it seems that it's not *.xml file)"); 
  } 
  if (!(model instanceof IDOMModel)) { 
  throw new Exception("Model getted is not DOM Model!!!"); 
  } 
 
  // Add listener to observe change of DOM (change is done with another editor). 
  model.addModelStateListener(listener); 
  return (IDOMModel) model; 

Cette méthode récupère dans un premier temps le modèle SSE en mode édition :

IModelManager manager = StructuredModelManager.getModelManager(); 
IStructuredModel model = manager.getExistingModelForEdit(file); 
if (model == null) { 
  model = manager.getModelForEdit(file); 
}

Dans un premier temps on tente de récupérer un modèle DOM-SSE existant (getExistingModelForEdit) (récupéré par un un autre éditeur (ex : Editeur XML)) en mode édition. Si le modèle est null, on appelle la méthode getModelForEdit. Pour plus d'information sur le mode Lecture/Edition, veuillez consulter la section getModel*ForRead OU getModel*ForEdit?.

puis teste si le DOM-SSE n'est pas null (dans le cas ou le content type est inconnu) :

 
if (model == null) { 
      throw new Exception( 
          "DOM Model is null, check the content type of your file (it seems that it's not *.xml file)"); 
    } 

puis vérifie que le modèle SSE est bien un DOM-SSE :

if (!(model instanceof IDOMModel)) { 
      throw new Exception("Model getted is not DOM Model!!!"); 
    } 
 

Observation DOM- IModelStateListener

Dans le cas ou le DOM-SSE a bien pu être récupére, on ajoute un listener IModelStateListener pour observer les changements du DOM. Ce listener permettra de synchronizer l'interface SWT qui utilise le DOM avec n'importe quel changement du DOM qui sont peuve être effectué via cet éditeur mais via d'autres editeurs comme celui de l'editeur XML.

model.addModelStateListener(listener);

La variable listener est de type IModelStateListener et est initialisé comme suit :

private IModelStateListener listener = new IModelStateListener() { 
 
  .. 
  public void modelChanged(IStructuredModel model) { 
      // UPdate UI From DOM Model which have changed 
      updateUIFromDOMModel(); 
    } 
 
    public void modelDirtyStateChanged(IStructuredModel model, 
        boolean isDirty) { 
      // dirty from DOM Model has changed (the XML content was changed 
      // with anothher editor), fire the dirty property change to 
      // indicate to the editor that dirty has changed. 
      firePropertyChange(IEditorPart.PROP_DIRTY); 
 
    } 
 
... 
  }; 
 

Les 2 méthodes qui nous intéressent sont :

  • IModelStateListener#modelChanged(IStructuredModel model) : pour détecter que le model SSE est modifié, autrement dit que le DOM change. Cette méthode appele ShapesEditor#updateUIFromDOMModel() qui permet de modifier l'UI SWT à partir du DOM.
  • IModelStateListener#modelDirtyStateChanged(IStructuredModel model, boolean isDirty) : pour détecter que le model SSE est dirty. Cette méthode permet de gérer l'état dirty de l'editor ShapesEditor.

Il est important de supprimer ce listener du modèle SSE lorsque l'editor ShapesEditor est fermé. Pour cela on supprime ce listener dans la méthode EditorPart#dispose() :

public void dispose() { 
... 
this.model.removeModelStateListener(listener); 
... 
}

ShapesEditor - dirty

L'état dirty de l'editor utilise l'état dirty du DOM-SSE, en implémentant la méthode EditorPart#isDirty() comme ceci :

public boolean isDirty() { 
  return model.isDirty(); 
}

Le contenu XML du DOM peut être modifié via un autre editeur (comme l'editeur XML) et doit donc mettre à jour l'état dirty de ShapesEditor. Le listener IModelStateListener permet de gérer ce cas-ci. La méthode IModelStateListener#modelDirtyStateChanged est déclenché lorsque le DOM est modifié. Son implémentation appele EditorPart#firePropertyChange(IEditorPart.PROP_DIRTY) de de ShapesEditor qui permet de notifier l'editeur que son état dirty doit être modifié. L'editeur ShapesEditor appelle ensuite sa méthode EditorPart#isDirty() pour vérifier que l'editor est en dirty ou non.

DOM To UI - ShapesEditor#updateUIFromDOMModel()

L'interface UI SWT doit être mise à jour à en utilisant le DOM à l'initialisation de l'editor et lorsque le DOM se modifie. Cette synchronisation DOM -> UI s'effectue dans la méthode updateUIFromDOMModel qui est appelé dans :

  • ShapesEditor#createPartControl(Composite parent) : qui permet de mettre à jour l'UI SWT à partir du DOM chargé lors de l'ouverture de l'editor (initialisation).
  • IModelStateListener#modelChanged(IStructuredModel model) : qui permet de mettre à jour l'UI SWT à partir du DOM modifié.

Les 2 widgets utilisés dans ShapesEditor sont :

  • une widget SWT Text : cette widget est lié à l'attribut title de l"element racine di DOM
  • une widget SWT Table : cette widget est lié à la liste d'elements shapes

Nous allons nous intéresser au cas de la widget SWT Text. Voici le code de updateUIFromDOMModel qui permet de mettre à jour la widget SWT Text (titleText) avec l'attribut title :

Document document = model.getDocument(); 
... 
String titleFromUI = titleText.getText(); 
String titleFromDOM = ShapesDiagramUtils.getDiagram_Title(document); 
if (!titleFromUI.equals(titleFromDOM)) { 
  titleText.setText(titleFromDOM); 

ShapesDiagramUtils.getDiagram_Title(document) est simplement une méthode utilitaire qui permet de naviguer dans le DOM w3c. Son code est le suivant :

Element root = document.getDocumentElement(); 
  if (root == null) 
  return ""; 
  String title = root.getAttribute("title"); 
  return (title != null ? title : ""); 

UI To DOM

Lorsque l'UI se modifie, elle doit mettre à jour le DOM. Par exemple la widget Text (titleText) doit mettre à jour le DOM lorsque l'utilisateur saisit du texte. Ceci s'effecue en ajoutant un listener ModifyListener à la widget :

titleText.addModifyListener(new ModifyListener() { 
  public void modifyText(ModifyEvent e) { 
  // Get DOM Document from DOM Model. 
  Document document = model.getDocument(); 
  
  // 1. Set that DOM will be change (no events will be fire although DOM content change) 
  model.aboutToChangeModel(); 
  
  // 2. Modify DOM atrtribute value 
  ShapesDiagramUtils.setDiagram_Title(document, titleText.getText()); 
    
  // 3. Set that DOM has been changed 
  model.changedModel(); 
  } 
});

La méthode ShapesDiagramUtils#setDiagram_Title est simplemenent une méthode utilitaire qui permet de mettre à jour le DOM :

Element root = document.getDocumentElement(); 
if (root != null) { 
  root.setAttribute("title", title); 
}

Comme vous pouvez le constater les méthodes IStructuredModel#aboutToChangeModel et IStructuredModel#changedModel ont été utilisées. Mais dans ce cas-ci elle ne sont pas obligatoires car UNE seule modification du DOM est effectuée (modification de l'attribut title).

ShapesEditor#doSave*

La sauvegarde de l'editor ShapesEditor s'effecte en implémentatnt les méthodes doSave et doSaveAs. Ces méthodes utilisent les méthodes save de IStructuredModel. Voici la méthode doSave :

public void doSave(IProgressMonitor monitor) { 
  try { 
  model.save(); 
  } catch (Exception e) { 
  e.printStackTrace(); 
  } 

Le code reste tres simple. Après avoir sauvegardé l'editor ShapesEditor, son état dirty doit passer à false. Cette gestion du dirty n'est pas effectuée dans la méthode doSave. En effet c'est le DOM model qui s'occupe de ca, car la méthode IDOMModel#save() s'occupe de sauvegarder le DOM, mais s'occupe aussi de gérer l'état dirty (en déclanchant un evenemement).

ShapesEditor#dispose

La méthode ShapesEditor#dispose est appelé lorsque l'éditeur est fermé. Son implémentation permet de réinitialiser le modèle DOM-SSE correctement. Voici son code :

public void dispose() { 
  ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); 
  IStructuredModel model = getDOMModel(); 
  // Remove listener 
  model.removeModelStateListener(listener); 
  if (model.isDirty()) { 
  // The DOM is changed and this editor was closed without save it 
  model.releaseFromEdit(); 
  } 
  super.dispose(); 
}

Le code :
// Remove listener 
model.removeModelStateListener(listener);

permet de supprimer le listener IModelStateListener lorsque l'éditeur ShapesEditor n'est plus utilisé (fermeture de l'éditeur). Ce code est très important, car la plupart des éditeur de WST récupère le modèle SSE existant à l'aide des méthodes getExistingModel*. Dans ce cas-ci, on peut considérer que le modèle SSE est un singleton partagé par plusieurs éditeurs. Il faut donc s'assurer que notre listener soit supprimé lorsque l'on en plus besoin.

Le code :
if (model.isDirty()) { 
  // The DOM is changed and this editor was closed without save it 
  model.releaseFromEdit(); 
}

permet de gérer le cas ou le DOM-SSE est en dirty (il a été modifié) et que l'utilisateur quitte l'éditeur sans sauvegarde. Dans ce cas-ci il faut annuler les modifications du DOM en appelant la méthode IStructuredModel#releaseFromEdit() qui permet d'annuler toutes les modifications du modèle SSE qui a été récupéré en mode édition.

getModel*ForRead OU getModel*ForEdit?

Dans notre cas les méthodes getModel*ForEdit qui permettent de récupérer un modèle SSE en mode édition sont utilisées et pas getModel*ForRead qui permettent de récupérer un modèle SSE en mode lecture car nous souhaitons modifier le DOM. J'ai beaucoup bataillé sur ces méthodes, car il faut savoir que si vous récupérez un modèle SSE en mode lecture (getModel*ForRead), il est quand même possible de sauvegarder le DOM à l'aide de la méthode IStructuredModel#save().

Donc pourquoi ne pas utiliser les méthodes getModel*ForRead dans le cas de ShapesEditor? Après avoir effectué de nombreux tests, la réponse se trouve dans la méthode dispose de ShapesEditor (voir explication ci-dessus) :

public void dispose() { 
... 
  if (model.isDirty()) { 
  // The DOM is changed and this editor was closed without save it 
  model.releaseFromEdit(); 
  } 
  super.dispose(); 
}

Il existe effectivement la méthode IStructuredModel#releaseFromRead(), mais qui n'a aucun effet si le modèle SSE n'a pas été récupéré en mode édition? La je ne saurrais pas vous donner de réponse à ce comportement.

Social Bookmarking:

                                     

Commentaires, Pingbacks:

Connectez-vous pour vous abonner à cet article:

Flux de commentaires pour cet article : Atom 1.0  RSS 2.0

Cet article n'a pas de Commentaires/Pingbacks pour le moment...

Vous devez être identifié pour poster un commentaire.

Liste des blogs

Akrogen Blog

Catégories


Rechercher

<  Novembre 2009  >
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
30

Syndiquez ce blog XML

Articles :

Commentaires :

Vos questions techniques : forum d'entraide Blogs - Publiez vos articles, tutoriels et cours
et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones
Nous contacter - Hébergement - Participez - Copyright © 2000-2010 www.developpez.com - Legal informations.