### Eclipse Workspace Patch 1.0 #P org.eclipse.jface.examples.databinding Index: src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface.examples.databinding/src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java,v retrieving revision 1.5 diff -u -r1.5 Snippet025TableViewerWithPropertyDerivedColumns.java --- src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java 25 May 2009 20:52:49 -0000 1.5 +++ src/org/eclipse/jface/examples/databinding/snippets/Snippet025TableViewerWithPropertyDerivedColumns.java 4 Feb 2010 21:23:33 -0000 @@ -14,6 +14,9 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.BeanProperties; @@ -21,10 +24,11 @@ import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.list.WritableList; import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.property.value.IValueProperty; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.databinding.swt.WidgetProperties; -import org.eclipse.jface.databinding.viewers.ViewerSupport; import org.eclipse.jface.databinding.viewers.ViewerProperties; +import org.eclipse.jface.databinding.viewers.ViewerSupport; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.viewers.ComboViewer; @@ -106,11 +110,28 @@ String name = "Donald Duck"; Person mother; Person father; + List children = new ArrayList(); public Person(String name, Person mother, Person father) { this.name = name; this.mother = mother; this.father = father; + if (mother != null) { + mother.addChild(this); + } + if (father != null) { + father.addChild(this); + } + } + + private void addChild(Person person) { + children.add(person); + firePropertyChange("children", null, children); + } + + private void removeChild(Person person) { + children.remove(person); + firePropertyChange("children", null, children); } public String getName() { @@ -125,8 +146,18 @@ return mother; } + public Person[] getChildren() { + return (Person[]) children.toArray(new Person[children.size()]); + } + public void setMother(Person mother) { + if (this.mother != null) { + this.mother.removeChild(this); + } firePropertyChange("mother", this.mother, this.mother = mother); + if (this.mother != null) { + this.mother.addChild(this); + } } public Person getFather() { @@ -134,7 +165,13 @@ } public void setFather(Person father) { + if (this.father != null) { + this.father.removeChild(this); + } firePropertyChange("father", this.father, this.father = father); + if (this.father != null) { + this.father.addChild(this); + } } } @@ -145,6 +182,7 @@ // data access tier. Since this snippet doesn't have any persistent objects // ro retrieve, this ViewModel just instantiates a model object to edit. static class ViewModel { + // The model to bind private IObservableList people = new WritableList(); { @@ -197,6 +235,7 @@ createColumn("Maternal Grandfather"); createColumn("Paternal Grandmother"); createColumn("Paternal Grandfather"); + createColumn("Children").setWidth(200); duckFamily.setLinesVisible(true); @@ -216,17 +255,18 @@ GridLayoutFactory.swtDefaults().numColumns(2).applyTo(shell); // Open and return the Shell - shell.setSize(800, 300); + shell.setSize(1000, 300); shell.open(); return shell; } - private void createColumn(String string) { + private TableColumn createColumn(String string) { final TableColumn column = new TableColumn(duckFamily, SWT.NONE); column.setText(string); column.pack(); if (column.getWidth() < 100) column.setWidth(100); + return column; } protected void bindGUI(DataBindingContext dbc) { @@ -240,10 +280,7 @@ }); ViewerSupport.bind(peopleViewer, viewModel.getPeople(), - BeanProperties.values(Person.class, new String[] { "name", - "mother.name", "father.name", "mother.mother.name", - "mother.father.name", "father.mother.name", - "father.father.name" })); + getProperties()); IObservableValue masterSelection = ViewerProperties .singleSelection().observe(peopleViewer); @@ -269,4 +306,19 @@ "father").observeDetail(masterSelection)); } } + + private static IValueProperty[] getProperties() { + List properties = new ArrayList(); + + // Add the "simple" properties. + properties.addAll(Arrays.asList(BeanProperties.values(Person.class, + new String[] { "name", "mother.name", "father.name", + "mother.mother.name", "mother.father.name", + "father.mother.name", "father.father.name" }))); + + // Add the "complex" property. + properties.add(BeanProperties.list("children").values("name").reduce()); + + return properties.toArray(new IValueProperty[properties.size()]); + } } #P org.eclipse.core.databinding.observable Index: src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.observable/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java,v retrieving revision 1.10 diff -u -r1.10 MasterDetailObservables.java --- src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java 17 Oct 2008 16:44:25 -0000 1.10 +++ src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java 4 Feb 2010 21:23:34 -0000 @@ -21,6 +21,8 @@ import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableMap; import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableSet; import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableValue; +import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList; +import org.eclipse.core.internal.databinding.observable.masterdetail.SetDetailValueObservableMap; /** * Allows for the observation of an attribute, the detail, of an observable @@ -29,7 +31,7 @@ * @since 1.0 */ public class MasterDetailObservables { - + /** * Creates a detail observable value from a master observable value and a * factory. This can be used to create observable values that represent a @@ -145,4 +147,58 @@ return new DetailObservableMap(detailFactory, master, detailKeyType, detailValueType); } + + /** + * Creates an unmodifiable observable list of detail values from an + * observable list of observable values. For every master value in the given + * list, the provided factory is used to create the corresponding detail + * observable value. This can e.g. be used to create a list of values that + * represent the same property of a set of selected objects in a table. + * + * @param masterList + * the list of master values to track + * @param detailFactory + * a factory for creating {@link IObservableValue} instances + * given a current value of one of the masters + * @param detailType + * the value type of the detail values, typically of type + * java.lang.Class and can be null + * @return a list of detail values of the given value type that, for any + * current value of any of the master values, behaves like the + * observable value created by the factory for that current value. + * + * @since 1.3 + */ + public static IObservableList detailListValues(IObservableList masterList, + IObservableFactory detailFactory, Object detailType) { + return new ListDetailValueObservableList(masterList, detailFactory, + detailType); + } + + /** + * Creates an unmodifiable observable set of detail values from an + * observable set of observable values. For every master value in the given + * set, the provided factory is used to create the corresponding detail + * observable value. This can e.g. be used to create a list of values that + * represent the same property of a set of selected objects in a table. + * + * @param masterSet + * the set of master values to track + * @param detailFactory + * a factory for creating {@link IObservableValue} instances + * given a current value of one of the masters + * @param detailType + * the value type of the detail values, typically of type + * java.lang.Class and can be null + * @return a list of detail values of the given value type that, for any + * current value of any of the master values, behaves like the + * observable value created by the factory for that current value. + * + * @since 1.3 + */ + public static IObservableMap detailSetValues(IObservableSet masterSet, + IObservableFactory detailFactory, Object detailType) { + return new SetDetailValueObservableMap(masterSet, detailFactory, + detailType); + } } Index: src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java diff -N src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailValueObservableList.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2010 Ovidio Mallo and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ovidio Mallo - initial API and implementation (bug 300043) + ******************************************************************************/ + +package org.eclipse.core.internal.databinding.observable.masterdetail; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.Iterator; +import java.util.RandomAccess; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.DisposeEvent; +import org.eclipse.core.databinding.observable.IDisposeListener; +import org.eclipse.core.databinding.observable.IObserving; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.list.AbstractObservableList; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiff; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.internal.databinding.identity.IdentitySet; + +/** + * + */ +public class ListDetailValueObservableList extends AbstractObservableList + implements IObserving, RandomAccess { + + private IObservableList masterList; + + private IObservableFactory detailFactory; + + private Object detailType; + + private ArrayList detailList; + + private IdentitySet staleDetailObservables = new IdentitySet(); + + private IListChangeListener masterListListener = new IListChangeListener() { + public void handleListChange(ListChangeEvent event) { + adaptMasterListDiff(event.diff); + } + }; + + private IValueChangeListener detailValueListener = new IValueChangeListener() { + public void handleValueChange(ValueChangeEvent event) { + if (!event.getObservable().isStale()) { + staleDetailObservables.remove(event.getObservable()); + } + handleDetailValueChange(event); + } + }; + + private IStaleListener detailStaleListener = new IStaleListener() { + public void handleStale(StaleEvent staleEvent) { + boolean wasStale = isStale(); + staleDetailObservables.add((staleEvent.getObservable())); + if (!wasStale) { + fireStale(); + } + } + }; + + /** + * + * @param masterObservableList + * @param detailFactory + * @param detailType + */ + public ListDetailValueObservableList(IObservableList masterObservableList, + IObservableFactory detailFactory, Object detailType) { + super(masterObservableList.getRealm()); + this.masterList = masterObservableList; + this.detailFactory = detailFactory; + this.detailType = detailType; + this.detailList = new ArrayList(); + + masterObservableList.addListChangeListener(masterListListener); + masterObservableList.addDisposeListener(new IDisposeListener() { + public void handleDispose(DisposeEvent event) { + dispose(); + } + }); + + ListDiff initMasterDiff = Diffs.computeListDiff(Collections.EMPTY_LIST, + masterObservableList); + adaptMasterListDiff(initMasterDiff); + } + + private void adaptMasterListDiff(ListDiff masterListDiff) { + boolean wasStale = isStale(); + + ListDiffEntry[] masterEntries = masterListDiff.getDifferences(); + ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length]; + for (int i = 0; i < masterEntries.length; i++) { + ListDiffEntry masterEntry = masterEntries[i]; + int index = masterEntry.getPosition(); + + Object detailValue; + if (masterEntry.isAddition()) { + Object masterElement = masterEntry.getElement(); + detailValue = addDetailObservable(masterElement, index); + } else { + detailValue = removeDetailObservable(index); + } + + // Create the corresponding diff for the detail list. + detailEntries[i] = Diffs.createListDiffEntry(index, masterEntry + .isAddition(), detailValue); + } + + if (!wasStale && isStale()) { + fireStale(); + } + + // Fire a list change event with the adapted diffs on the detail list. + fireListChange(Diffs.createListDiff(detailEntries)); + } + + private Object addDetailObservable(Object masterElement, int index) { + IObservableValue detail; + ObservableTracker.setIgnore(true); + try { + detail = (IObservableValue) detailFactory + .createObservable(masterElement); + } finally { + ObservableTracker.setIgnore(false); + } + + detailList.add(index, detail); + + detail.addValueChangeListener(detailValueListener); + + detail.addStaleListener(detailStaleListener); + if (detail.isStale()) { + staleDetailObservables.add(detail); + } + + return detail.getValue(); + } + + private Object removeDetailObservable(int index) { + IObservableValue detail = (IObservableValue) detailList.remove(index); + staleDetailObservables.remove(detail); + Object detailValue = detail.getValue(); + detail.dispose(); + return detailValue; + } + + private void handleDetailValueChange(ValueChangeEvent event) { + IObservableValue detail = event.getObservableValue(); + + BitSet detailIndexes = new BitSet(); + for (int i = 0; i < detailList.size(); i++) { + if (detailList.get(i) == detail) { + detailIndexes.set(i); + } + } + + Object oldValue = event.diff.getOldValue(); + Object newValue = event.diff.getNewValue(); + ListDiffEntry[] diffEntries = new ListDiffEntry[2 * detailIndexes + .cardinality()]; + int diffIndex = 0; + for (int b = 0; b != -1; b = detailIndexes.nextSetBit(b + 1)) { + diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, false, + oldValue); + diffEntries[diffIndex++] = Diffs.createListDiffEntry(b, true, + newValue); + } + fireListChange(Diffs.createListDiff(diffEntries)); + } + + protected int doGetSize() { + return detailList.size(); + } + + public Object get(int index) { + ObservableTracker.getterCalled(this); + return ((IObservableValue) detailList.get(index)).getValue(); + } + + public Object set(int index, Object element) { + IObservableValue detail = (IObservableValue) detailList.get(index); + Object oldElement = detail.getValue(); + detail.setValue(element); + return oldElement; + } + + public Object getElementType() { + return detailType; + } + + public boolean isStale() { + return super.isStale() + || (masterList != null && masterList.isStale()) + || (staleDetailObservables != null && !staleDetailObservables + .isEmpty()); + } + + public Object getObserved() { + return masterList; + } + + public synchronized void dispose() { + if (masterList != null) { + masterList.removeListChangeListener(masterListListener); + } + + if (detailList != null) { + for (Iterator iter = detailList.iterator(); iter.hasNext();) { + ((IObservableValue) iter.next()).dispose(); + } + } + + masterList = null; + detailFactory = null; + detailType = null; + masterListListener = null; + detailValueListener = null; + staleDetailObservables = null; + + super.dispose(); + } +} Index: src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java =================================================================== RCS file: src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java diff -N src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/core/internal/databinding/observable/masterdetail/SetDetailValueObservableMap.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2010 Ovidio Mallo and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Ovidio Mallo - initial API and implementation (bug 300043) + ******************************************************************************/ + +package org.eclipse.core.internal.databinding.observable.masterdetail; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.databinding.observable.IObserving; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.map.ComputedObservableMap; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.internal.databinding.identity.IdentitySet; + +/** + * + */ +public class SetDetailValueObservableMap extends ComputedObservableMap + implements IObserving { + + private IObservableFactory observableValueFactory; + + private Map detailObservableValueMap = new HashMap(); + + private IdentitySet staleDetailObservables = new IdentitySet(); + + private IStaleListener detailStaleListener = new IStaleListener() { + public void handleStale(StaleEvent staleEvent) { + addStaleDetailObservable((IObservableValue) staleEvent + .getObservable()); + } + }; + + /** + * @param masterKeySet + * @param observableValueFactory + * @param detailValueType + */ + public SetDetailValueObservableMap(IObservableSet masterKeySet, + IObservableFactory observableValueFactory, Object detailValueType) { + super(masterKeySet, detailValueType); + this.observableValueFactory = observableValueFactory; + } + + protected void hookListener(final Object addedKey) { + IObservableValue detailValue = getDetailObservableValue(addedKey); + + detailValue.addValueChangeListener(new IValueChangeListener() { + public void handleValueChange(ValueChangeEvent event) { + if (!event.getObservableValue().isStale()) { + staleDetailObservables.remove(event.getObservableValue()); + } + + fireSingleChange(addedKey, event.diff.getOldValue(), event.diff + .getNewValue()); + } + }); + + detailValue.addStaleListener(detailStaleListener); + } + + protected void unhookListener(Object removedKey) { + if (isDisposed()) { + return; + } + + IObservableValue detailValue = (IObservableValue) detailObservableValueMap + .remove(removedKey); + staleDetailObservables.remove(detailValue); + detailValue.dispose(); + } + + private IObservableValue getDetailObservableValue(Object masterKey) { + IObservableValue detailValue = (IObservableValue) detailObservableValueMap + .get(masterKey); + + if (detailValue == null) { + ObservableTracker.setIgnore(true); + try { + detailValue = (IObservableValue) observableValueFactory + .createObservable(masterKey); + } finally { + ObservableTracker.setIgnore(false); + } + + detailObservableValueMap.put(masterKey, detailValue); + + if (detailValue.isStale()) { + addStaleDetailObservable(detailValue); + } + } + + return detailValue; + } + + private void addStaleDetailObservable(IObservableValue detailObservable) { + boolean wasStale = isStale(); + staleDetailObservables.add(detailObservable); + if (!wasStale) { + fireStale(); + } + } + + protected Object doGet(Object key) { + IObservableValue detailValue = getDetailObservableValue(key); + return detailValue.getValue(); + } + + protected Object doPut(Object key, Object value) { + IObservableValue detailValue = getDetailObservableValue(key); + Object oldValue = detailValue.getValue(); + detailValue.setValue(value); + return oldValue; + } + + public boolean isStale() { + return super.isStale() || staleDetailObservables != null + && !staleDetailObservables.isEmpty(); + } + + public Object getObserved() { + return keySet(); + } + + public synchronized void dispose() { + super.dispose(); + + observableValueFactory = null; + detailObservableValueMap = null; + detailStaleListener = null; + staleDetailObservables = null; + } +} #P org.eclipse.core.databinding.property Index: src/org/eclipse/core/databinding/property/value/ValueProperty.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/value/ValueProperty.java,v retrieving revision 1.3 diff -u -r1.3 ValueProperty.java --- src/org/eclipse/core/databinding/property/value/ValueProperty.java 25 May 2009 20:52:27 -0000 1.3 +++ src/org/eclipse/core/databinding/property/value/ValueProperty.java 4 Feb 2010 21:23:35 -0000 @@ -14,8 +14,11 @@ import org.eclipse.core.databinding.observable.IObservable; import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; +import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.property.list.IListProperty; import org.eclipse.core.databinding.property.map.IMapProperty; @@ -56,6 +59,22 @@ .getRealm()), getValueType()); } + /** + * @since 1.3 + */ + public IObservableList observeDetail(IObservableList master) { + return MasterDetailObservables.detailListValues(master, + valueFactory(master.getRealm()), getValueType()); + } + + /** + * @since 1.3 + */ + public IObservableMap observeDetail(IObservableSet master) { + return MasterDetailObservables.detailSetValues(master, + valueFactory(master.getRealm()), getValueType()); + } + public final IValueProperty value(IValueProperty detailValue) { return new ValuePropertyDetailValue(this, detailValue); } Index: src/org/eclipse/core/databinding/property/list/IListProperty.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/IListProperty.java,v retrieving revision 1.3 diff -u -r1.3 IListProperty.java --- src/org/eclipse/core/databinding/property/list/IListProperty.java 25 May 2009 20:52:27 -0000 1.3 +++ src/org/eclipse/core/databinding/property/list/IListProperty.java 4 Feb 2010 21:23:35 -0000 @@ -111,4 +111,15 @@ * properties */ public IListProperty values(IValueProperty detailValue); + + /** + * Reduces this IListProperty to a IValueProperty by interpreting this + * property's list as a simple value. + * + * @return A value property reducing the list of this property to a simple + * value. + * + * @since 1.3 + */ + public IValueProperty reduce(); } Index: src/org/eclipse/core/databinding/property/list/ListProperty.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.property/src/org/eclipse/core/databinding/property/list/ListProperty.java,v retrieving revision 1.3 diff -u -r1.3 ListProperty.java --- src/org/eclipse/core/databinding/property/list/ListProperty.java 25 May 2009 20:52:27 -0000 1.3 +++ src/org/eclipse/core/databinding/property/list/ListProperty.java 4 Feb 2010 21:23:35 -0000 @@ -13,12 +13,16 @@ package org.eclipse.core.databinding.property.list; import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; +import org.eclipse.core.databinding.observable.value.ComputedValue; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.property.value.IValueProperty; +import org.eclipse.core.databinding.property.value.ValueProperty; import org.eclipse.core.internal.databinding.property.ListPropertyDetailValuesList; /** @@ -55,4 +59,30 @@ public final IListProperty values(IValueProperty detailValue) { return new ListPropertyDetailValuesList(this, detailValue); } + + /** + * @since 1.3 + */ + public IValueProperty reduce() { + return new ValueProperty() { + public IObservableValue observe(Realm realm, Object source) { + final IObservableList masterList = ListProperty.this.observe( + realm, source); + return new ComputedValue(getValueType()) { + protected Object calculate() { + ObservableTracker.getterCalled(masterList); + return masterList; + } + }; + } + + public Object getValueType() { + return null; + } + + public IObservableMap observeDetail(IObservableMap master) { + throw new UnsupportedOperationException(); + } + }; + } } Index: META-INF/MANIFEST.MF =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.core.databinding.property/META-INF/MANIFEST.MF,v retrieving revision 1.3 diff -u -r1.3 MANIFEST.MF --- META-INF/MANIFEST.MF 25 Aug 2009 04:57:24 -0000 1.3 +++ META-INF/MANIFEST.MF 4 Feb 2010 21:23:35 -0000 @@ -2,7 +2,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.databinding.property -Bundle-Version: 1.2.100.qualifier +Bundle-Version: 1.3.0.qualifier Bundle-ClassPath: . Bundle-Vendor: %providerName Bundle-Localization: plugin