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?
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