Archive for the 'swinger' Category

Swinger 0.4.1 is released

Comme le titre l’annonce, j’ai livré une version intermédiaire qui ajoute le support des layouts managers. Il est donc possible de choisir son layout, ce qui n’était pas le cas auparavant.

Cette version corrige également un bug concernant le chargement des scripts groovy à partir d’une librairie externe.

Publicités

Refactoring de Swinger

Je n’ai pas encore pu commencer les tests d’intégrations car j’ai rencontré quelques soucis d’architecture. Je me suis rendu compte que l’ajout d’un attribut au modèle m’obligeait à reporter cette modification à chaque classe de composant, ce qui est évidemment aberrant. J’ai donc réécrit une partie de mon api en utilisant un Decorator ce qui rend l’architecture beaucoup plus saine. Cela va m’obliger à mener des tests de non-régression car ce refactoring n’est pas sans risque vu qu’il touche au coeur de l’api. J’espère néanmoins tenir les délais que je me suis fixé et livrer la version 0.5 avant la fin de ce mois de juin.

Cette livraison portera sur une implémentation basique de CSS avec le support de quelques attributs seulement. Je n’ai pas encore pris de décision définitive pour la configuration du layout. Ce qui est certain est qu’il sera finalement possible de le configurer dans le fichier xml, mais je ne suis pas sûr de vouloir ajouter une propriété non standard à CSS.

J’avais un temps envisagé d’ajouter une balise <layout>, mais à bien y réfléchir cela me poserai un problème. J’ai en effet l’intention d’implémenter XPath pour permettre d’explorer le modèle. Or permettre de récupérer des valeurs de type différent apporterai un niveau de complexité supplémentaire. Je réserve donc cette possibilité à une future release.

Après l’implémentation des CSS, la prochaine étape est la possibilité de définir le modèle des composants complexes comme JTree ou JTable. Là encore, je n’utiliserai pas de balise comme cela se fait avec les tableaux HTML. La définition du modèle se fera dans un premier temps avec un format de fichier texte du type CSV, puis avec un format XML dans une release ultérieure.

Implémentation d’une indirection

J’ai eu le temps dans le train de réfléchir à quelques problèmes qui s’annoncent dans l’évolution à venir de Swinger. Jusqu’à présent, j’ai laissé de côté les composants complexes de Swing tels que les JTree ou encore les JTable. La raison est que cela nécessite de pouvoir spécifier des valeurs à insérer dans le model comme dans le cas des tables en HTML. J’avais commencé à introduire une balise <item> qui était utilisée pour les JList, mais je suis finalement revenu en arrière car l’approche n’était pas la bonne. Les items ne font pas partie de la couche Vue mais de la couche Model. Il n’était donc pas judicieux de traiter communément ces deux types d’élément.

La solution que j’adopterai du point de vue du langage sera l’introduction des namespaces. Ils permettront d’identifier les différentes couches de l’api Swing. Du point de vue de l’implémentation, je suis toujours confronté à la nécessité de différencier les traitements associés à ces couches. Pour cela, j’opterai pour l’implémentation d’une indirection.

Prenons comme hypothèse de départ que vous êtes en présence d’une architecture dans laquelle l’implémentation d’une responsabilité varie selon le type de composant passé en paramètre.

Plutôt que d’implémenter la responsabilité dans une seule méthode avec des vérifications de casting, je dispatch la responsabilité dans des méthodes différentes selon le type d’objet à traiter, et je délègue l’appel de la méthode idoine au niveau des composants subalternes.

Typiquement, l’implémentation sera celle-ci :

public class Child1 implements Indirection {
    @Override
    public void delegate(Parent parent) {
        parent.method1();
    }
}

public class Child2 implements Indirection {
    @Override
    public void delegate(Parent parent) {
        parent.method2();
    }
}

Cette approche me permettra de conserver le caractère modulaire de Swinger. Tout type de composant pourra être ajouté à un parent, ça sera le composant fils qui déterminera de quel manière il sera ajouté à son parent. Actuellement, seule la méthode Container.add() est utilisée. Avec cette nouvelle architecture, il sera même possible d’ajouter un layout à un conteneur grâce à une balise. Je rappelle que c’est ce problème qui m’avait amené à l’origine à implémenter CSS. Je n’ai pas pour autant l’intention d’abandonner CSS car je pense que cela apportera une plus grande simplicité dans la création d’IHM.

Implémentation d’un modèle CSS

J’ai presque terminé de développer mon modèle CSS. Il me manque néanmoins deux parties importantes, la distinction entre les différents type de valeurs : valeurs spécifiées, calculées et réelles, ainsi que le traitement des différents type de sélecteurs.

En revanche, l’héritage et la cascade sont entièrement implémentées, il ne me reste plus qu’à terminer quelques tests unitaires. C’est la classe CSS2EngineImpl qui a la responsabilité de la cascade et de l’héritage. Elle collabore avec les classes StyleRule.SelectorMessenger et Value pour le calcul de la spécificité des sélecteurs et de la prévalence des valeurs dans l’ordre de la cascade.

La réalisation des tests d’intégration prendra plus de temps car il me faut encore raccorder le parser CSS avec le modèle CSS. Je pense que je me contenterai d’une implémentation élémentaire du DOM en ne définissant que les méthodes qui sont effectivement appellées.

Enfin, la partie qui s’annonce la plus longue est la plus rébarbative est le support des divers propriétés CSS. Je me contenterai dans un premier temps des propriétés basiques comme la couleur, les dimensions et la position des composants. J’ajouterai une propriété non standard : layout, puisque c’est pour cette raison que j’ai implémenté le support de CSS.

Lorsque j’aurai terminé ces différents points, je publierai la version 0.5, si tout se déroule bien dans le courant du mois de juin.

Choix du modèle CSS

Cet article fait suite aux articles précédent :

J’ai finalement décidé de ne pas utiliser le moteur de CSS de batik. Plusieurs raisons m’ont amené à cette décision. Tout d’abord, batik n’implémente pas l’héritage, mais seulement la cascade, il revient donc au client de le faire. Mais ce qui m’a le plus dérangé, c’est le caractère obscure de l’api. Cette librairie est très difficile à comprendre, mal documentée pour la partie CSS et je dirais même mal conçue.

Un exemple concret est que le constructeur de CSSEngine prend 13 valeurs en argument. Oui vous avez bien lu, treize !!! Inévitablement, il est difficile de comprendre le rôle de chacun de ces arguments.

Il existe par ailleurs ce qui me semble être des incohérences. Par exemple, lorsqu’on appelle la méthode getCascadedStyleMap(CSSStylableElement,String), celle-ci cherche à récupérer les valeurs actuelles des attributs que l’on veut justement initialiser. Sans doute y a-t-il une logique dans ce mécanisme, mais elle m’échappe. Le seul cas de figure où il serait nécessaire d’y accéder serait le support des sélecteurs d’attributs, mais ce n’est pas le cas ici. Autre étrangeté, cette méthode, qui est censée être appelée après avoir parsé la feuille de style, accède à nouveau au parser CSS. Cela signifie que le modèle de la CSS est incomplet ou incohérent.

En outre, certains indices me laissent penser que les performances du moteur de style doivent être désastreuses.

Pour toutes ces raisons, je préfère implémenter moi-même le moteur de CSS. Je me baserai sur la spécification DOM-Level-2-Style pour la définition du modèle CSS. Néanmoins, je ne respecterai pas l’implémentation de cette spécification (org.w3c.dom.css) au pied de la lettre, car là encore, j’ai relevé des incohérences. La spécification précise clairement que le modèle ne doit pas donner accès aux valeurs dites « spécifiée » ou « réelle », mais seulement aux valeurs « calculée ». Ce qui signifie que les instances de CSSPrimitiveValue sont des valeurs calculées puisqu’elles sont obtenues à partir du modèle. Or cette même spécification précise que les valeurs calculées doivent être accessibles en mode « read-only » tandis que l’interface CSSPrimitiveValue expose des setters.

Le moteur de style sera applicable à tout modèle XML. J’ai donc dors et déjà commencé à implémenter un modèle XML standard. Mais je me suis lancé dans une approche expérimentale. En effet, l’api swing implémente déjà un modèle qui repose sur le pattern Composite. Implémenter un modèle parallèle obligerait donc à synchroniser ces deux modèles avec toutes les difficultés que cela entraîne. Sans compter les problèmes de fuite de mémoire.

Pour résoudre ces inconvénients, j’ai défini un pattern que j’appellerai Messenger.

Messenger est un croisement des patterns Adapter et Singleton. Il consiste à présenter au client l’interface qu’il attend, comme dans le cas d’Adapter, mais cette interface est implémentée par un Singleton, ce qui évite les fuites de mémoire et évacue les problèmes de synchronisation. L’inconvénient majeur de ce pattern est qu’il doit être utilisé avec beaucoup de précaution. En particulier, les instances de Messenger ne doivent être référencées que par des variables dont la portée est strictement limitée à la méthode qui les manipule. Si on a besoin de conserver une référence, celle-ci doit pointer sur la valeur véhiculée par le Messenger, et non sur le Messenger lui-même. En outre, Messenger n’est pas adapté à un context multi-thread.

Pour l’instant, je souhaite enrichir fonctionnellement le framework Swinger, mais je consacrerai une release à l’optimisation des performances. Le pattern Messenger offre un potentiel de gain assez important en particulier au niveau des parsers CSS et XML.

Il me reste dorénavant à implémenter le modèle de CSS ainsi que l’algorithme de la cascade et de l’héritage. Pour cela, il me faudra étudier en détail la spécification CSS2. Je vous ferai part de mes réflexions sur ce sujet dans un prochain billet.

Le choix de mon parser CSS

J’ai décidé d’implémenter les CSS dans mon projet swinger. Le W3C propose une interface (SAC) écrite en Java qui est implémentée par deux API, Flute et Batik.

Flute est une implémentation basique de SAC, il s’agit d’un simple parser, tandis que Batik est un projet beaucoup plus vaste qui est en fait un toolkit pour le support du format SVG.

Il n’existe que très peu de documentation sur ces deux parsers, j’ai donc commencé par étudier le code source de ces deux API. Le parser de Flute repose sur un ParserTokenManager qui a la responsabilité d’extraire les éléments gramaticaux du fichier CSS. Dans l’api Batik, ce role est dévolu à la classe Scanner. Dans les deux cas, le parser est associé à un DocumentHandler qui reçoit les évènements qui surviennent lors du parcours du fichier CSS.

J’ai effectué des tests de performance pour comparer les deux parsers, et il ressort que le parser de Batik est 40 à 50% plus rapide que celui de Flute. Cette différence ne me surprend pas vraiment car le parser de Flute a été généré avec JavaCC. Il s’agit d’un outil extrêmement puissant mais qui a l’inconvénient de produire un code absolument dégueulasse.

J’ai également comparé les évènements produits par les deux parsers. Et là je suis tombé sur un os. Toute la puissance du langage CSS repose sur les sélecteurs. Tant que l’on utilise des sélecteurs élémentaires telles que des id, des classes ou des élements xml, il n’y a aucun problème. Mais dès que l’on introduit un peu de complexité en jouant sur les opérateurs, on constate des différences d’interprétation. Je ne maîtrise pas suffisament les CSS pour savoir lequel des parsers est en faute.

Il s’agit d’un problème assez sérieux, mais pas rédhibitoire. La nécessité de standardiser les usages du web vient du fait qu’il existe une grande diversité de navigateurs. Firefox, Opera, Safari, Chrome, Internet Explorer, pour ne citer que les plus connus. Dans mon cas, la nécessité de respecter les standards n’est dicté que par le soucis de bien faire. Néanmoins, compte tenu de cette différence d’interprétation, il est évident qu’il sera nécessaire de modifier le code source et qu’il ne sera pas possible de considérer le parser comme une simple boîte noire.

Cette dernière considération me fait choisir le parser Batik, car l’utilisation de JavaCC dans le cas de Flute me parait rendre l’analyse et la correction de bugs trop complexe, sans compter qu’il n’est pas totalement exclu que JavaCC soit lui-même à l’origine de cette différence d’interprétation.

Maintenant que le choix de mon parser est fait, je dois maintenant décider jusqu’à quel point je souhaite utiliser la librairie Batik. En effet, Batik propose un modèle de CSS complet accompagné d’un moteur qui implémente l’héritage et la cascade. Mais cela reste à vérifier. Je vous ferai partager mes réflexions à ce sujet lors d’un projet billet.

Swinger est livré en version 0.4

Cela faisait presque un an que je n’avais pas touché à ce projet. Il s’agit d’un framework qui permet de construire des IHM swing grâce à un langage de balise comme le html.

J’ai complètement refondu l’api. La principale nouveauté est la possibilité de configurer l’application pour déclarer des objets auxquels on peut accéder dans le fichier swg grâce aux expressions langage.

Actuellement, le positionnement des composants n’est pas bien géré. Le framework ne permet pas de choisir son layout manager, il faut coder en java. Je souhaitais ajouter cette fonction dans la prochaine release, mais je me suis rendu compte que je me trouvais confronté à un problème de langage. J’ai déjà introduit les expressions langage au sein du format swg, mais je souhaite m’en tenir à des formes de notations communément admises, je ne souhaite pas innover en la matière car je pense que ça serait un frein à l’adoption de ce framework.

Or, l’utilisation des layout manager nécessite de pouvoir passer des paramètres d’initialisation, ce qui n’est pas possible dans le cadre actuel. La solution qui m’est venue immédiatement à l’esprit est de passer par un fichier de propriétés comme le fait la JSR-296. Je me suis un renseigné sur les avantages et les inconvénient de ce framework, et il me semble que le principal avantage est de permettre de gérer les différents cycles de son application. En revanche, je n’aime pas la manière dont sont gérés les resources, ce que je trouve aberrant est d’associer un fichier par classe, ça risque rapidement de devenir le foutoir. Concernant la gestion du style à proprement parler, l’inconvénient est que le format actuel ne gère pas l’héritage.

Finalement, je m’oriente vers une autre solution, l’implémentation des CSS. Je sais que le projet tk-ui implémente déjà ce support et qu’il en est question pour la prochaine version du framework Eclipse. Le site du W3C propose une interface qui est implémentée dans deux api, Flute et Batik. Je n’ai pas encore choisi quelle api je vais choisir, j’avais même pensé un moment développer moi-même le parser CSS en intégralité.

Je reviendrai prochainement sur les critères qui me feront choisir l’une ou l’autre de ces api.

http://code.google.com/p/swinger