I. Introduction

I-A. Spécification

Un modèle de graphe développé avec EMF a été proposé précédemment, nous souhaitons pouvoir interroger les instances de ce modèle via des requêtes.

Pour satisfaire les besoins de cet article, le modèle de graphe a été modifié. Un attribut coût sous la forme d'un entier est ajouté à chaque nœud.

Image non disponible

Nous désirons implémenter la requête suivante : tout nœud dont le coût est supérieur à 5 est sélectionné.

I-B. Lancement de la plate-forme Eclipse

Double-cliquer : Image non disponible ou le raccourci vers cet exécutable si vous l'avez créé dans le répertoire destiné à recevoir les « workspaces ». La plate-forme « Eclipse » est lancée :

Image non disponible

Choisir un workspace qui contiendra vos projets :

Image non disponible

Cliquer sur le bouton « OK », fermer la fenêtre « Welcome » :

I-C. Passage en perspective « Ecore »

Faire « Open Perspecitve -> Other » :

Image non disponible

Choisir la perspective « Ecore » afin d'ouvrir les vues dédiées à la construction de modèles EMF :

Image non disponible

Cliquer sur le bouton « OK » puis fermer certaines vues afin d'obtenir le résultat suivant :

Image non disponible

II. Création projet « EMF »

II-A. Création projet « Empty EMF Project »

Faire « New -> Project... » :

Image non disponible

Sélectionner l'élément « Empty EMF Project » puis cliquer sur le bouton « Next > » :

Image non disponible

Nommer le projet « exemple.graphe.emf », puis cliquer sur le bouton « Finish » :

Image non disponible

Le projet est créé et vous devriez obtenir le résultat suivant :

Image non disponible

III. Édition du modèle « Ecore »

Faire un clic droit sur « model » et sélectionner la commande :

Image non disponible

Nommer le diagramme en saisissant la valeur « Graphe.ecore » dans le champ « Domain file name » :

Image non disponible

Cliquer sur le bouton « Finish » :

III-A. Édition du diagramme

Partir du modèle EMF réalisé dans l'article précédent Génération d'un éditeur arborescent pour l'édition des instances d'un modèle EMF.

Pour les besoins de notre exemple, un « EAttribute » « cout » de type « EInt » est ajouté à chaque nœud.

Image non disponible

Afficher « Graphe.ecore » pour obtenir le résultat suivant :

Image non disponible

Vérifier que le modèle EMF est valide (menu contextuel de l'éditeur de « Graphe.ecore »).

IV. Génération du code

IV-A. Création du fichier de génération

Faire un clic droit sur « exemple.graphe.emf » et sélectionner « New -> Other... » :

Image non disponible

Sélectionner l'élément « EMF Generator Model » et cliquer sur le bouton « Next > » :

Image non disponible

Sélectionner le répertoire « model » comme répertoire parent, nommer le fichier de génération « Graphe.genmodel » puis cliquer sur le bouton « Next > » :

Image non disponible

La génération se fait à partir d'un modèle « Ecore » et cliquer sur le bouton « Next > » :

Image non disponible

Utiliser « Browse Workspace... » dans la fenêtre « New EMF Generator Model - Ecore Import » pour sélectionner notre modèle EMF et cliquer sur le bouton « OK » pour obtenir :

Image non disponible

Laisser la valeur proposée puis cliquer sur le bouton « Next > » :

Image non disponible

Ne rien modifier et cliquer sur le bouton « Finish » :

Image non disponible

Le fichier « Graphe.genmodel » de génération est créé :

Image non disponible

Faire un clic droit dans l'éditeur de « Graphe.genmodel » et sélectionner la commande « Generate Model Code » :

Image non disponible

Faire de même avec « Generate Edit Code », « Generate Editor Code » pour obtenir le résultat suivant :

Image non disponible

V. Création d'une requête

Nous allons créer un projet réalisant une requête simple sur le modèle en cours d'édition.

La requête porte sur l'attribut coût des nœuds : « Sélectionner tous les nœuds dont le coût est supérieur à 5 ». Nous allons créer un nouveau projet pour traiter la requête.

V-A. Création du projet « exemple.graphe.emf.statement »

À partir du menu « File -> Project... » :

Image non disponible

Sélectionner l'élément « Plug-in Project », puis cliquer sur le bouton « Next > » :

Image non disponible

Nommer le projet « exemple.graphe.emf.statement » puis cliquer sur le bouton « Next > » :

Image non disponible

Renommer l'« Activator » en « exemple.graphe.emf.statement.QueryPlugin » puis cliquer sur le bouton « Finish » :

Image non disponible

Accepter le changement de perspective en validant sur le bouton « Yes ».

Image non disponible

VI. Requête dans le « menuBar »

Nous souhaitons ajouter une commande dans la barre de menus qui permettra d'exécuter la requête.

VI-A. Création « package » destiné à recevoir le code

Dans le répertoire « src », créer le package via le menu « New -> Package » :

Image non disponible

Nommer le « package » via la valeur « monCode », puis cliquer sur le bouton « Finish » :

Image non disponible

Le résultat attendu est le suivant :

Image non disponible

VI-B. Dependencies

Le « plug-in » « exemple.graphe.emf.statement » fait référence à d'autres plug-ins. Dans le « Plugin Manifest Editor » du projet sélectionner l'onglet : « Dependencies ». Dans la zone « Required Plug-ins », cliquer sur le bouton « Add... » :

Image non disponible

Sélectionner le plugin « org.eclipse.emf.query » puis valider :

Image non disponible

Cliquer sur le bouton « Add... » et sélectionner les plugins « exemple.graphe.emf », « exemple.graphe.emf.edit » et « exemple.graphe.emf.editor » puis valider :

Image non disponible

Cliquer sur le bouton « Add... » et sélectionner le plugin « org.eclipse.emf » :

Image non disponible

Finalement nous obtenons le résultat suivant :

Image non disponible

VI-C. Définition des extensions

VI-C-1. Création du point d'extension « org.eclipse.ui.editorActions »

Dans le « Plugin Manifest Editor », sélectionner l'onglet « Extensions » puis dans la zone « All Extensions » cliquer sur le bouton « Add... » :

Depuis l'assistant « New Extension - Extension Point Selection » sélectionner le point d'extension « org.eclipse.ui.editorActions », puis cliquer sur le bouton « Finish » :

Image non disponible

Le point d'extension est créé avec un sous-élément (editor Contribution) :

Image non disponible

Sélectionner le sous-élément et initialiser ses « Extension Element Details » :

Image non disponible

Remarque : Utiliser le bouton « Browse... » pour sélectionner le « targetID* » :

Image non disponible

Ne pas oublier de faire une sauvegarde afin de mettre à jour la zone « All Extensions » :

Image non disponible

VI-C-2. Ajouter un menu

Faire un clic droit sur « exemple.graphe.editor... » et sélectionner « New > menu » :

Image non disponible

Le menu est alors créé :

Image non disponible

Initialiser les champs comme indiqué ci-dessous :

Image non disponible

Après sauvegarde, vous obtiendrez le résultat suivant :

Image non disponible

VI-C-3. Ajouter un « separator »

Faire :

Image non disponible

Le séparateur est créé :

Image non disponible

L'initialiser :

Image non disponible

Puis faire une sauvegarde pour obtenir :

Image non disponible

VI-C-4. Ajouter une action

Faire :

Image non disponible

Pour obtenir le résultat suivant :

Image non disponible

Initialiser les champs avec les valeurs suivantes :

Image non disponible

Faire une sauvegarde pour obtenir :

Image non disponible

Sélectionner « Select Big Nodes (action) » dans « Extension Element Details » et cliquer sur : « class*: » :

Définir la classe :

Image non disponible

Cliquer sur le bouton « Finish », la classe est alors créée :

Image non disponible

Nous l'éditerons par la suite. Revenons à la construction des extensions.

VI-C-5. Définition « enablement »

Retour dans le « Plugin Manifest Editor » onglet « Extensions » et faire :

Image non disponible

VI-C-6. Définition « objectClass »

Faire :

Image non disponible

Ce qui permet d'obtenir :

Image non disponible

Utiliser le bouton « Browse... » dans « Extension Element Details » pour initialiser « name*: » puis cliquer sur le bouton « OK » :

Image non disponible

Après sauvegarde vous obtiendrez :

Image non disponible

VI-D. Le fichier « plugin.xml »

Sélectionner l'onglet « plugin.xml » pour retrouver la définition complète de l'extension « org.eclipse.ui.editorActions » dans le fichier « plugin.xml » :

 
Sélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.editorActions">
      <editorContribution
            id="exemple.graphe.editorContribution"
            targetID="graphe.presentation.GrapheEditorID">
         <menu
               id="exemple.graphe.queryMenuID"
               label="Query"
               path="grapheMenuID/additions">
            <separator
                  name="additions">
            </separator>
         </menu>
         <action
               class="monCode.SelectBigCostNodesDelegate"
               id="exemple.graphe.SelectBigCostNode"
               label="Select Big Cost Nodes"
               menubarPath="grapheMenuID/exemple.graphe.queryMenuID/additions"
               style="push">
            <enablement>
               <objectClass
                     name="org.eclipse.emf.ecore.EObject">
               </objectClass>
            </enablement>
         </action>
      </editorContribution>
   </extension>
 
</plugin>

VI-E. Le code

L'action définie dans l'extension « org.eclipse.ui.editorAction » fait référence à la classe « monCode.SelectBigCostNodesDelegate » :

Image non disponible

C'est à dire :

Image non disponible

Cette classe sera définie comme sous-classe de la classe « AbstactQueryDelegate » classe qui est définie dans le paragraphe ci-dessous.

VI-E-1. La classe « AbstractQueryDelegate »

Faire :

Image non disponible

Définir la classe :

Image non disponible

Remarque : utiliser le bouton « Browse... » pour définir les interfaces :

Image non disponible
Image non disponible

Cliquer sur le bouton « Finish » la classe est alors créée :

 
Sélectionnez

package monCode;
 
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IActionDelegate2;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
 
public class AbstractQueryDelegate implements IEditorActionDelegate,
        IActionDelegate2 {
 
    @Override
    public void run(IAction action) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    public void selectionChanged(IAction action, ISelection selection) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    public void init(IAction action) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    public void dispose() {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    public void runWithEvent(IAction action, Event event) {
        // TODO Auto-generated method stub
 
    }
 
    @Override
    public void setActiveEditor(IAction action, IEditorPart targetEditor) {
        // TODO Auto-generated method stub
 
    }
 
}

L'éditer :

 
Sélectionnez
package monCode;
 
import graphe.presentation.GrapheEditor;
 
import java.util.Collection;
 
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate2;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;
 
/**
 * This action delegate queries the user for the name of a class. Then it uses
 * the query APIs to find the class and selects it in the editor.
 * 
 * @see IEditorActionDelegate
 */
publicabstractclass AbstractQueryDelegate
    implements IEditorActionDelegate, IActionDelegate2 {
 
    /**
     * Error message to display when an exception occurred
     */
    protectedstaticfinal String MESSAGE_EXCEPTION = 
        "QueryStatementsMessages.message_exception";
 
    /**
     * The shell this action is hosted in
     */
    protected Shell shell = null;
 
    /**
     * The active editor
     */
    protected GrapheEditor editor = null;
 
    /**
     * Selected EObjects
     */
    protected Collection<EObject> selectedEObjects = null;
 
    /**
     * The InputDialog title
     */
    protected String title;
 
    /**
     * The InputDialog message
     */
    protected String message;
 
    /**
     * The message to output when query result set is empty
     */
    protected String notFoundMessage;
 
    /**
     * Constructor
     */
    public AbstractQueryDelegate(String titleIn, 
            String messageIn, String notFoundMessageIn) {
        title = titleIn;
        message = messageIn;
        notFoundMessage = notFoundMessageIn;
    }
 
    /**
     * Perform a query that returns a set of objects.
     * 
     * @param value
     *            The value used to drive the query
     * @return The set of objects returned by the query
     */
    protectedabstract Collection<EObject> performQuery(Object value)
        throws Exception;
 
    /*
     * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction,
     *      org.eclipse.jface.viewers.ISelection)
     */
    publicvoid selectionChanged(IAction action, 
            final ISelection selection) {
        this.selectedEObjects = null;
        try {
            if (selection instanceof IStructuredSelection) {
                IStructuredSelection structuredSelection = 
                    (IStructuredSelection) selection;
                this.selectedEObjects = 
                    new java.util.ArrayList<EObject>();
                for (Object next : structuredSelection.toList()) {
                    if (next instanceof EObject) {
                        selectedEObjects.add((EObject) next);
                    }
                }
            }
        } catch (Exception e) {
            // Exceptions are not expected
            MessageDialog.openInformation(shell, 
                    title, MESSAGE_EXCEPTION);
            thrownew RuntimeException(e);
        } finally {
            action.setEnabled((null != selectedEObjects));
        }
    }
 
    /*
     * @see org.eclipse.ui.IActionDelegate2#dispose()
     */
    publicvoid dispose() {
        //No-op
    }
 
    /*
     * @see org.eclipse.ui.IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction,
     *      org.eclipse.ui.IEditorPart)
     */
    publicvoid setActiveEditor(IAction action, 
            IEditorPart targetEditor) {
        this.editor = (GrapheEditor) targetEditor;
        if ( targetEditor != null ) {
            this.shell = targetEditor.getSite().getShell();
        }
    }
 
    /*
     * @see org.eclipse.ui.IActionDelegate2#init(org.eclipse.jface.action.IAction)
     */
    publicvoid init(IAction action) {
        // No-op
    }
 
    /*
     * @see org.eclipse.ui.IActionDelegate2#runWithEvent(org.eclipse.jface.action.IAction,
     *      org.eclipse.swt.widgets.Event)
     */
    publicvoid runWithEvent(IAction action, Event event) {
        run(action);
    }
}

Faire une sauvegarde, la structure de la classe est la suivante :

Image non disponible

VI-E-2. La classe « SelectBigCostNodesDelegate »

Dans le « Package Explorer » double-cliquer « SelectBigCostNodesDelegate » :

Image non disponible

Éditer la classe :

 
Sélectionnez
package monCode;
 
import graphe.GraphePackage;
 
import java.util.Collection;
 
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.query.conditions.eobjects.EObjectCondition;
import org.eclipse.emf.query.conditions.eobjects.structuralfeatures.EObjectAttributeValueCondition;
import org.eclipse.emf.query.conditions.numbers.NumberCondition;
import org.eclipse.emf.query.statements.FROM;
import org.eclipse.emf.query.statements.SELECT;
import org.eclipse.emf.query.statements.WHERE;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
 
publicclass SelectBigCostNodesDelegate extends AbstractQueryDelegate {
 
    public SelectBigCostNodesDelegate() {
        super("Sélection nœuds de cout supérieur ou égal à 5",
                null,
                "Pas de nœud de cout supérieur ou égal à 5");
    }
 
    @Override
    publicvoid run(IAction action) {
        try {
            Collection<EObject> result = performQuery(null);
            if (result.isEmpty()) {
                MessageDialog
                    .openInformation(shell, title, notFoundMessage);
            } else {
                this.editor.setSelectionToViewer(result);
            }
        } catch (Exception e) {
            // Exceptions are not expected
            MessageDialog.openInformation(shell, title, MESSAGE_EXCEPTION);
            thrownew RuntimeException(e);
        }
 
    }
 
    @Override
    protected Collection<EObject> performQuery(Object value) throws Exception {
 
        /*
         * Looking for nodes whose "cout" > 5
         */
        EObjectCondition condition = 
            new EObjectAttributeValueCondition(
                GraphePackage.eINSTANCE.getNoeuds_Cout(), 
                NumberCondition.between(5,Integer.MAX_VALUE));
 
        // Build the select query statement
        SELECT select = new SELECT(
                new FROM(selectedEObjects), 
                new WHERE(condition));
 
        // Execute query
        return select.execute();
 
 
    }
}

Faire une sauvegarde, la structure de la classe est la suivante :

Image non disponible

VII. Test query dans « Menu Bar »

Exécuter le plug-in sur une nouvelle plateforme :

Image non disponible

Dans « Run Configurations » double-cliquer sur « Eclipse Application ». Mettre à jour le nom de la configuration en « EMFModelQuery ». Cliquer « Apply » :

Image non disponible

Suivre la procédure d'exécution suivante :

  1. Faire « Run », fermer la fenêtre « Welcome » et quelques vues afin de garder celles essentielles ;
  2. Ouvrir un projet « General > Project », le nommer « testQuery » ;
  3. Faire un clic droit sur le projet et sélectionner la commande : « New > Other... » ;
  4. Sélectionner « Example EMF Model Creation Wizards > Graphe Model » ;
  5. Nommer le Fichier « G1.graphe » ;
  6. Sélectionner l'objet du modèle à créer : « Graphe ».

Ouvrir la vue « Properties » puis éditer le modèle :

Image non disponible

Ouvrir le modèle textuel :

Image non disponible

Vous pouvez visualiser tous les attributs « Cout » associés aux nœuds :

Image non disponible

VII-A. Exécution « Query »

Dans l'éditeur de « G1.graphe » sélectionner « Graphe » puis faire :

Image non disponible

ce qui donne :

Image non disponible

les nœuds N5 et N6 qui ont un coût supérieur ou égal à 5 sont sélectionnés.

VIII. Requête dans le « popup » menu de l'éditeur

Pour créer rapidement cette requête, sélectionner l'onglet « plugin.xml » du « plug-in Manifest Editor », éditer pour ajouter l'extension « org.eclipse.ui.popupMenus » :

 
Sélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.editorActions">
      <editorContribution
            id="exemple.graphe.editorContribution"
            targetID="graphe.presentation.GrapheEditorID">
         <menu
               id="exemple.graphe.queryMenuID"
               label="Query"
               path="grapheMenuID/additions">
            <separator
                  name="additions">
            </separator>
         </menu>
         <action
               class="monCode.SelectBigCostNodesDelegate"
               id="exemple.graphe.SelectBigCostNode"
               label="Select Big Cost Nodes"
               menubarPath="grapheMenuID/exemple.graphe.queryMenuID/additions"
               style="push">
            <enablement>
               <objectClass
                     name="org.eclipse.emf.ecore.EObject">
               </objectClass>
            </enablement>
         </action>
      </editorContribution>
   </extension>
   <extension
         point="org.eclipse.ui.popupMenus">
      <viewerContribution
            id="exemple.graphe.viewerContribution"
            targetID="graphe.presentation.GrapheEditorID">
         <menu
               id="exemple.graphe.queryMenuID"
               label="Query"
               path="additions">
            <separator
                  name="separator">
            </separator>
         </menu>
         <action
               class="monCode.SelectBigCostNodesDelegate"
               id="exemple.graphe.SelectBigCostNodes"
               label="Select Big Cost Nodes"
               menubarPath="exemple.graphe.queryMenuID/additions">
            <enablement>
               <objectClass
                     name="org.eclipse.emf.ecore.EObject">
               </objectClass>
            </enablement>
         </action>
      </viewerContribution>
   </extension>
 
</plugin>

Ce qui donne si l'on sélectionne l'onglet « Extension » :

Image non disponible

IX. Test requête dans le « popup » menu

Relancer le test avec Image non disponible à partir de la barre des outils. Une nouvelle plateforme Eclipse est lancée, nous retrouvons le graphe précédemment édité.

Sélectionner « Graphe », faire un clic droit dans la zone d'édition. Dans le menu popup menu sélectionner « Query > Select Big Cost Nodes »

Image non disponible

Le résultat attendu est le suivant :

Image non disponible

X. Conclusions et remerciements

La programmation des « Query » peut paraître complexe. Le lecteur se reportera utilement au « help » grâce à la commande « Help > Help Contents ».

Nous tenons à remercier ClaudeLELOUP pour sa relecture attentive de cet article puis djibril et Keulkeul pour la mise au gabarit de l'article original.

XI. Licence

La licence « Creative Commons » s'applique à ce document, veuillez-vous référer à ce site pour de plus amples informations : http://creativecommons.org/licenses/by-nc-nd/2.0/fr/.