Introduction à la manipulation du code source Java avec Eclipse JDT

Salim Kebir

Cet article nous plonge dans la création d'un plugin Eclipse pour la génération de getters et setters des attributs d'une classe.

Commentez

- Titre : Introduction à la manipulation du code source Java avec Eclipse JDT

- Auteur : Salim Kebir

- Parution : 8 novembre 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2011 Salim Kebir. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- I-A. Prérequis

- II. Création du plugin org.sdf.gettersettergen

- II-A. Création du projet

- II-B. Création de l'extension

- II-C. Implémentation de la méthode run

- II-D. Récupération du nom et du type de l'attribut

- II-E. Génération du getter

- II-F. Génération du setter

- II-G. Récapitulation

- III. Test de notre plugin

- IV. Conclusion

- V. Codes sources du projet

- VI. Articles connexes

- VII. Remerciements

I - Introduction

Eclipse JDT (Java Development Tools) est un plugin incorporé à l'IDE Eclipse qui offre un environnement de développement pour le langage Java. Bien que nous ayons tous utilisé ce plugin implicitement dans nos projets Java, sa puissance réside dans le fait qu'il est extensible. Cette extensibilité se traduit par la possibilité d'ajouter de nouvelles fonctionnalités à l'environnement de développement Java comme : la manipulation du code source à travers les arbres syntaxiques abstraits, l'ajout de nouvelles actions aux différentes vues et éditeurs de l'environnement de développement Java, etc. Dans cet article, nous donnons une introduction au développement de plugins Eclipse au-dessus de JDT. Pour cela, nous allons construire petit à petit une action qui permet de générer les accesseurs (getters) et modificateurs (setters) des attributs d'une classe. Au cours de ce tutoriel, plusieurs concepts relatifs au développement de plugins Eclipse vont être présentés. Tout cela nécessite au préalable une connaissance des principaux concepts de l'environnement de développement de plugins Eclipse (Eclipse PDE).

I-A - Prérequis

Ce tutoriel nécessite les prérequis suivants :

- connaissances sur le développement de plugins Eclipse (Eclipse PDT)

- connaissances sur l'environnement Eclipse pour le développement Java (Eclipse JDT)

II - Création du plugin org.sdf.gettersettergen

II-A - Création du projet

Tout d'abord, nous commençons par créer le plugin Eclipse avec File->New->plugin Project.

Nous appelons notre plugin org.sdf.gettersettergen. Nous allons cibler la plateforme Eclipse 3.7.

Lorsque nous cliquons sur next, nous obtenons la page suivante :

Nous cochons la case This plugin will make contributions to the UI pour indiquer à Eclipse que notre plugin apportera une contribution à l'interface graphique d'Eclipse. Ainsi, l'IDE se chargera de créer les dépendances nécessaires pour les contributions à l'interface graphique.

Lorsque nous cliquons sur next, nous obtenons la page suivante :

Nous éviterons d'utiliser un template pour la création de notre plugin pour deux raisons. La première est parce que nous souhaitons le créer de A à Z sans passer par la modification d'un template. La seconde étant l'absence de templates pour les actions associées à des éditeurs.

Après avoir cliqué sur Finish, nous obtenons la page du résumé de notre plugin. Pour créer une extension, nous allons à l'onglet Extensions. Pour l'instant notre plugin ne possède aucune extension.

II-B - Création de l'extension

Les extensions et les points d'extension sont des mécanismes propres à Eclipse qui facilitent la connexion entre plugins. Les plugins qui définissent des points d'extension s'ouvrent aux autres plugins à travers ces points. Le plugin que nous allons développer exploite les points d'extension offerts par Eclipse JDT pour apporter de nouvelles fonctionnalités à l'éditeur Java.

Pour créer une extension, nous cliquons sur le bouton Add et nous indiquons quel type d'extension nous voulons créer.

Dans notre cas, nous voulons créer une action pour l'éditeur de code source Java. Une action se traduit par un petit bouton sur la barre d'outils ou bien par un item dans le menu.

Notons que l'utilisation des actions est une ancienne façon de procéder. Maintenant, il est recommandé d'utiliser les commandes au lieu des actions. Mais par souci de simplicité, dans cet article nous utiliserons des actions

Nous devons maintenant paramétrer notre action pour qu'elle ne soit visible que dans l'éditeur de code source Java.

Pour cela, nous attribuons à la propriété targetID l'identifiant de l'éditeur de code source Java ou bien nous cliquons sur Browse.. et nous choisissons l'éditeur de code source Java à partir d'une liste exhaustive.

L'identifiant de l'éditeur de code source Java est org.eclipse.jdt.ui.CompilationUnitEditor.

Nous créons l'action proprement dite en faisant un clic droit sur notre editorContribution, et en choisissant action dans le menu New.

Nous affectons à la propriété label de notre action la valeur Generate getter and setter. C'est le texte qui va apparaitre sur le bouton associé à notre action. Il est nécessaire aussi que nous indiquions le chemin dans la barre d'outils de notre action pour qu'elle soit visible. Pour cela, nous affectons la valeur org/sdf à la propriété toolbarPath. Il ne nous reste plus qu'à créer la classe à laquelle sera déléguée l'action en cliquant sur la propriété class.

Nous appelons cette classe EditorActionGetterSetterGen. Eclipse se charge de créer les méthodes qui implémentent les méthodes abstraites de l'interface IEditorActionDelegate. Nous obtenons le résultat suivant lorsque nous cliquons sur le bouton Finish.

package org.sdf.gettersettergen;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;

public class EditorActionGetterSetterGen implements IEditorActionDelegate {

@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 setActiveEditor(IAction action, IEditorPart targetEditor) {
// TODO Auto-generated method stub

}

}

Notre classe possède trois méthodes : la méthode run sera appelée lorsque nous exécuterons l'action (Clic sur le bouton sur la barre d'outils, ou bien clic sur l'item dans le menu) ; la méthode selectionChanged sera appelée à chaque fois que la sélection change ; la méthode setActiveEditor sera appelée à chaque fois que l'éditeur sera activé. Ce dernier peut être obtenu avec le paramètre targetEditor.

Seules les méthodes run et setActiveEditor nous intéressent. Dans la première méthode, nous mettrons notre code proprement dit de génération de getters et de setters. Dans la deuxième méthode, nous mettrons une instruction qui rafraichit l'éditeur lorsque celui-ci est activé. Nous obtiendrons ainsi le code source suivant :

package org.sdf.gettersettergen;

import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;

public class EditorActionGetterSetterGen implements IEditorActionDelegate {

private IEditorPart editorPart;

@Override
public void run(IAction action) {

}

@Override
public void selectionChanged(IAction action, ISelection selection) {

}

@Override
public void setActiveEditor(IAction action, IEditorPart targetEditor) {
editorPart = targetEditor;
}

}

Nous avons déclaré une variable qui s'appelle editorPart. Nous affectons à cette variable le nouvel éditeur qui vient d'être activé. Ainsi nous pourrons manipuler cette variable dans la méthode run.

II-C - Implémentation de la méthode run

Afin de pouvoir manipuler le code source Java, nous devons avoir une instance de l'interface ICompilationUnit à partir de l'éditeur de code source Java. Pour cela, il est nécessaire de s'appuyer sur deux dépendances appartenant à Eclipse Java Development Tools (Eclipse JDT) qui sont : org.eclipse.jdt.core et org.eclipse.jdt.ui. Nous devons donc ajouter ces dépendances dans l'onglet Dependencies de notre plugin.

Nous revenons maintenant à notre programme de génération de getters et de setters. Pour manipuler le code source Java, Eclipse JDT fournit une multitude de classes et d'interfaces. La plus importante et celle qui nous intéresse dans ce tutoriel est ICompilationUnit. À partir d'une instance d'une ICompilationUnit, nous pouvons obtenir tous les attributs (leur nom, leur type, etc.) ainsi que toutes les méthodes d'une classe. Dans le code source suivant, nous commençons par obtenir une instance de ICompilationUnit à partir de notre éditeur, ensuite nous extrayons de cette instance la classe déclarée dans ce code source sous la forme d'une instance de IType.

package org.sdf.gettersettergen;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;

public class EditorActionGetterSetterGen implements IEditorActionDelegate {

private IEditorPart editorPart;

@Override
public void run(IAction action) {
ICompilationUnit compilationUnit = JavaUI.getWorkingCopyManager().getWorkingCopy(editorPart.getEditorInput());
try {
IType type = compilationUnit.getTypes()[0];
IField[] fields = type.getFields();
for (IField field : fields) { // pour chaque attribut
//generer le getter
//generer le setter
}
} catch (JavaModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

@Override
public void selectionChanged(IAction action, ISelection selection) {

}

@Override
public void setActiveEditor(IAction action, IEditorPart targetEditor) {
editorPart = targetEditor;
}

}

JavaUI est la classe centrale qui permet de manipuler tous les composants de l'interface graphique de Eclipse JDT. Elle offre une multitude de méthodes statiques qui permettent d'obtenir des instances de ICompilationUnit d'un projet. Nous utilisons la méthode getWorkingCopyManager pour obtenir le gestionnaire des copies de code source sur lesquelles nous sommes en train de travailler. Ce gestionnaire fournit la méthode statique getWorkingCopy qui permet d'obtenir à partir d'un IEditorInput une ICompilationUnit. Nous passons comme paramètre à cette méthode le IEditorInput de notre variable editorPart.

En Java, il est possible de déclarer plusieurs classes (types) dans un même fichier. Pour cela, l'interface ICompilatioUnit possède la méthode getTypes() qui retourne un tableau de tous les types déclarés dans ce code source. Nous supposons qu'il n'existe qu'une seule classe par fichier. Donc, nous ne travaillerons que sur le premier type déclaré dans cette ICompilationUnit.

En utilisant cette instance de IType, nous obtenons tous les attributs (IField) à partir du tableau renvoyé par la méthode getFields. Nous parcourons ce tableau, pour générer un getter et un setter pour chaque attribut rencontré.

II-D - Récupération du nom et du type de l'attribut

Avant de pouvoir générer nos getters et setters, nous devons avoir deux données essentielles : le nom de l'attribut et son type. Pour rappel un getter est généré de la manière suivante :

public <type> get<nom-attribut-capitalisé>() {
return this.<nom-attribut>;
}

De la même façon, un setter est généré de la manière suivante :

public void set<nom-attribut-capitalisé>(<type> <nom-attribut>) {
this.<nom-attribut> = <nom-attribut>;
}

Les deux lignes suivantes permettent de récupérer le nom et le type d'un attribut field.

String fieldName = field.getElementName();
String fieldType = Signature.getSignatureSimpleName(field.getTypeSignature());

La première ligne parle d'elle-même. Elle récupère le nom de l'attribut dans la variable fieldName. La deuxième ligne permet d'obtenir un nom simple à partir de la signature du type de l'attribut. Pour cela, elle utilise la méthode statique getSignatureSimpleName de la classe Signature pour obtenir une représentation sous la forme d'une chaine de caractères du type de l'attribut.

II-E - Génération du getter

Maintenant que nous possédons le nom et le type de l'attribut, la génération du getter se fait naturellement en construisant la chaine de caractères correspondante au code source du getter. Le code suivant effectue la génération du getter et son ajout à la classe.

//génération du getter
StringBuilder getterBuilder = new StringBuilder();
getterBuilder.append("public ");
getterBuilder.append(fieldType);
getterBuilder.append(" get");
getterBuilder.append(Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1));
getterBuilder.append("() {\n");
getterBuilder.append("\treturn this.");
getterBuilder.append(fieldName);
getterBuilder.append("; \n");
getterBuilder.append("}");
type.createMethod(getterBuilder.toString(), null, false, null);

Nous avons choisi de créer de manière incrémentale notre getter en utilisant un StringBuilder. La dernière instruction permet d'ajouter une méthode à la classe. Le code source de la méthode est passé comme premier paramètre, le deuxième paramètre désigne l'endroit où placer la méthode (null pour ne pas donner un endroit spécifique), le troisième paramètre signifie si la méthode sera ajoutée si jamais elle existe déjà et le quatrième paramètre désigne le moniteur de progression qui devra être utilisé pour cette opération.

II-F - Génération du setter

Nous procédons de la même manière pour générer notre setter. Le code suivant permet de générer le setter pour notre attribut.

//génération du setter
StringBuilder setterBuilder = new StringBuilder();
setterBuilder.append("public void set");
setterBuilder.append(Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1));
setterBuilder.append("(");
setterBuilder.append(fieldType);
setterBuilder.append(" ");
setterBuilder.append(fieldName);
setterBuilder.append(") {\n");
setterBuilder.append("\tthis.");
setterBuilder.append(fieldName);
setterBuilder.append(" = ");
setterBuilder.append(fieldName);
setterBuilder.append(";\n}");
type.createMethod(setterBuilder.toString(), null, false, null);

II-G - Récapitulation

Nous donnons maintenant le code source complet de notre action en regroupant les bouts de code source donnés plus haut.

package org.sdf.gettersettergen;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;

public class EditorActionGetterSetterGen implements IEditorActionDelegate {

private IEditorPart editorPart;

@Override
public void run(IAction action) {
ICompilationUnit compilationUnit = JavaUI.getWorkingCopyManager().getWorkingCopy(editorPart.getEditorInput());
try {
IType type = compilationUnit.getTypes()[0];
IField[] fields = type.getFields();
for (IField field : fields) { // pour chaque attribut
//recuperation du nom et du type de l'attribut
String fieldName = field.getElementName();
String fieldType = Signature.getSignatureSimpleName(field.getTypeSignature());
//génération du getter
StringBuilder getterBuilder = new StringBuilder();
getterBuilder.append("public ");
getterBuilder.append(fieldType);
getterBuilder.append(" get");
getterBuilder.append(Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1));
getterBuilder.append("() {\n");
getterBuilder.append("\treturn this.");
getterBuilder.append(fieldName);
getterBuilder.append("; \n");
getterBuilder.append("}");
type.createMethod(getterBuilder.toString(), null, false, null);
//génération du setter
StringBuilder setterBuilder = new StringBuilder();
setterBuilder.append("public void set");
setterBuilder.append(Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1));
setterBuilder.append("(");
setterBuilder.append(fieldType);
setterBuilder.append(" ");
setterBuilder.append(fieldName);
setterBuilder.append(") {\n");
setterBuilder.append("\tthis.");
setterBuilder.append(fieldName);
setterBuilder.append(" = ");
setterBuilder.append(fieldName);
setterBuilder.append(";\n}");
type.createMethod(setterBuilder.toString(), null, false, null);
}
} catch (JavaModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

@Override
public void selectionChanged(IAction action, ISelection selection) {

}

@Override
public void setActiveEditor(IAction action, IEditorPart targetEditor) {
editorPart = targetEditor;
}

}

III - Test de notre plugin

Nous testons maintenant notre plugin en le lançant comme une application Eclipse. Nous créons une classe qui s'appelle Test et qui possède deux attributs i et j respectivement de type int et String. Nous remarquons que notre action est placée en haut de l'éditeur sous la forme d'un bouton. Ce bouton possède le texte de la propriété label que nous avons défini au début.

Nous cliquons sur le bouton Generate getters and setters. Et nous observons les changements apportés à notre classe.

Lorsque nous exécutons notre action, nous remarquons la génération des getters et setters pour les deux attributs i et j.

IV - Conclusion

Dans ce tutoriel, nous avons donné une introduction au développement de plugin Eclipse au-dessus de Eclipse JDT. Nous avons démontré que cela est loin d'être complexe. Pour cela, nous avons créé un plugin qui consiste en une action qui permet de générer les getters et setters des attributs d'une classe.

V - Codes sources du projet

Vous pouvez télécharger le code source du projet ici. Le projet a été créé avec Eclipse 3.7 classic.

VI - Articles connexes

Vous pouvez approfondir vos connaissances sur le développement de plugins Eclipse à travers les articles suivants :

- Extensions et Points d'extension avec Eclipse

- Construction de vues avec la plateforme Eclipse

- Eclipse Commands Tutorial

- Eclipse JDT - Abstract Syntax Tree (AST) and the Java Model - Tutorial

VII - Remerciements

Je tiens à remercier keulkeul pour la relecture technique.

Je remercie également djibril et claudeleloup pour l'excellent effort apporté lors de la relecture orthographique.