View | Details | Raw Unified | Return to bug 175737 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/jface/tests/databinding/BindingTestSuite.java (-1 / +3 lines)
Lines 14-20 Link Here
14
 *     Matthew Hall - bugs 210115, 212468, 212223, 206839, 208858, 208322,
14
 *     Matthew Hall - bugs 210115, 212468, 212223, 206839, 208858, 208322,
15
 *                    212518, 215531, 221351, 184830, 213145, 218269, 239015,
15
 *                    212518, 215531, 221351, 184830, 213145, 218269, 239015,
16
 *                    237703
16
 *                    237703
17
 *     Ovidio Mallo - bug 237163
17
 *     Ovidio Mallo - bugs 237163, 175737
18
 *******************************************************************************/
18
 *******************************************************************************/
19
package org.eclipse.jface.tests.databinding;
19
package org.eclipse.jface.tests.databinding;
20
20
Lines 114-119 Link Here
114
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableListTest;
114
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableListTest;
115
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableSetTest;
115
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableSetTest;
116
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableValueTest;
116
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.DetailObservableValueTest;
117
import org.eclipse.core.tests.internal.databinding.observable.masterdetail.ListDetailObservableListTest;
117
import org.eclipse.core.tests.internal.databinding.validation.AbstractStringToNumberValidatorTest;
118
import org.eclipse.core.tests.internal.databinding.validation.AbstractStringToNumberValidatorTest;
118
import org.eclipse.core.tests.internal.databinding.validation.NumberToByteValidatorTest;
119
import org.eclipse.core.tests.internal.databinding.validation.NumberToByteValidatorTest;
119
import org.eclipse.core.tests.internal.databinding.validation.NumberToDoubleValidatorTest;
120
import org.eclipse.core.tests.internal.databinding.validation.NumberToDoubleValidatorTest;
Lines 308-313 Link Here
308
		addTest(DetailObservableListTest.suite());
309
		addTest(DetailObservableListTest.suite());
309
		addTest(DetailObservableSetTest.suite());
310
		addTest(DetailObservableSetTest.suite());
310
		addTest(DetailObservableValueTest.suite());
311
		addTest(DetailObservableValueTest.suite());
312
		addTest(ListDetailObservableListTest.suite());
311
313
312
		// org.eclipse.core.tests.internal.databinding.validation
314
		// org.eclipse.core.tests.internal.databinding.validation
313
		addTestSuite(AbstractStringToNumberValidatorTest.class);
315
		addTestSuite(AbstractStringToNumberValidatorTest.class);
(-)src/org/eclipse/core/tests/internal/databinding/observable/masterdetail/ListDetailObservableListTest.java (+308 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Ovidio Mallo and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Ovidio Mallo - initial API and implementation (bug 175737)
10
 ******************************************************************************/
11
12
package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
13
14
import java.beans.PropertyChangeListener;
15
import java.beans.PropertyChangeSupport;
16
import java.util.ArrayList;
17
import java.util.List;
18
19
import junit.framework.Test;
20
import junit.framework.TestSuite;
21
22
import org.eclipse.core.databinding.beans.BeansObservables;
23
import org.eclipse.core.databinding.observable.IObservable;
24
import org.eclipse.core.databinding.observable.IObservableCollection;
25
import org.eclipse.core.databinding.observable.Realm;
26
import org.eclipse.core.databinding.observable.list.IObservableList;
27
import org.eclipse.core.databinding.observable.list.WritableList;
28
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
29
import org.eclipse.core.databinding.observable.value.IObservableValue;
30
import org.eclipse.core.databinding.observable.value.WritableValue;
31
import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailObservableList;
32
import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
33
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
34
import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
35
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
36
37
/**
38
 * @since 1.2
39
 */
40
public class ListDetailObservableListTest extends AbstractDefaultRealmTestCase {
41
42
	public static Test suite() {
43
		TestSuite suite = new TestSuite(ListDetailObservableListTest.class
44
				.getName());
45
		suite.addTestSuite(ListDetailObservableListTest.class);
46
		suite.addTest(ObservableListContractTest.suite(new Delegate()));
47
		return suite;
48
	}
49
50
	public void testUnmodifiability() {
51
		WritableList masterObservableList = new WritableList();
52
		masterObservableList.add(new WritableValue());
53
		ListDetailObservableList ldol = new ListDetailObservableList(
54
				masterObservableList, BeansObservables.valueFactory("name"),
55
				null);
56
57
		try {
58
			ldol.add(new WritableValue());
59
			fail("ListDetailObservableList must not be modifiable.");
60
		} catch (UnsupportedOperationException e) {
61
			// expected exception
62
		}
63
64
		try {
65
			ldol.remove(new WritableValue());
66
			fail("ListDetailObservableList must not be modifiable.");
67
		} catch (UnsupportedOperationException e) {
68
			// expected exception
69
		}
70
71
		try {
72
			ldol.set(0, new WritableValue());
73
			fail("ListDetailObservableList must not be modifiable.");
74
		} catch (UnsupportedOperationException e) {
75
			// expected exception
76
		}
77
	}
78
79
	public void testDetailValueType() {
80
		WritableList masterObservableList = new WritableList();
81
		masterObservableList.add(new WritableValue(new Person(), Person.class));
82
83
		ListDetailObservableList ldol1 = new ListDetailObservableList(
84
				masterObservableList, BeansObservables.valueFactory("name"),
85
				String.class);
86
		assertSame(String.class, ((IObservableValue) ldol1.get(0))
87
				.getValueType());
88
89
		ListDetailObservableList ldol2 = new ListDetailObservableList(
90
				masterObservableList, BeansObservables.valueFactory("name"),
91
				null);
92
		assertNull(((IObservableValue) ldol2.get(0)).getValueType());
93
	}
94
95
	public void testDetailListElementType() {
96
		ListDetailObservableList ldol = new ListDetailObservableList(
97
				new WritableList(), BeansObservables.valueFactory("name"),
98
				String.class);
99
100
		assertSame(IObservableValue.class, ldol.getElementType());
101
	}
102
103
	public void testMasterListInitiallyNotEmpty() {
104
		WritableList masterObservableList = new WritableList();
105
		masterObservableList.add(new WritableValue(new Person(), Person.class));
106
		ListDetailObservableList ldol = new ListDetailObservableList(
107
				masterObservableList, BeansObservables.valueFactory("name"),
108
				String.class);
109
110
		assertEquals(masterObservableList.size(), ldol.size());
111
	}
112
113
	public void testChangeMaster() {
114
		WritableList masterObservableList = new WritableList();
115
		ListDetailObservableList ldol = new ListDetailObservableList(
116
				masterObservableList, BeansObservables.valueFactory("name"),
117
				String.class);
118
119
		assertTrue(ldol.isEmpty());
120
121
		Person p1 = new Person();
122
		p1.setName("name1");
123
		masterObservableList.add(new WritableValue(p1, Person.class));
124
		assertEquals(masterObservableList.size(), ldol.size());
125
		assertEquals(p1.getName(), ((IObservableValue) ldol.get(0)).getValue());
126
127
		Person p2 = new Person();
128
		p2.setName("name2");
129
		masterObservableList.add(new WritableValue(p2, Person.class));
130
		assertEquals(masterObservableList.size(), ldol.size());
131
		assertEquals(p2.getName(), ((IObservableValue) ldol.get(1)).getValue());
132
133
		masterObservableList.remove(0);
134
		assertEquals(masterObservableList.size(), ldol.size());
135
		assertEquals(p2.getName(), ((IObservableValue) ldol.get(0)).getValue());
136
137
		masterObservableList.remove(0);
138
		assertTrue(ldol.isEmpty());
139
	}
140
141
	public void testChangeDetail() {
142
		WritableList masterObservableList = new WritableList();
143
		ListDetailObservableList ldol = new ListDetailObservableList(
144
				masterObservableList, BeansObservables.valueFactory("name"),
145
				String.class);
146
147
		WritableValue masterObservable = new WritableValue(null, Person.class);
148
		masterObservableList.add(masterObservable);
149
		IObservableValue detailObservable = (IObservableValue) ldol.get(0);
150
151
		// Change the detail attribute explicitly.
152
		Person p1 = new Person();
153
		masterObservable.setValue(p1);
154
		p1.setName("name1");
155
		assertEquals(p1.getName(), detailObservable.getValue());
156
		p1.setName("name2");
157
		assertEquals(p1.getName(), detailObservable.getValue());
158
159
		// Change the detail attribute by changing the master.
160
		Person p2 = new Person();
161
		masterObservable.setValue(p2);
162
		detailObservable.setValue("name3");
163
		assertEquals(p2.getName(), detailObservable.getValue());
164
	}
165
166
	public void testDetailObservableChangeEvent() {
167
		WritableList masterObservableList = new WritableList();
168
		ListDetailObservableList ldol = new ListDetailObservableList(
169
				masterObservableList, BeansObservables.valueFactory("name"),
170
				String.class);
171
172
		Person person = new Person();
173
		masterObservableList.add(new WritableValue(person, Person.class));
174
		IObservableValue detailObservable = (IObservableValue) ldol.get(0);
175
176
		ValueChangeEventTracker changeTracker = ValueChangeEventTracker
177
				.observe(detailObservable);
178
		detailObservable.addValueChangeListener(changeTracker);
179
180
		assertEquals(0, changeTracker.count);
181
182
		person.setName("new name");
183
		assertEquals(1, changeTracker.count);
184
	}
185
186
	public void testMasterNull() {
187
		WritableList masterObservableList = new WritableList();
188
		ListDetailObservableList ldol = new ListDetailObservableList(
189
				masterObservableList, BeansObservables.valueFactory("name"),
190
				String.class);
191
192
		masterObservableList.add(new WritableValue(null, null));
193
		assertNull(((IObservableValue) ldol.get(0)).getValue());
194
	}
195
196
	public void testDetailObservableValuesAreDisposed() {
197
		final List detailObservables = new ArrayList();
198
		IObservableFactory detailValueFactory = new IObservableFactory() {
199
			public IObservable createObservable(Object target) {
200
				TestObservableValue detailObservable = new TestObservableValue();
201
				detailObservables.add(detailObservable);
202
				return detailObservable;
203
			}
204
		};
205
		WritableList masterObservableList = new WritableList();
206
		ListDetailObservableList ldol = new ListDetailObservableList(
207
				masterObservableList, detailValueFactory, null);
208
209
		masterObservableList.add(new WritableValue(new Object(), null));
210
		masterObservableList.add(new WritableValue(new Object(), null));
211
212
		assertEquals(ldol.size(), detailObservables.size());
213
214
		assertFalse(((TestObservableValue) detailObservables.get(0)).disposed);
215
		assertFalse(((TestObservableValue) detailObservables.get(1)).disposed);
216
217
		masterObservableList.remove(1);
218
		assertFalse(((TestObservableValue) detailObservables.get(0)).disposed);
219
		assertTrue(((TestObservableValue) detailObservables.get(1)).disposed);
220
221
		ldol.dispose();
222
		assertTrue(((TestObservableValue) detailObservables.get(0)).disposed);
223
		assertTrue(((TestObservableValue) detailObservables.get(1)).disposed);
224
	}
225
226
	public void testMasterObservableValuesAreNotDisposed() {
227
		WritableList masterObservableList = new WritableList();
228
		new ListDetailObservableList(masterObservableList, BeansObservables
229
				.valueFactory("name"), String.class);
230
231
		TestObservableValue masterObservable = new TestObservableValue();
232
		masterObservableList.add(masterObservable);
233
234
		assertFalse(masterObservable.disposed);
235
236
		masterObservableList.remove(0);
237
		assertFalse(masterObservable.disposed);
238
	}
239
240
	private static class Delegate extends
241
			AbstractObservableCollectionContractDelegate {
242
		public IObservableCollection createObservableCollection(Realm realm,
243
				int elementCount) {
244
			WritableList masterObservableList = new WritableList(realm);
245
			for (int i = 0; i < elementCount; i++) {
246
				masterObservableList.add(new WritableValue(realm, new Person(),
247
						Person.class));
248
			}
249
250
			return new TestListDetailObservableList(masterObservableList,
251
					BeansObservables.valueFactory(realm, "name"), String.class);
252
		}
253
254
		public void change(IObservable observable) {
255
			TestListDetailObservableList ldol = (TestListDetailObservableList) observable;
256
			ldol.masterObservableList.add(new WritableValue(observable
257
					.getRealm(), new Person(), Person.class));
258
		}
259
260
		public Object getElementType(IObservableCollection collection) {
261
			return IObservableValue.class;
262
		}
263
	}
264
265
	private static class TestListDetailObservableList extends
266
			ListDetailObservableList {
267
		final IObservableList masterObservableList;
268
269
		public TestListDetailObservableList(
270
				IObservableList masterObservableList,
271
				IObservableFactory detailValueFactory, Object detailType) {
272
			super(masterObservableList, detailValueFactory, detailType);
273
			this.masterObservableList = masterObservableList;
274
		}
275
	}
276
277
	private static class TestObservableValue extends WritableValue {
278
		boolean disposed = false;
279
280
		public synchronized void dispose() {
281
			disposed = true;
282
			super.dispose();
283
		}
284
	}
285
286
	private static class Person {
287
		private final PropertyChangeSupport pcs = new PropertyChangeSupport(
288
				this);
289
290
		private String name;
291
292
		public String getName() {
293
			return name;
294
		}
295
296
		public void setName(String name) {
297
			pcs.firePropertyChange("name", this.name, this.name = name);
298
		}
299
300
		public void addPropertyChangeListener(PropertyChangeListener listener) {
301
			pcs.addPropertyChangeListener(listener);
302
		}
303
304
		public void removePropertyChangeListener(PropertyChangeListener listener) {
305
			pcs.removePropertyChangeListener(listener);
306
		}
307
	}
308
}
(-)src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.java (+30 lines)
Lines 9-14 Link Here
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Brad Reynolds - bug 147515
10
 *     Brad Reynolds - bug 147515
11
 *     Matthew Hall - bug 221704
11
 *     Matthew Hall - bug 221704
12
 *     Ovidio Mallo - bug 175737
12
 *******************************************************************************/
13
 *******************************************************************************/
13
14
14
package org.eclipse.core.databinding.observable.masterdetail;
15
package org.eclipse.core.databinding.observable.masterdetail;
Lines 21-26 Link Here
21
import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableMap;
22
import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableMap;
22
import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableSet;
23
import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableSet;
23
import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableValue;
24
import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableValue;
25
import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailObservableList;
24
26
25
/**
27
/**
26
 * Allows for the observation of an attribute, the detail, of an observable
28
 * Allows for the observation of an attribute, the detail, of an observable
Lines 116-119 Link Here
116
			IObservableFactory detailFactory) {
118
			IObservableFactory detailFactory) {
117
		return new DetailObservableMap(detailFactory, master);
119
		return new DetailObservableMap(detailFactory, master);
118
	}
120
	}
121
122
	/**
123
	 * Creates an unmodifiable observable list of detail observable values from
124
	 * an observable list of master observable values. For every master
125
	 * observable value in the given list, the provided factory is used to
126
	 * create the corresponding detail observable value. This can e.g. be used
127
	 * to create a list of observable values that represent the same property of
128
	 * a set of selected objects in a table.
129
	 * 
130
	 * @param masterList
131
	 *            the list of master observable values to track
132
	 * @param detailFactory
133
	 *            a factory for creating {@link IObservableValue} instances
134
	 *            given a current value of one of the masters
135
	 * @param detailType
136
	 *            the value type of the detail observable values, typically of
137
	 *            type java.lang.Class and can be <code>null</code>
138
	 * @return a list of observable values of the given value type that, for any
139
	 *         current value of any of the master values, behaves like the
140
	 *         observable value created by the factory for that current value.
141
	 * 
142
	 * @since 1.2
143
	 */
144
	public static IObservableList detailValueList(IObservableList masterList,
145
			IObservableFactory detailFactory, Object detailType) {
146
		return new ListDetailObservableList(masterList, detailFactory,
147
				detailType);
148
	}
119
}
149
}
(-)src/org/eclipse/core/internal/databinding/observable/masterdetail/ListDetailObservableList.java (+131 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Ovidio Mallo and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     Ovidio Mallo - initial API and implementation (bug 175737)
10
 *******************************************************************************/
11
package org.eclipse.core.internal.databinding.observable.masterdetail;
12
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.Iterator;
16
17
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.list.IListChangeListener;
19
import org.eclipse.core.databinding.observable.list.IObservableList;
20
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
21
import org.eclipse.core.databinding.observable.list.ListDiff;
22
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
23
import org.eclipse.core.databinding.observable.list.ObservableList;
24
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
25
import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
26
import org.eclipse.core.databinding.observable.value.IObservableValue;
27
28
/**
29
 * Unmodifiable observable list whose elements are the detail observable values
30
 * of the master observable values stored in a given list. For every master
31
 * observable value in that list, a user-provided factory is used to create the
32
 * corresponding detail observable value.
33
 * 
34
 * @since 1.2
35
 */
36
public class ListDetailObservableList extends ObservableList {
37
38
	private IObservableList masterObservableList;
39
40
	private IObservableFactory detailFactory;
41
42
	private Object detailType;
43
44
	private IListChangeListener masterListListener = new IListChangeListener() {
45
		public void handleListChange(ListChangeEvent event) {
46
			applyMasterListDiff(event.diff);
47
		}
48
	};
49
50
	/**
51
	 * Constructs a new ListDetailObservableList.
52
	 * 
53
	 * @param masterObservableList
54
	 *            the list of master observable values to track
55
	 * @param detailFactory
56
	 *            a factory for creating {@link IObservableValue} instances
57
	 *            given a current value of one of the masters
58
	 * @param detailType
59
	 *            the value type of the detail observable values or
60
	 *            <code>null</code>
61
	 */
62
	public ListDetailObservableList(IObservableList masterObservableList,
63
			IObservableFactory detailFactory, Object detailType) {
64
		super(masterObservableList.getRealm(), new ArrayList(),
65
				IObservableValue.class);
66
		this.masterObservableList = masterObservableList;
67
		this.detailFactory = detailFactory;
68
		this.detailType = detailType;
69
70
		masterObservableList.addListChangeListener(masterListListener);
71
72
		ListDiff initMasterDiff = Diffs.computeListDiff(Collections.EMPTY_LIST,
73
				masterObservableList);
74
		applyMasterListDiff(initMasterDiff);
75
	}
76
77
	private void applyMasterListDiff(ListDiff masterListDiff) {
78
		ListDiffEntry[] masterEntries = masterListDiff.getDifferences();
79
		ListDiffEntry[] detailEntries = new ListDiffEntry[masterEntries.length];
80
		for (int i = 0; i < masterEntries.length; i++) {
81
			ListDiffEntry masterEntry = masterEntries[i];
82
			int index = masterEntry.getPosition();
83
84
			IObservableValue detailObservable;
85
			if (masterEntry.isAddition()) {
86
				// When adding a new master, we must create a new detail.
87
				IObservableValue masterObservable = (IObservableValue) masterEntry
88
						.getElement();
89
				detailObservable = MasterDetailObservables.detailValue(
90
						masterObservable, detailFactory, detailType);
91
92
				// Perform the actual addition.
93
				wrappedList.add(index, detailObservable);
94
			} else {
95
				// When removing an existing master, we dispose the
96
				// corresponding detail.
97
				detailObservable = (IObservableValue) wrappedList.get(index);
98
				detailObservable.dispose();
99
100
				// Perform the actual removal.
101
				wrappedList.remove(index);
102
			}
103
104
			// Create the corresponding diff for the detail list.
105
			detailEntries[i] = Diffs.createListDiffEntry(index, masterEntry
106
					.isAddition(), detailObservable);
107
		}
108
109
		// Fire a list change event with the adapted diffs on the detail list.
110
		fireListChange(Diffs.createListDiff(detailEntries));
111
	}
112
113
	public void dispose() {
114
		if (masterObservableList != null) {
115
			masterObservableList.removeListChangeListener(masterListListener);
116
		}
117
118
		if (wrappedList != null) {
119
			for (Iterator iter = wrappedList.iterator(); iter.hasNext();) {
120
				((IObservableValue) iter.next()).dispose();
121
			}
122
		}
123
124
		masterObservableList = null;
125
		masterListListener = null;
126
		detailFactory = null;
127
		detailType = null;
128
129
		super.dispose();
130
	}
131
}

Return to bug 175737