Gregg Bolinger recently wrote a blog post illustratiing how Groovy's SwingBuilder can have two way integration with Java code. However, Gregg uses the javax.script APIs to integrate with groovy and Java. But the integration between the two can be made even tighter. How? Remember: every groovy script and class is also a Java Object. The GUI layout could be compiled in an entierly separate script, compiled, instantiated at runtime, and fed into the SwingBuilder. Consider these files...
//Panel.groovy
panel(id:'loginPanel') {
tableLayout {
tr {
td { label(text:'Username') }
td { textField(id:'usernameTextField', columns: 20) }
}
tr {
td { label(text:'Password') }
td { passwordField(id: 'passwordTextField', columns: 20) }
}
tr {
td { panel() }
td {
button(text:'Login', actionPerformed: {
println user.username
println user.password
})
}
}
}
}
bind(source:usernameTextField, sourceProperty:'text',
target:user, targetProperty:'username')
bind(source:passwordTextField, sourceProperty:'text',
target:user, targetProperty:'password')
With this model in a separate file (because I'm that kind of coder on occasion)
//User.groovy
class User {
String username
String password
}
Now before we go any further we will compile these scripts. Yes, with groovyc
. If you are wondering if these are the complete sources the answer is yes they are. You are also correct in noticing that there is no import or use of SwingBuidler. <soapbox>This, my friends, is the power of a dynamic language. The MOP dispatches these calls, and when the script is actually invoked is when these symbols are resolved. When the SwingBuilder consumes the script it links itself into the script's metaclass to intercept all unresolved calls.</soapbox> Sorry, evangelical tangent there for a moment. Let's get back on task. How does the Java code look?
//JavaSwingBuilderDemo.java
import javax.swing.JPanel;
import javax.swing.JDialog;
import groovy.swing.SwingBuilder;
public class JavaSwingBuilderDemo {
public static void main(String[] args) {
SwingBuilder swing = new SwingBuilder();
// load all externally referenced variables
User user = new User();
swing.setVariable("user", user);
swing.build(new Panel());
// unload the desired components
JPanel result = (JPanel)swing.getVariable("loginPanel");
JDialog dialog = new JDialog();
dialog.getContentPane().add(result);
dialog.pack();
dialog.show();
}
}
To invoke the script you do it much as you would in groovy, just with a lot more typeing. Both the keyboarding a casting kind. The main call is SwingBuilder.build(Script). This calls the script as though it was being evaluated in one of the builder's content nodes. Of course, before you call the script you need to load in any external variables, in this case a user object that contains the username and password. Then after the call you need to unload the variables you want to use. In Groovy I could just do swing.loginPanel
but in Java no implicit meanings are allowed, so that means more typing.
An interesting side note is that this is similar to something we are looking at doing inside my own company: using SwingBuilder to build and wire up the GUI while keeping the core of the application in Java and keeping those who don't have the time to learn Groovy or want to deal with it from having to do so. And that is what I find great about Groovy: you can use it where it makes sense and adds value, and where you transition it's relatively seamless (unless of course you are using Seam ;) )