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.
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 : 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 :
Choisir un workspace qui contiendra vos projets :
Cliquer sur le bouton « OK », fermer la fenêtre « Welcome » :
I-C. Passage en perspective « Ecore »▲
Faire « Open Perspecitve -> Other » :
Choisir la perspective « Ecore » afin d'ouvrir les vues dédiées à la construction de modèles EMF :
Cliquer sur le bouton « OK » puis fermer certaines vues afin d'obtenir le résultat suivant :
II. Création projet « EMF »▲
II-A. Création projet « Empty EMF Project »▲
Faire « New -> Project... » :
Sélectionner l'élément « Empty EMF Project » puis cliquer sur le bouton « Next > » :
Nommer le projet « exemple.graphe.emf », puis cliquer sur le bouton « Finish » :
Le projet est créé et vous devriez obtenir le résultat suivant :
III. Édition du modèle « Ecore »▲
Faire un clic droit sur « model » et sélectionner la commande :
Nommer le diagramme en saisissant la valeur « Graphe.ecore » dans le champ « Domain file name » :
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'éditiondes instances d'un modèle EMF.
Pour les besoins de notre exemple, un « EAttribute » « cout » de type « EInt » est ajouté à chaque nœud.
Afficher « Graphe.ecore » pour obtenir le résultat suivant :
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... » :
Sélectionner l'élément « EMF Generator Model » et cliquer sur le bouton « Next > » :
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 > » :
La génération se fait à partir d'un modèle « Ecore » et cliquer sur le bouton « Next > » :
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 :
Laisser la valeur proposée puis cliquer sur le bouton « Next > » :
Ne rien modifier et cliquer sur le bouton « Finish » :
Le fichier « Graphe.genmodel » de génération est créé :
Faire un clic droit dans l'éditeur de « Graphe.genmodel » et sélectionner la commande « Generate Model Code » :
Faire de même avec « Generate Edit Code », « Generate Editor Code » pour obtenir le résultat suivant :
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... » :
Sélectionner l'élément « Plug-in Project », puis cliquer sur le bouton « Next > » :
Nommer le projet « exemple.graphe.emf.statement » puis cliquer sur le bouton « Next > » :
Renommer l'« Activator » en « exemple.graphe.emf.statement.QueryPlugin » puis cliquer sur le bouton « Finish » :
Accepter le changement de perspective en validant sur le bouton « Yes ».
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 » :
Nommer le « package » via la valeur « monCode », puis cliquer sur le bouton « Finish » :
Le résultat attendu est le suivant :
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... » :
Sélectionner le plugin « org.eclipse.emf.query » puis valider :
Cliquer sur le bouton « Add... » et sélectionner les plugins « exemple.graphe.emf », « exemple.graphe.emf.edit » et « exemple.graphe.emf.editor » puis valider :
Cliquer sur le bouton « Add... » et sélectionner le plugin « org.eclipse.emf » :
Finalement nous obtenons le résultat suivant :
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 » :
Le point d'extension est créé avec un sous-élément (editor Contribution) :
Sélectionner le sous-élément et initialiser ses « Extension Element Details » :
Remarque : Utiliser le bouton « Browse... » pour sélectionner le « targetID* » :
Ne pas oublier de faire une sauvegarde afin de mettre à jour la zone « All Extensions » :
VI-C-2. Ajouter un menu▲
Faire un clic droit sur « exemple.graphe.editor... » et sélectionner « New > menu » :
Le menu est alors créé :
Initialiser les champs comme indiqué ci-dessous :
Après sauvegarde, vous obtiendrez le résultat suivant :
VI-C-3. Ajouter un « separator »▲
Faire :
Le séparateur est créé :
L'initialiser :
Puis faire une sauvegarde pour obtenir :
VI-C-4. Ajouter une action▲
Faire :
Pour obtenir le résultat suivant :
Initialiser les champs avec les valeurs suivantes :
Faire une sauvegarde pour obtenir :
Sélectionner « Select Big Nodes (action) » dans « Extension Element Details » et cliquer sur : « class*: » :
Définir la classe :
Cliquer sur le bouton « Finish », la classe est alors créée :
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 :
VI-C-6. Définition « objectClass »▲
Faire :
Ce qui permet d'obtenir :
Utiliser le bouton « Browse... » dans « Extension Element Details » pour initialiser « name*: » puis cliquer sur le bouton « OK » :
Après sauvegarde vous obtiendrez :
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 » :
<?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 » :
C'est à dire :
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 :
Définir la classe :
Remarque : utiliser le bouton « Browse... » pour définir les interfaces :
Cliquer sur le bouton « Finish » la classe est alors créée :
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 :
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 :
VI-E-2. La classe « SelectBigCostNodesDelegate »▲
Dans le « Package Explorer » double-cliquer « SelectBigCostNodesDelegate » :
Éditer la classe :
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 :
VII. Test query dans « Menu Bar »▲
Exécuter le plug-in sur une nouvelle plateforme :
Dans « Run Configurations » double-cliquer sur « Eclipse Application ». Mettre à jour le nom de la configuration en « EMFModelQuery ». Cliquer « Apply » :
Suivre la procédure d'exécution suivante :
- Faire « Run », fermer la fenêtre « Welcome » et quelques vues afin de garder celles essentielles ;
- Ouvrir un projet « General > Project », le nommer « testQuery » ;
- Faire un clic droit sur le projet et sélectionner la commande : « New > Other... » ;
- Sélectionner « Example EMF Model Creation Wizards > Graphe Model » ;
- Nommer le Fichier « G1.graphe » ;
- Sélectionner l'objet du modèle à créer : « Graphe ».
Ouvrir la vue « Properties » puis éditer le modèle :
Ouvrir le modèle textuel :
Vous pouvez visualiser tous les attributs « Cout » associés aux nœuds :
VII-A. Exécution « Query »▲
Dans l'éditeur de « G1.graphe » sélectionner « Graphe » puis faire :
ce qui donne :
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 » :
<?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 » :
IX. Test requête dans le « popup » menu▲
Relancer le test avec à 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 »
Le résultat attendu est le suivant :
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/.