Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 324659

Summary: [DataBinding] Support nested list-typed properties in BeansObservables/PojoObservables
Product: [Eclipse Project] Platform Reporter: Henno Vermeulen <strider80>
Component: UIAssignee: Platform UI Triaged <platform-ui-triaged>
Status: RESOLVED WORKSFORME QA Contact: Matthew Hall <qualidafial>
Severity: enhancement    
Priority: P3    
Version: 4.0   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard:

Description Henno Vermeulen CLA 2010-09-07 10:38:05 EDT
Build Identifier: 20090920-1017

Currently the BeansObservables/PojoObservables that return an IObservableValue support nested properties but the methods that return an IObservableList (/Set/Map) do not.

This is not hard to solve, for example I use:

import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.value.IObservableValue;

public final class BeansObservablesExtension {

	private BeansObservablesExtension() {
	}

	/**
	 * Same as
	 * {@link BeansObservables#observeDetailList(IObservableValue, String, Class)}
	 * except that nested properties are supported.
	 */
	public static IObservableList observeDetailList(
			IObservableValue modelObservable, String modelProperty,
			Class<?> propertyType) {
		IObservableValue leafModelObservable;
		if (modelProperty.contains(".")) {
			leafModelObservable = BeansObservables.observeDetailValue(
					modelObservable, modelProperty.substring(0, modelProperty
							.lastIndexOf('.')), null);
		} else {
			leafModelObservable = modelObservable;
		}
		return BeansObservables
				.observeDetailList(leafModelObservable, modelProperty
						.substring(modelProperty.lastIndexOf('.') + 1),
						propertyType);
	}

}




Reproducible: Always
Comment 1 Henno Vermeulen CLA 2010-09-07 10:43:44 EDT
I was a bit eager with simplifying and inlining my short helper methods for getting the property parts before and after the .

When modelProperty is a simple property that does not contain a ., I think that the code

modelProperty.substring(modelProperty.lastIndexOf('.') + 1)

actually works by accident because modelProperty.lastIndexOf('.') will then return -1, but this is a bit shady code.
Comment 2 Matthew Hall CLA 2010-09-07 17:37:19 EDT
If I understand your request correctly, you would like a simple way to observe a list property of a value property of an observable.  e.g.

  IObservableList siblingNodes = BeansObservables
      .observeDetailList(nodeObservable, "parent.children", Node.class);

If that is what you're asking, you should take a look at the property APIs--they support nested property chaining extensively.  The above code (which does not work BTW, but reflects my understanding of your request) would be written as:

  IObservableList siblingNodes = BeanProperties
      .value(Node.class, "parent")
      .list("children")
      .observeDetail(nodeObservable)

The reason we do not support nested properties with anything other than value properties is that it is impossible for us to know on the framework side which part of a nested property is the list property:

  BeansObservables.observeList(foo, "bar.baz.buz")

Presumably one of these properties is a list property, and the others are value properties.  But which one?

Our workaround was to separate keep the properties separate and only allow nested property names for value properties, but to allow you to chain all different types of properties (value, list, set, map) but still allow you to chain them in a single expression:

  IObservableList siblingNames = BeanProperties
      .value("parent")
      .list("children")
      .values("name") // note the plural "values" here
      .observe(person);
Comment 3 Matthew Hall CLA 2011-09-13 21:37:38 EDT
WORKSFORME. If I am reading your description correctly, your use case is already satisfied by using IValueProperty.list(IListProperty) property chaining.

If I've misunderstood your request, please reopen and elaborate.
Comment 4 Henno Vermeulen CLA 2011-09-14 12:05:13 EDT
Our use case is with master-detail binding. We have an IObservableValue for the master table selection which is of type CustomerContactRelation. It has exactly one ContactPerson object which in its turn has a List<ObtainedDegrees>. We wish to observe this List so that it can be used as the input for a detail table. So we wish to have an IObservableList for the property "contactPerson.obtainedDegrees" of the given IObservableValue where the first property is a single Object and the last property is a List.
Comment 5 Matthew Hall CLA 2011-09-14 12:36:27 EDT
So what you want to do is bind from the contact relation viewer selection to the input of the obtained degrees viewer.

On the obtained degrees viewer, use this property for content provider:

IListProperty childrenProperty = BeanProperties.value("contactPerson")
    .list("obtainedDegrees");
ViewerSupport.setUp(viewer, null, childrenProperty, labelProperty...);

Does this help?
Comment 6 Henno Vermeulen CLA 2011-09-15 04:22:52 EDT
Well, I guess that line of code would help in my example. However I wish to have a generic "util" method for binding to a list property in my model where the property may be nested but doesn't have to.

Maybe the code in my first comment can be rewritten using a combination of BeanProperties.value and IBeanValueProperty.list, but then it would be of the same length because you still have to parse the first and last part of the possibly nested property.
Comment 7 Matthew Hall CLA 2011-09-15 10:09:19 EDT
True, but there's a reason we kept those separate. By making you explicitly state which properties are values and which are lists, it is trivial for someone new to the code to understand what is going on.

Moreover, it's not a given that the last property in the chain in the list. You could have a value-list-value chain:

IListProperty siblingNames = BeanProperties
    .value("parent")
    .list("children")
    .values("name");

We chose this design so that Data Binding doesn't have to guess what's going on in your model.
Comment 8 Henno Vermeulen CLA 2011-09-15 11:04:25 EDT
Alright, I guess your last example is a more advanced usecase that I didn't think of when posting this :). Let's keep it at this, indeed it already works for me with the BeansObservablesExtension.observeDetailList code I wrote.
Comment 9 Matthew Hall CLA 2011-09-15 12:15:15 EDT
Keep the questions coming though--by challenging the API you help us see how folks instinctively use the API, and that helps us design things to be more intuitive. :)