### Eclipse Workspace Patch 1.0
#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 26 Feb 2010 06:01:38 -0000
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Brad Reynolds - bug 147515
* Matthew Hall - bug 221704, 226289
+ * Ovidio Mallo - bug 300043
*******************************************************************************/
package org.eclipse.core.databinding.observable.masterdetail;
@@ -21,6 +22,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 +32,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 +148,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: 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 26 Feb 2010 06:01:39 -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
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 26 Feb 2010 06:01:39 -0000
@@ -7,7 +7,7 @@
*
* Contributors:
* Matthew Hall - initial API and implementation (bug 194734)
- * Matthew Hall - bug 195222
+ * Matthew Hall - bug 195222, 300043
******************************************************************************/
package org.eclipse.core.databinding.property.list;
@@ -111,4 +111,12 @@
* properties
*/
public IListProperty values(IValueProperty detailValue);
+
+ /**
+ * @param reducer
+ * the strategy that reduces the list down to a value.
+ * @return a read-only reduced value from this list property
+ * @since 1.3
+ */
+ public IValueProperty reduce(IListReducer reducer);
}
Index: src/org/eclipse/core/databinding/property/list/IListReducer.java
===================================================================
RCS file: src/org/eclipse/core/databinding/property/list/IListReducer.java
diff -N src/org/eclipse/core/databinding/property/list/IListReducer.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/property/list/IListReducer.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Matthew Hall 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:
+ * Matthew Hall - initial API and implementation (bug 300043)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * A list-to-value reducer. List reducers may be used to condense a list
+ * property down to a value property.
+ *
+ * @since 1.3
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @see ListReducer
+ */
+public interface IListReducer {
+
+ /**
+ * @param list
+ * the list to reduce
+ * @return the list reduction
+ */
+ public Object reduce(List list);
+
+ /**
+ * @return the value type of the reduction
+ */
+ public Object getValueType();
+
+ /**
+ * @param master
+ * the master list to reduce
+ * @return an IObservableValue observing the reduction of the specified
+ * IObservableList
+ */
+ public IObservableValue observeDetail(IObservableList master);
+
+}
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 26 Feb 2010 06:01:40 -0000
@@ -7,7 +7,7 @@
*
* Contributors:
* Matthew Hall - initial API and implementation (bug 194734)
- * Matthew Hall - bug 195222
+ * Matthew Hall - bug 195222, 300043
******************************************************************************/
package org.eclipse.core.databinding.property.list;
@@ -20,6 +20,7 @@
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.property.value.IValueProperty;
import org.eclipse.core.internal.databinding.property.ListPropertyDetailValuesList;
+import org.eclipse.core.internal.databinding.property.list.ReducedListValueProperty;
/**
* Abstract implementation of IListProperty.
@@ -55,4 +56,11 @@
public final IListProperty values(IValueProperty detailValue) {
return new ListPropertyDetailValuesList(this, detailValue);
}
+
+ /**
+ * @since 1.3
+ */
+ public final IValueProperty reduce(IListReducer reducer) {
+ return new ReducedListValueProperty(this, reducer);
+ }
}
Index: src/org/eclipse/core/databinding/property/list/ListReducer.java
===================================================================
RCS file: src/org/eclipse/core/databinding/property/list/ListReducer.java
diff -N src/org/eclipse/core/databinding/property/list/ListReducer.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/property/list/ListReducer.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Matthew Hall 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:
+ * Matthew Hall - initial API and implementation (bug 300043)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.internal.databinding.property.value.ReducedListObservableValue;
+
+/**
+ * Abstract base implementation if {@link IListReducer}.
+ *
+ * @since 1.3
+ */
+public abstract class ListReducer implements IListReducer {
+
+ private final Object valueType;
+
+ /**
+ * Constructs a list reducer with the given value type
+ *
+ * @param valueType
+ * the reduction value type
+ */
+ protected ListReducer(Object valueType) {
+ this.valueType = valueType;
+ }
+
+ public Object getValueType() {
+ return valueType;
+ }
+
+ public IObservableValue observeDetail(IObservableList master) {
+ return new ReducedListObservableValue(master, this);
+ }
+
+}
Index: src/org/eclipse/core/databinding/property/list/ListReducers.java
===================================================================
RCS file: src/org/eclipse/core/databinding/property/list/ListReducers.java
diff -N src/org/eclipse/core/databinding/property/list/ListReducers.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/databinding/property/list/ListReducers.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Matthew Hall 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:
+ * Matthew Hall - initial API and implementation (bug 300043)
+ ******************************************************************************/
+
+package org.eclipse.core.databinding.property.list;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Factory class for common list reducers
+ *
+ * @since 1.3
+ */
+public class ListReducers {
+ /**
+ * @param delimiter
+ * the delimiter between each list element
+ * @return a String-valued list reducer which concatenates all elements in
+ * the list, with the specified delimiter between each element.
+ */
+ public static IListReducer join(final String delimiter) {
+ return new ListReducer(String.class) {
+ public Object reduce(List list) {
+ StringBuffer buf = new StringBuffer();
+
+ for (Iterator it = list.iterator(); it.hasNext();) {
+ buf.append(it.next());
+ if (it.hasNext())
+ buf.append(delimiter);
+ }
+
+ return buf.toString();
+ }
+ };
+ }
+}
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 26 Feb 2010 06:01:41 -0000
@@ -7,15 +7,18 @@
*
* Contributors:
* Matthew Hall - initial API and implementation (bug 194734)
- * Matthew Hall - bug 195222
+ * Matthew Hall - bugs 195222, 300043
******************************************************************************/
package org.eclipse.core.databinding.property.value;
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/internal/databinding/property/list/ReducedListValueProperty.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/property/list/ReducedListValueProperty.java
diff -N src/org/eclipse/core/internal/databinding/property/list/ReducedListValueProperty.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/property/list/ReducedListValueProperty.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Matthew Hall 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:
+ * Matthew Hall - initial API and implementation (bug 300043)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.list;
+
+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.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.list.IListReducer;
+import org.eclipse.core.databinding.property.list.ListProperty;
+import org.eclipse.core.databinding.property.value.ValueProperty;
+import org.eclipse.core.internal.databinding.property.PropertyObservableUtil;
+
+/**
+ *
+ */
+public class ReducedListValueProperty extends ValueProperty {
+
+ private final ListProperty masterProperty;
+ private final IListReducer reducer;
+
+ /**
+ * @param masterProperty
+ * @param reducer
+ */
+ public ReducedListValueProperty(ListProperty masterProperty,
+ IListReducer reducer) {
+ this.masterProperty = masterProperty;
+ this.reducer = reducer;
+ }
+
+ public Object getValueType() {
+ return reducer.getValueType();
+ }
+
+ public IObservableValue observe(Realm realm, Object source) {
+ IObservableList masterList;
+
+ ObservableTracker.setIgnore(true);
+ try {
+ masterList = masterProperty.observe(realm, source);
+ } finally {
+ ObservableTracker.setIgnore(false);
+ }
+
+ IObservableValue detailValue = reducer.observeDetail(masterList);
+ PropertyObservableUtil.cascadeDispose(detailValue, masterList);
+ return detailValue;
+ }
+
+ public IObservableList observeDetail(IObservableList master) {
+ return MasterDetailObservables.detailListValues(master,
+ valueFactory(master.getRealm()), getValueType());
+ }
+
+ public IObservableMap observeDetail(IObservableSet master) {
+ return MasterDetailObservables.detailSetValues(master,
+ valueFactory(master.getRealm()), getValueType());
+ }
+
+ public IObservableMap observeDetail(IObservableMap master) {
+ throw new UnsupportedOperationException();
+ }
+
+}
Index: src/org/eclipse/core/internal/databinding/property/value/ReducedListObservableValue.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/property/value/ReducedListObservableValue.java
diff -N src/org/eclipse/core/internal/databinding/property/value/ReducedListObservableValue.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/property/value/ReducedListObservableValue.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Matthew Hall 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:
+ * Matthew Hall - initial API and implementation (bug 300043)
+ ******************************************************************************/
+
+package org.eclipse.core.internal.databinding.property.value;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.property.list.IListReducer;
+
+/**
+ *
+ */
+public class ReducedListObservableValue extends ComputedValue {
+
+ private IObservableList masterList;
+ private IListReducer reducer;
+
+ /**
+ * @param masterList
+ * @param reducer
+ */
+ public ReducedListObservableValue(IObservableList masterList,
+ IListReducer reducer) {
+ super(masterList.getRealm(), reducer.getValueType());
+ this.masterList = masterList;
+ this.reducer = reducer;
+ }
+
+ protected Object calculate() {
+ return reducer.reduce(masterList);
+ }
+
+}
#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 26 Feb 2010 06:01:42 -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,12 @@
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.list.ListReducers;
+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 +111,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 +147,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 +166,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 +183,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 +236,7 @@
createColumn("Maternal Grandfather");
createColumn("Paternal Grandmother");
createColumn("Paternal Grandfather");
+ createColumn("Children").setWidth(200);
duckFamily.setLinesVisible(true);
@@ -216,17 +256,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 +281,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 +307,20 @@
"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(
+ ListReducers.join(", ")));
+
+ return properties.toArray(new IValueProperty[properties.size()]);
+ }
}