« Speaking at JavaOne! TS-5098 | Main | @Bindable - Observable Properties for Groovy »

SwingBuilder: Tight Groovy and Java Integration

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 ;) )

Comments (5)

Danno, very interesting. You really show my newbieness when it comes to groovy and SwingBuilder. :)

I know this is probably unrealistic in the real world but what approach would you take if the user wanted something more GroovyConsole'ish in that they can make modifications to scripts (forms) on the fly? That's the approach I was taking in my article.

Danno Ferrin [TypeKey Profile Page]:

There is also a variant of build that takes a raw String and a GroovyClassLoader (which you can just create a new one) to build the script as well, so you aren't limited to the pre-comiled approach.
What I was really going for was to illustrate that the DSL style script compiles out of any context of the SwingBuilder, and then that it can be brought into the builder and work as though it was written in the original code. Your post inspired me in that direction.

sri:

I don't think this doesn't apply anymore. Any new examples using updated API?

Chris DeLashmutt:

Thanks for that soapbox point! I've been trying to figure out how to separate the UI a bit from the script so that I can use it in two different places. Your example led me to the answer!

Nobody:

Hello internet, here is how you can get a swing component out of groovy and stick it in your swing java app, it is a kind of EVAL function.

private void jButtonRunScriptActionPerformed(java.awt.event.ActionEvent evt) {

// get this loaded from somewhere
String YOUR_SCRIPT_STRING = "
// THis is groovy code
import groovy.swing.SwingBuilder
import java.awt.BorderLayout as BL

///make the variable to be pulled out later
main = new SwingBuilder().panel { label('Hello World') }

// note SwingBuilder().panel in Groovy is just JPanel in swing

";

Binding binding = new Binding();

// This is how you pass vars into groovy
binding.setVariable("something", "quux");

GroovyShell shell = new GroovyShell(binding);
try {
Object value = shell.evaluate(YOUR_SCRIPT_STRING);

// now get a variable out of groovy, cast it to Swing equvalent
JPanel variable = (JPanel) binding.getVariable("main");

// and put it in some container
YOUR_CONTAINER.add(variable);


} catch (CompilationFailedException compilationFailedException) {
}
}

You're welcome :P

Post a comment


About

This page contains a single entry from the blog posted on April 9, 2008 8:52 PM.

The previous post in this blog was Speaking at JavaOne! TS-5098.

The next post in this blog is @Bindable - Observable Properties for Groovy.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33