Pour programmer avec la boîte à outils ICON, il n'est pas nécessaire de connaître la structure et les mécanismes de base (typage et exécution) des configurations d'entrée, décrits indépendamment par le modèle ICOM.
Cependant, l'extension de la bibliothèque d'ICON, le développement d'applications interactives configurables et la distribution de configurations d'entrées réutilisables nécessitent la connaissance de quelques mécanismes essentiels propres à la boîte à outils ICON. Nous les décrivons dans cette partie.
ICON fournit un dispositif utilitaire Script qui permet de décrire des comportements simples en JavaScript, directement dans l'éditeur de configurations et sans nécessité de recompilation. La liste des slots du dispositif ainsi que le code de traitement sont saisis dans la fenêtre de propriétés du dispositif, illustrée figure 4.6. Cet exemple décrit une fonction de transfert utilisée dans le contrôle d'un zoom par un dispositif isométrique.
Comme nous le verrons dans cette section, l'implémentation en Java d'un dispositif simple est relativement élémentaire. Les fonctionnalités d'un dispositif d'entrée sont décrites par l'interface Device dans l'API ICON. Cette interface est reproduite, avec les commentaires, sur la figure 4.7. La partie exécution des dispositifs d'entrée est décrite par l'interface Processor, reproduite sur la même figure.
En pratique, un dispositif dérive toujours de la classe AbstractDevice. Cette classe implémente un certain nombre de comportements par défaut communs à une grande majorité des dispositifs, à savoir, la gestion des slots d'entrée et de sortie à travers les méthodes addIn et addOut, l'auto-duplication (consistant en une ré-instanciation de la classe et la copie des valeurs des paramètres), et la gestion d'un processeur unique.
La figure 4.8 montre un exemple de dispositif utilitaire qui a été ajouté à la bibliothèque d'ICON pour permettre un contrôle vocal des attributs de la brosse dans ICONDraw. Ce dispositif DColorConv convertit des noms de couleur en leurs composantes RGB.
Écrire un dispositif utilitaire consiste essentiellement à déclarer ses slots d'entrée et de sortie, et à remplir le corps de sa méthode de mise à jour update, pour assigner les valeurs des slots de sortie en fonction de celles des slots d'entrée (la gestion des tables de couleurs n'est pas montrée dans le code). À chaque fois qu'une des valeurs en entrée changera, cette méthode de mise à jour sera automatiquement appelée par la machine réactive d'ICON.
La classe AbstractDevice gère un processeur unique: elle implémente l'interface Processor, et sa méthode open() retourne this par défaut. La gestion de processeurs multiples, utile pour faire de la spécialisation de code, s'effectue par dérivation de la méthode open() et l'utilisation d'inner classes.
Le dispositif peut être rendu paramétrable (par exemple, une liste de couleurs personnalisable), simplement en ajoutant des méthodes de type accesseurs à la classe. ICON déduit automatiquement les noms de paramètres par introspection et génère la fenêtre de propriétés correspondante dans l'éditeur.
L'implémentation d'un dispositif d'application est similaire à celle d'un dispositif utilitaire comme DColorConv, à ceci près que les assignations de slots de sortie sont remplacées par des appels de méthodes dans l'application elle-même.
Pour être configurable par ICON, une application doit créer une configuration d'entrée et faire le lien entre cette configuration et ses propres objets. L'ensemble des mécanismes de communication entre l'application et sa configuration d'entrée est résumé sur la figure fig:archi_contexte. La création d'une configuration d'entrée nécessite la déclaration d'un dossier de dispositifs et le passage d'un contexte d'exécution.
Le dossier de dispositifs contient l'ensemble des dispositifs qui pourront être utilisés dans la configuration, et qui seront visibles dans la partie gauche de l'éditeur de configurations (voir section 4.4.1). En général, il s'agit de la bibliothèque standard d'ICON comportant tous les dispositifs système et utilitaires organisés en sous-dossiers, à laquelle est ajouté un dossier spécifique à l'application. Comme tout dossier, ce dernier est un conteneur de dispositifs-prototypes, instances de dispositifs qui sont dupliquées pour être ajoutées à la configuration. Un ou plusieurs prototypes peuvent être déclarés par classe de dispositif, ce qui permet notamment de fournir des ensembles de dispositifs pré-paramétrés, ou de décrire des dispositifs d'instance statiques.
Le contexte d'exécution est une table de hachage qui contient toutes les informations nécessaires à l'exécution de l'ensemble des dispositifs du dossier. Les dispositifs de boîte à outils graphique, que nous avons évoqués précédemment, nécessitent une référence vers la ou les fenêtres qu'ils contrôlent. En outre, l'application possède ses propres dispositifs qui requièrent des références vers des objets de celle-ci, références qui sont ajoutées au contexte sur des clés spécifiques. Lors du lancement de la configuration d'entrée, chaque dispositif reçoit dans sa méthode open le contexte d'exécution d'où il va extraire les informations pertinentes, et le cas échéant produire un échec d'ouverture si celles-ci sont indisponibles.
![]() |
Le code type d'une application configurable par ICON est donné sur la figure 4.10. Ce code présente trois parties principales:
A. Initialisation de la configuration d'entrée. Ce code est ajouté à la fin de la méthode constructeur de l'application. Le dossier de dispositifs est créée en passant le dossier de l'application (MyDevices) au constructeur de la bibliothèque standard d'ICON (FRoot). La configuration d'entrée (ainsi que sa fenêtre d'édition) est ensuite créée, puis exécutée.
B. Gestion de la configuration. Une gestion minimale de la configuration consiste en l'ajout d'un élément interactif (élément de menu, par exemple) qui déclenche le passage en mode édition (méthode editInput()), et en la gestion propre de la désallocation des dispositifs lors de la fermeture de l'application (méthode quit()).
C. Dossier de dispositifs. Les dispositifs-prototypes de l'application sont déclarés par dérivation d'une classe abstraite (AbstractFolder).
Pour pouvoir être sauvegardées et chargées, les configurations d'entrée doivent être sérialisables. La sérialisation consiste à convertir un objet en une série d'octets (que nous nommerons signature) pour pouvoir, lors d'une exécution ultérieure, recréer le même objet par le processus inverse. Dans cette section, nous évoquerons essentiellement la sérialisation de dispositifs.
Établir la signature d'un dispositif système n'est pas trivial. En voici quelques raisons:
Établir des signatures les plus précises possibles, notamment en s'appuyant sur des identifiants uniques (à condition qu'ils soient fournis par l'API d'entrée), peut résoudre en partie ces problèmes. Des interrogations subsistent cependant: faut-il prendre en compte l'identifiant matériel du dispositif ou celui du connecteur sur lequel il est branché ? Si seul l'identifiant matériel est utilisé, la situation 4. évoquée précédemment ne sera pas résolue. Dans le cas contraire, c'est la situation 3. qui posera problème.
D'autres arguments vont à l'encontre de l'emploi d'identifiants uniques. Le remplacement d'une souris par une autre, par exemple, ne devrait pas rendre une configuration standard inutilisable. De même, certaines configurations d'entrée devraient pouvoir être réutilisables d'un poste à l'autre et d'un utilisateur à l'autre. Il est clair que la réutilisabilité des configurations d'entrée et l'identification précise des dispositifs physiques pour la persistance sont deux exigences incompatibles, et c'est pourquoi il est essentiel d'employer des stratégies de signature adaptées à chaque contexte.
Notons enfin que ces problèmes sont généralisables à tous les dispositifs comportant des entrées ou sorties implicites (les dispositifs d'application en particulier), car, d'une exécution à l'autre, il faut être capable de reconstruire ou de retrouver les objets extérieurs avec lesquels ils communiquaient.
La gestion de la sérialisation dans ICON est d'une grande souplesse, grâce au principe de descripteurs sur lequel il repose. La signature d'un dispositif est obtenue par la signature d'un de ses descripteurs. Des descripteurs plus ou moins sélectifs peuvent être construits, chaque dossier de dispositifs ayant la charge de fournir des descripteurs stricts (uniques) pour ses dispositifs fils.
Un descripteur est un ensemble non nécessairement dénombrable de dispositifs. En outre, chaque descripteur est lié à une représentation textuelle, tel qu'il existe une bijection entre l'ensemble des descripteurs et l'ensemble de leurs représentations textuelles.
Dans ICON, l'interface DeviceDescriptor est définie par les méthodes
suivantes:
public boolean contains(Device d)
public String getString()
Une classe de descripteurs est une classe:
Un descripteur est une instance d'une classe de descripteurs. La signature d'un descripteur est obtenue par concaténation de son nom de classe sans le préfixe DD, du signe =, et de sa représentation textuelle.
Exemple: pour une instance de DDFoo dont getString()=4, la signature est foo=4.
Il existe plusieurs classes de descripteurs prédéfinies dans ICON. Il est ainsi possible de décrire des ensembles de dispositifs possédant un nom donné, dérivant d'une classe donnée, possédant un ensemble minimal de slots (noms et/ou types), ou provenant d'un dossier donné. Ces descripteurs sont extrêmement puissants et la plupart permettent d'utiliser des caractères génériques (* et ?). De nouvelles classes de descripteurs peuvent créées et employées.
Les descripteurs sont composables: les meta-descripteurs sont des
descripteurs qui comportent un ou plusieurs descripteurs-fils. La plupart
permettent d'effectuer des opérations ensemblistes. Le descripteur DDAnd,
par exemple, effectue des intersections de descripteurs. À titre d'exemple, la
phrase « tous les dispositifs dont le nom commence par
"mouse" et qui dérivent d'une classe "DMouse" » sera construite ainsi:
DeviceDescriptor mydesc =
new DDAnd(new DDName("mouse*"), new
DDClass("*.DMouse"));
ou pourra être spécifiée par la signature suivante:
DeviceDescriptor desc = DescriptorUtilities.createDD("
and {
name=mouse*;
class=*.DMouse;
}
");
Lors du chargement d'un fichier de configuration, et pour chaque descripteur de ce fichier, le dossier de dispositifs est parcouru jusqu'à ce qu'un dispositif-prototype convienne (contains(d)==true). Celui-ci est alors copié, ajouté à la configuration et n'est plus pris en compte dans les recherches ultérieures.
Le descripteur DDProperties, qui décrit des paramétrages pour les dispositifs, joue un rôle particulier dans l'algorithme de désérialisation. Lorsqu'un dispositif-prototype correct a été trouvé, puis copié, l'algorithme lui applique le paramétrage spécifié par le descripteur lorsqu'il existe. Si ce paramétrage provoque une erreur (paramètre inexistant ou valeur incorrecte), l'algorithme reprend la recherche.
Lors d'une désérialisation, plusieurs dispositifs-prototypes peuvent répondre à un descripteur. C'est le premier rencontré qui est choisi, puis éliminé des choix ultérieurs. L'algorithme effectue cependant des retours arrière lorsqu'il aboutit à une impasse (dispositif introuvable).
Lors d'une sauvegarde, ICON utilise par défaut des descripteurs stricts, garantissant l'unicité. Chaque dossier de dispositifs est chargé de fournir des descripteurs stricts pour ses dispositifs, par la méthode getDescriptor(Device d). Dans la classe AbstractFolder, cette méthode retourne simplement un descripteur sur la classe du dispositif passé en paramètre. Ce comportement par défaut convient aux dispositifs utilitaires et à la plupart des dispositifs d'application. Le plus souvent, l'implémentation d'une application configurable par ICON ne nécessite donc aucune connaissance sur les descripteurs.
Pour un dossier comportant plusieurs prototypes de même classe, il devient cependant nécessaire de retourner un descripteur plus précis, portant par exemple sur le nom du dispositif. Les dossiers de dispositifs système, quant à eux, devront retourner une liste complète des caractéristiques physiques du dispositif pour s'assurer que deux dispositifs ne possèdent jamais le même descripteur. Des classes spécifiques de descripteurs pourront également être créées et employées.
Les fichiers de configurations sont générés dans un langage de script dédié nommé ICSCRIPT. Un exemple de fichier de configuration est donné figure 4.11. Celui-ci est partagé en trois sections (en gras italique): la déclaration des dispositifs composée de descripteurs, les connections, et la partie présentation, optionnelle et propre à l'éditeur de configurations. Chaque descripteur est nommé (en gras) dans la première section afin de pouvoir être référencé dans les sections suivantes. Une syntaxe similaire permet également le nommage des descripteurs de slots, lorsque des connexions doivent être spécifiées entre des slots dont le nom concret n'est pas connu (attribut name absent du descripteur ou comportant des caractères génériques, par exemple).
L'auteur d'une configuration peut éditer ce fichier et modifier les descripteurs qui s'y trouvent avant de le distribuer à d'autres utilisateurs. Par exemple, les slots non utilisés d'un dispositif peuvent être supprimés de sa déclaration, ou plusieurs dispositifs interchangeables peuvent être spécifiés en reliant simplement leurs descripteurs par un or. Les opérations ensemblistes and, or et not peuvent être imbriquées et appliquées sur n'importe quel sous-ensemble de descripteurs.
Le langage ICSCRIPT est un premier pas vers la réutilisabilité des
configurations d'entrée. À terme, des stratégies de réutilisabilité intégrées
pourront être mises en
uvre sur la base des descripteurs, telles que la
visualisation et l'édition graphique des descripteurs, ou l'utilisation d'un
moteur de résolution de contraintes dans l'algorithme de chargement des
configurations.