Thursday, December 06, 2007

Looking for feedback: nested attributes in tables

Data binding makes it relatively easy to set up a table displaying 'live' data. You don't have to implement any listeners yourself. The first step is to use ObservableListContentProvider or ObservableSetContentProvider, and an observable set or list as the viewer's input. Then you can use ObservableMapLabelProvider to define the columns in your table.

But what about nested attributes in a table? For example, if you are writing a genealogy application, you would want to display the father's and mother's name in a table like this:



The 'tricky' part of this is that the cells for Mother and Father need to update on two conditions - when the name of a father or mother changes, or when the parent relationship between two persons changes.

I have a patch that implements this and a new snippet, and am looking for feedback. See bug 196785.

Note that this is only the low-level machinery; we have some thoughts about making this easer to set up in bug 144260.

Wednesday, October 31, 2007

Getting rid of those pesky "Could not attach listener to " log messages

If you've ever received...

Could not attach listener to org.eclipse.jface.examples.databinding.snippets.Snippet000HelloWorld$Person@92668c
java.lang.NoSuchMethodException: org.eclipse.jface.examples.databinding.snippets.Snippet000HelloWorld$Person.addPropertyChangeListener(java.beans.PropertyChangeListener)
at java.lang.Class.getMethod(Class.java:986)
...

and then asked...
"Why is this exception being logged?"

and we said...
"Because your object is not a Bean."

and you said...
"Correct. But I don't want that logged."

and we said...
"Well, if it's not a Bean then BeanObservables might not be the correct thing for you."

and you said...
"So what is the correct thing for me?"

and we said...
"It doesn't exist. Roll your own."

and you said...
"!@#$%^%@#$%!"

We heard your cries and created PojoObservables. It does everything that BeansObservables does except that it doesn't register PropertyChangeListeners. As a result it won't respond to changes in the POJO but it provides IObservable* implementations to be used in Bindings. It makes its first appearance in 3.4M3 as a result of bug 198201.

Sunday, September 30, 2007

JFace Data Binding Conformance Tests

I'm a bit of a test junky. I don't necessarily enjoy writing them but I can't do without them. They're a necessity and I consider a team's attitude toward unit testing a metric of assessing a project's health. One of my post 1.0 ambitions was to create a solid testing infrastructure to ease the burden of writing tests for JDB and to not start over every time a new observable is written. Another part of this was that I wanted to expose the infrastructure to our consumers so that you don't have to duplicate the some of the tediousness we have gone through in order to ensure the proper behavior of our implementations.

As a result org.eclipse.jface.tests.databinding.conformance has been created. An introduction of the ideas and how to consume the project can be found on the Eclipse Wiki at JFace Data Binding/Conformance Tests. The main idea is that you provide a delegate that allows us to interact with your observable. You then select the TestCases to run and we will handle the rest. The conformance tests are JUnit3 based and should integrate into any existing JUnit testing framework. The project should be considered alpha and 1.0 is tentatively scheduled to be released with Eclipse 3.4. Any and all feedback is welcome.

Happy testing.

Tuesday, August 21, 2007

Post 3.3 and 3.4M1 Conversations

It took us on JFace Data Binding a little while to get going again after our 1.0 release because of all the normal reasons: the Europa release, summer vacations, and a rare debilitating ear infection. But things have been pretty active recently in the world of JFace Data Binding and I figured I'd blog a bit about a few of the current conversations.

3.3.1 maintenance fixes

We haven't seen many issues with the 1.0 release but if you run into any please log them as soon as possible. If you have logged one that you think should go into 3.3.1 ping us again on the bug with a comment saying you think it should go in 3.3.1.(bugzilla query)

Support for converting and validating Java 5 data types

One of the limitations of the 1.0 release is that org.eclipse.core.databinding can only depend upon types found in the CDC 1.0. Because of this we only provide default support for types found in the CDC. This is less than ideal for those of us who are developing against >= Java 5 JDK. If you have added support in your own code for these types and would like to see them in the JDB core feel free to log enhancement requests (with patches!) or reopen existing ones so that we can get everything we can in for 3.4.(bug 197798)

Simplify API to observe nested attributes

The master detail APIs work well but could use some simplification.(bug 196785)

Creation of conformance tests

In order to speed up and raise the quality of development of observables we're planning on exposing conformance tests. These will be public and in their own plug-in so that whether you're writing an observable for your own object or contributing to JDB you'll have a suite of ready made tests to run. These tests will assert that any observable conforms to the defined contract. (bug 182059)

Observe Multiple Selections of Viewers

Thanks to Peter Centgraf and Boris we now have the ability to observe multiple selections in Viewers. To take it for a test drive check out org.eclipse.jface.databinding from HEAD and take a look at ViewersObservables.observeMultiSelection(ISelectionProvider selectionProvider). Technically you can observe the selection of any ISelectionProvider with the API, not just Viewers.

bug 124683

Sunday, July 29, 2007

See a bug, log a bug

A little word of advice to anyone who thinks they have found a bug in JFace Data Binding. After performing due diligence and the behavior seems to be incorrect, disagrees with the documentation, etc. log a bug. For some there's a tendency to post to the newsgroup with "Is this a bug?" somewhere in the message. This is OK to do but it's not necessary. If you think you have found a bug go ahead and log it to bugzilla. If it ends up not being a bug we'll close it as WONTFIX or INVALID, depending upon what is appropriate. This is actually easier on us, the committers and contributors, as we don't have to monitor multiple outlets for bugs. If you still aren't comfortable with going directly to bugzilla go ahead and post to the newsgroup. But if there is no answer for a day or two go ahead and log it to bugzilla.

Also don't take a resolution of INVALID or WONTFIX as a slap in the face or as anyone saying "you shouldn't have logged this". These are perfectly valid resolutions and are an acceptable way to declare that we disagree with the assertion or that our library is not the appropriate place for the functionality. Basically don't allow these resolutions to keep you from logging other bugs. There is never a negative tone attached to a resolution. If we think that the question should have initially gone to the newsgroup we'll say so. Don't get me wrong, bugzilla is not the newsgroup. You should ask "how do I?" sort of questions there. But if you have found a bug don't hesitate to log it to bugzilla.

Keep up the bug reports, enhancement requests, newsgroup posts, and feedback. We appreciate every bit of it.

Sunday, June 24, 2007

QSEclipse Blog

I created a blog for QSEclipse so that I don't end up creating off topic posts here... after this one. I'm not working on the project often but when something frustrates me enough I just can't help myself. When I make releases I'll post to the blog and also try to blog about anything I think is relevant in its development since the Quicksilver APIs are largely undocumented and is not open source.

QSEclipse Blog

Thursday, March 29, 2007

EMF Observable Support

One of the recurring requests is for JFace Data Binding IObservable implementations for EMF. A lot of folks out there use EMF for their model and would like to hook that up with all the binding goodness of JFace Data Binding. If you have that itch you might want to follow bug 75625.

Review JFace's data binding design to determine how best to exploit it
https://bugs.eclipse.org/bugs/show_bug.cgi?id=75625

Monday, March 26, 2007

3.3M6 JFace Data Binding API Changes

3.3M6 was the API freeze and JFace Data Binding had a few changes in regards to API that were necessary for 1.0. All API changes can be found with this query. The most dramatic changes can be found in bugs 175840 and 177463.

175840 - [DataBinding] Remove BindingListener/BindingEvent from the API


The title doesn't quite do this justice. The change that will effect most consumers is that we removed BindSpec and the configuration of bindings has changes because of this. We have introduced 2 new classes to replace BindSpec: UpdateValueStrategy and UpdateListStrategy.

177463 - [DataBinding] SWT Observable API additions


The essence of this change is that all SWTObservables.observe*(...) methods are now untyped meaning they all accept a type of Control. This was done in anticipation of supporting multiple SWT versions. If we were to put the latest and greatest widgets in the method signatures it would prohibit us from using this class with Eclipse 3.2.

Friday, March 02, 2007

Monitoring JFace Data Binding Wiki Changes

Since Eclipsepedia is publishing recent changes I threw together a quick Yahoo Pipe to monitor changes to the JFace Data Binding articles. Our wiki documentation is going to be changing quite a bit and hopefully soon. If interested this is one way to keep track of those changes.

JFace Data Binding Wiki Changes

Update: This isn't working at the moment. When I can find some time I'll update the pipe.

Eclipsepedia Recent Changes Feed


My thanks go out to whoever got this working. When this popped up in NetNewsWire it made my day.

Eclipsepedia
Eclipsepedia Recent Changes

Monday, February 19, 2007

Quicksilver + Eclipse = QSEclipse

This is an off topic post as it has nothing to do with Data Binding so I'll keep it short...

In my spare spare time I've been hacking on a Quicksilver plugin to make it aware of Eclipse workspaces. If you're into Quicksilver and would like to learn more about the plugin head on over to the qseclipse project on Google Code. I consider it alpha but it works. Direct any feedback to the qseclipse Google group.

BeansObservables and registering for PropertyChangeEvents

In the past we've been a bit inconsistent about how BeansObservables registered for PropertyChangeEvents. In some cases we'd look for the addPropertyChangeListener(...) method that accepted the name of the property and in others cases we didn't. In order to ensure that your bean worked in all scenarios you had to implement both. As of this weekend we now abide by the bean spec (see section 7.4.1 and 7.4.5) in all cases. We look for the optional, with name, version first but fall back to the unnamed version if it is not found. Also if neither is found a warning is logged with the intention of making issues easier to debug.

Tuesday, February 13, 2007

EclipseCon Data Binding BoF

There's a BoF proposal for Data Binding at EclipseCon. If you'd like to attend head on over and comment so that we can know how many to expect. Hope to see you there.

Sunday, February 11, 2007

Upgrading to JFace Data Binding 3.3M5 Gotcha

There shouldn't be many API changes in Eclipse 3.3M5 that won't be obvious when you upgrade (just look for the red Xs). But there was a change in the number and sometimes order of parameters in the WritableValue, WritableSet, and WritableList constructors. If you're using any of these classes it would be wise to do a quick search to ensure that what you're passing is what we're expecting. The parameters to the constructors are untyped so the compiler won't be able to catch all of these changes.

At the moment we only have one API change planned that we feel still needs to happen before we hit 1.0 and it will hopefully occur in the beginning of M6 development. We're getting close to 1.0 so if you have any issues/complaints/thorns in your side, any at all, please let us know. We take a lot of pride in this and want to make sure that we're starting off on the right foot.

Saturday, January 20, 2007

Data Binding Validation Story

In preparation for the 1.0 release the validation story has changed. Previously for a target observable value change the pipeline was:

  1. BindingEvent.PIPELINE_AFTER_GET - retrieve target value

  2. BindingEvent.PIPELINE_AFTER_VALIDATE - validate target value

  3. BindingEvent.PIPELINE_AFTER_CONVERT - convert target value to model type

  4. BindingEvent.PIPELINE_AFTER_BUSINESS_VALIDATE - validate converted value

  5. BindingEvent.PIPELINE_AFTER_CHANGE - set converted value on model



Validation occurred during PIPELINE_AFTER_VALIDATE and PIPELINE_AFTER_BUSINESS_VALIDATE.

The pipeline for value bindings has become:

  1. BindingEvent.PIPELINE_AFTER_GET - retrieve target value

  2. BindingEvent.PIPELINE_AFTER_CONVERT - convert target value

  3. BindingEvent.PIPELINE_BEFORE_CHANGE - a placeholder for expensive validation

  4. BindingEvent.PIPELINE_AFTER_CHANGE - set on model



Validators can now be registered for any position, multiples can be registered for a single position, and validation can occur on model to target changes as well as target to model.

Policies


Policies are the way to define when the model is to be updated. The currently supported policies are BindSpec.POLICY_AUTOMATIC and BindSpec.POLICY_EXPLICIT. A common need in dialogs is the ability to defer updating of a model until the user selects OK/Apply. Previously in order to accomplish this you had to create a temporary model to bind to the UI so that it wouldn't update the live model. Now you can specify BindSpec.POLICY_EXPLICIT for the update policy via the BindSpec and control when the model is updated. You can also specify the target validation policy so that the target is validated on change but the model is not updated until you invoke an explicit update.

There aren't any snippets of the functionality yet but hopefully I'll be able to create one soon. I just wanted to give a heads up for anyone using HEAD as there are some nonpassive changes in this.

Wednesday, January 10, 2007

Master Detail - Distilling the Pattern

Yesterday I went over master detail in the context of list selection. In the list selection post there were 3 steps that were required to setup the list selection master detail. But the guts of master detail were really just the first two, the third was to setup the viewing of the detail.

  1. Observe changes of the viewer's selection.

  2. Observe the name property of the selection.

  3. Bind the text of the Text widget to the name detail (selected person's name).


In the 2 steps we're concerned with we have 3 players:

  1. viewer - maintains a reference to the selection and provides change events when it changes

  2. selection (person)- maintains a reference to the name and provides change events when it changes

  3. name - value to display


If using dot notation we'd be able to access the value of the detail with something like the following (assuming the selection is not null)...
viewer.selection.name

If we genericize the roles a bit we get...
container.master.detail

The Pattern


The master detail APIs will satisfy your use case if...

  1. you need to observe container.master.detail

  2. AND the master instance can be replaced over time



Non Selection Use Case


A use case that arose recently on the newsgroup was how to setup the binding to a Person model that can be replaced over time. This was in the context of implementing presentation model. The following code was being used as the presentation model.
class ViewModel {
private Person person;

public void getPerson() {
return person;
}

public void setPerson(Person person) {
this.person = person;
}
}

Over time a new person would be set and the UI had to update. Expressed in dot notation this was viewModel.person.name. The only change that needed to occur to the code was that ViewModel needed to fire change events for Person and then the binding was wired up like normal.

class ViewModel {
private Person person;
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);

public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}

public Person getPerson() {
return person;
}

public void setPerson(Person person) {
changeSupport.firePropertyChange("person", this.person, this.person = person);
}
}

// Create an observable to observe changes in the view model
IObservableValue personValue = BeansObservables.observeValue(
viewModel, "person");

//Bind to the name detail of the current person
dbc.bindValue(SWTObservables.observeText(form.getTextName(),
SWT.Modify), BeansObservables.observeDetailValue(Realm
.getDefault(), personValue, "name",
String.class), null);


WritableValue, for those looking for a shortcut


If you're in a position where it doesn't matter if your model is a java bean, look into one of my favorite classes WritableValue. Your code then becomes...

class ViewModel {
/**
* observable value to maintain the person instance
*/
private IObservableValue personObservable = new WritableValue(Person.class);

public IObservableValue getPersonObservable() {
return personObservable;
}
}

//Observe the detail.
IObservableValue detailObservable = BeansObservables
.observeDetailValue(Realm.getDefault(), viewModel.getPersonObservable(), "name",
String.class);

Code to set the person would be...

viewModel.getPersonObservable().setValue(person);

Asking JFace Data Binding questions

Today was the data binding webinar and from what I can tell it went well. There were quite a few questions and I think most if not all got answered. But if you attended or not and have any questions about data binding feel free to ask them on the eclipse.platform newsgroup. You should always be able to find the latest information on how to get in contact with us on the FAQ.

Also in the evenings and on weekends I try to be on IRC on #eclipse and #eclipse-dev. I'm bradleyjames. Feel free to stop by and say hello.

Tuesday, January 09, 2007

Master Detail - Selection

I find myself answering quite a few questions on the newsgroup hinting that the solution is the master detail APIs. I figured that a verbose explanation couldn't hurt the cause. The stereotypical master detail use case contains a list and upon selection in the list details will be displayed in a separate set of widgets. I'll attempt to explain the core concepts involved.

To explain we'll use the master detail snippet available in the data binding examples project. The snippet displays an SWT List filled with Persons. The Person model has one property, name. Upon selection in the List the name of the currently selected person is displayed in a Text widget.



The relevant data binding code is copied and pasted below...
// 1. Observe changes in selection.
IObservableValue selectionObservable = ViewersObservables
.observeSingleSelection(viewer);

// 2. Observe the name property of the current selection.
IObservableValue detailObservable = BeansObservables
.observeDetailValue(Realm.getDefault(), selectionObservable, "name",
String.class);

// 3. Bind the Text widget to the name detail (selection's name).
new DataBindingContext().bindValue(SWTObservables.observeText(name,
SWT.None), detailObservable, new BindSpec().setUpdateModel(false));


If you follow the comments in the snippet you'll see that we need to perform 3 steps:

  1. Observe changes of the viewer's selection.

  2. Observe the name property of the selection.

  3. Bind the text of the Text widget to the name detail (selected person's name).


1. Observe changes in the viewer's selection


IObservableValue has a value. I know, rocket science. But what IObservableValue provides us is a reference to a transient instance and we can observe changes from one instance to the next. If the selection of the viewer is a person the value of the selection observable will be this person. If the selection is null the value of the selection observable will be null. Regardless of the value we have a reference to an object that will contain the viewer's selection. Change events will fire when a new person, or null, is set as the value of this observable. This is the master.

2. Observe the detail of the master (name of the selected person)


When we observe a detail of the master we're effectively saying...
"observe the value of a property of the value contained in an observable".


Translated into our use case we're saying...
"observe the value of the name property of the person contained in the selection"


If the selection of the the Viewer is a Person with the name "mickey mouse" the following will be true...

  • selectionObservable.getValue() == Person("mickey mouse")

  • detailObservable.getValue() == "mickey mouse"


When the selection changes to a Person with the name of "daffy duck" the following will be true...

  • selectionObservable.getValue() == Person("daffy duck")

  • detailObservable.getValue() == "daffy duck"


When the selection changes to null the following will be true...

  • selectionObservable.getValue() == null

  • detailObservable.getValue() == null


When the master value changes the detail populates itself with the value from the master. This gives us the master detail behavior. To finish it off we need to...

3. Bind the detail to the Text widget


The last step is to bind the detail observable to the Text widget in order to display the detail. Enough said.

Tomorrow night I'll describe a use case that explains how this fits into using JFace Data Binding and the presentation model design pattern.