Résumé - enstb.org

publicité
Générateur
Automatique de
Belle
Interface
 Auteur :
Pierre Paquin
 Encadrents :
Ronan Keryell
Stéphanie Even
-1-
Résumé
De nombreux logiciels utilisés pour la recherche scientifique nécessitent de grandes quantités
de données représentées sous forme de fichiers textes, analysés en entrée de logiciels de calcul
par des parseurs (par exemple Lex et Yacc). L’origine de projet GABI a pour but la création
d’une application capable d’interpréter cette grammaire pour générer automatiquement et
dynamiquement des interfaces graphiques pour ces logiciels. L’outil actuellement en cours de
développement est programmé en JAVA et s’appuie sur JLEX et CUP.
Mon rôle dans le projet G.A.B.I. a été dans un premier temps de faciliter la possibilité
d’ajouts de commentaires dans les fichiers de données. Dans un second temps, il m’a été
demandé d’étudier la généralisation de la possibilité de découper le fichier de données généré
par G.A.B.I., au moyen d’inclusions dans un fichier parent.
Mots Clefs

Générateur automatique d’interface

I.H.M.

Parseurs

Arbres de données

JTREE

Insertion

Remarques
-2-
Table des matières :
RESUME
2
INTRODUCTION
4
I.1 LES GUI-BUILDERS
I.2 LES GUI-GENERATORS
I.3 ET GABI DANS TOUT ÇA ?
4
5
5
ETAT D’AVANCEMENT INITIAL DU PROJET
8
2.1 PRESENTATION GENERALE
2.2 FONCTIONNEMENT DE GABI
2.2.1 FENETRE DE CREATION DE L’ARBRE
2.2.2 FENETRE PRINCIPALE
2.2.3 FONCTIONS PROPOSEES
2.3 FONCTIONNEMENT D’UN ARBRE JTREE
2.3.1 LE TREEMODEL
2.3.2 LA GESTION DE LA SELECTION
2.3.3 LA GESTION DE L’APPARENCE
2.3.4 LE JTREE DANS GABI
LA GESTION DES COMMENTAIRES
3.1 PREMIERE ESSAI
3.2 REALISATION FINALE
3.21.1 DANS PARSER/PARSETREEBUILDER.JAVA
3.2.2DANS GRAPHIC/MAINWINDOW.JAVA
3.3 CONCLUSION SUR LES COMMENTAIRES:
8
8
8
10
11
12
12
12
13
ERROR! BOOKMARK NOT DEFINED.
14
ERROR! BOOKMARK NOT DEFINED.
14
14
15
16
GESTION DE L’INCLUSION:
17
4.1 L’IDEE DE BASE:
4.2 REALISATION
4.2.1 MODIFICATION AU NIVEAU DES PARSERCOMPOSITES
4.2.3 GESTION DE L'AFFICHAGE:
4.2.4 AJOUT D'UN DEBUT D'INCLUSION
4.2.5 AJOUT D’UNE FIN D'INCLUSIONS
4.2.6 FONCTION PARCOURS GRAPH:
4.2.7 LA TREEMODEL
18
18
18
21
22
23
24
26
CONCLUSION DE L’INSERTION
28
BIBLIOGRAPHIE
LE JTREE
RAPPORTS PRECEDENTS
INTERFACES AUTOMATIQUES:
30
30
30
30
-3-
Chapitre 1
Introduction
Les logiciels scientifiques, développés et utilisés notamment par des instituts de recherche,
requièrent en entrée des fichiers de données relativement complexes et volumineux. Ces
fichiers sont ensuite analysés et interprétés par des parseurs, conçus pour en comprendre la
grammaire. Certaines interfaces graphiques servent à faciliter la saisie de tels fichiers.
Certains outils de génération automatique d'interface existent déjà. Dans sa thèse, Max Schlee
nous explique qu'il en existe deux sortes principales : Les « GUI builder » (Graphical User
Interface Builder) et les « GUI generators ».
I.1 Les GUI-Builders
Ces programmes aidant la conception d'interface font partis des RAD (Rappid Application
Development), c'est à dire qu'ils proposent de créer un programme complet, tout en
permettant de générer facilement son interface. Ils mettent ainsi à disposition du programmeur
un éventail de choix de composants (boutons, listes, cadres, fenêtres ...etc.) qu'il peut à sa
guise intégrer dans son application. D'après Monsieur Schlee, cela correspond à l’adage
« What you see is what you get ». Autrement dit, le programme générera par la suite une
interface correspondant à celle que vous pourriez voir durant la phase de programmation.
Comme exemple, nous pouvons donner des programmes comme Power-Builder, sous
windows, ou QT-Disigner s'appuyant sur la librairie Qt propre à KDE ou Glade qui
fonctionne via la librairie GTK+ qu'utilise GNOME sous linux.
Ce type de génération soulève le problème de l'évolutivité. En effet, une fois générée,
l'interface ne peut plus changer, sans avoir à tout reprogrammer. Or nous aimerions une
solution qui s'adapte à une syntaxe et une grammaire à priori inconnue. De plus, une bonne
partie de la conception reste manuelle, cette solution ne peut donc être retenue. Une autre
solution, bien plus proche cette fois du projet GABI existe. Ce sont les GUI generators.
-4-
I.2 Les GUI-Generators
Ce type de générateur d’interface permet de libérer le programmeur de l’aspect « design ».
Comme il est reconnu que le temps de programmation de l'interface est au moins aussi long
que celui du développement du programme, cela est un grand gain de temps, bien que la perte
de liberté pour le design est néanmoins significatif... Ces générateurs peuvent avoir plusieurs
modes de fonctionnement:
- Fonctionnement par vues : Le générateur prend en entrée deux composants : Les Data
models et les views. C'est à dire une forme de syntaxe des données ainsi qu'une liste de ce qui
doit apparaître dans les différentes vues du programme. (Sans expliciter plus la façon dont ils
doivent apparaître) Par la suite, à chaque vue sera associée une fenêtre dont les composants
internes dépendront du Data Model.
- Fonctionnement par frame : Les frames sont des patterns qui sont préprogrammés dans le
générateur. On déclare donc au préalable ce que l'on désire voir, sous forme d'association
pattern – paramètre, puis le générateur construira une interface via les frames.
- Fonctionnement par Feature Models : Ce dernier fonctionne grâce à la notion d'arbre proche
de celle utilisée par GABI, que nous expliciterons dans le prochain paragraphe. Le générateur
demande en entrée un ensemble de données sous la forme d'arbres. Chaque noeud de l'arbre
est inclus dans le précédent : par exemple, une roue est incluse dans la voiture qui elle même
peut être incluse dans autre chose. C'est en s'appuyant sur cette notion d'arbre que le
générateur construira une interface.
Cette dernière méthode est intéressante car la notion d'arbre permet une adaptation de
l'interface aux données. Cela nécessite toutefois que l'arbre soit pré défini et donné en entrée
ce qui n'est pas notre cas dans G.A.B.I. En dernier lieu, des outils étant des générateurs
« généralistes », conçus par des designers, ils ne sont généralement pas optimisés et des
fonctions simples peuvent parfois s'avérer difficiles à définir.
I.3 Et GABI dans tout ça ?
Les outils précédents fournissent une aide à la production d'éléments d'interface, mais ne
résolvent pas un problème crucial de notre point de vue qui est que ces interfaces doivent être
revues dès qu'une modification a lieu dans le logiciel interfacé. S'ils sont une aide pour une
partie du travail, ils ne suppriment pas forcément le côté fastidieux de la mise à jour d'une
interface. Le risque est grand alors, soit de figer complètement les choses et de limiter les
perspectives d'évolution d'un logiciel, soit de passer un temps considérable dans la gestion de
l'information. Le but de G.A.B.I. est donc de proposer un générateur d’interface permettant de
construire ce même fichier, en s’appuyant directement sur les parseurs devant analyser le
fichier de données
-5-
Figure 1 : Fonctionnement général de G.A.B.I.
G.A.B.I. reçoit donc en entrée des parseurs. Une fois analysés, ces parseurs mettent en
évidence une grammaire pouvant se développer sous la forme d’un arbre. Par exemple, le
noeud principal sera constitué de tous les éléments de bases possibles. Les nœuds suivants
seront ces éléments en particuliers, pouvant se traduire par des listes ou des tableaux. Viendra
enfin la valeur associée aux paramètres finaux. Ces paramètres et leurs valeurs seront les
feuilles de l’arbre. La figure suivante montre un exemple de grammaire développée sous
forme d’arbre :
-6-
Figure 2 : Exemple d’arbre de données.
Grâce à cette structure en arbre, il est possible de construire une structure JTREE pour le
restituer. Chaque élément pouvant constitué un nœud étant propre au nœud précédent, la liste
des éléments possibles sera mise à jour à chaque clic sur un nœud.
-7-
Chapitre 2
Etat d’avancement initial du projet
2.1 Présentation générale
A l’origine du présent projet, une version opérationnelle du générateur automatique G.A.B.I.
existait Cependant, quelques fonctionnalités restaient limitées ou impossibles, comme
l’inclusion, ou l’importation d’un arbre déjà construit. L’outil était donc capable d’éditer une
interface à partir des parseurs Lex et Yacc Fournis au préalable. Cette interface permet la
saisie de la plupart des fonctionnalités descriptibles dans les parseurs, comme les paramètres,
les listes …etc. Il est également possible d’ajouter certains commentaires, ainsi que certaines
inclusions, dans un domaine limité que nous verrons plus loin.
2.2 Fonctionnement de GABI
2.2.1 Création de l’arbre
Cette fenêtre a pour but la construction d’un arbre de données à partir de parseurs Lex et
Yacc. En effet, l’information contenue dans ces parseurs peut être vu comme une hiérarchie
de données pouvant se représenter par un arbre, comme le montrait la figure 2 du chapitre
précédent. Pour modéliser cette structure d’arbre en Java, plusieurs classes on été créées :
 Les ParserComponents : Cette classe est une classe abstraite, qui définie les actions
possibles aux différents positions de l’arbre (nœud ou feuille).

Les ParserComposites : Chaque action de la grammaire décrite par les parseurs sera
représentée par un ParserComposite, et pourra constituer un nœud de l’arbre.
Toutefois, les descendants de chaque ParserComposite ne seront pas toujours les
mêmes, et dépendront directement de la grammaire. En effet, un paramètre aura
comme descendant un nom, un comparateur et une valeur, par exemple, alors qu’un
flottant ne pourra être par exemple qu’un entier ou un double (voir la figure 2). Il faut
donc que chaque instance de ParserComposite ait une liste propre de descendants
possibles.
Chaque ParserComposite possède donc une double structure de descendants : Un
vecteur de « PossibleParserComponents » correspondant au sous actions possibles
d’une action de la grammaire, ainsi qu’un vecteur de « ParserComponents »,
correspondants aux descendants réels, instancié par l’utilisateur au fur et à mesure
qu’il crée sa propre structure de données.
-8-

IdentifierComponent, ValueComponent, ListComponent : Ces 3 classes sont les
différents feuilles possibles. Elles correspondent respectivement aux valeurs connues,
aux valeurs spécifiées par l’utilisateur et aux listes de plusieurs valeurs possibles.
Toutes trois héritent bien sûr de la classe ParserComponents.
ParserComponent
« composites »
« PossibleParserComponent »
ParserComponent i,1
possibleParserComponent i
ParserComponent i,2
…
ParserComponent i,3
…
Selection
ParserComponent i,1
ParserComponent i,2
ParserComponent …
ParserComponent i,n
Affiché dans la fenetre
de saisie des choix de
l’utilisateur
Fonctionnement des ParserComponents
Une fois la structure générale de l’arbre générée et sauvegardée, la fenêtre principale
(mainWindow) qui contiendra l’interface proprement dite est ouverte. La première fenêtre
jouera alors le rôle de console pour divers information données à l’utilisateur par le système
(confirmations, erreurs …etc.).
Figure 3 : Fenêtre permettant d’extraire une structure d’arbre à partir des parseurs
-9-
2.2.2 L’interface graphique
La fenêtre principale de l’interface graphique aura toujours la même base. Nous y trouvons 5
parties principales :
 Sur la moitié gauche de la fenêtre se trouve l’arbre représentant les données telles
qu’elles auront été implémentées par l’utilisateur, et qui seront contenues dans le
fichier texte produit par l’interface.
 Sur la moitié droite se trouvent 4 parties. En haut se trouve la liste des éléments
pouvant être insérés dans l’arbre au niveau du nœud en cours (c’est la liste des
PossibleParserComponents correspondants au nœud en cours).
 Au centre à droite se trouve la fenêtre des commentaires. Elle permettra d’afficher les
descriptifs de chaque type de données.
 Au centre en bas, dans la fenêtre d’édition, se trouve la liste des éléments du nœud.
C’est là que les différentes valeurs sont choisies.
 En bas à droite se trouve finalement un aperçu de ce que contiendra le fichier texte au
final.
Figure 4 : La fenêtre principale de l’I.H.M. avec arbre
- 10 -
Figure 5 : La fenetre de l'I.H.M., arbre vide
2.2.3 Fonctions proposées
 Ajout de composants:
Le bouton « AJOUTER » à gauche de la liste déroulante permet de choisir quel élément
ajouter au niveau du nœud sélectionné. La liste en question étant mise à jour
dynamiquement lors de la sélection dans l’arbre.
C’est aussi à ce niveau qu’étaient gérés, à l’origine, les commentaires. Ils étaient alors
introduits à chaque nœud de l’arbre sous la forme de PossibleParserComponent. Ce
dernier étant toujours présent dans la liste, il la rendait donc un peu plus lourde. L’un des
objectifs de ce projet a donc été de déplacer cette gestion de commentaires.
 Suppression de composant :
Ce bouton permet à l’inverse du bouton précédent, de supprimer un élément sélectionné
de l’arbre. Afin d’assurer la cohérence de l’arbre lors de la suppression, l’ensemble des
éléments d’une action seront supprimés lorsque l’utilisateur voudra supprimer l’un d’entre
eux.
 Insertion:
Ce que l’on entend par insertion est ici la possibilité de structurer l’information en
plusieurs fichiers. En effet, si la quantité de données se révèle démesurée, il peut être
judicieux d’en scinder une partie pour la mettre dans un autre fichier, à la manière d’un
« include » en langage C.
A l’origine, la gestion des insertions se faisait au niveau d’un nœud. Tout les descendants
de ce nœud étaient alors mis dans l’insertion. Ceci restreint fortement l’utilisateur car ce
dernier ne peut pas mettre dans un fichier spécifique une partie seulement de l’information
contenue à partir du nœud, ou même du même niveau.
- 11 -
Le second enjeu du projet sera donc d’améliorer la gestion de l’inclusion, et de la
généraliser.
2.3 Fonctionnement d’un arbre JTREE
L’une des difficultés majeures rencontrées dans ce projet a été pour moi la découverte des
arbres JTREE. En effet, n’ayant jamais eu l’occasion de les utiliser, il m’a fallu un certain
temps d’adaptation.
Il existe deux principales méthodes de construire un arbre JTREE. Il est possible d’utiliser les
DefaultTreeNode, définissant les nœuds par défauts, ou de définir ses propres nœuds. Pour
cela, il est nécessaire de définir un TreeModel spécifique grâce auquel les nœuds et feuilles
seront utilisés.
2.3.1 Le TreeModel
Un TreeModel sert à expliquer à JAVA comment est construit un arbre. Le TreeModel sera
appelé à chaque fois que le système a besoin d’une information sur un nœud, comme le
nombre de ses descendants. Il a donc un certain nombre de fonctions prénommées qui seront
automatiquement appelées lorsque JAVA en aura besoin (le nombre de descendant par
exemple). Il est donc possible de construire un arbre de cette façon. Comme nous l’avons vu
au paragraphe 2.2.1, le TreeModel de G.A.B.I. sera composé de ParserComponents :
Figure 6 : Structure de l’arbre dans G.A.B.I.
2.3.2 La gestion de la sélection
La gestion d’un JTREE se fait principalement par le biais de « listeners » Ces classes gèrent
certains évènements comme le clic de la souris sur l’arbre. Grâce à elles, il est possible de
réagir à la sélection d’un élément de l’arbre.
Ces listeners sont ajoutés à la création de l’arbre, tout comme le TreeModel. Après chaque
modification de la structure de l’arbre, deux méthodes peuvent être appelées, selon la
nécessité. FireTreeStructureChange peut être appelé si la structure profonde a été changée.
- 12 -
Sinon, la fonction updateUI() de la classe JTREE suffit a prendre en compte les nouveaux
changements de présentation uniquement.
2.3.3 La gestion de l’apparence
L’apparence de l’arbre peut aussi être géré. En effet, chaque nœud a un CellRenderer. En
agissant sur celui-ci, il est possible de changer l’image du nœud, ou la couleur du texte…etc.
- 13 -
Chapitre 3
La gestion des Commentaires
La première tache qui m’a été confié a été la modification de la gestion des commentaires.
Comme nous l’avons vu plus haut, ces derniers étaient gérés au niveau des nœuds, par le biais
de la liste déroulante, alourdissant ainsi l’arbre. L’idée était de déplacer la gestion au niveau
du menu.
Les commentaires visés sont des commentaires linéaires, précédés d’un « tag » comme « # »
ou « // ». La détection de ce tag était déjà en place lors de la reprise de ce projet. En effet, si
aucun commentaire n’est prévu par les parsers, il n’est pas nécessaire de les gérer.
3.1 Réalisation finale
Pour la réalisation, il m'a fallu modifier la conception même de l'arbre des données en
supprimant l’ajout automatique d’un nœud commentaire à tous les nœuds de l’arbre lors de la
construction de celui-ci. Cette construction se fait après analyse des parseurs Lex et Yaccs
dans un fichier sérialisable. (Fichier parser/ParseTreeBuilder.java)
Par la suite il m'a fallu créer une nouvelle boîte de dialogue qui accueille les commentaires, et
inclure la gestion de cette dernière dans le menu principal. Enfin, après avoir validé les
commentaires, une gestion de la mise en place différente de l'initiale est mise en place.
3.2 Les modifications apportées :
3.2.1 Dans parser/ParseTreeBuilder.java
Cette classe reconstitue l'arbre de données à partir du résultat de l’analyse des parseurs Lex et
Yacc. Lors de cette analyse, il est possible de détecter la présence d’une possibilité d’ajout de
commentaire. Ainsi, l’analyse cherche s’il y a un tag associé un mot commençant par
« COM » (comme commentaires, ou comment). Dans ce cas, ce tag est associé aux
commentaire (là je ne suis pas sûr)
Dans l'état d'origine, lors de la présence de ce tag, un PossibleParserComponent « Comment »
était automatiquement ajouté. Ce PossibleParserComponent permettait alors l’ajout d’un
commentaire au niveau de chaque nœud de l’arbre. Autrement dit, le vecteur
PossibleParserComponent contenait automatiquement un élément « Comment », ce qui
alourdissait considérablement l'arbre. (Si il y a 100 000 composites, il y aura 100 000
commentaires alors que peut être seul 10% sera réellement utilisé!)
L'idée est alors de supprimer, lors de la construction de l'arbre, tous les commentaires des
PossibleParserComponent. Afin de laisser une possibilité d’ajout de commentaire, il est
nécessaire de garder quelque part le PossibleParserComponent « Comment ». Comme le
nœud root doit toujours être présent, c’est à ce nœud que l’on va ajouter « Comment ». Par la
suite, si l'on veut ajouter un commentaire, il suffira de cloner root afin d'ajouter un
- 14 -
commentaire au composite. Avec cette technique, tous les commentaires, instancié sous la
forme d’un ParserComposite, correspondent à un commentaire réel.
376 : if (commentaire != null)
rootComponent.addCommentParserComponent((ParserComposite) commentaire);
3.2.2Dans graphic/MainWindow.java
La possibilité d’ajouter un commentaire se gère au niveau du menu. Le menu commentaire
n’apparaîtra si l'élément racine de l'arbre contient des commentaires, autrement dit, si la
définition d’un commentaire a été détectée au niveau des grammaires d’origines:
if (windowTreeRoot.getCommentParserComponent(0).toString() != null) {
MenuComment jc = new MenuComment(this);
mainMenuBar.addMenu((JMenu)jc);
} // end of if ()
Par la suite, si un élément est sélectionné et que l'on ajoute un commentaire, il est nécessaire
de récupérer le ParserComponent « Comment ». Pour cela, on clone le commentaire de la
racine (root) avant de faire appel à CommentBox, qui va permettre la saisie de notre texte. Il
est à noter que l'ajout de commentaire à la racine a été supprimé pour éviter des
complications.
public void addCommentMenuSelection(String comment)
{
if (selectedItem == null)
{
System.out.println ("Aucun component sélectionné");
}
else
{
/*************************************************************
* Le composite sélectionné n'a pas de commentaire, on va donc
* cloner le commentaire de la racine (qu'on ne peut changer)
* , et ensuite l'insérer dans l'élément sélectionné.
************************************************************/
ParserComposite newComposite = (ParserComposite)
windowTreeRoot.getCommentParserComponent(0).clone();
selectedItem.addCommentParserComponent(newComposite);
/************************************************************
* Maintenant que le commentaire est créé, on ajoute le texte
************************************************************/
if (selectedItem != windowTreeRoot &&(selectedItem.getCommentParserComponent(0) !=
&& (comment != null))
{
selectedItem.setValue(selectedItem.toString() + " (COMMENTAIRE)");
selectedItem.getCommentParserComponent(0).getChild(1).setValue(comment);
fireAddComponentEvent (selectedItem, (ParserComponent)
selectedItem.getCommentParserComponent(0));
} // end of if ()
}
}
null)
La classe CommentBox ne présente que peu d'intérêt ; elle est formée d'une boite de saisie et
d'un bouton valider. Je ne détaillerai donc pas ici son code.
- 15 -
3.3 Conclusion sur les commentaires:
Ce travail m'a permis de prendre pied dans le projet déjà volumineux. C'est cette même taille
qui m'a ralenti, car il est difficile de prendre un projet en cours, et d'autant plus difficile que ce
projet est grand. (Plus de 70 fichiers de classes)
Ceci a été très intéressant car je n'avais jamais utilisé de JTREE. Pour tout ce temps de
découverte, j'ai mis plus d'un mois. Le problème des commentaires a été résolu juste avant les
vacances de noël.
- 16 -
Chapitre 4
Gestion de l’inclusion:
Le but de la modification de la gestion des insertion est double : généraliser le principe
d’insertion, mais aussi simplifier la visualisation de l’information aussi bien dans le fichier
généré que dans l’arbre de données.
while(condition)
Dans un premier temps, il s’agit donc de reprendre le
{
principe d’insertion et de le généraliser. Pour expliquer cela,
variable++;
prenons l’exemple du langage C : lors de l’utilisation d’une
}
inclusion dans une boucle « while », nous pouvons doit pouvoir devenir :
théoriquement mettre le début de la définition du « while » hors
while(condition)
{
de l’inclusion, et la fin à l’intérieur (voir encadré). C'est ce genre
#include « reste.h »
de généralisation que nous voulons rendre possible dans GABI.
Où
reste.h
contient:
En effet, l'inclusion était jusque là gérée au niveau des nœud et
variable++;
ne rendait donc possible que le « tout ou rien » (tout le « while »
}
en inclusion, ou rien du tout).
Dans un deuxième temps, il faut permettre de simplifier
Exemple d’utilisation
l’intelligibilité du fichier de donnée. En effet, l’idée de
logique de l’inclusion :
l’inclusion est aussi de regrouper les informations de façon
#include “librairies.h”
logique et utile (voir encadré). Il faut donc que G.A.B.I.
#include “variables.h”
reprenne cette idée. Le but est ici de pouvoir rendre visible une
I=2;
« preview » du fichier de donnée contenant les appels à
…etc.
l’inclusion, et cachant le contenu de ces inclusions. Ce contenu
sera visible d’une autre façon, séparément du reste des
informations.
Ce même principe doit être appliqué à la représentation de l’arbre des données qui doit
pouvoir cacher le contenu des inclusions, pour ne rendre visible que le contenu d’un fichier à
la fois.
Nœud While
Nœud While
While
While
{
{
Debut inclusion
Debut inclusion
Variable ++
Fin inclusion
}
……
..
Fin inclusion
- 17 -
4.1 L’idée de base:
L'idée développée est alors d'inclure dans l'arbre des marqueurs de début et de fin d’inclusion,
ainsi que le principe d'une machine à états. Les deux états présents seront l’état « hors
inclusion » (état A de la figure 6), et l’état « en inclusion » (état B de la figure 6). La
condition de changement d’état sera la rencontre d’un marqueur début ou fin d’inclusion, où
nous passerons de l’état A à B ou inversement (voir figure 6). En ne lisant l’arbre que dans
l’état « hors inclusion », nous pourrons donc « sauter » une partie correspondant à une
inclusion.
Afin de gérer plusieurs inclusions emboîtées, un compteur est ajouté dans la machine à états
pour compter le niveau de l’inclusion. A chaque début d’inclusion, le compteur sera
incrémenté, et à chaque fin d’inclusion, il sera décrémenté. L’état passera alors de « en
inclusion » à « hors inclusion » lors du passage sur un marqueur « fin d’inclusion » et
qu’aucune inclusion n’est en cours (compteur à 0). Ainsi nous pourrons savoir quel fin
correspond à quel début, et également s’il y a autant de fin que de début, dans un souci de
logique.
Figure 7 : Exemple de fonctionnement de la machine à états
4.2 Réalisation
4.2.1 Modification au niveau des ParserComposites
Un paramètre des ParserComposites dans la version d'origine de GABI était le paramètre
isInclus. Ce paramètre booléen servait à indiquer que le nœuds, contenait une inclusion. Etant
- 18 -
donné que la gestion ne fait plus au niveau des noeud, l'intérêt de ce paramètre fut changé: il
va servir de marqueur pour changer d'état.
IsInclus devient alors un entier pouvant prendre 3 valeurs:
0 : on reste dans l'état courant
1 : changement d'état pour un début d'inclusion
-1 : changement d'état pour une fin d'inclusion
De plus, les fonctions getInclus et setInclus permettront respectivement de récupérer la valeur
de isInclus, ou de la modifier.
La classe ParserComposite a étée modifiée pour ajouter un paramètre : la liste de la position
des débuts d’inclusion et des fin
d’inclusion contenus dans une instance
Nœud While : Début [3] ; Fin [N]
de la classe. Ce paramètre permet de
savoir simplement en les lisant, (sans
1
While
regarder le contenu du nœud), de savoir
2
s’il contient plus de début d’inclusion
{
que de fin, auquel cas un changement
d’état vers « en inclusion » est
envisageable (ou le contraire si plus de
3
fin que de début sont présents).
Debut inclusion
Dans tous les cas, au passage sur un
nœud, le compteur du niveau d’inclusion
Variable ++
sera incrémenté ou décrémenté de la
différence entre le nombre de début ou
}
de fin, et ceci sans lire le contenu du
……
nœud, évitant ainsi une lecture pouvant
..
N
être très longue
Fin inclusion
- 19 -
4.2.2 Localiser de l'action
Alors qu'à l'origine, toute action s'effectuait au niveau des nœuds de l'arbre, nous avons
introduit la possibilité d’agir également au niveau des feuilles. Cela implique quelques
changements. En effet, lorsque j'ai pu commencer à travailler sur GABI, la sélection
s'effectuait au niveau de ParserComposites. Dans l'arbre précédent (figure 6), si l'on
sélectionne « NOM », l'élément sélectionné par GABI (ou selectedItem dans le programme)
sera en fait le ParserComposite « setenv ». Comment dans ce cas GABI peut-il savoir que
l'inclusion doit se faire au niveau de « NOM »?
Pour que G.A.B.I. puisse repérer la position de « NOM » dans le nœud, j'ai donc modifié le
treeSelectionListener de façon à ce qu'il prenne également en compte les feuilles afin de
repérer la position de la feuille « Nom » par exemple. Cette position sera alors gardée dans la
variable selectionNumber (Classe TreeSelection du fichier MainWindowObserver) Pour le
bon fonctionnement du programme, la variable selectedItem devra rester le ParserComposite
père de l'élément sélectionné:
ParserComponent selectedItem = (ParserComponent) leadPath.getLastPathComponent ();
De plus, il faut garder une trace de l'élément réellement sélectionné. Pour cela, le paramètre
selectionEvent, se rapportant à la ligne sélectionnée, est ajoutée à MainWindow. A chaque
sélection, ce dernier est mis à jour à partir de MainWindowObserver:
parentWindow.setSelectionEvent(e);
Si l'élément sélectionné n'est pas une feuille, la gestion du programme restera identique. Il
faut donc appeler la fonction « treeSelectionChange » de la fenêtre. Cette fonction sert à gérer
dynamiquement les éléments de la fenêtre en fonction de la sélection.
if (!selectedItem.isLeaf()) {
parentWindow.treeSelectionChanged ((ParserComposite) selectedItem, leadPath);
Si l'élément sélectionné est une feuille, deux cas se présentent: soit la feuille marque le début
d'une inclusion, soit c'est un autre type de feuille. Dans le second cas, il suffit comme
précédemment, d'appeler treeSelectionChanger avec le bon paramètre.
Si l'élément sélectionné marque le début d'une inclusion, il faut alors gérer l'affichage de toute
l'inclusion dans la fenêtre de visualisation. On va alors récupérer le Writer associé et parcourir
l'arbre, en changeant d'état le cas échéant, afin d'afficher ce qui est nécessaire.
De plus, à chaque clic sur un début d'inclusion, l'arbre doit se « replier » pour cacher ce qui
est inclus ou le découvrir. Dans le cas d'un grand nombre d'information, ceci est fort utile pour
simplifier la visibilité de l'arbre.
- 20 -
if(((ParserComponent)e.getNewLeadSelectionPath().getLastPathComponent()).getInclus()=
= 1){
// récupération du Writer
parentWindow.getTextArea().getWriter().reset();
//variable contenant l'état
int etat;
// numéro servant à repérer la position de l'élément d'origine du parcours
// le graph sera parcouru a partir de cet élément.
int selectionNumber = selectedItem.getParent().getIndexOfChild(selectedItem);
// Le pareserComposite associé
selectedItem=(ParserComposite)selectedItem.getParent();
// Parcours du graph vers les enfants de selectedItem.
etat = parentWindow.parcoursGraphe((ParserComposite)
selectedItem,1,selectionNumber,parentWindow.getTextArea().getWriter(), compteur);
// Parcours du graph vers les parents de selectedItem.
while((!selectedItem.equals(parentWindow.getWindowTreeRoot())) && (etat>0)){
selectionNumber = ((ParserComposite)
selectedItem.getParent()).getIndexOfChild(selectedItem);
etat = parentWindow.parcoursGraphe((ParserComposite)
selectedItem,etat,selectionNumber,parentWindow.getTextArea().getWriter(),
compteur);
}
// changement de l'arborescence de l'arbre
parentWindow.treeVisibleChange();
}else{
parentWindow.treeSelectionChanged ((ParserComposite)
selectedItem.getParent(),
leadPath.getParentPath());
}
4.2.3 Gestion de l'affichage:
Deux cas se présentent:
 On clique sur un début d'inclusion
Dans ce cas, on désire voir apparaître dans la fenêtre de visualisation le contenu du
fichier inclus. Cela veut dire non seulement ce qui est dans le noeud, mais également
se qui suit jusqu'à l'apparition du marqueur de fin d'inclusion (lorsque isInclus = -1 et
compteur=0).
Dans le code précédent, on voit apparaître à plusieurs reprises la fonction
parentWindow.parcoursGraphe. Cette fonction sert à parcourir le graphe à partir d'une
position précise, donnée par le composite père selectedItem et par le numéro
d'emplacement du fils selectionNumber. Nous passons également en paramètre l'état
initial (à savoir si on se trouve dans l’état « en inclusion » ou « hors inclusion »), le
Writer ( permet d’écrire dans un fichier ou dans la fenêtre texte) où écrire et le nombre
d'inclusion déjà parcouru (paramètre compteur). La valeur retournée est l'état après
parcours du graphe jusqu'à la fin du noeud.
Le fait de ne pas dépasser le noeud implique que si on se trouve toujours dans une
inclusion à la sortie (état « en inclusion »), alors il faut continuer. On appelle de
nouveau la même fonction mais pour le noeud père jusqu'à ce qu'on arrive au noeud
principal de l'arbre ou que l'état revienne à « hors inclusion ».
- 21 -

On clique sur un autre élément:
Dans ce cas, tout se passe comme à l'origine : L’arbre est lu en affichant toutes les
feuilles jusqu’à la fin du nœud (ou ParserComposite) sélectionné. Le cas échéant, on
appelle alors la fonction TreeSelectionChanged pour mettre à jour l'affichage.
Cet affichage contient également une fonction ParcoursGraph qui comme la
précédente sert à afficher le contenue d'un noeud. Elle marche également comme une
machine à états et compte combien d'inclusions passent.
4.2.4 Ajout d'un début d'inclusion
Le menu d’ajout d’une inclusion est associé à la fonction addInclureMenuSelection(). Cette
fonction commence par créer le ParserComponent inclusion qui sera ajouté au niveau du
ParserComposite sélectionné.
 Si l’utilisateur a sélectionné une feuille, le ParserComponent sera ajouté en tant que
nouvelle
 S’il a sélectionné un nœud, on ajoute l’inclusion dans ce nœud en tant que nouvelle
feuille également.
if ((selectedItem.getInclureParserComponent(0) != null) && (name != null))
{
int place =0;
try {
// création du composite inclusion
ParserComposite inclusionFlag = new ParserComposite("inclusionFlag");
ValueComponent nom = new ValueComponent("inclusion : " + name);
nom.setValue("inclusion "+name);
inclusionFlag.addComponent(nom);
// Ajout du composite
System.out.println ("Ajout d'un " + inclusionFlag.toString());
// On envoie l'événement aux objets intéressés
System.out.println("selectionEvent : "+selectionEvent);
Suivant l’élément sélectionné, l’action diffère:
 dans le cas de la racine, on crée une nouvelle feuille à l’élément sélectionné
 dans le cas d’un composite, on fait de même
 dans le cas d’une feuille, on crée également une feuille, mais ajoutée au père de
l’élément sélectionné.
if(selectedItem!=windowTreeRoot){
// si on clic sur un parserComposite
if(selectionEvent.getNewLeadSelectionPath().getLastPathComponent().getClass().
toString().equals(new String("class datacomponents.ParserComposite"))){
fireAddComponentEvent (selectedItem, (ParserComponent) inclusionFlag, 0);
selectedItem.getChild(0).setInclus(1);
}else{
//si c'est une feuille
place = navigationTree.getMinSelectionRow() - navigationTree. (selectionEvent.
getNewLeadSelectionPath().getParentPath()) - 1;
fireAddComponentEvent (selectedItem, (ParserComponent) inclusionFlag, place);
- 22 -
selectedItem.getChild(place).setInclus(1);
}
}
else{
//si c'est le noeud principal
fireAddComponentEvent (selectedItem, (ParserComponent) inclusionFlag, 0);
selectedItem.getChild(0).setInclus(1);
}
}
catch(ParserComponentException e) {
System.out.println("erreur durant l'insertion : " + e);
}
Par la suite, il est nécessaire d’indiquer au composite sélectionné ainsi qu’à tous les
composites précédents qu’une inclusion a été ajouté grâce à la fonction addBeginInsert().
//Positionnement du nouveau début d'inclusion sur l'arbre de données
ParserComposite currentComposite;
currentComposite = selectedItem;
String tmp=currentComposite.toString()+place;
currentComposite.addBeginInsert(place);
while (!currentComposite.equals(windowTreeRoot)){
place = currentComposite.getParent().getIndexOfChild(currentComposite);
currentComposite = (ParserComposite)currentComposite.getParent();
tmp=currentComposite.toString()+place;
currentComposite.addBeginInsert(place);
}
navigationTree.updateUI();
} // end of if ()
4.2.5 Ajout d’une fin d'inclusion
La gestion de la fin d’inclusion est sensiblement identique à l’ajout. On commence par créer
le composite de fin d’inclusion, puis on l’insère à la place désirée, suivant que l’on ait
sélectionné une feuille, un nœud ou la racine de l’arbre. Ensuite, on met à jour les vecteurs de
fin d’inclusion des composites concernés par l’action, puis on met à jour l’arbre :
if ((selectedItem.getInclureParserComponent(0) != null) && selectedItem != windowTreeRoot)
{
int place = 0;
try {
// création du composite inclusion
ParserComposite endInclusionFlag = new ParserComposite("endInclusionFlag");
ValueComponent nom = new ValueComponent("endInclusion");
nom.setValue("endInclusion");
endInclusionFlag.addComponent(nom);
// Ajout du composite
System.out.println ("Ajout d'un " + endInclusionFlag.toString());
// On envoie l'événement aux objets intéressés
if(selectionEvent.getNewLeadSelectionPath().getLastPathComponent().getClass().
- 23 -
toString().equals(new String("class datacomponents.ParserComposite"))){
fireAddComponentEvent (selectedItem, (ParserComponent) endInclusionFlag, 0);
selectedItem.getChild(0).setInclus(-1);
}else{
place = navigationTree.getMinSelectionRow() - navigationTree.getRowForPath(
selectionEvent.getNewLeadSelectionPath().getParentPath());
fireAddComponentEvent (selectedItem, (ParserComponent) endInclusionFlag, place);
selectedItem.getChild(place).setInclus(-1);
System.out.println(selectedItem+"."+selectedItem.getChild(place)+".getInclus="+selectedItem.getChil
d(place).getInclus());
}
}
catch(ParserComponentException e) {
System.out.println("erreur durant l'insertion : " + e);
}
// Positionnement du nouveau début d'inclusion sur l'arbre de données
ParserComposite currentComposite;
currentComposite = selectedItem;
currentComposite.addEndInsert(place);
while (!currentComposite.equals(windowTreeRoot)){
place = currentComposite.getParent().getIndexOfChild(currentComposite);
currentComposite = (ParserComposite)currentComposite.getParent();
currentComposite.addEndInsert(place);
navigationTree.updateUI();
} // end of if ()
}
4.2.6 Fonction de parcours graphe:
Cette fonction, présente en deux versions différentes dans MainWindow et Write, permet
l’affichage des données de l’arbre dans une fenêtre de l’interface, présentant une « preview »
de ce que sera le fichier généré.
A l’origine, elle n’était présente que dans Write (car utilisée uniquement pour l’affichage de la
preview). Le fait de devoir gérer l’affichage de l’intérieur d’une inclusion, donc également au
niveau du JTREE, a forcé l’ajout de cette fonction dans la classe MainWindow. Dans le futur,
il pourra être envisagé de la déplacer totalement dans ce fichier et de l’enlever de Write.
C’est dans cette fonction que sont utilisés le mécanisme de machine à état et les modifications
apportées aux ParserComposites. En effet, cette fonction va parcourir le graphe et en fonction
des données recueillies à chaque nœud ou feuille, afficher le contenu de l’information dans la
fenêtre ou la cacher, révélant ainsi ce que l’utilisateur voulait mettre en insertion, ou pas.
La fonction a plusieurs paramètres. Comme toute machine à état, elle contient l’état présent
et l’état futur. A l’initialisation, ces états sont passés en paramètre. Ainsi, si l’on appelle la
fonction avec l’état précédent, ou l’état d’initialisation, il sera possible de répercuter cette
donnée tout au long du parcours de l’arbre, grâce à la récursivité. Ces paramètres sont les
seuls ajoutés dans cette nouvelle version.
int etat_present=etat;
int etat_futur=etat;
ParserComposite nveauComposite;
ValueComponent nvelleValue;
IdentifierComponent nvelIdentifier;
ListComponent nvelleList;
Le paramètre selectionNumber permet de savoir à partir de quel descendant de selectedItem il
faut commencer à parcourir l’arbre. A chaque nouveau descendant abordé, il est nécessaire de
mettre à jour l’état futur. Dans le cas d’un isInclus égal à 1, nous sommes en présence d’un
- 24 -
début d’insertion. L’état futur passe donc à 0 pour ne plus lire le contenu de l’arbre. De plus,
il est nécessaire de mettre à jour le paramètre compteur en ajoutant 1. Dans le cas d’un
isInclus égal à -1, nous sommes en présence d’une fin d’inclusion, il faut donc décrémenter le
compteur de 1.
Ce compteur est une classe à part entière, ne contenant qu’un entier. Il est nécessaire de faire
une classe car 2 informations sont à retenir de cette fonction, le compteur permettant de savoir
si nous sommes ou non à l’intérieur d’une inclusion, ainsi que l’état à la fin du parcours du
nœud. Comme en java il est impossible de retourner 2 éléments, on agit sur un objet, de la
même manière qu’en C, on utilise des pointeurs.
for (int i = selectionNumber+1;i < parserComposite.getChildCount ();i++)
{
ParserComponent child = (ParserComponent) parserComposite.getChild (i);
//gestion des etats
if(child.getInclus()==1){
etat_futur=0;
compteur.setCompte(compteur.getCompte()+1);
}
if(child.getInclus()==-1){
if(compteur.getCompte() == 1) {
etat_futur=1;
}
compteur.setCompte(compteur.getCompte()-1);
if(compteur.getCompte()==-1) return -1;
}
int itype = child.getParserComponentType ();
L’action à effectuer dépend de la nature du fils rencontré. Dans le cas d’un nœud, on relance
la fonction parcoursGraph récursivement pour mettre à jour à la fois l’état, et le compteur.
Sinon, et si l’état est à 1, on affiche le contenu de l’information.
switch (itype)
{
case ParserComponent.COMPOSITE:
nveauComposite = (ParserComposite) parserComposite.getChild(i);
etat_futur=parcoursGraphe(nveauComposite,etat_futur,-1, write,compteur);
break;
case ParserComponent.VALUE:
if(etat_present==1){
nvelleValue = (ValueComponent) parserComposite.getChild (i);
if (nvelleValue.getValue ().startsWith("incl")
&&parserComposite.getIndexOfChild(child)!=0) {
write.write("\n");
System.out.println("##" +"n");
}
write.write(nvelleValue.getValue () + " ");
System.out.println("##" +nvelleValue.getValue () + " ");
}
break;
case ParserComponent.IDENTIFIER:
if(etat_present==1){
nvelIdentifier = (IdentifierComponent) parserComposite.getChild (i);
write.write(nvelIdentifier.getValue() + " ");
System.out.println("##" + nvelIdentifier.getValue() + " ");
}
break;
case ParserComponent.LIST:
nvelleList = (ListComponent) parserComposite.getChild (i);
write.write(nvelleList.getValue () + " ");
- 25 -
System.out.println("##" +nvelleList.getValue () + " ");
break;
}
A la fin du composite, on passe à la ligne dans la fenêtre :
if ((i == parserComposite.getChildCount ()-1)&&(child.isLeaf())&&(etat==1)){
write.write("\n");
System.out.println("##" +"&n");
}
etat_present=etat_futur;
}
return etat_futur;
4.2.7 La treeModel
Comme nous l’avons dit, le TreeModel sert à construire l’arbre JTREE. Ainsi, lorsque l’on
clique sur un début d’inclusion, l’arbre se replie ou se déplie. Pour cela, il faut mettre à jour
deux éléments : getChildrenCount() indiquant le nombre d’enfant, et getChild(indice) pour
récupérer l’enfant correspondant à un indice.
Pour différencier les états développés ou non du JTREE, la variable visible a été ajoutée au
treeModel. Ce booléen peut être mis à jour à partir de MainWindow par la fonction
treeVisibleChange qui le passe alternativement de vrai à faux. Cette fonction sera appelée à
chaque clic sur un début d’inclusion, comme on peut le remarquer dans le paragraphe 4.2.2
Plusieurs autres paramètres ont été ajoutés au TreeModel :
 Nbr représente le nombre d’enfants visibles déjà comptés
 Contenu est le vecteur des enfants visibles
 Compteur permet de savoir si nous nous trouvons à l’intérieur d’une inclusion.
Tout d’abord, il est nécessaire de savoir si l’on se trouve dans un nœud. Dans ce cas, le
component sélectionné est un composite, et on peu mettre à jour le compteur. En effet, il est
fort possible que nous nous trouvions dès le début dans une inclusion. C’est le cas si le début
se trouve dans un nœud précédent.
public int getChildCount (Object object)
{
int nbr;
contenu.clear();
nbr = ((ParserComponent) object).getChildCount ();
if((!((ParserComponent) object).isLeaf())){
ParserComposite noeud = ((ParserComposite)object);
if(visible){
nbr = 0;
int compteur = 0;
if(noeud.getBeginInserts().size()-noeud.getEndInserts().size() < 0){
compteur = noeud.getEndInserts().size() noeud.getBeginInserts().size();
}
Pour mettre le compteur à jour, il suffit de faire la différence entre le nombre de début et de
fin d’inclusion qu’il contient. En effet, le compteur doit retomber à 0 en fin de parcours.
Ensuite, il suffit de parcourir le nœud en mettant à jour compteur, puis nbr et contenu si
compteur vaut 0 :
String message;
int lastValue=0;
Iterator itNoeud = noeud.getChildren().iterator();
while(itNoeud.hasNext()){
- 26 -
ParserComponent child = (ParserComponent)itNoeud.next();
if(child.isLeaf()){
if(compteur==0){
nbr++;
contenu.add(child);
}
if(child.getInclus()==1){
compteur++;
}
if(child.getInclus()==-1){
compteur--;
}
}else{
if(compteur==0){
nbr++;
contenu.add(child);
}
int temp =
((ParserComposite)child).getBeginInserts().size()((ParserComposite)child).getEndInserts().size();
compteur += temp;
if(compteur==0){
nbr++;
contenu.add(child);
}
}
}
noeud.setContenu(contenu);
}
}
return nbr;
}
La fonction getChild se trouve très simplifiée car contenu est déjà prêt à son appel. Il suffit de
vérifier que visible est bien vrai et de retourner l’élément correspondant de contenu. Si visible
est faux, on renvoie les enfant normaux du nœud.
- 27 -
Chapitre 5
5.1 Conclusion de l’insertion
Tel que nous l’avons présentée, l’insertion généralisée fonctionne au niveau du fichier de
sortie. En effet, il est dorénavant possible de faire débuter et finir une inclusion n’importe où.
Il reste cependant beaucoup à faire au niveau du JTREE. En effet, pour le moment, la seule
option possible est de tout replier d’un coup ; on ne peut sélectionner l’inclusion à replier…
Ceci économise bien sûr de la place, mais reste peu satisfaisant. Un repliage sélectif reste
donc à mettre en place.
De plus, les nœuds d’inclusion ne sont que peu différenciés des autres, on peut en jouant sur
les Rendrerer changer la couleur des nœuds
Il serait certainement judicieux de prendre plus de temps pour réfléchir aux algorithmes de
recherche à mettre en place pour trouver les insertions développées ou non. Le problème
actuel est que tout doit se faire en même temps, parcours de l’arbre, recherche des fils,
recherche des inclusions…etc. Il est donc difficile d’obtenir de bons résultats.
En conclusion, nous pouvons dire que l’inclusion est fonctionnelle, mais que des
développements restent à faire pour aboutir à une version vraiment fonctionnelle et complète.
5.2 Perspectives
En ce qui concerne les commentaire, seuls les commentaires de type linéaire ont été traités
(Commentaire sur une ligne commençant par un symbole, typiquement // ou # ). Pour
atteindre un niveau de généralisation plus élevé, il serait nécessaire de contrôler également les
commentaires multi lignes (Commentaires sur plusieurs lignes encadrés par des symboles,
typiquement /** et **/ ).
Du point de vue de l’inclusion, la finition au niveau du repliage sélectif. Le problème à ce
niveau sera nettement plus complexe car il ne faudra plus seulement gérer le changement au
niveau d’une inclusion, mais au niveau de plusieurs. Il faudra repérer et différencier les
insertions à cacher, ou à montrer. Ceci doit également être fait de façon à ce que le temps de
réponse reste correct. En effet, si l’arbre est parcouru à chaque intersection, le temps de
réponse peut en être grandement augmenté !
Le travail sur G.A.B.I., et notamment lors des tests, m’a montré combien la possibilité de lire
un arbre en entrée manque. En effet, il serait nécessaire de pouvoir exporter un arbre, au
format XML, par exemple, afin de pouvoir le rouvrir ensuite sans avoir à le reconstruire
entièrement.
- 28 -
Conclusion
Ce projet m’a permis de prendre conscience du travail nécessaire à fournir lors de la reprise
d’un projet volumineux, et surtout de l’importance des documents laissés à l’attention des
futurs développeurs. La reprise peut en effet être grandement facilité si les documents de
spécifications sont correctement réalisés.
Etant également débutant en ce qui concerne les JTREE, j’ai beaucoup appris par ce projet qui
pousse loin leur utilisation, notamment en ce qui concerne le repliement de l’arbre des
données qui ne s’effectue pas qu’au niveau des nœud. (ce qui constitue l’un des plus gros
problème de la gestion généralisée de l’inclusion)
- 29 -
Bibliographie
Le JTREE
http://java.sun.com/docs/books/tutorial/uiswing/components/tree.html
http://java.sun.com/
http://forum.hardware.fr/forum2.php?config=hardwarefr.inc&post=60948&cat=10&cache=&
sondage=0&owntopic=0&p=1&trash=0&subcat=390
Rapports précédents
Rapport du projet S5 2001 de Stéphane Bonniez
Projet PaP 2001 (groupe 4J)
Rapport 2002 et 2003 de Mme Stephanie Even
Interfaces automatiques:
Rapport de fin de thèse de Max Schlee du 28 novembre 2004
http://glade.gnome.com
http://www-rocq.inria.fr/verso/ACTIVEVIEWS/docs/interface/ presentation_interface.html
Table des illustrations
Figure 1 : Fonctionnement général de G.A.B.I. ......................................................................... 6
Figure 2 : Exemple d’arbre de données. ..................................................................................... 7
Figure 3 : Fenêtre permettant d’extraire une structure d’arbre à partir des parseurs ................ 9
Figure 4 : La fenêtre principale de l’I.H.M. avec arbre............................................................ 10
Figure 5 : La fenetre de l'I.H.M., arbre vide............................................................................. 11
Figure 6 : Structure de l’arbre dans G.A.B.I. ........................................................................... 12
Figure 7 : Exemple de fonctionnement de la machine à états .................................................. 18
- 30 -
Téléchargement