Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 246103
Collapse All | Expand All

(-)src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.java (-10 / +29 lines)
Lines 8-14 Link Here
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Brad Reynolds - bug 164653
10
 *     Brad Reynolds - bug 164653
11
 *     Matthew Hall - bugs 118516, 146397, 226289
11
 *     Matthew Hall - bugs 118516, 146397, 226289, 246103
12
 *******************************************************************************/
12
 *******************************************************************************/
13
13
14
package org.eclipse.core.databinding.observable.map;
14
package org.eclipse.core.databinding.observable.map;
Lines 38-44 Link Here
38
public abstract class AbstractObservableMap extends AbstractMap implements
38
public abstract class AbstractObservableMap extends AbstractMap implements
39
		IObservableMap {
39
		IObservableMap {
40
40
41
	private ChangeSupport changeSupport;
41
	private final class PrivateChangeSupport extends ChangeSupport {
42
		private PrivateChangeSupport(Realm realm) {
43
			super(realm);
44
		}
45
46
		protected void firstListenerAdded() {
47
			AbstractObservableMap.this.firstListenerAdded();
48
		}
49
50
		protected void lastListenerRemoved() {
51
			AbstractObservableMap.this.lastListenerRemoved();
52
		}
53
54
		protected boolean hasListeners() {
55
			return super.hasListeners();
56
		}
57
	}
58
59
	private PrivateChangeSupport changeSupport;
42
	private boolean disposed = false;
60
	private boolean disposed = false;
43
61
44
	private boolean stale;
62
	private boolean stale;
Lines 66-79 Link Here
66
	 */
84
	 */
67
	public AbstractObservableMap(Realm realm) {
85
	public AbstractObservableMap(Realm realm) {
68
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
86
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
69
		changeSupport = new ChangeSupport(realm){
87
		changeSupport = new PrivateChangeSupport(realm);
70
			protected void firstListenerAdded() {
71
				AbstractObservableMap.this.firstListenerAdded();
72
			}
73
			protected void lastListenerRemoved() {
74
				AbstractObservableMap.this.lastListenerRemoved();
75
			}
76
		};
77
	}
88
	}
78
89
79
	public synchronized void addMapChangeListener(IMapChangeListener listener) {
90
	public synchronized void addMapChangeListener(IMapChangeListener listener) {
Lines 97-102 Link Here
97
	}
108
	}
98
109
99
	/**
110
	/**
111
	 * @return whether the observable map has listeners registered
112
	 * @since 1.2
113
	 */
114
	protected synchronized boolean hasListeners() {
115
		return !disposed && changeSupport.hasListeners();
116
	}
117
118
	/**
100
	 * @since 1.2
119
	 * @since 1.2
101
	 */
120
	 */
102
	public void addDisposeListener(IDisposeListener listener) {
121
	public void addDisposeListener(IDisposeListener listener) {
(-)src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableMapTest.java (-1 / +36 lines)
Lines 7-26 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Matthew Hall - bugs 213145, 241585
10
 *     Matthew Hall - bugs 213145, 241585, 246103
11
 *******************************************************************************/
11
 *******************************************************************************/
12
12
13
package org.eclipse.core.tests.internal.databinding.beans;
13
package org.eclipse.core.tests.internal.databinding.beans;
14
14
15
import java.beans.PropertyDescriptor;
15
import java.beans.PropertyDescriptor;
16
import java.util.Collections;
16
import java.util.HashSet;
17
import java.util.HashSet;
17
18
18
import junit.framework.Test;
19
import junit.framework.Test;
19
import junit.framework.TestCase;
20
import junit.framework.TestCase;
20
import junit.framework.TestSuite;
21
import junit.framework.TestSuite;
21
22
23
import org.eclipse.core.databinding.beans.BeansObservables;
22
import org.eclipse.core.databinding.observable.Realm;
24
import org.eclipse.core.databinding.observable.Realm;
23
import org.eclipse.core.databinding.observable.map.IMapChangeListener;
25
import org.eclipse.core.databinding.observable.map.IMapChangeListener;
26
import org.eclipse.core.databinding.observable.map.IObservableMap;
24
import org.eclipse.core.databinding.observable.map.MapChangeEvent;
27
import org.eclipse.core.databinding.observable.map.MapChangeEvent;
25
import org.eclipse.core.databinding.observable.map.MapDiff;
28
import org.eclipse.core.databinding.observable.map.MapDiff;
26
import org.eclipse.core.databinding.observable.set.WritableSet;
29
import org.eclipse.core.databinding.observable.set.WritableSet;
Lines 28-33 Link Here
28
import org.eclipse.core.tests.databinding.observable.ThreadRealm;
31
import org.eclipse.core.tests.databinding.observable.ThreadRealm;
29
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
32
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
30
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
33
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
34
import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
31
35
32
/**
36
/**
33
 * @since 3.2
37
 * @since 3.2
Lines 166-171 Link Here
166
		assertTrue(bean.hasListeners("value"));
170
		assertTrue(bean.hasListeners("value"));
167
	}
171
	}
168
172
173
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
174
		// The java bean spec allows the old and new values in a
175
		// PropertyChangeEvent to be null, which indicates that an unknown
176
		// change occured.
177
178
		// This test ensures that JavaBeanObservableValue fires the correct
179
		// value diff even if the bean implementor is lazy :-P
180
181
		WritableSet set = new WritableSet(new CurrentRealm(true));
182
183
		Bean bean = new AnnoyingBean();
184
		bean.setValue("old");
185
		set.add(bean);
186
187
		IObservableMap map = BeansObservables.observeMap(set, Bean.class,
188
				"value");
189
		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
190
191
		bean.setValue("new");
192
193
		assertEquals(1, tracker.count);
194
195
		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getAddedKeys());
196
		assertEquals(Collections.singleton(bean), tracker.event.diff
197
				.getChangedKeys());
198
		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getRemovedKeys());
199
200
		assertEquals("old", tracker.event.diff.getOldValue(bean));
201
		assertEquals("new", tracker.event.diff.getNewValue(bean));
202
	}
203
169
	private static class MapChangeListener implements IMapChangeListener {
204
	private static class MapChangeListener implements IMapChangeListener {
170
		int count;
205
		int count;
171
206
(-)src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableValueTest.java (-1 / +19 lines)
Lines 9-15 Link Here
9
 *     Brad Reynolds - initial API and implementation
9
 *     Brad Reynolds - initial API and implementation
10
 *     Brad Reynolds - bug 171616
10
 *     Brad Reynolds - bug 171616
11
 *     Katarzyna Marszalek - test case for bug 198519
11
 *     Katarzyna Marszalek - test case for bug 198519
12
 *     Matthew Hall - bug 213145
12
 *     Matthew Hall - bug 213145, 246103
13
 ******************************************************************************/
13
 ******************************************************************************/
14
14
15
package org.eclipse.core.tests.internal.databinding.beans;
15
package org.eclipse.core.tests.internal.databinding.beans;
Lines 31-36 Link Here
31
import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
31
import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
32
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
32
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
33
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
33
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
34
import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
34
import org.eclipse.jface.examples.databinding.model.SimplePerson;
35
import org.eclipse.jface.examples.databinding.model.SimplePerson;
35
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
36
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
36
37
Lines 121-126 Link Here
121
		assertFalse(bean.hasListeners(propertyName));
122
		assertFalse(bean.hasListeners(propertyName));
122
	}
123
	}
123
	
124
	
125
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
126
		// The java bean spec allows the old and new values in a PropertyChangeEvent to
127
		// be null, which indicates that an unknown change occured.
128
129
		// This test ensures that JavaBeanObservableValue fires the correct value diff
130
		// even if the bean implementor is lazy :-P
131
132
		Bean bean = new AnnoyingBean();
133
		bean.setValue("old");
134
		IObservableValue observable = BeansObservables.observeValue(bean, "value");
135
		ValueChangeEventTracker tracker = ValueChangeEventTracker.observe(observable);
136
		bean.setValue("new");
137
		assertEquals(1, tracker.count);
138
		assertEquals("old", tracker.event.diff.getOldValue());
139
		assertEquals("new", tracker.event.diff.getNewValue());
140
	}
141
124
	public static Test suite() {
142
	public static Test suite() {
125
		TestSuite suite = new TestSuite(JavaBeanObservableValueTest.class.getName());
143
		TestSuite suite = new TestSuite(JavaBeanObservableValueTest.class.getName());
126
		suite.addTestSuite(JavaBeanObservableValueTest.class);
144
		suite.addTestSuite(JavaBeanObservableValueTest.class);
(-)src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedListTest.java (-1 / +28 lines)
Lines 7-13 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Brad Reynolds - initial API and implementation
9
 *     Brad Reynolds - initial API and implementation
10
 *     Matthew Hall - bugs 221351, 213145, 244098
10
 *     Matthew Hall - bugs 221351, 213145, 244098, 246103
11
 ******************************************************************************/
11
 ******************************************************************************/
12
12
13
package org.eclipse.core.tests.internal.databinding.beans;
13
package org.eclipse.core.tests.internal.databinding.beans;
Lines 16-28 Link Here
16
import java.beans.PropertyChangeEvent;
16
import java.beans.PropertyChangeEvent;
17
import java.beans.PropertyChangeListener;
17
import java.beans.PropertyChangeListener;
18
import java.beans.PropertyDescriptor;
18
import java.beans.PropertyDescriptor;
19
import java.util.ArrayList;
19
import java.util.Arrays;
20
import java.util.Arrays;
20
import java.util.Collection;
21
import java.util.Collection;
22
import java.util.Collections;
21
import java.util.List;
23
import java.util.List;
22
24
23
import junit.framework.Test;
25
import junit.framework.Test;
24
import junit.framework.TestSuite;
26
import junit.framework.TestSuite;
25
27
28
import org.eclipse.core.databinding.beans.BeansObservables;
26
import org.eclipse.core.databinding.observable.IObservable;
29
import org.eclipse.core.databinding.observable.IObservable;
27
import org.eclipse.core.databinding.observable.IObservableCollection;
30
import org.eclipse.core.databinding.observable.IObservableCollection;
28
import org.eclipse.core.databinding.observable.Realm;
31
import org.eclipse.core.databinding.observable.Realm;
Lines 32-37 Link Here
32
import org.eclipse.core.internal.databinding.beans.JavaBeanObservableList;
35
import org.eclipse.core.internal.databinding.beans.JavaBeanObservableList;
33
import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
36
import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
34
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
37
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
38
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
35
import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
39
import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
36
import org.eclipse.jface.databinding.swt.SWTObservables;
40
import org.eclipse.jface.databinding.swt.SWTObservables;
37
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
41
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
Lines 446-451 Link Here
446
		assertEquals(1, listener.count);
450
		assertEquals(1, listener.count);
447
	}
451
	}
448
452
453
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
454
		// The java bean spec allows the old and new values in a
455
		// PropertyChangeEvent to be null, which indicates that an unknown
456
		// change occured.
457
458
		// This test ensures that JavaBeanObservableValue fires the correct
459
		// value diff even if the bean implementor is lazy :-P
460
461
		Bean bean = new AnnoyingBean();
462
		bean.setArray(new Object[] { "old" });
463
		IObservableList observable = BeansObservables.observeList(
464
				new CurrentRealm(true), bean, "array");
465
		ListChangeEventTracker tracker = ListChangeEventTracker
466
				.observe(observable);
467
		bean.setArray(new Object[] { "new" });
468
		assertEquals(1, tracker.count);
469
470
		List list = new ArrayList();
471
		list.add("old");
472
		tracker.event.diff.applyTo(list);
473
		assertEquals(Collections.singletonList("new"), list);
474
	}
475
449
	private static void assertEntry(ListDiffEntry entry, boolean addition,
476
	private static void assertEntry(ListDiffEntry entry, boolean addition,
450
			int position, Object element) {
477
			int position, Object element) {
451
		assertEquals("addition", addition, entry.isAddition());
478
		assertEquals("addition", addition, entry.isAddition());
(-)src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableSetTest.java (-2 / +27 lines)
Lines 7-13 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Brad Reynolds - initial API and implementation
9
 *     Brad Reynolds - initial API and implementation
10
 *     Matthew Hall - bugs 221351, 213145, 244098
10
 *     Matthew Hall - bugs 221351, 213145, 244098, 246103
11
 ******************************************************************************/
11
 ******************************************************************************/
12
12
13
package org.eclipse.core.tests.internal.databinding.beans;
13
package org.eclipse.core.tests.internal.databinding.beans;
Lines 15-26 Link Here
15
import java.beans.IntrospectionException;
15
import java.beans.IntrospectionException;
16
import java.beans.PropertyDescriptor;
16
import java.beans.PropertyDescriptor;
17
import java.util.Arrays;
17
import java.util.Arrays;
18
import java.util.Collections;
18
import java.util.HashSet;
19
import java.util.HashSet;
19
20
20
import junit.framework.Test;
21
import junit.framework.Test;
21
import junit.framework.TestCase;
22
import junit.framework.TestCase;
22
import junit.framework.TestSuite;
23
import junit.framework.TestSuite;
23
24
25
import org.eclipse.core.databinding.beans.BeansObservables;
24
import org.eclipse.core.databinding.observable.IObservable;
26
import org.eclipse.core.databinding.observable.IObservable;
25
import org.eclipse.core.databinding.observable.IObservableCollection;
27
import org.eclipse.core.databinding.observable.IObservableCollection;
26
import org.eclipse.core.databinding.observable.Realm;
28
import org.eclipse.core.databinding.observable.Realm;
Lines 32-37 Link Here
32
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
34
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
33
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
35
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
34
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
36
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
37
import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
35
import org.eclipse.jface.databinding.swt.SWTObservables;
38
import org.eclipse.jface.databinding.swt.SWTObservables;
36
import org.eclipse.swt.widgets.Display;
39
import org.eclipse.swt.widgets.Display;
37
40
Lines 105-111 Link Here
105
		ChangeEventTracker.observe(observableSet);
108
		ChangeEventTracker.observe(observableSet);
106
		assertFalse(bean.hasListeners(propertyName));
109
		assertFalse(bean.hasListeners(propertyName));
107
	}
110
	}
108
	
111
112
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
113
		// The java bean spec allows the old and new values in a
114
		// PropertyChangeEvent to be null, which indicates that an unknown
115
		// change occured.
116
117
		// This test ensures that JavaBeanObservableValue fires the correct
118
		// value diff even if the bean implementor is lazy :-P
119
120
		Bean bean = new AnnoyingBean();
121
		bean.setSet(Collections.singleton("old"));
122
		IObservableSet observable = BeansObservables.observeSet(
123
				new CurrentRealm(true), bean, "set");
124
		SetChangeEventTracker tracker = SetChangeEventTracker
125
				.observe(observable);
126
		bean.setSet(Collections.singleton("new"));
127
		assertEquals(1, tracker.count);
128
		assertEquals(Collections.singleton("old"), tracker.event.diff
129
				.getRemovals());
130
		assertEquals(Collections.singleton("new"), tracker.event.diff
131
				.getAdditions());
132
	}
133
109
	static class SetChangeListener implements ISetChangeListener {
134
	static class SetChangeListener implements ISetChangeListener {
110
		int count;
135
		int count;
111
		public void handleSetChange(SetChangeEvent event) {
136
		public void handleSetChange(SetChangeEvent event) {
(-)src/org/eclipse/core/tests/internal/databinding/beans/Bean.java (-5 / +19 lines)
Lines 15-20 Link Here
15
import java.beans.PropertyChangeListener;
15
import java.beans.PropertyChangeListener;
16
import java.beans.PropertyChangeSupport;
16
import java.beans.PropertyChangeSupport;
17
import java.util.List;
17
import java.util.List;
18
import java.util.Map;
18
import java.util.Set;
19
import java.util.Set;
19
20
20
/**
21
/**
Lines 23-34 Link Here
23
 * @since 3.3
24
 * @since 3.3
24
 */
25
 */
25
public class Bean {
26
public class Bean {
26
	/* package */PropertyChangeSupport changeSupport = new PropertyChangeSupport(
27
	protected PropertyChangeSupport changeSupport = new PropertyChangeSupport(
27
			this);
28
			this);
28
	private String value;
29
	protected String value;
29
	private Object[] array;
30
	protected Object[] array;
30
	private List list;
31
	protected List list;
31
	private Set set;
32
	protected Set set;
33
	protected Map map;
32
34
33
	public Bean() {
35
	public Bean() {
34
	}
36
	}
Lines 49-54 Link Here
49
		this.set = set;
51
		this.set = set;
50
	}
52
	}
51
53
54
	public Bean(Map map) {
55
		this.map = map;
56
	}
57
52
	public void addPropertyChangeListener(PropertyChangeListener listener) {
58
	public void addPropertyChangeListener(PropertyChangeListener listener) {
53
		changeSupport.addPropertyChangeListener(listener);
59
		changeSupport.addPropertyChangeListener(listener);
54
	}
60
	}
Lines 91-96 Link Here
91
		changeSupport.firePropertyChange("set", this.set, this.set = set);
97
		changeSupport.firePropertyChange("set", this.set, this.set = set);
92
	}
98
	}
93
99
100
	public Map getMap() {
101
		return map;
102
	}
103
104
	public void setMap(Map map) {
105
		changeSupport.firePropertyChange("map", this.map, this.map = map);
106
	}
107
94
	public boolean hasListeners(String propertyName) {
108
	public boolean hasListeners(String propertyName) {
95
		return changeSupport.hasListeners(propertyName);
109
		return changeSupport.hasListeners(propertyName);
96
	}
110
	}
(-)src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableArrayBasedSetTest.java (-12 / +36 lines)
Lines 8-14 Link Here
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 221351)
9
 *     Matthew Hall - initial API and implementation (bug 221351)
10
 *     Brad Reynolds - through JavaBeanObservableArrayBasedListTest.java
10
 *     Brad Reynolds - through JavaBeanObservableArrayBasedListTest.java
11
 *     Matthew Hall - bug 213145, 244098
11
 *     Matthew Hall - bug 213145, 244098, 246103
12
 ******************************************************************************/
12
 ******************************************************************************/
13
13
14
package org.eclipse.core.tests.internal.databinding.beans;
14
package org.eclipse.core.tests.internal.databinding.beans;
Lines 25-30 Link Here
25
import junit.framework.Test;
25
import junit.framework.Test;
26
import junit.framework.TestSuite;
26
import junit.framework.TestSuite;
27
27
28
import org.eclipse.core.databinding.beans.BeansObservables;
28
import org.eclipse.core.databinding.observable.IObservable;
29
import org.eclipse.core.databinding.observable.IObservable;
29
import org.eclipse.core.databinding.observable.IObservableCollection;
30
import org.eclipse.core.databinding.observable.IObservableCollection;
30
import org.eclipse.core.databinding.observable.Realm;
31
import org.eclipse.core.databinding.observable.Realm;
Lines 33-38 Link Here
33
import org.eclipse.core.internal.databinding.beans.JavaBeanObservableSet;
34
import org.eclipse.core.internal.databinding.beans.JavaBeanObservableSet;
34
import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
35
import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
35
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
36
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
37
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
36
import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
38
import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
37
import org.eclipse.jface.databinding.swt.SWTObservables;
39
import org.eclipse.jface.databinding.swt.SWTObservables;
38
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
40
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
Lines 129-135 Link Here
129
		assertEquals(0, bean.getArray().length);
131
		assertEquals(0, bean.getArray().length);
130
	}
132
	}
131
133
132
	public void testRemoveListChangeEvent() throws Exception {
134
	public void testRemove_SetChangeEvent() throws Exception {
133
		String element = "1";
135
		String element = "1";
134
		set.add(element);
136
		set.add(element);
135
		assertEquals(1, set.size());
137
		assertEquals(1, set.size());
Lines 165-171 Link Here
165
		assertEquals(2, bean.getArray().length);
167
		assertEquals(2, bean.getArray().length);
166
	}
168
	}
167
169
168
	public void testAddAllListChangEvent() throws Exception {
170
	public void testAddAll_SetChangeEvent() throws Exception {
169
		Collection elements = Arrays.asList(new String[] { "1", "2" });
171
		Collection elements = Arrays.asList(new String[] { "1", "2" });
170
		assertEquals(0, set.size());
172
		assertEquals(0, set.size());
171
173
Lines 200-206 Link Here
200
		assertEquals(0, bean.getArray().length);
202
		assertEquals(0, bean.getArray().length);
201
	}
203
	}
202
204
203
	public void testRemoveAllListChangeEvent() throws Exception {
205
	public void testRemoveAll_SetChangeEvent() throws Exception {
204
		Collection elements = Arrays.asList(new String[] { "1", "2" });
206
		Collection elements = Arrays.asList(new String[] { "1", "2" });
205
		set.addAll(elements);
207
		set.addAll(elements);
206
208
Lines 235-241 Link Here
235
		assertTrue(set.containsAll(Arrays.asList(new String[] { "1", "0" })));
237
		assertTrue(set.containsAll(Arrays.asList(new String[] { "1", "0" })));
236
	}
238
	}
237
239
238
	public void testRetainAllListChangeEvent() throws Exception {
240
	public void testRetainAll_SetChangeEvent() throws Exception {
239
		set.addAll(Arrays.asList(new String[] { "0", "1", "2", "3" }));
241
		set.addAll(Arrays.asList(new String[] { "0", "1", "2", "3" }));
240
242
241
		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
243
		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
Lines 261-267 Link Here
261
		});
263
		});
262
	}
264
	}
263
265
264
	public void testListChangeEventFiresWhenNewListIsSet() throws Exception {
266
	public void testSetChangeEventFiresWhenNewSetIsSet() throws Exception {
265
		Bean[] elements = new Bean[] { new Bean(), new Bean() };
267
		Bean[] elements = new Bean[] { new Bean(), new Bean() };
266
268
267
		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
269
		SetChangeEventTracker listener = SetChangeEventTracker.observe(set);
Lines 271-276 Link Here
271
		assertEquals(1, listener.count);
273
		assertEquals(1, listener.count);
272
	}
274
	}
273
275
276
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
277
		// The java bean spec allows the old and new values in a
278
		// PropertyChangeEvent to be null, which indicates that an unknown
279
		// change occured.
280
281
		// This test ensures that JavaBeanObservableValue fires the correct
282
		// value diff even if the bean implementor is lazy :-P
283
284
		Bean bean = new AnnoyingBean();
285
		bean.setArray(new Object[] { "old" });
286
		IObservableSet observable = BeansObservables.observeSet(
287
				new CurrentRealm(true), bean, "array");
288
		SetChangeEventTracker tracker = SetChangeEventTracker
289
				.observe(observable);
290
		bean.setArray(new Object[] { "new" });
291
		assertEquals(1, tracker.count);
292
		assertEquals(Collections.singleton("old"), tracker.event.diff
293
				.getRemovals());
294
		assertEquals(Collections.singleton("new"), tracker.event.diff
295
				.getAdditions());
296
	}
297
274
	private static void assertPropertyChangeEvent(Bean bean, Runnable runnable) {
298
	private static void assertPropertyChangeEvent(Bean bean, Runnable runnable) {
275
		PropertyChangeTracker listener = new PropertyChangeTracker();
299
		PropertyChangeTracker listener = new PropertyChangeTracker();
276
		bean.addPropertyChangeListener(listener);
300
		bean.addPropertyChangeListener(listener);
Lines 286-292 Link Here
286
		assertTrue("old value", Arrays.equals(old, (Object[]) event
310
		assertTrue("old value", Arrays.equals(old, (Object[]) event
287
				.getOldValue()));
311
				.getOldValue()));
288
		assertTrue("new value", Arrays.equals(bean.getArray(), (Object[]) event.getNewValue()));
312
		assertTrue("new value", Arrays.equals(bean.getArray(), (Object[]) event.getNewValue()));
289
		assertFalse("lists are equal", Arrays.equals(bean.getArray(), old));
313
		assertFalse("sets are equal", Arrays.equals(bean.getArray(), old));
290
	}
314
	}
291
315
292
	private static class PropertyChangeTracker implements
316
	private static class PropertyChangeTracker implements
Lines 326-336 Link Here
326
			}
350
			}
327
			Object bean = new Bean(new Object[0]);
351
			Object bean = new Bean(new Object[0]);
328
352
329
			IObservableSet list = new JavaBeanObservableSet(realm, bean,
353
			IObservableSet set = new JavaBeanObservableSet(realm, bean,
330
					propertyDescriptor, String.class);
354
					propertyDescriptor, String.class);
331
			for (int i = 0; i < elementCount; i++)
355
			for (int i = 0; i < elementCount; i++)
332
				list.add(createElement(list));
356
				set.add(createElement(set));
333
			return list;
357
			return set;
334
		}
358
		}
335
359
336
		public Object createElement(IObservableCollection collection) {
360
		public Object createElement(IObservableCollection collection) {
Lines 342-349 Link Here
342
		}
366
		}
343
367
344
		public void change(IObservable observable) {
368
		public void change(IObservable observable) {
345
			IObservableSet list = (IObservableSet) observable;
369
			IObservableSet set = (IObservableSet) observable;
346
			list.add(createElement(list));
370
			set.add(createElement(set));
347
		}
371
		}
348
	}
372
	}
349
}
373
}
(-)src/org/eclipse/core/tests/internal/databinding/beans/JavaBeanObservableListTest.java (-1 / +28 lines)
Lines 7-13 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Brad Reynolds - initial API and implementation
9
 *     Brad Reynolds - initial API and implementation
10
 *     Matthew Hall - bugs 221351, 213145, 244098
10
 *     Matthew Hall - bugs 221351, 213145, 244098, 246103
11
 ******************************************************************************/
11
 ******************************************************************************/
12
12
13
package org.eclipse.core.tests.internal.databinding.beans;
13
package org.eclipse.core.tests.internal.databinding.beans;
Lines 19-29 Link Here
19
import java.util.ArrayList;
19
import java.util.ArrayList;
20
import java.util.Arrays;
20
import java.util.Arrays;
21
import java.util.Collection;
21
import java.util.Collection;
22
import java.util.Collections;
22
import java.util.List;
23
import java.util.List;
23
24
24
import junit.framework.Test;
25
import junit.framework.Test;
25
import junit.framework.TestSuite;
26
import junit.framework.TestSuite;
26
27
28
import org.eclipse.core.databinding.beans.BeansObservables;
27
import org.eclipse.core.databinding.observable.IObservable;
29
import org.eclipse.core.databinding.observable.IObservable;
28
import org.eclipse.core.databinding.observable.IObservableCollection;
30
import org.eclipse.core.databinding.observable.IObservableCollection;
29
import org.eclipse.core.databinding.observable.Realm;
31
import org.eclipse.core.databinding.observable.Realm;
Lines 34-39 Link Here
34
import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
36
import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
35
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
37
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
36
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
38
import org.eclipse.jface.databinding.conformance.util.ChangeEventTracker;
39
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
37
import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
40
import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
38
import org.eclipse.jface.databinding.swt.SWTObservables;
41
import org.eclipse.jface.databinding.swt.SWTObservables;
39
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
42
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
Lines 476-481 Link Here
476
		assertFalse(bean.hasListeners("list"));
479
		assertFalse(bean.hasListeners("list"));
477
	}
480
	}
478
481
482
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
483
		// The java bean spec allows the old and new values in a
484
		// PropertyChangeEvent to be null, which indicates that an unknown
485
		// change occured.
486
487
		// This test ensures that JavaBeanObservableValue fires the correct
488
		// value diff even if the bean implementor is lazy :-P
489
490
		Bean bean = new AnnoyingBean();
491
		bean.setList(Collections.singletonList("old"));
492
		IObservableList observable = BeansObservables.observeList(
493
				new CurrentRealm(true), bean, "list");
494
		ListChangeEventTracker tracker = ListChangeEventTracker
495
				.observe(observable);
496
		bean.setList(Collections.singletonList("new"));
497
498
		assertEquals(1, tracker.count);
499
		
500
		List list = new ArrayList();
501
		list.add("old");
502
		tracker.event.diff.applyTo(list);
503
		assertEquals(Collections.singletonList("new"), list);
504
	}
505
479
	private static void assertEntry(ListDiffEntry entry, boolean addition,
506
	private static void assertEntry(ListDiffEntry entry, boolean addition,
480
			int position, Object element) {
507
			int position, Object element) {
481
		assertEquals("addition", addition, entry.isAddition());
508
		assertEquals("addition", addition, entry.isAddition());
(-)src/org/eclipse/jface/tests/databinding/BindingTestSuite.java (-1 / +3 lines)
Lines 13-19 Link Here
13
 *     Ashley Cambrell - bugs 198903, 198904
13
 *     Ashley Cambrell - bugs 198903, 198904
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, 237718, 222289, 247394, 233306, 247647
16
 *                    237703, 237718, 222289, 247394, 233306, 247647, 246103
17
 *     Ovidio Mallo - bug 237163, bug 235195
17
 *     Ovidio Mallo - bug 237163, bug 235195
18
 *******************************************************************************/
18
 *******************************************************************************/
19
package org.eclipse.jface.tests.databinding;
19
package org.eclipse.jface.tests.databinding;
Lines 80-85 Link Here
80
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableMapTest;
80
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableMapTest;
81
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableSetTest;
81
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableSetTest;
82
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableValueTest;
82
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanObservableValueTest;
83
import org.eclipse.core.tests.internal.databinding.beans.JavaBeanPropertyObservableMapTest;
83
import org.eclipse.core.tests.internal.databinding.beans.ListenerSupportTest;
84
import org.eclipse.core.tests.internal.databinding.beans.ListenerSupportTest;
84
import org.eclipse.core.tests.internal.databinding.conversion.DateConversionSupportTest;
85
import org.eclipse.core.tests.internal.databinding.conversion.DateConversionSupportTest;
85
import org.eclipse.core.tests.internal.databinding.conversion.IdentityConverterTest;
86
import org.eclipse.core.tests.internal.databinding.conversion.IdentityConverterTest;
Lines 303-308 Link Here
303
		addTest(JavaBeanObservableMapTest.suite());
304
		addTest(JavaBeanObservableMapTest.suite());
304
		addTest(JavaBeanObservableSetTest.suite());
305
		addTest(JavaBeanObservableSetTest.suite());
305
		addTest(JavaBeanObservableValueTest.suite());
306
		addTest(JavaBeanObservableValueTest.suite());
307
		addTestSuite(JavaBeanPropertyObservableMapTest.class);
306
		addTestSuite(ListenerSupportTest.class);
308
		addTestSuite(ListenerSupportTest.class);
307
		
309
		
308
		//org.eclipse.core.tests.internal.databinding.observable
310
		//org.eclipse.core.tests.internal.databinding.observable
(-)src/org/eclipse/core/tests/internal/databinding/beans/AnnoyingBean.java (+106 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall 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
 *     Matthew Hall - initial API and implementation (bug 246103)
10
 ******************************************************************************/
11
12
package org.eclipse.core.tests.internal.databinding.beans;
13
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17
18
/**
19
 * A bean in which all property change events are fired according to an annoying
20
 * provision in the bean spec, where <code>(oldValue == null && newValue ==
21
 * null)</code> indicates that an unknown change occured.
22
 * 
23
 * @since 3.2
24
 */
25
public class AnnoyingBean extends Bean {
26
	public void setValue(String value) {
27
		this.value = value;
28
		changeSupport.firePropertyChange("value", null, null);
29
	}
30
31
	public void setArray(Object[] array) {
32
		this.array = array;
33
		changeSupport.firePropertyChange("array", null, null);
34
	}
35
36
	public void setList(List list) {
37
		this.list = list;
38
		changeSupport.firePropertyChange("list", null, null);
39
	}
40
41
	public void setSet(Set set) {
42
		this.set = set;
43
		changeSupport.firePropertyChange("set", null, null);
44
	}
45
46
	public void setMap(Map map) {
47
		this.map = map;
48
		changeSupport.firePropertyChange("map", null, null);
49
	}
50
}
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 Matthew Hall 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
 *     Matthew Hall - initial API and implementation (bug 246103)
10
 ******************************************************************************/
11
12
package org.eclipse.core.tests.internal.databinding.beans;
13
14
import java.util.Collections;
15
16
import org.eclipse.core.databinding.beans.BeansObservables;
17
import org.eclipse.core.databinding.observable.map.IObservableMap;
18
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
19
import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
20
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
21
22
/**
23
 * @since 3.2
24
 * 
25
 */
26
public class JavaBeanPropertyObservableMapTest extends
27
		AbstractDefaultRealmTestCase {
28
	public void testSetBeanProperty_CorrectForNullOldAndNewValues() {
29
		// The java bean spec allows the old and new values in a
30
		// PropertyChangeEvent to be null, which indicates that an unknown
31
		// change occured.
32
33
		// This test ensures that JavaBeanObservableValue fires the correct
34
		// value diff even if the bean implementor is lazy :-P
35
36
		Bean bean = new AnnoyingBean();
37
		bean.setMap(Collections.singletonMap("key", "old"));
38
39
		IObservableMap map = BeansObservables.observeMap(
40
				new CurrentRealm(true), bean, "map");
41
		MapChangeEventTracker tracker = MapChangeEventTracker.observe(map);
42
43
		bean.setMap(Collections.singletonMap("key", "new"));
44
45
		assertEquals(1, tracker.count);
46
47
		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getAddedKeys());
48
		assertEquals(Collections.singleton("key"), tracker.event.diff
49
				.getChangedKeys());
50
		assertEquals(Collections.EMPTY_SET, tracker.event.diff.getRemovedKeys());
51
52
		assertEquals("old", tracker.event.diff.getOldValue("key"));
53
		assertEquals("new", tracker.event.diff.getNewValue("key"));
54
	}
55
56
}
(-)src/org/eclipse/core/internal/databinding/beans/JavaBeanObservableMap.java (-7 / +59 lines)
Lines 8-14 Link Here
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Brad Reynolds - bug 171616
10
 *     Brad Reynolds - bug 171616
11
 *     Matthew hall - bugs 223164, 241585, 226289
11
 *     Matthew hall - bugs 223164, 241585, 226289, 246103
12
 *******************************************************************************/
12
 *******************************************************************************/
13
13
14
package org.eclipse.core.internal.databinding.beans;
14
package org.eclipse.core.internal.databinding.beans;
Lines 16-27 Link Here
16
import java.beans.PropertyChangeListener;
16
import java.beans.PropertyChangeListener;
17
import java.beans.PropertyDescriptor;
17
import java.beans.PropertyDescriptor;
18
import java.lang.reflect.Method;
18
import java.lang.reflect.Method;
19
import java.util.HashMap;
20
import java.util.Map;
19
21
20
import org.eclipse.core.databinding.beans.IBeanObservable;
22
import org.eclipse.core.databinding.beans.IBeanObservable;
21
import org.eclipse.core.databinding.observable.Diffs;
23
import org.eclipse.core.databinding.observable.Diffs;
22
import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
24
import org.eclipse.core.databinding.observable.map.ComputedObservableMap;
23
import org.eclipse.core.databinding.observable.set.IObservableSet;
25
import org.eclipse.core.databinding.observable.set.IObservableSet;
24
import org.eclipse.core.databinding.util.Policy;
26
import org.eclipse.core.databinding.util.Policy;
27
import org.eclipse.core.internal.databinding.Util;
25
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.core.runtime.IStatus;
26
import org.eclipse.core.runtime.Status;
29
import org.eclipse.core.runtime.Status;
27
30
Lines 39-47 Link Here
39
			if (!updating) {
42
			if (!updating) {
40
				getRealm().exec(new Runnable() {
43
				getRealm().exec(new Runnable() {
41
					public void run() {
44
					public void run() {
42
						fireMapChange(Diffs.createMapDiffSingleChange(
45
						Object source = event.getSource();
43
								event.getSource(), event.getOldValue(), event
46
						Object oldValue = event.getOldValue();
44
								.getNewValue()));
47
						Object newValue = event.getNewValue();
48
						if (oldValue == null && newValue == null) {
49
							oldValue = cachedValues.get(new IdentityWrapper(
50
									source));
51
							newValue = doGet(source);
52
						}
53
						cachedValues.put(new IdentityWrapper(source), newValue);
54
						if (!Util.equals(oldValue, newValue)) {
55
							fireMapChange(Diffs.createMapDiffSingleChange(
56
									source, oldValue, newValue));
57
						}
45
					}
58
					}
46
				});
59
				});
47
			}
60
			}
Lines 54-59 Link Here
54
67
55
	private boolean attachListeners;
68
	private boolean attachListeners;
56
69
70
	// Applicable only while hasListeners() == true
71
	private Map cachedValues;
72
57
	/**
73
	/**
58
	 * @param domain
74
	 * @param domain
59
	 * @param propertyDescriptor
75
	 * @param propertyDescriptor
Lines 80-93 Link Here
80
		}
96
		}
81
	}
97
	}
82
98
99
	protected void firstListenerAdded() {
100
		if (attachListeners) {
101
			cachedValues = new HashMap();
102
		}
103
		super.firstListenerAdded();
104
	}
105
106
	protected void lastListenerRemoved() {
107
		super.lastListenerRemoved();
108
		if (attachListeners) {
109
			cachedValues = null;
110
		}
111
	}
112
83
	protected void hookListener(Object domainElement) {
113
	protected void hookListener(Object domainElement) {
84
		if (attachListeners && domainElement != null) {
114
		if (attachListeners && domainElement != null) {
85
			listenerSupport.hookListener(domainElement);
115
			listenerSupport.hookListener(domainElement);
116
			cachedValues.put(new IdentityWrapper(domainElement),
117
					doGet(domainElement));
86
		}
118
		}
87
	}
119
	}
88
120
89
	protected void unhookListener(Object domainElement) {
121
	protected void unhookListener(Object domainElement) {
90
		if (attachListeners && domainElement != null) {
122
		if (attachListeners && domainElement != null) {
123
			cachedValues.remove(new IdentityWrapper(domainElement));
91
			listenerSupport.unhookListener(domainElement);
124
			listenerSupport.unhookListener(domainElement);
92
		}
125
		}
93
	}
126
	}
Lines 113-121 Link Here
113
	protected Object doPut(Object key, Object value) {
146
	protected Object doPut(Object key, Object value) {
114
		try {
147
		try {
115
			Object oldValue = get(key);
148
			Object oldValue = get(key);
116
			propertyDescriptor.getWriteMethod().invoke(key,
149
			if (!Util.equals(oldValue, value)) {
117
					new Object[] { value });
150
				Method writeMethod = propertyDescriptor.getWriteMethod();
118
			keySet().add(key);
151
				if (!writeMethod.isAccessible()) {
152
					writeMethod.setAccessible(true);
153
				}
154
				writeMethod.invoke(key, new Object[] { value });
155
			}
156
157
			if (hasListeners()) {
158
				// oldValue contains the live value which may be different from
159
				// the cached value if the bean does not have listener API or
160
				// does not fire events properly. For consistency we want to
161
				// provide the cached value as the old value, rather than the
162
				// live value so that consumers that hook/unhook listeners can
163
				// do so without maintaining caches of their own.
164
				Object newValue = doGet(key);
165
				oldValue = cachedValues.put(new IdentityWrapper(key), newValue);
166
167
				if (!Util.equals(oldValue, newValue)) {
168
					fireSingleChange(key, oldValue, newValue);
169
				}
170
			}
119
			return oldValue;
171
			return oldValue;
120
		} catch (Exception e) {
172
		} catch (Exception e) {
121
			Policy.getLog().log(
173
			Policy.getLog().log(
(-)src/org/eclipse/core/internal/databinding/beans/JavaBeanPropertyObservableMap.java (-12 / +22 lines)
Lines 7-13 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 221704)
9
 *     Matthew Hall - initial API and implementation (bug 221704)
10
 *     Matthew Hall - bug 223164, 226289, 244098
10
 *     Matthew Hall - bug 223164, 226289, 244098, 246103
11
 *******************************************************************************/
11
 *******************************************************************************/
12
12
13
package org.eclipse.core.internal.databinding.beans;
13
package org.eclipse.core.internal.databinding.beans;
Lines 84-94 Link Here
84
					if (!updating) {
84
					if (!updating) {
85
						getRealm().exec(new Runnable() {
85
						getRealm().exec(new Runnable() {
86
							public void run() {
86
							public void run() {
87
								Map oldValue = wrappedMap;
87
								Map oldMap = (Map) event.getOldValue();
88
								Map newValue = (Map) event.getNewValue();
88
								Map newMap = (Map) event.getNewValue();
89
								wrappedMap = new HashMap(newValue);
89
								if (oldMap == null && newMap == null) {
90
								
90
									oldMap = wrappedMap;
91
								fireMapChange(Diffs.computeMapDiff(oldValue, newValue));
91
									newMap = getMap();
92
								}
93
94
								if (!Util.equals(oldMap, newMap)) {
95
									wrappedMap = new HashMap(newMap);
96
									fireMapChange(Diffs.computeMapDiff(oldMap,
97
											newMap));
98
								}
92
							}
99
							}
93
						});
100
						});
94
					}
101
					}
Lines 161-170 Link Here
161
		checkRealm();
168
		checkRealm();
162
		updating = true;
169
		updating = true;
163
		try {
170
		try {
171
			boolean add = !wrappedMap.containsKey(key);
164
			Object result = wrappedMap.put(key, value);
172
			Object result = wrappedMap.put(key, value);
165
			if (!Util.equals(result, value)) {
173
			if (!Util.equals(result, value)) {
166
				setMap();
174
				setMap();
167
				if (result == null) {
175
				if (add) {
168
					fireMapChange(Diffs.createMapDiffSingleAdd(key, value));
176
					fireMapChange(Diffs.createMapDiffSingleAdd(key, value));
169
				} else {
177
				} else {
170
					fireMapChange(Diffs.createMapDiffSingleChange(key, result,
178
					fireMapChange(Diffs.createMapDiffSingleChange(key, result,
Lines 187-194 Link Here
187
				Map.Entry entry = (Entry) it.next();
195
				Map.Entry entry = (Entry) it.next();
188
				Object key = entry.getKey();
196
				Object key = entry.getKey();
189
				Object newValue = entry.getValue();
197
				Object newValue = entry.getValue();
198
				boolean add = !wrappedMap.containsKey(key);
190
				Object oldValue = wrappedMap.put(key, newValue);
199
				Object oldValue = wrappedMap.put(key, newValue);
191
				if (oldValue == null) {
200
				if (add) {
192
					addedKeys.add(key);
201
					addedKeys.add(key);
193
				} else if (!Util.equals(oldValue, newValue)) {
202
				} else if (!Util.equals(oldValue, newValue)) {
194
					changes.put(key, oldValue);
203
					changes.put(key, oldValue);
Lines 207-219 Link Here
207
216
208
	public Object remove(Object key) {
217
	public Object remove(Object key) {
209
		checkRealm();
218
		checkRealm();
219
		if (!wrappedMap.containsKey(key)) {
220
			return null;
221
		}
210
		updating = true;
222
		updating = true;
211
		try {
223
		try {
212
			Object result = wrappedMap.remove(key);
224
			Object result = wrappedMap.remove(key);
213
			if (result!=null) {
225
			setMap();
214
				setMap();
226
			fireMapChange(Diffs.createMapDiffSingleRemove(key, result));
215
				fireMapChange(Diffs.createMapDiffSingleRemove(key, result));
216
			}
217
			return result;
227
			return result;
218
		} finally {
228
		} finally {
219
			updating = false;
229
			updating = false;
(-)src/org/eclipse/core/internal/databinding/beans/JavaBeanObservableValue.java (-23 / +55 lines)
Lines 9-14 Link Here
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Brad Reynolds - bug 164653
10
 *     Brad Reynolds - bug 164653
11
 *     Brad Reynolds - bug 164134, 171616
11
 *     Brad Reynolds - bug 164134, 171616
12
 *     Matthew Hall - bug 246103
12
 *******************************************************************************/
13
 *******************************************************************************/
13
package org.eclipse.core.internal.databinding.beans;
14
package org.eclipse.core.internal.databinding.beans;
14
15
Lines 23-29 Link Here
23
import org.eclipse.core.databinding.observable.Diffs;
24
import org.eclipse.core.databinding.observable.Diffs;
24
import org.eclipse.core.databinding.observable.Realm;
25
import org.eclipse.core.databinding.observable.Realm;
25
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
26
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
26
import org.eclipse.core.databinding.observable.value.ValueDiff;
27
import org.eclipse.core.databinding.util.Policy;
27
import org.eclipse.core.databinding.util.Policy;
28
import org.eclipse.core.internal.databinding.Util;
28
import org.eclipse.core.internal.databinding.Util;
29
import org.eclipse.core.runtime.IStatus;
29
import org.eclipse.core.runtime.IStatus;
Lines 42-47 Link Here
42
42
43
	private boolean attachListeners;
43
	private boolean attachListeners;
44
44
45
	// Applicable only while hasListeners() == true
46
	private Object cachedValue;
47
45
	/**
48
	/**
46
	 * @param realm
49
	 * @param realm
47
	 * @param object
50
	 * @param object
Lines 70-94 Link Here
70
		if (!attachListeners) {
73
		if (!attachListeners) {
71
			return;
74
			return;
72
		}
75
		}
73
			
76
74
		PropertyChangeListener listener = new PropertyChangeListener() {
75
			public void propertyChange(java.beans.PropertyChangeEvent event) {
76
				if (!updating) {
77
					final ValueDiff diff = Diffs.createValueDiff(event.getOldValue(),
78
											event.getNewValue());
79
					getRealm().exec(new Runnable(){
80
						public void run() {
81
							fireValueChange(diff);
82
						}});
83
				}
84
			}
85
		};
86
		
87
		if (listenerSupport == null) {
77
		if (listenerSupport == null) {
88
			listenerSupport = new ListenerSupport(listener, propertyDescriptor.getName());
78
			PropertyChangeListener listener = new PropertyChangeListener() {
79
				public void propertyChange(
80
						final java.beans.PropertyChangeEvent event) {
81
					if (!updating) {
82
						getRealm().exec(new Runnable() {
83
							public void run() {
84
								Object oldValue = event.getOldValue();
85
								Object newValue = event.getNewValue();
86
								if (oldValue == null && newValue == null) {
87
									// this condition is provided for in the
88
									// bean spec,
89
									// and indicates that an unknown change
90
									// occured.
91
92
									oldValue = cachedValue;
93
									newValue = doGetValue();
94
								}
95
								cachedValue = newValue;
96
								if (!Util.equals(oldValue, newValue)) {
97
									fireValueChange(Diffs.createValueDiff(
98
											oldValue, newValue));
99
								}
100
							}
101
						});
102
					}
103
				}
104
			};
105
			listenerSupport = new ListenerSupport(listener, propertyDescriptor
106
					.getName());
89
		}
107
		}
90
		
108
		
91
		listenerSupport.hookListener(object);
109
		listenerSupport.hookListener(object);
110
		cachedValue = doGetValue();
92
	}
111
	}
93
112
94
	public void doSetValue(Object value) {
113
	public void doSetValue(Object value) {
Lines 96-111 Link Here
96
		try {
115
		try {
97
			Object oldValue = doGetValue();
116
			Object oldValue = doGetValue();
98
			
117
			
99
			if (Util.equals(oldValue, value)) {
118
			if (!Util.equals(oldValue, value)) {
100
				return;
119
				Method writeMethod = propertyDescriptor.getWriteMethod();
120
				if (!writeMethod.isAccessible()) {
121
					writeMethod.setAccessible(true);
122
				}
123
				writeMethod.invoke(object, new Object[] { value });
101
			}
124
			}
102
			
125
			
103
			Method writeMethod = propertyDescriptor.getWriteMethod();
126
			if (hasListeners()) {
104
			if (!writeMethod.isAccessible()) {
127
				// oldValue contains the live value which may be different from
105
				writeMethod.setAccessible(true);
128
				// the cached value if the bean does not have listener API or
129
				// does not fire events properly. For consistency we want to
130
				// provide the cached value as the old value, rather than the
131
				// live value so that consumers that hook/unhook listeners can
132
				// do so without maintaining caches of their own.
133
				oldValue = cachedValue;
134
				cachedValue = doGetValue();
135
				if (!Util.equals(oldValue, cachedValue)) {
136
					fireValueChange(Diffs
137
							.createValueDiff(oldValue, cachedValue));
138
				}
106
			}
139
			}
107
			writeMethod.invoke(object, new Object[] { value });
108
			fireValueChange(Diffs.createValueDiff(oldValue, doGetValue()));
109
		} catch (InvocationTargetException e) {
140
		} catch (InvocationTargetException e) {
110
			/*
141
			/*
111
			 * InvocationTargetException wraps any exception thrown by the
142
			 * InvocationTargetException wraps any exception thrown by the
Lines 165-170 Link Here
165
	}
196
	}
166
197
167
	private void unhookListener() {
198
	private void unhookListener() {
199
		cachedValue = null;
168
		if (listenerSupport != null) {
200
		if (listenerSupport != null) {
169
			listenerSupport.dispose();
201
			listenerSupport.dispose();
170
			listenerSupport = null;
202
			listenerSupport = null;

Return to bug 246103