### Eclipse Workspace Patch 1.0
#P org.eclipse.jface.tests.databinding
Index: src/org/eclipse/jface/tests/databinding/BindingTestSuite.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/BindingTestSuite.java,v
retrieving revision 1.87
diff -u -r1.87 BindingTestSuite.java
--- src/org/eclipse/jface/tests/databinding/BindingTestSuite.java 3 Jul 2008 16:30:03 -0000 1.87
+++ src/org/eclipse/jface/tests/databinding/BindingTestSuite.java 19 Jul 2008 18:18:27 -0000
@@ -14,7 +14,7 @@
* Matthew Hall - bugs 210115, 212468, 212223, 206839, 208858, 208322,
* 212518, 215531, 221351, 184830, 213145, 218269, 239015,
* 237703
- * Ovidio Mallo - bug 237163
+ * Ovidio Mallo - bugs 237163, 175737
*******************************************************************************/
package org.eclipse.jface.tests.databinding;
@@ -114,6 +114,7 @@
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableListTest;
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableSetTest;
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableValueTest;
+import org.eclipse.core.tests.internal.databinding.observable.masterdetail.ListDetailObservableListTest;
import org.eclipse.core.tests.internal.databinding.validation.AbstractStringToNumberValidatorTest;
import org.eclipse.core.tests.internal.databinding.validation.NumberToByteValidatorTest;
import org.eclipse.core.tests.internal.databinding.validation.NumberToDoubleValidatorTest;
@@ -308,6 +309,7 @@
addTest(DetailObservableListTest.suite());
addTest(DetailObservableSetTest.suite());
addTest(DetailObservableValueTest.suite());
+ addTest(ListDetailObservableListTest.suite());
// org.eclipse.core.tests.internal.databinding.validation
addTestSuite(AbstractStringToNumberValidatorTest.class);
Index: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailObservableListTest.java
===================================================================
RCS file: src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailObservableListTest.java
diff -N src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailObservableListTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailObservableListTest.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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 175737)
+ ******************************************************************************/
+
+package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.eclipse.core.databinding.beans.BeansObservables;
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailObservableList;
+import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
+import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
+import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
+import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
+
+/**
+ * @since 1.2
+ */
+public class ListDetailObservableListTest extends AbstractDefaultRealmTestCase {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(ListDetailObservableListTest.class
+ .getName());
+ suite.addTestSuite(ListDetailObservableListTest.class);
+ suite.addTest(ObservableListContractTest.suite(new Delegate()));
+ return suite;
+ }
+
+ public void testUnmodifiability() {
+ WritableList masterObservableList = new WritableList();
+ masterObservableList.add(new WritableValue());
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ null);
+
+ try {
+ ldol.add(new WritableValue());
+ fail("ListDetailObservableList must not be modifiable.");
+ } catch (UnsupportedOperationException e) {
+ // expected exception
+ }
+
+ try {
+ ldol.remove(new WritableValue());
+ fail("ListDetailObservableList must not be modifiable.");
+ } catch (UnsupportedOperationException e) {
+ // expected exception
+ }
+
+ try {
+ ldol.set(0, new WritableValue());
+ fail("ListDetailObservableList must not be modifiable.");
+ } catch (UnsupportedOperationException e) {
+ // expected exception
+ }
+ }
+
+ public void testDetailValueType() {
+ WritableList masterObservableList = new WritableList();
+ masterObservableList.add(new WritableValue(new Person(), Person.class));
+
+ ListDetailObservableList ldol1 = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ String.class);
+ assertSame(String.class, ((IObservableValue) ldol1.get(0))
+ .getValueType());
+
+ ListDetailObservableList ldol2 = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ null);
+ assertNull(((IObservableValue) ldol2.get(0)).getValueType());
+ }
+
+ public void testDetailListElementType() {
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ new WritableList(), BeansObservables.valueFactory("name"),
+ String.class);
+
+ assertSame(IObservableValue.class, ldol.getElementType());
+ }
+
+ public void testMasterListInitiallyNotEmpty() {
+ WritableList masterObservableList = new WritableList();
+ masterObservableList.add(new WritableValue(new Person(), Person.class));
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ String.class);
+
+ assertEquals(masterObservableList.size(), ldol.size());
+ }
+
+ public void testChangeMaster() {
+ WritableList masterObservableList = new WritableList();
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ String.class);
+
+ assertTrue(ldol.isEmpty());
+
+ Person p1 = new Person();
+ p1.setName("name1");
+ masterObservableList.add(new WritableValue(p1, Person.class));
+ assertEquals(masterObservableList.size(), ldol.size());
+ assertEquals(p1.getName(), ((IObservableValue) ldol.get(0)).getValue());
+
+ Person p2 = new Person();
+ p2.setName("name2");
+ masterObservableList.add(new WritableValue(p2, Person.class));
+ assertEquals(masterObservableList.size(), ldol.size());
+ assertEquals(p2.getName(), ((IObservableValue) ldol.get(1)).getValue());
+
+ masterObservableList.remove(0);
+ assertEquals(masterObservableList.size(), ldol.size());
+ assertEquals(p2.getName(), ((IObservableValue) ldol.get(0)).getValue());
+
+ masterObservableList.remove(0);
+ assertTrue(ldol.isEmpty());
+ }
+
+ public void testChangeDetail() {
+ WritableList masterObservableList = new WritableList();
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ String.class);
+
+ WritableValue masterObservable = new WritableValue(null, Person.class);
+ masterObservableList.add(masterObservable);
+ IObservableValue detailObservable = (IObservableValue) ldol.get(0);
+
+ // Change the detail attribute explicitly.
+ Person p1 = new Person();
+ masterObservable.setValue(p1);
+ p1.setName("name1");
+ assertEquals(p1.getName(), detailObservable.getValue());
+ p1.setName("name2");
+ assertEquals(p1.getName(), detailObservable.getValue());
+
+ // Change the detail attribute by changing the master.
+ Person p2 = new Person();
+ masterObservable.setValue(p2);
+ detailObservable.setValue("name3");
+ assertEquals(p2.getName(), detailObservable.getValue());
+ }
+
+ public void testDetailObservableChangeEvent() {
+ WritableList masterObservableList = new WritableList();
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ String.class);
+
+ Person person = new Person();
+ masterObservableList.add(new WritableValue(person, Person.class));
+ IObservableValue detailObservable = (IObservableValue) ldol.get(0);
+
+ ValueChangeEventTracker changeTracker = ValueChangeEventTracker
+ .observe(detailObservable);
+ detailObservable.addValueChangeListener(changeTracker);
+
+ assertEquals(0, changeTracker.count);
+
+ person.setName("new name");
+ assertEquals(1, changeTracker.count);
+ }
+
+ public void testMasterNull() {
+ WritableList masterObservableList = new WritableList();
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, BeansObservables.valueFactory("name"),
+ String.class);
+
+ masterObservableList.add(new WritableValue(null, null));
+ assertNull(((IObservableValue) ldol.get(0)).getValue());
+ }
+
+ public void testDetailObservableValuesAreDisposed() {
+ final List detailObservables = new ArrayList();
+ IObservableFactory detailValueFactory = new IObservableFactory() {
+ public IObservable createObservable(Object target) {
+ TestObservableValue detailObservable = new TestObservableValue();
+ detailObservables.add(detailObservable);
+ return detailObservable;
+ }
+ };
+ WritableList masterObservableList = new WritableList();
+ ListDetailObservableList ldol = new ListDetailObservableList(
+ masterObservableList, detailValueFactory, null);
+
+ masterObservableList.add(new WritableValue(new Object(), null));
+ masterObservableList.add(new WritableValue(new Object(), null));
+
+ assertEquals(ldol.size(), detailObservables.size());
+
+ assertFalse(((TestObservableValue) detailObservables.get(0)).disposed);
+ assertFalse(((TestObservableValue) detailObservables.get(1)).disposed);
+
+ masterObservableList.remove(1);
+ assertFalse(((TestObservableValue) detailObservables.get(0)).disposed);
+ assertTrue(((TestObservableValue) detailObservables.get(1)).disposed);
+
+ ldol.dispose();
+ assertTrue(((TestObservableValue) detailObservables.get(0)).disposed);
+ assertTrue(((TestObservableValue) detailObservables.get(1)).disposed);
+ }
+
+ public void testMasterObservableValuesAreNotDisposed() {
+ WritableList masterObservableList = new WritableList();
+ new ListDetailObservableList(masterObservableList, BeansObservables
+ .valueFactory("name"), String.class);
+
+ TestObservableValue masterObservable = new TestObservableValue();
+ masterObservableList.add(masterObservable);
+
+ assertFalse(masterObservable.disposed);
+
+ masterObservableList.remove(0);
+ assertFalse(masterObservable.disposed);
+ }
+
+ private static class Delegate extends
+ AbstractObservableCollectionContractDelegate {
+ public IObservableCollection createObservableCollection(Realm realm,
+ int elementCount) {
+ WritableList masterObservableList = new WritableList(realm);
+ for (int i = 0; i < elementCount; i++) {
+ masterObservableList.add(new WritableValue(realm, new Person(),
+ Person.class));
+ }
+
+ return new TestListDetailObservableList(masterObservableList,
+ BeansObservables.valueFactory(realm, "name"), String.class);
+ }
+
+ public void change(IObservable observable) {
+ TestListDetailObservableList ldol = (TestListDetailObservableList) observable;
+ ldol.masterObservableList.add(new WritableValue(observable
+ .getRealm(), new Person(), Person.class));
+ }
+
+ public Object getElementType(IObservableCollection collection) {
+ return IObservableValue.class;
+ }
+ }
+
+ private static class TestListDetailObservableList extends
+ ListDetailObservableList {
+ final IObservableList masterObservableList;
+
+ public TestListDetailObservableList(
+ IObservableList masterObservableList,
+ IObservableFactory detailValueFactory, Object detailType) {
+ super(masterObservableList, detailValueFactory, detailType);
+ this.masterObservableList = masterObservableList;
+ }
+ }
+
+ private static class TestObservableValue extends WritableValue {
+ boolean disposed = false;
+
+ public synchronized void dispose() {
+ disposed = true;
+ super.dispose();
+ }
+ }
+
+ private static class Person {
+ private final PropertyChangeSupport pcs = new PropertyChangeSupport(
+ this);
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ pcs.firePropertyChange("name", this.name, this.name = name);
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener listener) {
+ pcs.addPropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ pcs.removePropertyChangeListener(listener);
+ }
+ }
+}
#P org.eclipse.core.databinding
Index: src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java,v
retrieving revision 1.9
diff -u -r1.9 MasterDetailObservables.java
--- src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java 24 Mar 2008 19:13:39 -0000 1.9
+++ src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java 19 Jul 2008 18:18:27 -0000
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Brad Reynolds - bug 147515
* Matthew Hall - bug 221704
+ * Ovidio Mallo - bug 175737
*******************************************************************************/
package org.eclipse.core.databinding.observable.masterdetail;
@@ -21,6 +22,7 @@
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.ListDetailObservableList;
/**
* Allows for the observation of an attribute, the detail, of an observable
@@ -116,4 +118,32 @@
IObservableFactory detailFactory) {
return new DetailObservableMap(detailFactory, master);
}
+
+ /**
+ * Creates an unmodifiable observable list of detail observable values from
+ * an observable list of master observable values. For every master
+ * observable 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 observable values that represent the same property of
+ * a set of selected objects in a table.
+ *
+ * @param masterList
+ * the list of master observable 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 observable values, typically of
+ * type java.lang.Class and can be null
+ * @return a list of observable 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.2
+ */
+ public static IObservableList detailValueList(IObservableList masterList,
+ IObservableFactory detailFactory, Object detailType) {
+ return new ListDetailObservableList(masterList, detailFactory,
+ detailType);
+ }
}
Index: src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailObservableList.java
===================================================================
RCS file: src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailObservableList.java
diff -N src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailObservableList.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailObservableList.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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 175737)
+ *******************************************************************************/
+package org.eclipse.core.internal.databinding.observable.masterdetail;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.Diffs;
+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.list.ObservableList;
+import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
+import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * Unmodifiable observable list whose elements are the detail observable values
+ * of the master observable values stored in a given list. For every master
+ * observable value in that list, a user-provided factory is used to create the
+ * corresponding detail observable value.
+ *
+ * @since 1.2
+ */
+public class ListDetailObservableList extends ObservableList {
+
+ private IObservableList masterObservableList;
+
+ private IObservableFactory detailFactory;
+
+ private Object detailType;
+
+ private IListChangeListener masterListListener = new IListChangeListener() {
+ public void handleListChange(ListChangeEvent event) {
+ applyMasterListDiff(event.diff);
+ }
+ };
+
+ /**
+ * Constructs a new ListDetailObservableList.
+ *
+ * @param masterObservableList
+ * the list of master observable 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 observable values or
+ * null
+ */
+ public ListDetailObservableList(IObservableList masterObservableList,
+ IObservableFactory detailFactory, Object detailType) {
+ super(masterObservableList.getRealm(), new ArrayList(),
+ IObservableValue.class);
+ this.masterObservableList = masterObservableList;
+ this.detailFactory = detailFactory;
+ this.detailType = detailType;
+
+ masterObservableList.addListChangeListener(masterListListener);
+
+ ListDiff initMasterDiff = Diffs.computeListDiff(Collections.EMPTY_LIST,
+ masterObservableList);
+ applyMasterListDiff(initMasterDiff);
+ }
+
+ private void applyMasterListDiff(ListDiff masterListDiff) {
+ 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();
+
+ IObservableValue detailObservable;
+ if (masterEntry.isAddition()) {
+ // When adding a new master, we must create a new detail.
+ IObservableValue masterObservable = (IObservableValue) masterEntry
+ .getElement();
+ detailObservable = MasterDetailObservables.detailValue(
+ masterObservable, detailFactory, detailType);
+
+ // Perform the actual addition.
+ wrappedList.add(index, detailObservable);
+ } else {
+ // When removing an existing master, we dispose the
+ // corresponding detail.
+ detailObservable = (IObservableValue) wrappedList.get(index);
+ detailObservable.dispose();
+
+ // Perform the actual removal.
+ wrappedList.remove(index);
+ }
+
+ // Create the corresponding diff for the detail list.
+ detailEntries[i] = Diffs.createListDiffEntry(index, masterEntry
+ .isAddition(), detailObservable);
+ }
+
+ // Fire a list change event with the adapted diffs on the detail list.
+ fireListChange(Diffs.createListDiff(detailEntries));
+ }
+
+ public void dispose() {
+ if (masterObservableList != null) {
+ masterObservableList.removeListChangeListener(masterListListener);
+ }
+
+ if (wrappedList != null) {
+ for (Iterator iter = wrappedList.iterator(); iter.hasNext();) {
+ ((IObservableValue) iter.next()).dispose();
+ }
+ }
+
+ masterObservableList = null;
+ masterListListener = null;
+ detailFactory = null;
+ detailType = null;
+
+ super.dispose();
+ }
+}