-
Notifications
You must be signed in to change notification settings - Fork 16
Tutorial
Disclaimer: Palantir Technologies is not affiliated with, endorsed or sponsored by Palantir.net, Inc. Palantir.net's website is http://palantir.net
Here's a very basic, "Hello, World"-style example. We'll create a Model that contains a single boolean and a Swing View to allow altering (JCheckBox) and viewing (JLabel) its state. Here's the code:
public class BoundJCheckBoxExample {
public static class Model extends DefaultBindableModel {
private boolean state;
public void setState(boolean state) {
this.state = state;
update();
}
public boolean isState() {
return state;
}
}
private final Model model = new Model();
@Bound(to = "state")
private final JCheckBox box = new JCheckBox("State");
@Bound(to = "state")
private final JLabel stateLabel = new JLabel("?");
private final JPanel panel = new JPanel();
private final Bindings bindings = Bindings.standard();
public BoundJCheckBoxExample() {
panel.setLayout(new BorderLayout());
panel.add(box, BorderLayout.CENTER);
panel.add(stateLabel, BorderLayout.SOUTH);
bindings.bind(this);
}
// main() removed
}
There are several things to discuss in this example. First, let's note what's missing: there are no ActionListeners, PropertyChangeListeners, EventListenerLists, etc. All of this boilerplate is taken care of in the default implementation of BindableModel and reflectively through the @Bound annotations.
Next, note that all of the fields are marked final. This is required of all BindableModels (unless annotated as @NotBindable) and @Bound Swing components. This is done for two reasons. The primary reason is that it's generally a good idea to have fields be final (makes future reasoning about the class much easier) and this was a convenient way to enforce it! The technical reason is that when bindings.bind(this) is called the framework needs to know which particular objects to bind together. If the value of the references can change over time, the framework will have bound stale references. In practice, this requirement of marking all field as final has not been at all limiting.
Note the call to update()
in the setState()
method of the Model
. This is one of the few pieces of boilerplate that remains. Whenever the model changes state it has to alert its bound listeners and this is the mechanism to do that. In some cases, we've not used these calls and made the manipulator of the model (i.e. a Controller) manually call update()
on the model after several accumulated changes.
The other remaining piece of boilerplate is the bindings
field declaration and bindings.bind(this)
call. This tells the framework to wire up the bound objects. Neither of these pieces of boilerplate are likely to go away unless some sort of Aspect-oriented approach is used.
Lastly, note the value of the to
field of the @Bound
annotations. That string, "state", is used to find the properties to read and manipulate the value of the Model by the View. That string is used, via reflection, to map the binding to the state
field on the Model
class. The lack of type safety is definitely one of the costs associated with using this framework.
Let's augment this example and add a Controller that will directly manipulate the Model:
public class BoundJCheckBoxExample {
public static class Model extends DefaultBindableModel {
// ... same as before ...
}
public static class Controller {
private final Model model;
public Controller(Model model) {
this.model = model;
}
public void setToTrue() {
model.setState(true);
}
public void setToFalse() {
model.setState(false);
}
}
private final Model model = new Model();
@Bindable
private final Controller controller = new Controller(model);
@Bound(to = "state")
private final JCheckBox box = new JCheckBox("State");
@Bound(to = "state")
private final JLabel stateLabel = new JLabel("?");
@Action(call = "setToTrue")
private final JButton trueButton = new JButton("Set True");
@Action(call = "setToFalse")
private final JButton falseButton = new JButton("Set False");
private final JPanel panel = new JPanel();
private final Bindings bindings = Bindings.standard();
public BoundJCheckBoxExample() {
// ... layout panel ...
bindings.bind(this);
}
// main()
}
There's a few new things to discuss. First, we added a Controller class. This class has no special method signatures or superclasses, it's just a POJO.
To call functions on our controller object we wire up some JButtons with the @Action
annotation. Note that the controller field is marked @Bindable
, this indicates to the framework that the methods on that field can be accessed via @Actions and other annotations that call functions.
- Examples - A list of all our example code and the features they highlight