Qu’est-ce qu’un builder Groovy ?
Le langage Groovy dispose de la notion de builder : au moyen d’une syntaxe particulière vous pouvez créer (on pourrait dire bâtir) de manière simple et claire toute une hiérarchie d’objets ou de données. En choisissant l’un des builders disponibles dans le langage, vous pourrez par exemple facilement créer des documents XML complexes, ou encore générer des interfaces graphiques utilisateur avec des composants Swing.
Les builders utilisent le mécanisme suivant : les appels de méthodes sur une instance d’un builder vont correspondre à un nœud de la hiérarchie.
Dans la cas du builder MarkupBuilder par exemple, l’appel à builder.HTML() permettra de générer la balise HTML.
Ensuite les méthodes appelées dans une closure rattachée à la méthode de départ généreront les nœuds enfants.
Examinons maintenant les classes builders MarkupBuilder et SwingBuilder.
Le constructeur MarkupBuilder
Le builder MarkupBuilder va nous permettre de construire tout document à base de balises (XML, HTML, etc.).
Supposons que nous voulions produire le document XML suivant :
<nom>odelia</nom>
<site-web href='http://www.odelia-technologie.com'>odelia technologies</site-web>
<frameworks>
<framework>Grails</framework>
<framework>Cocoon</framework>
<framework>MonFramework</framework>
</frameworks>
</contact>
Dans le code Groovy ci-dessous (qu’il est possible de tester directement dans la console Groovy), les appels aux méthodes de l’instance de type MarkupBuilder créée en début de code, vont permettre la construction des éléments XML.
Notez par exemple que la génération de l’élément XML nom comme sous-élément de l’élément document contact correspond bien à l’appel de la méthode nom("odelia") dans la closure de l’appel à la méthode contact(). Par ailleurs, la valeur de l’argument qui lui est transmis deviendra le contenu de l’élément nom.
De la même manière, l’élément site-web, enfant de l’élément contact, correspond à un appel de méthode ; la syntaxe de cet appel est particulier : en effet, site-web est entre des guillemets, parce qu’il demande au builder de prendre le nom de méthode tel que (et donc d’élément XML), et que ce nom n’est pas un nom de méthode valide dans le langage Groovy.
D’autre part, pour donner un attribut XML au futur élément, il suffit de passer en paramètre un argument nommé avec sa valeur, ici href, avec la valeur http://www.odelia-technologie.com.
def writer = new StringWriter()
def builder = new MarkupBuilder(writer)
def f = ["Grails", "Cocoon", "MonFramework"]
builder.contact() {
nom("odelia")
"site-web"("odelia technologies", href: "http://www.odelia-technologie.com")
frameworks() {
f.each { framework(it) }
}
}
println writer.toString()
Après l’appel de la méthode contact(), le builder a terminé son travail, et l’on peut ici tout simplement afficher le document XML résultant.
Construire des interfaces graphiques avec SwingBuilder
De même que nous avons pu générer un document qui est représenté par une hiérarchie de balises, nous pouvons construire littéralement toute une interface graphique à base de composants Swing : il s’agit aussi d’une hiérarchie, mais de composants. Le même principe de construction se retrouve avec le builder SwingBuilder.
L’exemple de code ci-dessous a été testé avec l’IDE Eclipse 3.2 et le plugin pour le support du langage Groovy. Vous retrouverez la création de différents composants graphiques tels que volet (panel), bouton, case à cocher, et boîte combo.
Il est possible de répondre aux actions de l’utilisateur au moyen de closures ; donnons comme exemple la réponse au click sur le bouton qui permet de quitter l’application :
actionPerformed: { System.exit(0) }
import java.awt.*
class ExempleBuilder {
static void main(args) {
def swinger = new SwingBuilder()
def noms = ["Java", "Groovy", "Grails"];
def composants = [:]
def dlg = swinger.frame(title: "Construire avec SwingBuilder !", size: [300, 300]) {
label(text: "Exemple avec SwingBuilder", constraints:BorderLayout.NORTH)
panel(layout: new GridLayout(3, 1), constraints:BorderLayout.CENTER) {
panel(layout: new FlowLayout()) {
for (texte in noms) { checkBox(text: texte) }
}
panel(layout: new FlowLayout()) {
composants["comboBox"] = comboBox(items: noms, actionPerformed: {
def msg = composants["comboBox"].selectedItem;
swinger.optionPane(message: msg).createDialog(null, "Votre choix").show()
})
}
}
panel(layout: new FlowLayout(), constraints:BorderLayout.SOUTH) {
button(text: "OK", actionPerformed: { System.exit(0) })
}
}
dlg.show()
}
}