« SwingBuilder: Tight Groovy and Java Integration | Main | Consumer JRE - I think they may have pulled it off. »

@Bindable - Observable Properties for Groovy

Groovy 1.6 is about to hit it's first beta here pretty soon, so I thought I'de spend some time writing about an implementation of the second big feature. in 1.6. Call site caching is the first big feature, and that is really just a technical term for 'faster.' The second big feature is AST Transformations. Right now it just triggers off of annotations, but there are already two implementations of it that are slated to ship, @Mixin (which I won't go into here) and @Bindable/@Vetoable. I'll go into the details of how you could write a transform later, but now I will focus on the most common task, simply using it.

@groovy.beans.Bindable is an annotation that can go either on a Groovy class property or a Groovy class itself. When the annotation is on a class you are telling the transform that all properties declared in the class should be treated as having @Bindable (whether or not they already have it). When a property has this annotation the AST Transformation will generate (if it doesn't exist) a java.beans.PropertyChangeSupport object, appropriate listener addition methods, and then a setter that uses the property change support. The end result is a bound JavaBeans property, with a whole lot less boilerplate code. What was once this:

import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;

class MyBean {
  private String prop;

  PropertyChangeSupport pcs = new PropertyChangeSupport(this);

  public void addPropertyChangeListener(PropertyChangeListener l) {
    pcs.add(l);
  }

  public void removePropertyChangeListener(PropertyChangeListener l) {
    pcs.remove(l);
  }

  public String getProp() {
    return prop;
  }

  public void setProp(String prop) {
    pcs.firePropertyChanged("prop", this.prop, this.prop=prop);
  }
}

now looks a lot more concise and to the point:

import groovy.beans.Bindable

class MyBean {
  @Bindable String prop
}

This goes hand in hand with the bind() node in swingbuilder, making a model update a text field automatically when it changes is now a simple as textField(text:bind(source:myBeanInstance, sourceProperty:'prop')). This is a very sublte thing to grok, but it really goes a long way in cutting down the amount of lines it takes to link the model and the view togeather in a client application.

The same premise goes for @Vetoable, you know, for all those constrained JavaBeans properties you write. You know, just in case. I have seen some code in the wild that uses VetoableChangeListeners, but not indexed JavaBean properties. It is useful for validation, when architected properly.

Comments (4)

Hi Danno,

I've been playing around with this today. I wonder if there is a nice, Groovy way to receive events from lists and maps. For example, if I had the following class:

class Zoo {
List animals = []
}

I would want to know if the whole animals List was replaced with zoo.animals = ["lions", "tigers", "bears"]

But I'm also interested in knowing if animals are added or removed:
zoo.animals << "swan" or zoo.animals += "duck"

I wonder if you could create a groovy.beans.CollectionChangeSupport that basically wrapped another collection but fired property change events and then wire that up in an automatically-generated getAnimals() method:
List getAnimals() {
return new CollectionChangeSupport(animals, pcs, "animals")
}

Or is there a simpler/better/existing way to do this?

Cheers,
Josh

Danno Ferrin [TypeKey Profile Page]:

There is groovy.util.ObserbableMap, however we don't have an ObservableList. Post a jira at codehause and mark Andres as the owner. He did the map implementation.

I checked out the implementation of ObservableMap. It looks similar to what I had in mind but it doesn't look complete--e.g. it doesn't fire events when keys are removed.

I may look at adding that support and also adding support for passing in a PropertyChangeSupport object so that events can be fired from the enclosing Groovy class. For my purposes, I would much rather receive the event with a source of the Zoo class rather than a source of the actual collection/map. As is, I'd probably need to set a listener on the collection and then re-broadcast those events.

Danno Ferrin [TypeKey Profile Page]:

Look in the 1.6 beta... GROOVY-2786 in particular addressed this. You may be able to rip out the code and use it in a different package if you need it before 1.6 releases.

Post a comment


About

This page contains a single entry from the blog posted on April 29, 2008 9:10 AM.

The previous post in this blog was SwingBuilder: Tight Groovy and Java Integration.

The next post in this blog is Consumer JRE - I think they may have pulled it off..

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

Powered by
Movable Type 3.33