Home » Eclipse Projects » Rich Client Platform (RCP) » [DataBinding] Does there exist a BufferedObservable?
[DataBinding] Does there exist a BufferedObservable? [message #485109] |
Thu, 10 September 2009 14:20 |
Daniel Krügler Messages: 853 Registered: July 2009 |
Senior Member |
|
|
Hi,
I'm currently evaluating a switch from jgoodies databinding
(using mostly self-defined SWT bindings) to jface/eclipse
databinding.
JGoodies databinding provides two useful concepts among others -
BufferedValueModel and PresentationModel - where I currently
found no counter parts in jface databinding, even though I'm
sure they could be easily built on top of the API. Before I roll
my own implementation I would like to ask, whether they are
already provided, but possibly with a different name (which would
be not surprising to me, because jface databinding clearly and
intentionally defines it's own concepts).
Here a short description of what I mean:
A BufferedValueModel is actually some IObservableValue which holds
a reference to another IObservableValue (like a delegate). We could
represent it by the following interface
interface IBufferedObservableValue extends IObservableValue {
void triggerCommit();
void triggerFlush();
}
Any modifications on the wrapper *only* act on the wrapper (which
has it's own value element), and *no* delegation happens to the
wrapped observable. But if anyone invokes triggerCommit() on the
IBufferedObservableValue, then the internally hold observable's
value is set with the current value of the buffer. On the other
hand, if triggerFlush() is used, the buffer is reset to the value of
the internally hold observable - quite simple but useful.
A Presentation model is more or less a access point for observables.
It is typically constructed with a bean and is used as the actual
API for clients that ask for observables. Clients aren't aware,
which kind of data is wrapped, they just ask for a property from a
bean (This is very similar to querying BeanProperties, but the
difference is that the PresentationModel already knows the bean,
so the client query *only* provides the property name to access the
corresponding property).
I would be happy to know, if anyone is aware of existing
implementation in jface databinding that already match these
concepts or - if not - if jface databinding would be interested
in some contribution of such concepts.
Thanks in advance,
Daniel Krügler
|
|
|
Re: [DataBinding] Does there exist a BufferedObservable? [message #485129 is a reply to message #485109] |
Thu, 10 September 2009 15:05 |
Matthew Hall Messages: 368 Registered: July 2009 |
Senior Member |
|
|
Daniel, see responses below:
Daniel Krügler wrote:
> JGoodies databinding provides two useful concepts among others -
> BufferedValueModel and PresentationModel - where I currently
> found no counter parts in jface databinding, even though I'm
> sure they could be easily built on top of the API. Before I roll
> my own implementation I would like to ask, whether they are
> already provided, but possibly with a different name (which would
> be not surprising to me, because jface databinding clearly and
> intentionally defines it's own concepts).
>
> Here a short description of what I mean:
>
> A BufferedValueModel is actually some IObservableValue which holds
> a reference to another IObservableValue (like a delegate). We could
> represent it by the following interface
>
> interface IBufferedObservableValue extends IObservableValue {
> void triggerCommit();
> void triggerFlush();
> }
>
> Any modifications on the wrapper *only* act on the wrapper (which
> has it's own value element), and *no* delegation happens to the
> wrapped observable. But if anyone invokes triggerCommit() on the
> IBufferedObservableValue, then the internally hold observable's
> value is set with the current value of the buffer. On the other
> hand, if triggerFlush() is used, the buffer is reset to the value of
> the internally hold observable - quite simple but useful.
In Eclipse DataBinding we don't have an equivalent observable, but we do
have a method of handling these situations:
1. To hold off on updating the model until a certain trigger action
occurs (e.g. the user clicks "Apply"), use an
UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST) for the
target-to-model update strategy:
bindingContext.bindValue(
WidgetProperties.text(SWT.Modify).observe(nameText),
BeanProperties.value("name").observe(person),
new UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST),
null);
When you are ready to update the model, you can either call
DataBindingContext.updateModels() (to update all bindings at once) or
Binding.updateTargetToModel() to update a single binding.
2. To hold off on updating the model until some cross-field validation
constraint is satisfied (e.g. in a date range, the start date must be on
or before the end date) you can use MultiValidator:
final IObservableValue startDateObs =
WidgetProperties.selection().observe(startDate);
final IObservableValue endDateObs =
WidgetProperties.selection().observe(endDate);
MultiValidator dateRangeValidator = new MultiValidator() {
protected IStatus validate() {
// Using ObservableTracker magic, MultiValidator knows which
// observables you access inside this method and listens for
// changes to them so that it can revalidate as necessary
Date start = startDateObs.getValue();
Date end = endDateObs.getValue();
if (start.compareTo(end) > 0)
return ValidationStatus.error(
"Start date cannot be later than end date");
return ValidationStatus.ok();
}
};
bindingContext.addValidationStatusProvider(dateRangeValidato r);
// Wrap the start and end date observables in validated wrappers so that
// they only change when the validation constraints are satisfied
bindingContext.bindValue(
dateRangeValidator.observeValidatedValue(startDateObs),
BeanProperties.value("startDate").observe(eventObject));
bindingContext.bindValue(
dateRangeValidator.observeValidatedValue(endDateObs),
BeanProperties.value("endDate").observe(eventObject));
> A Presentation model is more or less a access point for observables.
> It is typically constructed with a bean and is used as the actual
> API for clients that ask for observables. Clients aren't aware,
> which kind of data is wrapped, they just ask for a property from a
> bean (This is very similar to querying BeanProperties, but the
> difference is that the PresentationModel already knows the bean,
> so the client query *only* provides the property name to access the
> corresponding property).
This sounds like an inverse of BeanProperties, where instead of:
BeanValueProperty {
PropertyDescriptor property;
public Object getValue(Object source);
public void setValue(Object source, Object value);
}
you have:
BeanPropertyAccessor {
Object bean;
public Object getValue(String propertyName);
public void setValue(String propertyName, Object value);
}
If my understanding is correct, then no, we don't have anything like
that. Could you provide some sample code to show how this would be useful?
Matthew
|
|
|
Re: [DataBinding] Does there exist a BufferedObservable? [message #485273 is a reply to message #485129] |
Fri, 11 September 2009 06:48 |
Daniel Krügler Messages: 853 Registered: July 2009 |
Senior Member |
|
|
Matthew Hall wrote:
> Daniel, see responses below:
>
> Daniel Krügler wrote:
>> JGoodies databinding provides two useful concepts among others -
>> BufferedValueModel and PresentationModel - where I currently
>> found no counter parts in jface databinding, even though I'm
>> sure they could be easily built on top of the API. Before I roll
>> my own implementation I would like to ask, whether they are
>> already provided, but possibly with a different name (which would
>> be not surprising to me, because jface databinding clearly and
>> intentionally defines it's own concepts).
>>
>> Here a short description of what I mean:
>>
>> A BufferedValueModel is actually some IObservableValue which holds
>> a reference to another IObservableValue (like a delegate). We could
>> represent it by the following interface
>>
>> interface IBufferedObservableValue extends IObservableValue {
>> void triggerCommit();
>> void triggerFlush();
>> }
>>
>> Any modifications on the wrapper *only* act on the wrapper (which
>> has it's own value element), and *no* delegation happens to the
>> wrapped observable. But if anyone invokes triggerCommit() on the
>> IBufferedObservableValue, then the internally hold observable's
>> value is set with the current value of the buffer. On the other
>> hand, if triggerFlush() is used, the buffer is reset to the value of
>> the internally hold observable - quite simple but useful.
After having written this description out of my head I tried to code
it down and found out that I gave some misleading descriptions :-(
Here a corrected form:
1) BufferedValueModel: Like in the following hierarchy:
public interface IBufferedObservable extends IObservable {
// Observable with value type boolean.class
IObservableValue getTriggerChannel();
void setTriggerChannel(IObservableValue trigger);
// Signals whether the buffered observable returns data from
// the wrapped observable or it's copy of the data:
boolean isBuffering();
}
public interface IBufferedObservableValue extends IBufferedObservable,
IObservableValue {
// The actually wrapped observable. It's value type is
// the value type of IBufferedObservableValue:
IObservableValue getObservable();
void setObservable(IObservableValue value);
}
Where are the triggerCommit() and triggerFlush() methods I were talking
about? They are provided by the presentation model described in bullet
(2), not by IBufferedObservableValue! The trigger channel is the
"button" that ensures that the buffered value will be flushed or
committed depending on it's value (true or false). PresentationModel is
the one who's pressing the "button".
2) PresentationModel:
interface PresentationModel extends IBufferedObservable {
// Flush all buffered models/observables:
void triggerFlush();
// Commit all buffered models/observables:
void triggerCommit();
// Get the observable that corresponds to the property name:
IObservableValue getModel(String property);
// Get the buffered observable that corresponds to the property name:
IBufferedObservableValue getBufferedModel(String property);
}
I omitted some further details, but the rough picture should now be
corrected, I hope ;-)
> In Eclipse DataBinding we don't have an equivalent observable, but we do
> have a method of handling these situations:
>
> 1. To hold off on updating the model until a certain trigger action
> occurs (e.g. the user clicks "Apply"), use an
> UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST) for the
> target-to-model update strategy:
>
> bindingContext.bindValue(
> WidgetProperties.text(SWT.Modify).observe(nameText),
> BeanProperties.value("name").observe(person),
> new UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST),
> null);
>
> When you are ready to update the model, you can either call
> DataBindingContext.updateModels() (to update all bindings at once) or
> Binding.updateTargetToModel() to update a single binding.
I agree that this is also some form of buffering strategy, but it is
local to one binding (which sometimes is an advantage, of-course) and
validation is also deferred according to my understanding of
POLICY_ON_REQUEST. This is an important difference, because we use
at several locations buffered models, which do validate on-the-fly,
but still don't transfer it's data until
PresentationModel.triggerCommit() is performed.
> 2. To hold off on updating the model until some cross-field validation
> constraint is satisfied (e.g. in a date range, the start date must be on
> or before the end date) you can use MultiValidator:
>
> final IObservableValue startDateObs =
> WidgetProperties.selection().observe(startDate);
> final IObservableValue endDateObs =
> WidgetProperties.selection().observe(endDate);
>
> MultiValidator dateRangeValidator = new MultiValidator() {
> protected IStatus validate() {
> // Using ObservableTracker magic, MultiValidator knows which
> // observables you access inside this method and listens for
> // changes to them so that it can revalidate as necessary
> Date start = startDateObs.getValue();
> Date end = endDateObs.getValue();
> if (start.compareTo(end) > 0)
> return ValidationStatus.error(
> "Start date cannot be later than end date");
> return ValidationStatus.ok();
> }
> };
>
> bindingContext.addValidationStatusProvider(dateRangeValidato r);
>
> // Wrap the start and end date observables in validated wrappers so that
> // they only change when the validation constraints are satisfied
> bindingContext.bindValue(
> dateRangeValidator.observeValidatedValue(startDateObs),
> BeanProperties.value("startDate").observe(eventObject));
> bindingContext.bindValue(
> dateRangeValidator.observeValidatedValue(endDateObs),
> BeanProperties.value("endDate").observe(eventObject));
Thanks, this looks very cool! Nevertheless, I assume that I need
at least some form of adaption to the jgoodies binding for easier
adaption of the new binding.
>> A Presentation model is more or less a access point for observables.
>> It is typically constructed with a bean and is used as the actual
>> API for clients that ask for observables. Clients aren't aware,
>> which kind of data is wrapped, they just ask for a property from a
>> bean (This is very similar to querying BeanProperties, but the
>> difference is that the PresentationModel already knows the bean,
>> so the client query *only* provides the property name to access the
>> corresponding property).
>
> This sounds like an inverse of BeanProperties, where instead of:
>
> BeanValueProperty {
> PropertyDescriptor property;
> public Object getValue(Object source);
> public void setValue(Object source, Object value);
> }
>
> you have:
>
> BeanPropertyAccessor {
> Object bean;
> public Object getValue(String propertyName);
> public void setValue(String propertyName, Object value);
> }
>
> If my understanding is correct, then no, we don't have anything like
> that. Could you provide some sample code to show how this would be useful?
I wasn't very clear in my first descriptions and even made some
blatant errors describing the BufferedValueModel and PresentationModel.
So one difference is that PresentationModel is more or less a map
from property to observable (not to the value, even though for
convenience reasons they are also accessible). Let me give a sketch
in which way we use PresentationModel's and BufferedValueModel's in
our code:
1) Per service we get some simple value object (POJO):
class SomeDTO {
....
}
2) We currently need to wrap this POJO in a Bean, that simply forwards
all properties of the POJO but sends proper Java Bean property change
events (I'm very impressed that JFace databinding allows direct usage
of POJO's!).
3) Before entering the UI layer, the beans are wrapped as presentation
models:
PresentationModel pres = new PresentationModel(bean);
4) The UI layer gets the presentation model(s) and simply binds it's
observables "by name" to the corresponding widgets:
Text txt = new Text(parent, ...);
....
BufferedModel firstName = pres.getBufferedModel("firstName");
....
BindingFacade.bind(txt, firstName);
They are actually not aware of the direct nature of the complete
bean. They only need to now that there are some properties to
bind to widget's.
5) Especially inside a local scope (dialog/wizard) it's nice to
simply say on Ok-pressed: pres.triggerCommit() and on any cancelling
step (either cancel-pressed or undo) triggerFlush() which unrolls
the current changes back to the original ones.
Does that give a somewhat clearer picture?
Finally let me add one further remark: In the jgoodies databinding
framework you don't always need to have an individual observable
just to observe changes of some characteristic property: E.g. I
can add or remove change listener's to the buffering state of
the IBufferedObservableValue without access to the actual observable,
because in this case I only need the change deltas in the property
observer and nowhere else. I sometimes miss that fact in Jface
databinding, although it (usually) provides what I need: With further
wrapper techniques I could provide a *constant* view onto the
buffering state or the trigger channel (jface has ConstantObservable)
which do only exist such that clients can register
IValueChangeListener's on them.
Thanks for your interest,
Daniel
|
|
|
Re: [DataBinding] Does there exist a BufferedObservable? [message #485489 is a reply to message #485273] |
Sat, 12 September 2009 00:30 |
Thomas Schindl Messages: 6651 Registered: July 2009 |
Senior Member |
|
|
Just a short question is all this buffering needed because of undo/redo?
Or is there a deeper reason behind it?
Tom
Daniel Krügler schrieb:
> Matthew Hall wrote:
>> Daniel, see responses below:
>>
>> Daniel Krügler wrote:
>>> JGoodies databinding provides two useful concepts among others -
>>> BufferedValueModel and PresentationModel - where I currently
>>> found no counter parts in jface databinding, even though I'm
>>> sure they could be easily built on top of the API. Before I roll
>>> my own implementation I would like to ask, whether they are
>>> already provided, but possibly with a different name (which would
>>> be not surprising to me, because jface databinding clearly and
>>> intentionally defines it's own concepts).
>>>
>>> Here a short description of what I mean:
>>>
>>> A BufferedValueModel is actually some IObservableValue which holds
>>> a reference to another IObservableValue (like a delegate). We could
>>> represent it by the following interface
>>>
>>> interface IBufferedObservableValue extends IObservableValue {
>>> void triggerCommit();
>>> void triggerFlush();
>>> }
>>>
>>> Any modifications on the wrapper *only* act on the wrapper (which
>>> has it's own value element), and *no* delegation happens to the
>>> wrapped observable. But if anyone invokes triggerCommit() on the
>>> IBufferedObservableValue, then the internally hold observable's
>>> value is set with the current value of the buffer. On the other
>>> hand, if triggerFlush() is used, the buffer is reset to the value of
>>> the internally hold observable - quite simple but useful.
>
> After having written this description out of my head I tried to code
> it down and found out that I gave some misleading descriptions :-(
>
> Here a corrected form:
>
> 1) BufferedValueModel: Like in the following hierarchy:
>
> public interface IBufferedObservable extends IObservable {
>
> // Observable with value type boolean.class
> IObservableValue getTriggerChannel();
>
> void setTriggerChannel(IObservableValue trigger);
>
> // Signals whether the buffered observable returns data from
> // the wrapped observable or it's copy of the data:
> boolean isBuffering();
>
> }
>
> public interface IBufferedObservableValue extends IBufferedObservable,
> IObservableValue {
>
> // The actually wrapped observable. It's value type is
> // the value type of IBufferedObservableValue:
> IObservableValue getObservable();
>
> void setObservable(IObservableValue value);
>
> }
>
> Where are the triggerCommit() and triggerFlush() methods I were talking
> about? They are provided by the presentation model described in bullet
> (2), not by IBufferedObservableValue! The trigger channel is the
> "button" that ensures that the buffered value will be flushed or
> committed depending on it's value (true or false). PresentationModel is
> the one who's pressing the "button".
>
> 2) PresentationModel:
>
> interface PresentationModel extends IBufferedObservable {
>
> // Flush all buffered models/observables:
> void triggerFlush();
>
> // Commit all buffered models/observables:
> void triggerCommit();
>
> // Get the observable that corresponds to the property name:
> IObservableValue getModel(String property);
>
> // Get the buffered observable that corresponds to the property name:
> IBufferedObservableValue getBufferedModel(String property);
>
> }
>
> I omitted some further details, but the rough picture should now be
> corrected, I hope ;-)
>
>> In Eclipse DataBinding we don't have an equivalent observable, but we
>> do have a method of handling these situations:
>>
>> 1. To hold off on updating the model until a certain trigger action
>> occurs (e.g. the user clicks "Apply"), use an
>> UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST) for the
>> target-to-model update strategy:
>>
>> bindingContext.bindValue(
>> WidgetProperties.text(SWT.Modify).observe(nameText),
>> BeanProperties.value("name").observe(person),
>> new UpdateValueStrategy(UpdateValueStrategy.POLICY_ON_REQUEST),
>> null);
>>
>> When you are ready to update the model, you can either call
>> DataBindingContext.updateModels() (to update all bindings at once) or
>> Binding.updateTargetToModel() to update a single binding.
>
> I agree that this is also some form of buffering strategy, but it is
> local to one binding (which sometimes is an advantage, of-course) and
> validation is also deferred according to my understanding of
> POLICY_ON_REQUEST. This is an important difference, because we use
> at several locations buffered models, which do validate on-the-fly,
> but still don't transfer it's data until
> PresentationModel.triggerCommit() is performed.
>
>> 2. To hold off on updating the model until some cross-field validation
>> constraint is satisfied (e.g. in a date range, the start date must be
>> on or before the end date) you can use MultiValidator:
>>
>> final IObservableValue startDateObs =
>> WidgetProperties.selection().observe(startDate);
>> final IObservableValue endDateObs =
>> WidgetProperties.selection().observe(endDate);
>>
>> MultiValidator dateRangeValidator = new MultiValidator() {
>> protected IStatus validate() {
>> // Using ObservableTracker magic, MultiValidator knows which
>> // observables you access inside this method and listens for
>> // changes to them so that it can revalidate as necessary
>> Date start = startDateObs.getValue();
>> Date end = endDateObs.getValue();
>> if (start.compareTo(end) > 0)
>> return ValidationStatus.error(
>> "Start date cannot be later than end date");
>> return ValidationStatus.ok();
>> }
>> };
>>
>> bindingContext.addValidationStatusProvider(dateRangeValidato r);
>>
>> // Wrap the start and end date observables in validated wrappers so that
>> // they only change when the validation constraints are satisfied
>> bindingContext.bindValue(
>> dateRangeValidator.observeValidatedValue(startDateObs),
>> BeanProperties.value("startDate").observe(eventObject));
>> bindingContext.bindValue(
>> dateRangeValidator.observeValidatedValue(endDateObs),
>> BeanProperties.value("endDate").observe(eventObject));
>
> Thanks, this looks very cool! Nevertheless, I assume that I need
> at least some form of adaption to the jgoodies binding for easier
> adaption of the new binding.
>
>>> A Presentation model is more or less a access point for observables.
>>> It is typically constructed with a bean and is used as the actual
>>> API for clients that ask for observables. Clients aren't aware,
>>> which kind of data is wrapped, they just ask for a property from a
>>> bean (This is very similar to querying BeanProperties, but the
>>> difference is that the PresentationModel already knows the bean,
>>> so the client query *only* provides the property name to access the
>>> corresponding property).
>>
>> This sounds like an inverse of BeanProperties, where instead of:
>>
>> BeanValueProperty {
>> PropertyDescriptor property;
>> public Object getValue(Object source);
>> public void setValue(Object source, Object value);
>> }
>>
>> you have:
>>
>> BeanPropertyAccessor {
>> Object bean;
>> public Object getValue(String propertyName);
>> public void setValue(String propertyName, Object value);
>> }
>>
>> If my understanding is correct, then no, we don't have anything like
>> that. Could you provide some sample code to show how this would be
>> useful?
>
> I wasn't very clear in my first descriptions and even made some
> blatant errors describing the BufferedValueModel and PresentationModel.
>
> So one difference is that PresentationModel is more or less a map
> from property to observable (not to the value, even though for
> convenience reasons they are also accessible). Let me give a sketch
> in which way we use PresentationModel's and BufferedValueModel's in
> our code:
>
> 1) Per service we get some simple value object (POJO):
>
> class SomeDTO {
> ....
> }
>
> 2) We currently need to wrap this POJO in a Bean, that simply forwards
> all properties of the POJO but sends proper Java Bean property change
> events (I'm very impressed that JFace databinding allows direct usage
> of POJO's!).
>
> 3) Before entering the UI layer, the beans are wrapped as presentation
> models:
>
> PresentationModel pres = new PresentationModel(bean);
>
> 4) The UI layer gets the presentation model(s) and simply binds it's
> observables "by name" to the corresponding widgets:
>
> Text txt = new Text(parent, ...);
> ...
> BufferedModel firstName = pres.getBufferedModel("firstName");
>
> ...
> BindingFacade.bind(txt, firstName);
>
> They are actually not aware of the direct nature of the complete
> bean. They only need to now that there are some properties to
> bind to widget's.
>
> 5) Especially inside a local scope (dialog/wizard) it's nice to
> simply say on Ok-pressed: pres.triggerCommit() and on any cancelling
> step (either cancel-pressed or undo) triggerFlush() which unrolls
> the current changes back to the original ones.
>
> Does that give a somewhat clearer picture?
>
> Finally let me add one further remark: In the jgoodies databinding
> framework you don't always need to have an individual observable
> just to observe changes of some characteristic property: E.g. I
> can add or remove change listener's to the buffering state of
> the IBufferedObservableValue without access to the actual observable,
> because in this case I only need the change deltas in the property
> observer and nowhere else. I sometimes miss that fact in Jface
> databinding, although it (usually) provides what I need: With further
> wrapper techniques I could provide a *constant* view onto the
> buffering state or the trigger channel (jface has ConstantObservable)
> which do only exist such that clients can register
> IValueChangeListener's on them.
>
> Thanks for your interest,
>
> Daniel
>
|
|
|
Re: [DataBinding] Does there exist a BufferedObservable? [message #485496 is a reply to message #485273] |
Sat, 12 September 2009 04:47 |
Matthew Hall Messages: 368 Registered: July 2009 |
Senior Member |
|
|
Daniel Krügler wrote:
> After having written this description out of my head I tried to code
> it down and found out that I gave some misleading descriptions :-(
>
> Here a corrected form:
<snip>
I also went through the PresentationModel pdf at JGoodies.com which
filled in the rest of the picture. I think I follow you now.
> I agree that this is also some form of buffering strategy, but it is
> local to one binding (which sometimes is an advantage, of-course) and
> validation is also deferred according to my understanding of
> POLICY_ON_REQUEST
You are correct, I should have said POLICY_CONVERT. The basic data
binding pipeline is:
1. get source observable value
2. validate after get
3. convert to destination type
4. validate after convert
5. validate before set
6. set destination observable value
POLICY_ON_REQUEST does not execute this pipeline automatically, but
waits until Binding.updateModel() is called. POLICY_CONVERT, by
contrast, performs steps 1 through 4. POLICY_UPDATE performs all steps.
>> 2. To hold off on updating the model until some cross-field validation
>> constraint is satisfied (e.g. in a date range, the start date must be
>> on or before the end date) you can use MultiValidator:
>
> Thanks, this looks very cool! Nevertheless, I assume that I need
> at least some form of adaption to the jgoodies binding for easier
> adaption of the new binding.
I not sure what you mean here.
> So one difference is that PresentationModel is more or less a map
> from property to observable (not to the value, even though for
> convenience reasons they are also accessible). Let me give a sketch
> in which way we use PresentationModel's and BufferedValueModel's in
> our code:
Question: from what I saw in the JGoodies slideshow, it looks like the
PresentationModel is intimately familiar with the model object. However
using strings to distinguish property names is not going to be portable
to, say, EMF. So I'm wonder However the way you describe it makes it
look like a generic switchboard:
interface IPresentationModel {
public IObservableValue getBufferedValue(IValueProperty)
public IObservableList getBufferedList(IListProperty)
public IObservableSet getBufferedSet(ISetProperty)
public IObservableMap getBufferedMap(IMapProperty)
}
We wouldn't need to create IBufferedObservable* interfaces since we can
use IObservable.isStale and IStaleListener to track whether each
observable is "buffering" (i.e. out of sync with model)
> 2) We currently need to wrap this POJO in a Bean, that simply forwards
> all properties of the POJO but sends proper Java Bean property change
> events (I'm very impressed that JFace databinding allows direct usage
> of POJO's!).
Quick note, the POJO observables only send change events if the change
is initiated *through the IObservable's interface*, and only for that
particular observable instance. So setting values directly on the POJO
is discouraged since you don't get updates.
> 3) Before entering the UI layer, the beans are wrapped as presentation
> models:
>
> PresentationModel pres = new PresentationModel(bean);
>
> 4) The UI layer gets the presentation model(s) and simply binds it's
> observables "by name" to the corresponding widgets:
>
> Text txt = new Text(parent, ...);
> ...
> BufferedModel firstName = pres.getBufferedModel("firstName");
>
> ...
> BindingFacade.bind(txt, firstName);
>
> They are actually not aware of the direct nature of the complete
> bean. They only need to now that there are some properties to
> bind to widget's.
This sounds essentially like a two-stage binding. The presentation
model would bind directly to the bean (or whatever type of model object)
using POLICY_CONVERT update policies. In turn each UI form would bind
its controls to the PM's observables.
> 5) Especially inside a local scope (dialog/wizard) it's nice to
> simply say on Ok-pressed: pres.triggerCommit() and on any cancelling
> step (either cancel-pressed or undo) triggerFlush() which unrolls
> the current changes back to the original ones.
void triggerCommit() { bindingContext.updateModels(); }
void triggerFlush() { bindingContext.updateTargets(); }
> Finally let me add one further remark: In the jgoodies databinding
> framework you don't always need to have an individual observable
> just to observe changes of some characteristic property: E.g. I
> can add or remove change listener's to the buffering state of
> the IBufferedObservableValue without access to the actual observable,
> because in this case I only need the change deltas in the property
> observer and nowhere else.
We've tried to reduce the cost of creating observables by not
registering listeners on the underlying object (e.g. bean) until you
actually add listeners to the observable--not sure if that mitigates
your concern but I thought I'd mention it.
> I sometimes miss that fact in Jface
> databinding, although it (usually) provides what I need: With further
> wrapper techniques I could provide a *constant* view onto the
> buffering state or the trigger channel (jface has ConstantObservable)
> which do only exist such that clients can register
> IValueChangeListener's on them.
I don't follow you here.
It sounds like this is something we should take a closer look at. Would
you submit a feature enhancement so we can discuss this further?
Matthew
|
|
| |
Re: [DataBinding] Does there exist a BufferedObservable? [message #485614 is a reply to message #485496] |
Mon, 14 September 2009 07:00 |
Daniel Krügler Messages: 853 Registered: July 2009 |
Senior Member |
|
|
Matthew Hall wrote:
> Daniel Krügler wrote:
[..]
>> I agree that this is also some form of buffering strategy, but it is
>> local to one binding (which sometimes is an advantage, of-course) and
>> validation is also deferred according to my understanding of
>> POLICY_ON_REQUEST
>
> You are correct, I should have said POLICY_CONVERT. The basic data
> binding pipeline is:
>
> 1. get source observable value
> 2. validate after get
> 3. convert to destination type
> 4. validate after convert
> 5. validate before set
> 6. set destination observable value
>
> POLICY_ON_REQUEST does not execute this pipeline automatically, but
> waits until Binding.updateModel() is called. POLICY_CONVERT, by
> contrast, performs steps 1 through 4. POLICY_UPDATE performs all steps.
Yes, you are right, this seems to be the right way to go!
>>> 2. To hold off on updating the model until some cross-field
>>> validation constraint is satisfied (e.g. in a date range, the start
>>> date must be on or before the end date) you can use MultiValidator:
>>
>> Thanks, this looks very cool! Nevertheless, I assume that I need
>> at least some form of adaption to the jgoodies binding for easier
>> adaption of the new binding.
>
> I not sure what you mean here.
I simply meant that even though JFace Databinding gives us a huge
amount of capabilities for free I probably still need to provide
an adaptor to the jgoodies-style data binding to ensure that large
code parts don't need to be touched yet.
> Question: from what I saw in the JGoodies slideshow, it looks like the
> PresentationModel is intimately familiar with the model object. However
> using strings to distinguish property names is not going to be portable
> to, say, EMF. So I'm wonder However the way you describe it makes it
> look like a generic switchboard:
>
> interface IPresentationModel {
> public IObservableValue getBufferedValue(IValueProperty)
> public IObservableList getBufferedList(IListProperty)
> public IObservableSet getBufferedSet(ISetProperty)
> public IObservableMap getBufferedMap(IMapProperty)
> }
>
> We wouldn't need to create IBufferedObservable* interfaces since we can
> use IObservable.isStale and IStaleListener to track whether each
> observable is "buffering" (i.e. out of sync with model)
This seems like an excellent approach to me! With the existing
property-framework in JFace Databinding this seems the best way to
go.
>> 2) We currently need to wrap this POJO in a Bean, that simply forwards
>> all properties of the POJO but sends proper Java Bean property change
>> events (I'm very impressed that JFace databinding allows direct usage
>> of POJO's!).
>
> Quick note, the POJO observables only send change events if the change
> is initiated *through the IObservable's interface*, and only for that
> particular observable instance. So setting values directly on the POJO
> is discouraged since you don't get updates.
This really doesn't matter in our use-cases, because the POJO object
wont be touched by the layer which gets the presentation model.
>> 4) The UI layer gets the presentation model(s) and simply binds it's
>> observables "by name" to the corresponding widgets:
>>
>> Text txt = new Text(parent, ...);
>> ...
>> BufferedModel firstName = pres.getBufferedModel("firstName");
>>
>> ...
>> BindingFacade.bind(txt, firstName);
>>
>> They are actually not aware of the direct nature of the complete
>> bean. They only need to now that there are some properties to
>> bind to widget's.
>
> This sounds essentially like a two-stage binding. The presentation
> model would bind directly to the bean (or whatever type of model object)
> using POLICY_CONVERT update policies. In turn each UI form would bind
> its controls to the PM's observables.
Yes, this description is quite correct. I think for this reason my
own opinion to that was that it should be simply possible to
get the jgoodies presentation model adapted purely based on public
JFace DataBinding API. In some sense the PresentationModel can be
thought of a higher-level access level to JFace DataBinding
functionality.
>> 5) Especially inside a local scope (dialog/wizard) it's nice to
>> simply say on Ok-pressed: pres.triggerCommit() and on any cancelling
>> step (either cancel-pressed or undo) triggerFlush() which unrolls
>> the current changes back to the original ones.
>
> void triggerCommit() { bindingContext.updateModels(); }
> void triggerFlush() { bindingContext.updateTargets(); }
<nod> yes that seems to be adequate operation assuming the user
selected the right policy for all corresponding bindings.
>> Finally let me add one further remark: In the jgoodies databinding
>> framework you don't always need to have an individual observable
>> just to observe changes of some characteristic property: E.g. I
>> can add or remove change listener's to the buffering state of
>> the IBufferedObservableValue without access to the actual observable,
>> because in this case I only need the change deltas in the property
>> observer and nowhere else.
>
> We've tried to reduce the cost of creating observables by not
> registering listeners on the underlying object (e.g. bean) until you
> actually add listeners to the observable--not sure if that mitigates
> your concern but I thought I'd mention it.
I agree that this is a good foundation the JFace DataBinding
architecture is based on, because it consequently supports
lazy-loading.
>> I sometimes miss that fact in Jface
>> databinding, although it (usually) provides what I need: With further
>> wrapper techniques I could provide a *constant* view onto the
>> buffering state or the trigger channel (jface has ConstantObservable)
>> which do only exist such that clients can register
>> IValueChangeListener's on them.
>
> I don't follow you here.
I tried to describe my workaround here. Users typically need to know,
when the buffering state of the presentation model changes. The JFace
way is to provide the observable of the buffer state as part of the
interface, the jgoodies way is to provide
void addBufferingChangeListener(...);
void removeBufferingChangeListener(...);
as part of the presentation model interface. The latter interface is
restricted, because users cannot unintentionally access the buffering
observable and actively change it's state, but with a normal (mutable)
JFace Databinding observable the user indeed might change the value
of this property. So my current workaround in the JFace Databinding
world is to provide immutable observables just to allow the user
to register as listener for it's changes.
> It sounds like this is something we should take a closer look at. Would
> you submit a feature enhancement so we can discuss this further?
Thanks for your encouraging opinion. I will start as soon as possible to
prepare this issue.
- Daniel
|
|
| | | |
Goto Forum:
Current Time: Mon Jan 20 20:16:54 GMT 2025
Powered by FUDForum. Page generated in 0.03644 seconds
|