• Aucun résultat trouvé

Traitement de l’information : composition des préoccupations

CHAPITRE 6 IMPLEMENTATION DE JADAPT

6.3.2 Traitement de l’information : composition des préoccupations

JAdapt encapsule les préoccupations dans des paquetages Java et les adaptateurs dans des fi-chiers XML. JAdapt permet de composer les préoccupations grâce aux adaptations contenues dans les adaptateurs. Toutes ces informations ont été extraites et réifiées par la partie avant ; elles sont donc pleinement manipulables.

Nous allons maintenant présenter les différentes sous-parties de la composition des préoccupa-tions. Dans un premier temps, les adaptateurs sont contrôlés par un «vérificateur de typage » puis, la hiérarchie des adaptateurs est aplatie et, enfin, les adaptations contenues dans les adaptateurs sont réalisées.

6.3.2.1 Vérification du typage des adaptateurs

La vérification du typage des adaptateurs correspond à deux phases qui s’enchaînent. La pre-mière est un filtre qui vérifie certaines propriétés sur les adaptateurs qui sont nécessaires à la phase suivante. La deuxième phase fait une vérification plus poussée de ces mêmes adaptateurs. Si cette deuxième phase ne produit pas un résultat globalement correct alors la suite de l’exécution est court-circuitée. En effet, l’étape suivante ne peut être convenablement réalisée s’il subsiste des erreurs de typage.

La première phase de la vérification du typage est composée d’un ensemble de tests effectués dans l’ordre suivant :

1- Respect des conventions de nommage. Nous avons choisi d’appliquer les

conventions de nommage de Java : le nom de l’adaptateur doit correspondre au nom du fichier qui le contient (sans l’extension), de plus ce nom doit être unique pour un même paquetage Java.

2- Unicité des identificateurs dans les adaptateurs. Les noms de variable et les

noms des adaptations contenus dans un adaptateur doivent être uniques dans l’espace de nommage de l’adaptateur.

3- Adaptateur générateur. Les adaptateurs peuvent générer un nouveau paquetage

et un paquetage ne peut être généré que par un seul adaptateur.

Tous les adaptateurs qui ne satisfont pas aux tests de la première phase ne sont plus considérés pour la suite ; ils sont rejetés (et les raisons sont signalées à l’utilisateur) et n’entrent pas en compte pour la composition des préoccupations. Les autres subiront les tests de la seconde phase. Celle-ci contient les tests suivants :

1- Existence des importations. De même qu’une classe Java un adaptateur peut importer

des paquetages ou des classes. Ceux-ci doivent soit exister, soit être générés par un autre adaptateur.

2- Caractère abstrait ou concret de l’adaptateur. Tout comme les classes, les

adapta-teurs peuvent être abstraits ou concrets. Les cas suivants engendrent une erreur de ty-page :

– Une variable abstraite présente dans un adaptateur concret ; – Une variable abstraite possède une valeur ;

– Une variable est notée concrète mais ne possède pas de valeur ; – Une adaptation abstraite dans un adaptateur concret ;

– Une adaptation abstraite possède toute l’information pour être réalisable ; – Une adaptation concrète ne possède pas toute l’information pour être réali-sable.

3- Existence d’un super-adaptateur. Comme pour les classes les adaptateurs autorisent la

déclaration d’une relation d’héritage. Le super-adaptateur d’un adaptateur doit exister dans l’espace de nommage construit en utilisant les conventions de visibilité des classes Java.

Si l’ensemble des adaptateurs admis à passer les tests de la seconde phase les satisfont tous, alors la vérification du typage est terminée. JAdapt passe à l’étape suivante qui est la résolution de l’héritage. Si le moindre adaptateur échoue, même à un seul test, alors la vérification du typage n’est pas correcte et le préprocesseur termine son exécution.

6.3.2.2 Aplatissement de la hiérarchie des adaptateurs

Notre modèle permet de définir des hiérarchies d’adaptateurs pour permettre d’abstraire et de réutiliser la composition des préoccupations à travers la définition et l’utilisation de protocoles de composition (voir chapitre 4 et chapitre 5).

Pour simplifier la composition des préoccupations qui est effectuée dans une troisième étape, on propose d’optimiser la hiérarchie des adaptateurs. La transformation qui est réalisée dans ce but permet de ne garder que les adaptateurs feuilles marqués comme étant non abstraits. Le lien d’héritage entre deux adaptateurs est alors supprimé et le super-adaptateur est inséré dans l’adaptateur. Cette opération correspond à l’aplatissement du lien d’héritage.

Les adaptateurs abstraits qui sont des feuilles de l’arbre d’héritage sont naturellement éliminés car le fait qu’ils soient abstraits ne leur permet pas de participer à la composition.

L’aplatissement d’un arbre d’héritage est une opération récursive qui part des feuilles de l’arbre d’héritage pour remonter à travers les relations d’héritage successives jusqu’à la racine. L’algorithme utilisé est le suivant :

1 Pour chaque a : Adaptateur ∈∈∈∈ Ensemble des feuilles faire

2 MettreAPlat_Recursif(a)

3 FinPour

4 Enlever de l’ensemble des feuilles les adaptateurs abstraits 5

6 procedure MetteAPlatRecursif(a : Adaptateur)

7 debut

8 Si a n’est pas mis à plat Et a possède un super adaptateur Alors

9

10 MetteAPlatRecursif(Super Adaptateur de a); 11

12 MettreAPlat(a);

13 FinSi

14 fin

L’aplatissement d’un adaptateur est réalisé par les opérations suivantes :

– Fusion des déclarations d’importation du super-adaptateur dans l’adaptateur. – Fusion des variables et des adaptations du super-adaptateur dans l’adaptateur.

Ensuite, tous les adaptateurs abstraits sont retirés de l’ensemble des feuilles, et ce dernier ainsi réduit est transmis à l’étape suivante qui a la responsabilité de mettre en œuvre toutes les adapta-tions contenues dans ces adaptateurs.

6.3.2.3 Réalisation des adaptations

La réalisation des adaptations modifie l’arbre de syntaxe abstraite, qui a été construit par les analyses syntaxique et lexicale des fichiers sources du projet ; ces modifications dépendent du contenu des adaptateurs une fois aplatis.

Dans un premier temps les adaptateurs sont triés en fonction d’une relation de dépendance : un adaptateur X est dépendant d’un adaptateur Y, si Y génère un paquetage qui est importé par X. Les éventuels cycles dans le graphe des dépendances sont détectés pendant ce tri, et les adaptateurs incriminés sont rejetés.

Dans un deuxième temps, la liste triée des adaptateurs est parcourue, et les adaptations qu’ils définissent sont réalisées dans l’ordre de leurs déclarations.

Nous étudions maintenant comment l’arbre de syntaxe d’un programme doit être modifié pour réaliser tous les types d’adaptation supportés par notre modèle. Pour plus de détails sur l’implémentation des différentes adaptations, se référer à la section 5.4.

Notons que, quelle que soit d’adaptation à réaliser elle utilise au minimum un point de jointure. Les points de jointure (et les variables des adaptateurs) sont décrits par des chaînes de caractères qui contiennent des expressions régulières au format de l’API java.util.regex qui est fournie avec le langage Java. Cette API nous permet donc à partir du point de jointure de trouver toutes les cibles qui lui correspondent.

6.3.2.4 Réalisation d’une interception de méthode

Notre modèle propose quatre sortes d’interception de méthode : – les interceptions avant la méthode,

– les interceptions après la méthode, – les interceptions sur levée d’exception, – les interceptions autour de la méthode.

Quel que soit le type d’interception une technique générale consiste à successivement : i) mas-quer la méthode originale qui est interceptée en remplaçant son nom par un nom unique généré automatiquement, ii) créer une méthode avec le nom et la signature de la méthode originale, et iii) créer le corps de cette méthode en fonction de l’interception demandée.

Les adaptations à appliquer lorsqu’il s’agit de l’interception d’accès aux variables de classe ou d’instance sont similaires à celles mises en œuvre pour l’interception de méthode. La principale différence consiste, pour masquer la variable, à remplacer tous les accès à cette variable par des appels de méthodes (qui sont à créer).

6.3.2.5 Réalisation d’une fusion de classes

La fusion de classes est une opération qui, comme son nom l’indique, fusionne un ensemble or-donné de classes dans chacune des classes cibles. Pour plus de détails sur la fusion, voir la section 5.4.3.

6.3.2.6 Ajout de membres ou d’interfaces à une classe

L’ajout de membres (variables ou méthodes) ou de nouvelles relations d’implémentation d’interface à une classe existante est une opération très simple car les modifications sur l’arbre de syntaxe sont élémentaires. Il faut simplement vérifier que le membre à ajouter n’existe pas déjà ; dans le cas contraire il faut refuser l’ajout sauf pour les méthodes. On considère alors l’ajout comme une redéfinition, ce qui revient à remplacer l’implémentation initiale par la nouvelle.