computed/derived properties

4 views
Skip to first unread message

SlowStrider

unread,
Apr 8, 2010, 4:21:12 AM4/8/10
to Client-Objects User Group
We are using your excellent tool inside Eclipse with JFace binding to
bind JPA entities to user interface widgets. It works like a charm for
simple getters and setters.
However there is one showstopper: how do we handle computed/derived
properties?

As an example, we have a Product entity which has a buyingPrice and a
sellingPrice property. We wish to bind the text of a label widget to
the ratio sellingPrice/buyingPrice between these properties.

In the ideal case we don't need to have a ratio field, only a getRatio
method and a property change on "ratio" would automatically be fired
when either the buyingPrice or sellingPrice changes.

As a workaround I tried adding a ratio field and a setRatio and
getRatio method and call setRatio inside the setters for buyingPrice
and sellingPrice, but the change event proxy is not called when I do
this.

Is there a way to fix this while keeping the entity a POJO or should I
simply do property change support the old fashioned way and fire a
"ratio" change in the setter for both buyingPrice and a sellingPrice?

Yegor

unread,
Apr 8, 2010, 11:00:08 AM4/8/10
to Client-Objects User Group
Hi, SlowStrider,

The general problem with computed properties (with getters and no
setters) is that the framework cannot automatically figure out which
other properties the computed property depends on. It would require a
hint from the developer of the POJO class in form of an annotation or
some sort of convention. Neither currently exist in the framework.
However, there is a solution that I hope will work for you that does
not require any special treatment of the computed property in the POJO
class:

Now, I am not well versed in JFace, but if you are able to create and
register listeners manually, then you can create a
java.beans.PropertyChangeListener for "ratio", but register it against
both "buyingPrice" and "sellingPrice". The listener will be invoked
when either of the two properties changes.

Here is a test-case you can use as a working example (based on a POJO
ComputedPropertyPojo with "firstName", "lastName" properties and a
computed property "fullName", you can find the source code in the
client-objects' test source tree):

public void testHowToListenToComputedPropertyChanges() {
// Make sure client-objects is aware of your POJO class
BeanRegistry.registerType(ComputedPropertyPojo.class);

// Initialize data
ComputedPropertyPojo originalPojo = new
ComputedPropertyPojo();
originalPojo.setFirstName("John");
originalPojo.setLastName("Smith");

// Enhance POJO
ComputedPropertyPojo enhanced =
BeanEnhancer.addPropertyChangeSupport(originalPojo);
assertEquals("John Smith", enhanced.getFullName());

// For this test let's use this flag to track changes
final AtomicBoolean fullNameChanged = new
AtomicBoolean(false);

// This is the listener that will be called when full name
changes
PropertyChangeListener fullNameListener = new
PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
fullNameChanged.set(true);
}
};

// This cast is safe, I should document it if I haven't
already
PropertyChangeListenerSupport pcls =
(PropertyChangeListenerSupport) enhanced;

// As the developer of the POJO class I know that full name is
affected by
// two other properties - firstName and lastName - and only
these properties.
// I therefore register my fullNameListener against both
source properties.
pcls.addPropertyChangeListener("firstName", fullNameListener);
pcls.addPropertyChangeListener("lastName", fullNameListener);

// Now let's run some tests
assertFalse(fullNameChanged.get());
enhanced.setFirstName("George");
assertEquals("George Smith", enhanced.getFullName());
assertTrue(fullNameChanged.get()); // Our listener was
notified

// Reset the flag
fullNameChanged.set(false);
enhanced.setLastName("Johnson");
assertEquals("George Johnson", enhanced.getFullName());
assertTrue(fullNameChanged.get()); // Our listener was
notified
}

I am going to add this test-case to the trunk to make it official.

Thanks,

Yegor

Reply all
Reply to author
Forward
0 new messages