Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
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 Go to next message
Daniel Krügler is currently offline 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 Go to previous messageGo to next message
Matthew Hall is currently offline 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 Go to previous messageGo to next message
Daniel Krügler is currently offline 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 Go to previous messageGo to next message
Thomas Schindl is currently offline Thomas Schindl
Messages: 5419
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 Go to previous messageGo to next message
Matthew Hall is currently offline 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 #485605 is a reply to message #485489] Mon, 14 September 2009 06:14 Go to previous messageGo to next message
Daniel Krügler is currently offline Daniel Krügler
Messages: 853
Registered: July 2009
Senior Member
Tom Schindl wrote:
> Just a short question is all this buffering needed because of undo/redo?
> Or is there a deeper reason behind it?

Yes, the buffering model is needed for undo/redo.
I assume you want to point out that this can be
realized via databindingContext.updateTargets()
and I agree that this seems the appropriate
alternative. But I probably need to provide some
wrapper for some code parts that don't want to
be rewritten yet so that this wrapper would act
as an adaptor to the old-style binding.

- Daniel
Re: [DataBinding] Does there exist a BufferedObservable? [message #485614 is a reply to message #485496] Mon, 14 September 2009 07:00 Go to previous messageGo to next message
Daniel Krügler is currently offline 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
Re: [DataBinding] Does there exist a BufferedObservable? [message #485647 is a reply to message #485605] Mon, 14 September 2009 10:04 Go to previous messageGo to next message
Thomas Schindl is currently offline Thomas Schindl
Messages: 5419
Registered: July 2009
Senior Member
I see. May strategy (well I'm using EMF) is to set a savepoint and on
cancle I rollback to it. For me this has the advantage that I can check
cross constraints using OCL.

We are discussing Undo/Redo support in bugzilla 285307 and 116465.

Tom

Daniel Krügler schrieb:
> Tom Schindl wrote:
>> Just a short question is all this buffering needed because of undo/redo?
>> Or is there a deeper reason behind it?
>
> Yes, the buffering model is needed for undo/redo.
> I assume you want to point out that this can be
> realized via databindingContext.updateTargets()
> and I agree that this seems the appropriate
> alternative. But I probably need to provide some
> wrapper for some code parts that don't want to
> be rewritten yet so that this wrapper would act
> as an adaptor to the old-style binding.
>
> - Daniel
Re: [DataBinding] Does there exist a BufferedObservable? [message #487941 is a reply to message #485647] Thu, 24 September 2009 23:20 Go to previous messageGo to next message
Tamar Cohen is currently offline Tamar Cohen
Messages: 100
Registered: July 2009
Senior Member
Hi All, thanks for the excellent discussion. So I'm looking for the same thing; when I am not using EMF for databinding but I want to support Undo / Redo.

Does anyone know the ETA for WorkbenchProperties support for undoable? Is this definitely going to be the way we go?

What are we supposed to do in the meantime when we can't use EMF? Will we have to do something like Daniel is doing?

Daniel, can you post your conclusion and what you have implemented?

thanks

Tamar
Re: [DataBinding] Does there exist a BufferedObservable? [message #488490 is a reply to message #487941] Mon, 28 September 2009 21:09 Go to previous message
Matthew Hall is currently offline Matthew Hall
Messages: 368
Registered: July 2009
Senior Member
Tamar, see comments below:

Tamar Cohen wrote:
> Does anyone know the ETA for WorkbenchProperties support for undoable?

I do not work on Eclipse full-time and unfortunately I have very little
spare time to work on this at the moment. I'm hoping to get this done
in time for 3.6 though.

> Is this definitely going to be the way we go?

I think so, this seems to be the only solution proposed so far that
solves the major issue without crazy side effects or turning your code
into an ugly mess.

> What are we supposed to do in the meantime when we can't use EMF? Will
> we have to do something like Daniel is doing?

Using WorkbenchProperties.undoable() will essentially accomplish the
same thing that EMFEditProperties is doing.

Matthew
Previous Topic:Commandframework
Next Topic:how to refresh Common Navigator after workspace modifications?
Goto Forum:
  


Current Time: Thu Oct 23 20:55:36 GMT 2014

Powered by FUDForum. Page generated in 0.02182 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software