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

Collapse All | Expand All

(-)src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableValueTest.java (-4 / +141 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
11
 ******************************************************************************/
11
12
12
package org.eclipse.core.tests.internal.databinding.observable;
13
package org.eclipse.core.tests.internal.databinding.observable;
Lines 16-21 Link Here
16
17
17
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.IObservable;
19
import org.eclipse.core.databinding.observable.IObservable;
20
import org.eclipse.core.databinding.observable.ObservableTracker;
19
import org.eclipse.core.databinding.observable.Realm;
21
import org.eclipse.core.databinding.observable.Realm;
20
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
22
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
21
import org.eclipse.core.databinding.observable.value.IObservableValue;
23
import org.eclipse.core.databinding.observable.value.IObservableValue;
Lines 26-31 Link Here
26
import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
28
import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
27
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
29
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
28
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
30
import org.eclipse.jface.databinding.conformance.util.CurrentRealm;
31
import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
29
import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
32
import org.eclipse.jface.databinding.conformance.util.ValueChangeEventTracker;
30
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
33
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
31
34
Lines 36-42 Link Here
36
public class ValidatedObservableValueTest extends AbstractDefaultRealmTestCase {
39
public class ValidatedObservableValueTest extends AbstractDefaultRealmTestCase {
37
	private ValidatedObservableValue validated;
40
	private ValidatedObservableValue validated;
38
	private ObservableValueStub target;
41
	private ObservableValueStub target;
39
	private IObservableValue validationStatus;
42
	private ValidationObservableValue validationStatus;
40
43
41
	private Object oldValue;
44
	private Object oldValue;
42
	private Object newValue;
45
	private Object newValue;
Lines 47-54 Link Here
47
		newValue = new Object();
50
		newValue = new Object();
48
		target = new ObservableValueStub(Realm.getDefault());
51
		target = new ObservableValueStub(Realm.getDefault());
49
		target.setValue(oldValue);
52
		target.setValue(oldValue);
50
		validationStatus = new WritableValue(ValidationStatus.ok(),
53
		validationStatus = new ValidationObservableValue();
51
				IStatus.class);
52
		validated = new ValidatedObservableValue(target, validationStatus);
54
		validated = new ValidatedObservableValue(target, validationStatus);
53
	}
55
	}
54
56
Lines 56-62 Link Here
56
		CurrentRealm realm1 = new CurrentRealm(true);
58
		CurrentRealm realm1 = new CurrentRealm(true);
57
		CurrentRealm realm2 = new CurrentRealm(true);
59
		CurrentRealm realm2 = new CurrentRealm(true);
58
		target = new ObservableValueStub(realm1);
60
		target = new ObservableValueStub(realm1);
59
		validationStatus = new WritableValue(realm2);
61
		validationStatus = new ValidationObservableValue(realm2);
60
		try {
62
		try {
61
			new ValidatedObservableValue(target, validationStatus);
63
			new ValidatedObservableValue(target, validationStatus);
62
			fail("Expected exception--target and validation status should have the same realm");
64
			fail("Expected exception--target and validation status should have the same realm");
Lines 65-80 Link Here
65
	}
67
	}
66
68
67
	public void testIsStale_WhenTargetIsStale() {
69
	public void testIsStale_WhenTargetIsStale() {
70
		ValueChangeEventTracker validatedValueTracker = ValueChangeEventTracker
71
				.observe(validated);
72
		StaleEventTracker validatedStaleTracker = StaleEventTracker
73
				.observe(validated);
74
68
		assertFalse(target.isStale());
75
		assertFalse(target.isStale());
69
		assertFalse(validated.isStale());
76
		assertFalse(validated.isStale());
77
		assertEquals(0, validatedValueTracker.count);
78
		assertEquals(0, validatedStaleTracker.count);
70
79
71
		target.fireStale();
80
		target.fireStale();
72
81
73
		assertTrue(target.isStale());
82
		assertTrue(target.isStale());
74
		assertTrue(validated.isStale());
83
		assertTrue(validated.isStale());
84
		assertEquals(0, validatedValueTracker.count);
85
		assertEquals(1, validatedStaleTracker.count); // +1
86
87
		// Set the same value on the target again. Since we are stale, this
88
		// should trigger a value change event to become unstale.
89
		target.setValue(target.getValue());
90
91
		assertFalse(target.isStale());
92
		assertFalse(validated.isStale());
93
		assertEquals(1, validatedValueTracker.count); // +1
94
		assertEquals(1, validatedStaleTracker.count);
75
	}
95
	}
76
96
77
	public void testIsStale_WhileChangesPending() {
97
	public void testIsStale_WhileChangesPending() {
98
		ValueChangeEventTracker validatedValueTracker = ValueChangeEventTracker
99
				.observe(validated);
100
		StaleEventTracker validatedStaleTracker = StaleEventTracker
101
				.observe(validated);
102
78
		assertFalse(target.isStale());
103
		assertFalse(target.isStale());
79
		assertFalse(validated.isStale());
104
		assertFalse(validated.isStale());
80
105
Lines 84-98 Link Here
84
		// value but the validation status is not OK.
109
		// value but the validation status is not OK.
85
		assertFalse(target.isStale());
110
		assertFalse(target.isStale());
86
		assertFalse(validated.isStale());
111
		assertFalse(validated.isStale());
112
		assertEquals(0, validatedValueTracker.count);
113
		assertEquals(0, validatedStaleTracker.count);
87
114
88
		target.setValue(newValue);
115
		target.setValue(newValue);
89
116
90
		assertFalse(target.isStale());
117
		assertFalse(target.isStale());
91
		assertTrue(validated.isStale());
118
		assertTrue(validated.isStale());
119
		assertEquals(0, validatedValueTracker.count);
120
		assertEquals(1, validatedStaleTracker.count); // +1
92
121
93
		validationStatus.setValue(ValidationStatus.ok());
122
		validationStatus.setValue(ValidationStatus.ok());
94
123
95
		assertFalse(validated.isStale());
124
		assertFalse(validated.isStale());
125
		assertEquals(1, validatedValueTracker.count); // +1
126
		assertEquals(1, validatedStaleTracker.count);
127
	}
128
129
	public void testIsStale_WhenStatusIsStale() {
130
		ValueChangeEventTracker validatedValueTracker = ValueChangeEventTracker
131
				.observe(validated);
132
		StaleEventTracker validatedStaleTracker = StaleEventTracker
133
				.observe(validated);
134
135
		assertFalse(validationStatus.isStale());
136
		assertFalse(validated.isStale());
137
		assertEquals(0, validatedValueTracker.count);
138
		assertEquals(0, validatedStaleTracker.count);
139
140
		// Become stale and check that we receive a stale event.
141
		validationStatus.setStale(true);
142
143
		assertTrue(validationStatus.isStale());
144
		assertTrue(validated.isStale());
145
		assertEquals(0, validatedValueTracker.count);
146
		assertEquals(1, validatedStaleTracker.count); // +1
147
148
		// Become unstale again and check that we receive a change event.
149
		validationStatus.setStale(false);
150
151
		assertFalse(validationStatus.isStale());
152
		assertFalse(validated.isStale());
153
		assertEquals(1, validatedValueTracker.count); // +1
154
		assertEquals(1, validatedStaleTracker.count);
155
	}
156
157
	public void testEvents_WhenStatusIsStale() {
158
		ValueChangeEventTracker validatedValueTracker = ValueChangeEventTracker
159
				.observe(validated);
160
		StaleEventTracker validatedStaleTracker = StaleEventTracker
161
				.observe(validated);
162
163
		assertFalse(validationStatus.isStale());
164
		assertFalse(validated.isStale());
165
		assertEquals(0, validatedValueTracker.count);
166
		assertEquals(0, validatedStaleTracker.count);
167
168
		// Become stale and check that we receive a stale event.
169
		validationStatus.setStale(true);
170
171
		assertTrue(validationStatus.isStale());
172
		assertTrue(validated.isStale());
173
		assertEquals(0, validatedValueTracker.count);
174
		assertEquals(1, validatedStaleTracker.count); // +1
175
176
		// Changes to the target observable should not be propagated while the
177
		// validation is stale.
178
		target.setValue(new Object());
179
180
		assertEquals(0, validatedValueTracker.count);
181
		assertEquals(1, validatedStaleTracker.count);
182
183
		// Changes to the validated observable should be propagated while the
184
		// validation is stale but we remain stale.
185
		validated.setValue(new Object());
186
187
		assertTrue(validated.isStale());
188
		assertEquals(1, validatedValueTracker.count); // +1
189
		assertEquals(1, validatedStaleTracker.count);
190
191
		// Finally, become unstale and check that we receive a change event.
192
		validationStatus.setStale(false);
193
194
		assertFalse(validationStatus.isStale());
195
		assertFalse(validated.isStale());
196
		assertEquals(2, validatedValueTracker.count); // +1
197
		assertEquals(1, validatedStaleTracker.count);
96
	}
198
	}
97
199
98
	public void testGetValueType_SameAsTarget() {
200
	public void testGetValueType_SameAsTarget() {
Lines 212-221 Link Here
212
		assertTrue(target.isStale());
314
		assertTrue(target.isStale());
213
		assertTrue(validated.isStale());
315
		assertTrue(validated.isStale());
214
316
317
		// Set a new value on the target.
215
		target.setValue(newValue);
318
		target.setValue(newValue);
216
319
217
		assertTrue(target.isStale());
320
		assertTrue(target.isStale());
218
		assertTrue(validated.isStale());
321
		assertTrue(validated.isStale());
322
323
		// Set a new value on the validated observable.
324
		validated.setValue(new Object());
325
326
		assertTrue(target.isStale());
327
		assertTrue(validated.isStale());
219
	}
328
	}
220
329
221
	static class ObservableValueStub extends AbstractObservableValue {
330
	static class ObservableValueStub extends AbstractObservableValue {
Lines 302-305 Link Here
302
		}
411
		}
303
	}
412
	}
304
413
414
	private static class ValidationObservableValue extends WritableValue {
415
		private boolean stale = false;
416
417
		public ValidationObservableValue() {
418
			this(Realm.getDefault());
419
		}
420
421
		public ValidationObservableValue(Realm realm) {
422
			super(realm, ValidationStatus.ok(), IStatus.class);
423
		}
424
425
		public boolean isStale() {
426
			ObservableTracker.getterCalled(this);
427
			return stale;
428
		}
429
430
		public void setStale(boolean stale) {
431
			if (this.stale != stale) {
432
				this.stale = stale;
433
				if (stale) {
434
					fireStale();
435
				} else {
436
					fireValueChange(Diffs.createValueDiff(doGetValue(),
437
							doGetValue()));
438
				}
439
			}
440
		}
441
	}
305
}
442
}
(-)src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableSetTest.java (-1 / +119 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
11
 ******************************************************************************/
11
12
12
package org.eclipse.core.tests.internal.databinding.observable;
13
package org.eclipse.core.tests.internal.databinding.observable;
Lines 14-22 Link Here
14
import java.util.Collections;
15
import java.util.Collections;
15
16
16
import junit.framework.Test;
17
import junit.framework.Test;
18
import junit.framework.TestSuite;
17
19
20
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.IObservable;
21
import org.eclipse.core.databinding.observable.IObservable;
19
import org.eclipse.core.databinding.observable.IObservableCollection;
22
import org.eclipse.core.databinding.observable.IObservableCollection;
23
import org.eclipse.core.databinding.observable.ObservableTracker;
20
import org.eclipse.core.databinding.observable.Realm;
24
import org.eclipse.core.databinding.observable.Realm;
21
import org.eclipse.core.databinding.observable.set.IObservableSet;
25
import org.eclipse.core.databinding.observable.set.IObservableSet;
22
import org.eclipse.core.databinding.observable.set.WritableSet;
26
import org.eclipse.core.databinding.observable.set.WritableSet;
Lines 27-37 Link Here
27
import org.eclipse.core.runtime.IStatus;
31
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
32
import org.eclipse.jface.databinding.conformance.MutableObservableSetContractTest;
29
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
33
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
34
import org.eclipse.jface.databinding.conformance.util.SetChangeEventTracker;
35
import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
30
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
36
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
31
37
32
public class ValidatedObservableSetTest extends AbstractDefaultRealmTestCase {
38
public class ValidatedObservableSetTest extends AbstractDefaultRealmTestCase {
39
	private ValidatedObservableSet validated;
40
	private IObservableSet target;
41
	private ValidationObservableValue validationStatus;
42
43
	protected void setUp() throws Exception {
44
		super.setUp();
45
46
		target = new WritableSet();
47
		validationStatus = new ValidationObservableValue();
48
		validated = new ValidatedObservableSet(target, validationStatus);
49
	}
50
51
	public void testIsStale_WhenStatusIsStale() {
52
		SetChangeEventTracker validatedChangeTracker = SetChangeEventTracker
53
				.observe(validated);
54
		StaleEventTracker validatedStaleTracker = StaleEventTracker
55
				.observe(validated);
56
57
		assertFalse(validationStatus.isStale());
58
		assertFalse(validated.isStale());
59
		assertEquals(0, validatedChangeTracker.count);
60
		assertEquals(0, validatedStaleTracker.count);
61
62
		// Become stale and check that we receive a stale event.
63
		validationStatus.setStale(true);
64
65
		assertTrue(validationStatus.isStale());
66
		assertTrue(validated.isStale());
67
		assertEquals(0, validatedChangeTracker.count);
68
		assertEquals(1, validatedStaleTracker.count); // +1
69
70
		// Become unstale again and check that we receive a change event.
71
		validationStatus.setStale(false);
72
73
		assertFalse(validationStatus.isStale());
74
		assertFalse(validated.isStale());
75
		assertEquals(1, validatedChangeTracker.count); // +1
76
		assertEquals(1, validatedStaleTracker.count);
77
	}
78
79
	public void testEvents_WhenStatusIsStale() {
80
		SetChangeEventTracker validatedChangeTracker = SetChangeEventTracker
81
				.observe(validated);
82
		StaleEventTracker validatedStaleTracker = StaleEventTracker
83
				.observe(validated);
84
85
		assertFalse(validationStatus.isStale());
86
		assertFalse(validated.isStale());
87
		assertEquals(0, validatedChangeTracker.count);
88
		assertEquals(0, validatedStaleTracker.count);
89
90
		// Become stale and check that we receive a stale event.
91
		validationStatus.setStale(true);
92
93
		assertTrue(validationStatus.isStale());
94
		assertTrue(validated.isStale());
95
		assertEquals(0, validatedChangeTracker.count);
96
		assertEquals(1, validatedStaleTracker.count); // +1
97
98
		// Changes to the target observable should not be propagated while the
99
		// validation is stale.
100
		target.add(new Object());
101
102
		assertEquals(0, validatedChangeTracker.count);
103
		assertEquals(1, validatedStaleTracker.count);
104
105
		// Changes to the validated observable should be propagated while the
106
		// validation is stale but we remain stale.
107
		validated.add(new Object());
108
109
		assertTrue(validated.isStale());
110
		assertEquals(1, validatedChangeTracker.count); // +1
111
		assertEquals(1, validatedStaleTracker.count);
112
113
		// Finally, become unstale and check that we receive a change event.
114
		validationStatus.setStale(false);
115
116
		assertFalse(validationStatus.isStale());
117
		assertFalse(validated.isStale());
118
		assertEquals(2, validatedChangeTracker.count); // +1
119
		assertEquals(1, validatedStaleTracker.count);
120
	}
121
33
	public static Test suite() {
122
	public static Test suite() {
34
		return MutableObservableSetContractTest.suite(new Delegate());
123
		TestSuite suite = new TestSuite(ValidatedObservableSetTest.class
124
				.getName());
125
		suite.addTestSuite(ValidatedObservableSetTest.class);
126
		suite.addTest(MutableObservableSetContractTest.suite(new Delegate()));
127
		return suite;
35
	}
128
	}
36
129
37
	static class Delegate extends AbstractObservableCollectionContractDelegate {
130
	static class Delegate extends AbstractObservableCollectionContractDelegate {
Lines 85-88 Link Here
85
			this.validationStatus = validationStatus;
178
			this.validationStatus = validationStatus;
86
		}
179
		}
87
	}
180
	}
181
182
	private static class ValidationObservableValue extends WritableValue {
183
		private boolean stale = false;
184
185
		public ValidationObservableValue() {
186
			super(ValidationStatus.ok(), IStatus.class);
187
		}
188
189
		public boolean isStale() {
190
			ObservableTracker.getterCalled(this);
191
			return stale;
192
		}
193
194
		public void setStale(boolean stale) {
195
			if (this.stale != stale) {
196
				this.stale = stale;
197
				if (stale) {
198
					fireStale();
199
				} else {
200
					fireValueChange(Diffs.createValueDiff(doGetValue(),
201
							doGetValue()));
202
				}
203
			}
204
		}
205
	}
88
}
206
}
(-)src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableListTest.java (-1 / +118 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
11
 ******************************************************************************/
11
12
12
package org.eclipse.core.tests.internal.databinding.observable;
13
package org.eclipse.core.tests.internal.databinding.observable;
Lines 14-22 Link Here
14
import java.util.ArrayList;
15
import java.util.ArrayList;
15
16
16
import junit.framework.Test;
17
import junit.framework.Test;
18
import junit.framework.TestSuite;
17
19
20
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.IObservable;
21
import org.eclipse.core.databinding.observable.IObservable;
19
import org.eclipse.core.databinding.observable.IObservableCollection;
22
import org.eclipse.core.databinding.observable.IObservableCollection;
23
import org.eclipse.core.databinding.observable.ObservableTracker;
20
import org.eclipse.core.databinding.observable.Realm;
24
import org.eclipse.core.databinding.observable.Realm;
21
import org.eclipse.core.databinding.observable.list.IObservableList;
25
import org.eclipse.core.databinding.observable.list.IObservableList;
22
import org.eclipse.core.databinding.observable.list.WritableList;
26
import org.eclipse.core.databinding.observable.list.WritableList;
Lines 27-37 Link Here
27
import org.eclipse.core.runtime.IStatus;
31
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
32
import org.eclipse.jface.databinding.conformance.MutableObservableListContractTest;
29
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
33
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
34
import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
35
import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
30
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
36
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
31
37
32
public class ValidatedObservableListTest extends AbstractDefaultRealmTestCase {
38
public class ValidatedObservableListTest extends AbstractDefaultRealmTestCase {
39
	private ValidatedObservableList validated;
40
	private IObservableList target;
41
	private ValidationObservableValue validationStatus;
42
43
	protected void setUp() throws Exception {
44
		super.setUp();
45
46
		target = new WritableList();
47
		validationStatus = new ValidationObservableValue();
48
		validated = new ValidatedObservableList(target, validationStatus);
49
	}
50
51
	public void testIsStale_WhenStatusIsStale() {
52
		ListChangeEventTracker validatedChangeTracker = ListChangeEventTracker
53
				.observe(validated);
54
		StaleEventTracker validatedStaleTracker = StaleEventTracker
55
				.observe(validated);
56
57
		assertFalse(validationStatus.isStale());
58
		assertFalse(validated.isStale());
59
		assertEquals(0, validatedChangeTracker.count);
60
		assertEquals(0, validatedStaleTracker.count);
61
62
		// Become stale and check that we receive a stale event.
63
		validationStatus.setStale(true);
64
65
		assertTrue(validationStatus.isStale());
66
		assertTrue(validated.isStale());
67
		assertEquals(0, validatedChangeTracker.count);
68
		assertEquals(1, validatedStaleTracker.count); // +1
69
70
		// Become unstale again and check that we receive a change event.
71
		validationStatus.setStale(false);
72
73
		assertFalse(validationStatus.isStale());
74
		assertFalse(validated.isStale());
75
		assertEquals(1, validatedChangeTracker.count); // +1
76
		assertEquals(1, validatedStaleTracker.count);
77
	}
78
79
	public void testEvents_WhenStatusIsStale() {
80
		ListChangeEventTracker validatedChangeTracker = ListChangeEventTracker
81
				.observe(validated);
82
		StaleEventTracker validatedStaleTracker = StaleEventTracker
83
				.observe(validated);
84
85
		assertFalse(validationStatus.isStale());
86
		assertFalse(validated.isStale());
87
		assertEquals(0, validatedChangeTracker.count);
88
		assertEquals(0, validatedStaleTracker.count);
89
90
		// Become stale and check that we receive a stale event.
91
		validationStatus.setStale(true);
92
93
		assertTrue(validationStatus.isStale());
94
		assertTrue(validated.isStale());
95
		assertEquals(0, validatedChangeTracker.count);
96
		assertEquals(1, validatedStaleTracker.count); // +1
97
98
		// Changes to the target observable should not be propagated while the
99
		// validation is stale.
100
		target.add(new Object());
101
102
		assertEquals(0, validatedChangeTracker.count);
103
		assertEquals(1, validatedStaleTracker.count);
104
105
		// Changes to the validated observable should be propagated while the
106
		// validation is stale but we remain stale.
107
		validated.add(new Object());
108
109
		assertTrue(validated.isStale());
110
		assertEquals(1, validatedChangeTracker.count); // +1
111
		assertEquals(1, validatedStaleTracker.count);
112
113
		// Finally, become unstale and check that we receive a change event.
114
		validationStatus.setStale(false);
115
116
		assertFalse(validationStatus.isStale());
117
		assertFalse(validated.isStale());
118
		assertEquals(2, validatedChangeTracker.count); // +1
119
		assertEquals(1, validatedStaleTracker.count);
120
	}
121
33
	public static Test suite() {
122
	public static Test suite() {
34
		return MutableObservableListContractTest.suite(new Delegate());
123
		TestSuite suite = new TestSuite(ValidatedObservableListTest.class.getName());
124
		suite.addTestSuite(ValidatedObservableListTest.class);
125
		suite.addTest(MutableObservableListContractTest.suite(new Delegate()));
126
		return suite;
35
	}
127
	}
36
128
37
	static class Delegate extends AbstractObservableCollectionContractDelegate {
129
	static class Delegate extends AbstractObservableCollectionContractDelegate {
Lines 85-88 Link Here
85
			this.validationStatus = validationStatus;
177
			this.validationStatus = validationStatus;
86
		}
178
		}
87
	}
179
	}
180
181
	private static class ValidationObservableValue extends WritableValue {
182
		private boolean stale = false;
183
184
		public ValidationObservableValue() {
185
			super(ValidationStatus.ok(), IStatus.class);
186
		}
187
188
		public boolean isStale() {
189
			ObservableTracker.getterCalled(this);
190
			return stale;
191
		}
192
193
		public void setStale(boolean stale) {
194
			if (this.stale != stale) {
195
				this.stale = stale;
196
				if (stale) {
197
					fireStale();
198
				} else {
199
					fireValueChange(Diffs.createValueDiff(doGetValue(),
200
							doGetValue()));
201
				}
202
			}
203
		}
204
	}
88
}
205
}
(-)src/org/eclipse/core/tests/internal/databinding/observable/ValidatedObservableMapTest.java (+144 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 248868)
10
 ******************************************************************************/
11
12
package org.eclipse.core.tests.internal.databinding.observable;
13
14
import junit.framework.Test;
15
import junit.framework.TestSuite;
16
17
import org.eclipse.core.databinding.observable.Diffs;
18
import org.eclipse.core.databinding.observable.ObservableTracker;
19
import org.eclipse.core.databinding.observable.map.IObservableMap;
20
import org.eclipse.core.databinding.observable.map.WritableMap;
21
import org.eclipse.core.databinding.observable.value.WritableValue;
22
import org.eclipse.core.databinding.validation.ValidationStatus;
23
import org.eclipse.core.internal.databinding.observable.ValidatedObservableMap;
24
import org.eclipse.core.runtime.IStatus;
25
import org.eclipse.jface.databinding.conformance.util.MapChangeEventTracker;
26
import org.eclipse.jface.databinding.conformance.util.StaleEventTracker;
27
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
28
29
public class ValidatedObservableMapTest extends AbstractDefaultRealmTestCase {
30
	private ValidatedObservableMap validated;
31
	private IObservableMap target;
32
	private ValidationObservableValue validationStatus;
33
34
	protected void setUp() throws Exception {
35
		super.setUp();
36
37
		target = new WritableMap();
38
		validationStatus = new ValidationObservableValue();
39
		validated = new ValidatedObservableMap(target, validationStatus);
40
	}
41
42
	public void testIsStale_WhenStatusIsStale() {
43
		MapChangeEventTracker validatedChangeTracker = MapChangeEventTracker
44
				.observe(validated);
45
		StaleEventTracker validatedStaleTracker = StaleEventTracker
46
				.observe(validated);
47
48
		assertFalse(validationStatus.isStale());
49
		assertFalse(validated.isStale());
50
		assertEquals(0, validatedChangeTracker.count);
51
		assertEquals(0, validatedStaleTracker.count);
52
53
		// Become stale and check that we receive a stale event.
54
		validationStatus.setStale(true);
55
56
		assertTrue(validationStatus.isStale());
57
		assertTrue(validated.isStale());
58
		assertEquals(0, validatedChangeTracker.count);
59
		assertEquals(1, validatedStaleTracker.count); // +1
60
61
		// Become unstale again and check that we receive a change event.
62
		validationStatus.setStale(false);
63
64
		assertFalse(validationStatus.isStale());
65
		assertFalse(validated.isStale());
66
		assertEquals(1, validatedChangeTracker.count); // +1
67
		assertEquals(1, validatedStaleTracker.count);
68
	}
69
70
	public void testEvents_WhenStatusIsStale() {
71
		MapChangeEventTracker validatedChangeTracker = MapChangeEventTracker
72
				.observe(validated);
73
		StaleEventTracker validatedStaleTracker = StaleEventTracker
74
				.observe(validated);
75
76
		assertFalse(validationStatus.isStale());
77
		assertFalse(validated.isStale());
78
		assertEquals(0, validatedChangeTracker.count);
79
		assertEquals(0, validatedStaleTracker.count);
80
81
		// Become stale and check that we receive a stale event.
82
		validationStatus.setStale(true);
83
84
		assertTrue(validationStatus.isStale());
85
		assertTrue(validated.isStale());
86
		assertEquals(0, validatedChangeTracker.count);
87
		assertEquals(1, validatedStaleTracker.count); // +1
88
89
		// Changes to the target observable should not be propagated while the
90
		// validation is stale.
91
		target.put(new Object(), new Object());
92
93
		assertEquals(0, validatedChangeTracker.count);
94
		assertEquals(1, validatedStaleTracker.count);
95
96
		// Changes to the validated observable should be propagated while the
97
		// validation is stale but we remain stale.
98
		validated.put(new Object(), new Object());
99
100
		assertTrue(validated.isStale());
101
		assertEquals(1, validatedChangeTracker.count); // +1
102
		assertEquals(1, validatedStaleTracker.count);
103
104
		// Finally, become unstale and check that we receive a change event.
105
		validationStatus.setStale(false);
106
107
		assertFalse(validationStatus.isStale());
108
		assertFalse(validated.isStale());
109
		assertEquals(2, validatedChangeTracker.count); // +1
110
		assertEquals(1, validatedStaleTracker.count);
111
	}
112
113
	public static Test suite() {
114
		TestSuite suite = new TestSuite(ValidatedObservableMapTest.class
115
				.getName());
116
		suite.addTestSuite(ValidatedObservableMapTest.class);
117
		return suite;
118
	}
119
120
	private static class ValidationObservableValue extends WritableValue {
121
		private boolean stale = false;
122
123
		public ValidationObservableValue() {
124
			super(ValidationStatus.ok(), IStatus.class);
125
		}
126
127
		public boolean isStale() {
128
			ObservableTracker.getterCalled(this);
129
			return stale;
130
		}
131
132
		public void setStale(boolean stale) {
133
			if (this.stale != stale) {
134
				this.stale = stale;
135
				if (stale) {
136
					fireStale();
137
				} else {
138
					fireValueChange(Diffs.createValueDiff(doGetValue(),
139
							doGetValue()));
140
				}
141
			}
142
		}
143
	}
144
}
(-)src/org/eclipse/core/databinding/validation/MultiValidator.java (-25 / +29 lines)
Lines 9-15 Link Here
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Boris Bokowski - bug 218269
10
 *     Boris Bokowski - bug 218269
11
 *     Matthew Hall - bug 237884, 240590
11
 *     Matthew Hall - bug 237884, 240590
12
 *     Ovidio Mallo - bug 238909
12
 *     Ovidio Mallo - bugs 238909, 248868
13
 ******************************************************************************/
13
 ******************************************************************************/
14
14
15
package org.eclipse.core.databinding.validation;
15
package org.eclipse.core.databinding.validation;
Lines 265-280 Link Here
265
265
266
	/**
266
	/**
267
	 * Returns a wrapper {@link IObservableValue} which stays in sync with the
267
	 * Returns a wrapper {@link IObservableValue} which stays in sync with the
268
	 * given target observable only when the validation status is valid.
268
	 * given target observable only when the validation status is valid and not
269
	 * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
269
	 * stale. Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
270
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
270
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
271
	 * <p>
271
	 * <p>
272
	 * The wrapper behaves as follows with respect to the validation status:
272
	 * The wrapper behaves as follows with respect to the validation status:
273
	 * <ul>
273
	 * <ul>
274
	 * <li>While valid, the wrapper stays in sync with its target observable.
274
	 * <li>While valid and not stale, the wrapper stays in sync with its target
275
	 * <li>While invalid, the wrapper's value is the target observable's last
275
	 * observable.
276
	 * valid value. If the target changes value, a stale event is fired
276
	 * <li>While invalid or stale, the wrapper's value is the target
277
	 * signaling that a change is pending.
277
	 * observable's last valid value. If the target changes value, a stale event
278
	 * is fired signaling that a change is pending.
278
	 * <li>When status changes from invalid to valid, the wrapper takes the
279
	 * <li>When status changes from invalid to valid, the wrapper takes the
279
	 * value of the target observable, and synchronization resumes.
280
	 * value of the target observable, and synchronization resumes.
280
	 * </ul>
281
	 * </ul>
Lines 292-307 Link Here
292
293
293
	/**
294
	/**
294
	 * Returns a wrapper {@link IObservableList} which stays in sync with the
295
	 * Returns a wrapper {@link IObservableList} which stays in sync with the
295
	 * given target observable only when the validation status is valid.
296
	 * given target observable only when the validation status is valid and not
296
	 * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
297
	 * stale. Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
297
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
298
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
298
	 * <p>
299
	 * <p>
299
	 * The wrapper behaves as follows with respect to the validation status:
300
	 * The wrapper behaves as follows with respect to the validation status:
300
	 * <ul>
301
	 * <ul>
301
	 * <li>While valid, the wrapper stays in sync with its target observable.
302
	 * <li>While valid and not stale, the wrapper stays in sync with its target
302
	 * <li>While invalid, the wrapper's elements are the target observable's
303
	 * observable.
303
	 * last valid elements. If the target changes elements, a stale event is
304
	 * <li>While invalid or stale, the wrapper's elements are the target
304
	 * fired signaling that a change is pending.
305
	 * observable's last valid elements. If the target changes elements, a stale
306
	 * event is fired signaling that a change is pending.
305
	 * <li>When status changes from invalid to valid, the wrapper takes the
307
	 * <li>When status changes from invalid to valid, the wrapper takes the
306
	 * elements of the target observable, and synchronization resumes.
308
	 * elements of the target observable, and synchronization resumes.
307
	 * </ul>
309
	 * </ul>
Lines 319-334 Link Here
319
321
320
	/**
322
	/**
321
	 * Returns a wrapper {@link IObservableSet} which stays in sync with the
323
	 * Returns a wrapper {@link IObservableSet} which stays in sync with the
322
	 * given target observable only when the validation status is valid.
324
	 * given target observable only when the validation status is valid and not
323
	 * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
325
	 * stale. Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
324
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
326
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
325
	 * <p>
327
	 * <p>
326
	 * The wrapper behaves as follows with respect to the validation status:
328
	 * The wrapper behaves as follows with respect to the validation status:
327
	 * <ul>
329
	 * <ul>
328
	 * <li>While valid, the wrapper stays in sync with its target observable.
330
	 * <li>While valid and not stale, the wrapper stays in sync with its target
329
	 * <li>While invalid, the wrapper's elements are the target observable's
331
	 * observable.
330
	 * last valid elements. If the target changes elements, a stale event is
332
	 * <li>While invalid or stale, the wrapper's elements are the target
331
	 * fired signaling that a change is pending.
333
	 * observable's last valid elements. If the target changes elements, a stale
334
	 * event is fired signaling that a change is pending.
332
	 * <li>When status changes from invalid to valid, the wrapper takes the
335
	 * <li>When status changes from invalid to valid, the wrapper takes the
333
	 * elements of the target observable, and synchronization resumes.
336
	 * elements of the target observable, and synchronization resumes.
334
	 * </ul>
337
	 * </ul>
Lines 346-361 Link Here
346
349
347
	/**
350
	/**
348
	 * Returns a wrapper {@link IObservableMap} which stays in sync with the
351
	 * Returns a wrapper {@link IObservableMap} which stays in sync with the
349
	 * given target observable only when the validation status is valid.
352
	 * given target observable only when the validation status is valid and not
350
	 * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
353
	 * stale. Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
351
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
354
	 * {@link IStatus#WARNING WARNING} severity are considered valid.
352
	 * <p>
355
	 * <p>
353
	 * The wrapper behaves as follows with respect to the validation status:
356
	 * The wrapper behaves as follows with respect to the validation status:
354
	 * <ul>
357
	 * <ul>
355
	 * <li>While valid, the wrapper stays in sync with its target observable.
358
	 * <li>While valid and not stale, the wrapper stays in sync with its target
356
	 * <li>While invalid, the wrapper's entries are the target observable's
359
	 * observable.
357
	 * last valid entries. If the target changes entries, a stale event is fired
360
	 * <li>While invalid or stale, the wrapper's entries are the target
358
	 * signaling that a change is pending.
361
	 * observable's last valid entries. If the target changes entries, a stale
362
	 * event is fired signaling that a change is pending.
359
	 * <li>When status changes from invalid to valid, the wrapper takes the
363
	 * <li>When status changes from invalid to valid, the wrapper takes the
360
	 * entries of the target observable, and synchronization resumes.
364
	 * entries of the target observable, and synchronization resumes.
361
	 * </ul>
365
	 * </ul>
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableSet.java (-31 / +61 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Matthew Hall - bug 248868
11
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
12
 ******************************************************************************/
11
13
12
package org.eclipse.core.internal.databinding.observable;
14
package org.eclipse.core.internal.databinding.observable;
Lines 40-50 Link Here
40
	private IObservableValue validationStatus;
42
	private IObservableValue validationStatus;
41
43
42
	// Only true when out of sync with target due to validation status
44
	// Only true when out of sync with target due to validation status
43
	private boolean stale;
45
	private boolean dirty = false;
44
46
45
	// True when validation status changes from invalid to valid.
47
	// True when validaton status changes from invalid or stale to valid and
48
	// non-stale.
46
	private boolean computeNextDiff = false;
49
	private boolean computeNextDiff = false;
47
50
51
	private boolean stale;
52
48
	private boolean updatingTarget = false;
53
	private boolean updatingTarget = false;
49
54
50
	private ISetChangeListener targetChangeListener = new ISetChangeListener() {
55
	private ISetChangeListener targetChangeListener = new ISetChangeListener() {
Lines 52-62 Link Here
52
			if (updatingTarget)
57
			if (updatingTarget)
53
				return;
58
				return;
54
			IStatus status = (IStatus) validationStatus.getValue();
59
			IStatus status = (IStatus) validationStatus.getValue();
55
			if (isValid(status)) {
60
			if (!validationStatus.isStale() && isValid(status)) {
56
				if (stale) {
61
				// Update the staleness state. Note that we do not care at this
57
					// this.stale means we are out of sync with target,
62
				// point whether that state changes from stale to non-stale
63
				// since we are always firing a change event below anyway.
64
				stale = target.isStale() || validationStatus.isStale();
65
66
				if (dirty) {
67
					// this.dirty means we are out of sync with target,
58
					// so reset wrapped list to exactly mirror target
68
					// so reset wrapped list to exactly mirror target
59
					stale = false;
69
					dirty = false;
60
					updateWrappedSet(new HashSet(target));
70
					updateWrappedSet(new HashSet(target));
61
				} else {
71
				} else {
62
					SetDiff diff = event.diff;
72
					SetDiff diff = event.diff;
Lines 68-101 Link Here
68
					fireSetChange(diff);
78
					fireSetChange(diff);
69
				}
79
				}
70
			} else {
80
			} else {
81
				// We are not propagating a received change from the target list
82
				// so we become dirty and stale.
83
				dirty = true;
71
				makeStale();
84
				makeStale();
72
			}
85
			}
73
		}
86
		}
74
	};
87
	};
75
88
76
	private IStaleListener targetStaleListener = new IStaleListener() {
77
		public void handleStale(StaleEvent staleEvent) {
78
			fireStale();
79
		}
80
	};
81
82
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
89
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
83
		public void handleValueChange(ValueChangeEvent event) {
90
		public void handleValueChange(ValueChangeEvent event) {
84
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
85
			IStatus newStatus = (IStatus) event.diff.getNewValue();
91
			IStatus newStatus = (IStatus) event.diff.getNewValue();
86
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
92
			if (!validationStatus.isStale() && isValid(newStatus)) {
87
				// this.stale means we are out of sync with target,
93
				// Update the staleness state and remember whether we were stale
88
				// reset wrapped set to exactly mirror target
94
				// before.
89
				stale = false;
95
				boolean wasStale = stale;
90
				updateWrappedSet(new HashSet(target));
96
				stale = target.isStale() || validationStatus.isStale();
91
97
92
				// If the validation status becomes valid because of a change in
98
				if (dirty) {
93
				// target observable
99
					// this.dirty means we are out of sync with target,
94
				computeNextDiff = true;
100
					// reset wrapped set to exactly mirror target
101
					dirty = false;
102
					updateWrappedSet(new HashSet(target));
103
104
					// If the validation status becomes valid because of a
105
					// change in target observable
106
					computeNextDiff = true;
107
				} else {
108
					// If we are becoming unstale, we must fire a change event
109
					// to signal this.
110
					if (wasStale && !stale) {
111
						fireSetChange(Diffs.createSetDiff(
112
								Collections.EMPTY_SET, Collections.EMPTY_SET));
113
					}
114
				}
95
			}
115
			}
96
		}
116
		}
97
	};
117
	};
98
118
119
	private IStaleListener staleListener = new IStaleListener() {
120
		public void handleStale(StaleEvent staleEvent) {
121
			makeStale();
122
		}
123
	};
124
125
	private static boolean isValid(IStatus status) {
126
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
127
	}
128
99
	/**
129
	/**
100
	 * @param target
130
	 * @param target
101
	 * @param validationStatus
131
	 * @param validationStatus
Lines 110-118 Link Here
110
						"Target and validation status observables must be on the same realm"); //$NON-NLS-1$
140
						"Target and validation status observables must be on the same realm"); //$NON-NLS-1$
111
		this.target = target;
141
		this.target = target;
112
		this.validationStatus = validationStatus;
142
		this.validationStatus = validationStatus;
143
		this.stale = target.isStale() || validationStatus.isStale();
113
		target.addSetChangeListener(targetChangeListener);
144
		target.addSetChangeListener(targetChangeListener);
114
		target.addStaleListener(targetStaleListener);
145
		target.addStaleListener(staleListener);
115
		validationStatus.addValueChangeListener(validationStatusChangeListener);
146
		validationStatus.addValueChangeListener(validationStatusChangeListener);
147
		validationStatus.addStaleListener(staleListener);
116
	}
148
	}
117
149
118
	private void updateWrappedSet(Set newSet) {
150
	private void updateWrappedSet(Set newSet) {
Lines 122-131 Link Here
122
		fireSetChange(diff);
154
		fireSetChange(diff);
123
	}
155
	}
124
156
125
	private static boolean isValid(IStatus status) {
126
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
127
	}
128
129
	private void applyDiff(SetDiff diff, Set set) {
157
	private void applyDiff(SetDiff diff, Set set) {
130
		for (Iterator iterator = diff.getRemovals().iterator(); iterator
158
		for (Iterator iterator = diff.getRemovals().iterator(); iterator
131
				.hasNext();) {
159
				.hasNext();) {
Lines 147-158 Link Here
147
	private void updateTargetSet(SetDiff diff) {
175
	private void updateTargetSet(SetDiff diff) {
148
		updatingTarget = true;
176
		updatingTarget = true;
149
		try {
177
		try {
150
			if (stale) {
178
			if (dirty) {
151
				stale = false;
179
				dirty = false;
152
				applyDiff(Diffs.computeSetDiff(target, wrappedSet), target);
180
				applyDiff(Diffs.computeSetDiff(target, wrappedSet), target);
153
			} else {
181
			} else {
154
				applyDiff(diff, target);
182
				applyDiff(diff, target);
155
			}
183
			}
184
			stale = target.isStale() || validationStatus.isStale();
156
		} finally {
185
		} finally {
157
			updatingTarget = false;
186
			updatingTarget = false;
158
		}
187
		}
Lines 160-166 Link Here
160
189
161
	public boolean isStale() {
190
	public boolean isStale() {
162
		getterCalled();
191
		getterCalled();
163
		return stale || target.isStale();
192
		return stale;
164
	}
193
	}
165
194
166
	public boolean add(Object o) {
195
	public boolean add(Object o) {
Lines 262-270 Link Here
262
291
263
	public synchronized void dispose() {
292
	public synchronized void dispose() {
264
		target.removeSetChangeListener(targetChangeListener);
293
		target.removeSetChangeListener(targetChangeListener);
265
		target.removeStaleListener(targetStaleListener);
294
		target.removeStaleListener(staleListener);
266
		validationStatus
295
		validationStatus
267
				.removeValueChangeListener(validationStatusChangeListener);
296
				.removeValueChangeListener(validationStatusChangeListener);
297
		validationStatus.removeStaleListener(staleListener);
268
		super.dispose();
298
		super.dispose();
269
	}
299
	}
270
}
300
}
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableMap.java (-28 / +60 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Matthew Hall - bug 248868
11
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
12
 ******************************************************************************/
11
13
12
package org.eclipse.core.internal.databinding.observable;
14
package org.eclipse.core.internal.databinding.observable;
Lines 39-49 Link Here
39
	private IObservableValue validationStatus;
41
	private IObservableValue validationStatus;
40
42
41
	// Only true when out of sync with target due to validation status
43
	// Only true when out of sync with target due to validation status
42
	private boolean stale;
44
	private boolean dirty = false;
43
45
44
	// True when validation status changes from invalid to valid.
46
	// True when validaton status changes from invalid or stale to valid and
47
	// non-stale.
45
	private boolean computeNextDiff = false;
48
	private boolean computeNextDiff = false;
46
49
50
	private boolean stale;
51
47
	private boolean updatingTarget = false;
52
	private boolean updatingTarget = false;
48
53
49
	private IMapChangeListener targetChangeListener = new IMapChangeListener() {
54
	private IMapChangeListener targetChangeListener = new IMapChangeListener() {
Lines 51-61 Link Here
51
			if (updatingTarget)
56
			if (updatingTarget)
52
				return;
57
				return;
53
			IStatus status = (IStatus) validationStatus.getValue();
58
			IStatus status = (IStatus) validationStatus.getValue();
54
			if (isValid(status)) {
59
			if (!validationStatus.isStale() && isValid(status)) {
55
				if (stale) {
60
				// Update the staleness state. Note that we do not care at this
56
					// this.stale means we are out of sync with target,
61
				// point whether that state changes from stale to non-stale
62
				// since we are always firing a change event below anyway.
63
				stale = target.isStale() || validationStatus.isStale();
64
65
				if (dirty) {
66
					// this.dirty means we are out of sync with target,
57
					// so reset wrapped list to exactly mirror target
67
					// so reset wrapped list to exactly mirror target
58
					stale = false;
68
					dirty = false;
59
					updateWrappedMap(new HashMap(target));
69
					updateWrappedMap(new HashMap(target));
60
				} else {
70
				} else {
61
					MapDiff diff = event.diff;
71
					MapDiff diff = event.diff;
Lines 67-100 Link Here
67
					fireMapChange(diff);
77
					fireMapChange(diff);
68
				}
78
				}
69
			} else {
79
			} else {
80
				// We are not propagating a received change from the target list
81
				// so we become dirty and stale.
82
				dirty = true;
70
				makeStale();
83
				makeStale();
71
			}
84
			}
72
		}
85
		}
73
	};
86
	};
74
87
75
	private IStaleListener targetStaleListener = new IStaleListener() {
76
		public void handleStale(StaleEvent staleEvent) {
77
			fireStale();
78
		}
79
	};
80
81
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
88
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
82
		public void handleValueChange(ValueChangeEvent event) {
89
		public void handleValueChange(ValueChangeEvent event) {
83
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
84
			IStatus newStatus = (IStatus) event.diff.getNewValue();
90
			IStatus newStatus = (IStatus) event.diff.getNewValue();
85
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
91
			if (!validationStatus.isStale() && isValid(newStatus)) {
86
				// this.stale means we are out of sync with target,
92
				// Update the staleness state and remember whether we were stale
87
				// reset wrapped map to exactly mirror target
93
				// before.
88
				stale = false;
94
				boolean wasStale = stale;
89
				updateWrappedMap(new HashMap(target));
95
				stale = target.isStale() || validationStatus.isStale();
90
96
91
				// If the validation status becomes valid because of a change in
97
				if (dirty) {
92
				// target observable
98
					// this.dirty means we are out of sync with target,
93
				computeNextDiff = true;
99
					// reset wrapped map to exactly mirror target
100
					dirty = false;
101
					updateWrappedMap(new HashMap(target));
102
103
					// If the validation status becomes valid because of a
104
					// change in target observable
105
					computeNextDiff = true;
106
				} else {
107
					// If we are becoming unstale, we must fire a change event
108
					// to signal this.
109
					if (wasStale && !stale) {
110
						fireMapChange(Diffs.createMapDiff(
111
								Collections.EMPTY_SET, Collections.EMPTY_SET,
112
								Collections.EMPTY_SET, Collections.EMPTY_MAP,
113
								Collections.EMPTY_MAP));
114
					}
115
				}
94
			}
116
			}
95
		}
117
		}
96
	};
118
	};
97
119
120
	private IStaleListener staleListener = new IStaleListener() {
121
		public void handleStale(StaleEvent staleEvent) {
122
			makeStale();
123
		}
124
	};
125
98
	/**
126
	/**
99
	 * @param target
127
	 * @param target
100
	 * @param validationStatus
128
	 * @param validationStatus
Lines 109-117 Link Here
109
						"Target and validation status observables must be on the same realm"); //$NON-NLS-1$
137
						"Target and validation status observables must be on the same realm"); //$NON-NLS-1$
110
		this.target = target;
138
		this.target = target;
111
		this.validationStatus = validationStatus;
139
		this.validationStatus = validationStatus;
140
		this.stale = target.isStale() || validationStatus.isStale();
112
		target.addMapChangeListener(targetChangeListener);
141
		target.addMapChangeListener(targetChangeListener);
113
		target.addStaleListener(targetStaleListener);
142
		target.addStaleListener(staleListener);
114
		validationStatus.addValueChangeListener(validationStatusChangeListener);
143
		validationStatus.addValueChangeListener(validationStatusChangeListener);
144
		validationStatus.addStaleListener(staleListener);
115
	}
145
	}
116
146
117
	private void updateWrappedMap(Map newMap) {
147
	private void updateWrappedMap(Map newMap) {
Lines 122-128 Link Here
122
	}
152
	}
123
153
124
	private static boolean isValid(IStatus status) {
154
	private static boolean isValid(IStatus status) {
125
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
155
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
126
	}
156
	}
127
157
128
	private void applyDiff(MapDiff diff, Map map) {
158
	private void applyDiff(MapDiff diff, Map map) {
Lines 151-162 Link Here
151
	private void updateTargetMap(MapDiff diff) {
181
	private void updateTargetMap(MapDiff diff) {
152
		updatingTarget = true;
182
		updatingTarget = true;
153
		try {
183
		try {
154
			if (stale) {
184
			if (dirty) {
155
				stale = false;
185
				dirty = false;
156
				applyDiff(Diffs.computeMapDiff(target, wrappedMap), target);
186
				applyDiff(Diffs.computeMapDiff(target, wrappedMap), target);
157
			} else {
187
			} else {
158
				applyDiff(diff, target);
188
				applyDiff(diff, target);
159
			}
189
			}
190
			stale = target.isStale() || validationStatus.isStale();
160
		} finally {
191
		} finally {
161
			updatingTarget = false;
192
			updatingTarget = false;
162
		}
193
		}
Lines 164-170 Link Here
164
195
165
	public boolean isStale() {
196
	public boolean isStale() {
166
		getterCalled();
197
		getterCalled();
167
		return stale || target.isStale();
198
		return stale;
168
	}
199
	}
169
200
170
	public void clear() {
201
	public void clear() {
Lines 220-228 Link Here
220
251
221
	public synchronized void dispose() {
252
	public synchronized void dispose() {
222
		target.removeMapChangeListener(targetChangeListener);
253
		target.removeMapChangeListener(targetChangeListener);
223
		target.removeStaleListener(targetStaleListener);
254
		target.removeStaleListener(staleListener);
224
		validationStatus
255
		validationStatus
225
				.removeValueChangeListener(validationStatusChangeListener);
256
				.removeValueChangeListener(validationStatusChangeListener);
257
		validationStatus.removeStaleListener(staleListener);
226
		super.dispose();
258
		super.dispose();
227
	}
259
	}
228
}
260
}
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableList.java (-33 / +62 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Matthew Hall - bug 248868
11
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
12
 ******************************************************************************/
11
13
12
package org.eclipse.core.internal.databinding.observable;
14
package org.eclipse.core.internal.databinding.observable;
Lines 20-26 Link Here
20
22
21
import org.eclipse.core.databinding.observable.Diffs;
23
import org.eclipse.core.databinding.observable.Diffs;
22
import org.eclipse.core.databinding.observable.IStaleListener;
24
import org.eclipse.core.databinding.observable.IStaleListener;
23
import org.eclipse.core.databinding.observable.ObservableTracker;
24
import org.eclipse.core.databinding.observable.StaleEvent;
25
import org.eclipse.core.databinding.observable.StaleEvent;
25
import org.eclipse.core.databinding.observable.list.IListChangeListener;
26
import org.eclipse.core.databinding.observable.list.IListChangeListener;
26
import org.eclipse.core.databinding.observable.list.IObservableList;
27
import org.eclipse.core.databinding.observable.list.IObservableList;
Lines 44-54 Link Here
44
	private IObservableValue validationStatus;
45
	private IObservableValue validationStatus;
45
46
46
	// Only true when out of sync with target due to validation status
47
	// Only true when out of sync with target due to validation status
47
	private boolean stale;
48
	private boolean dirty = false;
48
49
49
	// True when validaton status changes from invalid to valid.
50
	// True when validaton status changes from invalid or stale to valid and
51
	// non-stale.
50
	private boolean computeNextDiff = false;
52
	private boolean computeNextDiff = false;
51
53
54
	private boolean stale;
55
52
	private boolean updatingTarget = false;
56
	private boolean updatingTarget = false;
53
57
54
	private IListChangeListener targetChangeListener = new IListChangeListener() {
58
	private IListChangeListener targetChangeListener = new IListChangeListener() {
Lines 56-66 Link Here
56
			if (updatingTarget)
60
			if (updatingTarget)
57
				return;
61
				return;
58
			IStatus status = (IStatus) validationStatus.getValue();
62
			IStatus status = (IStatus) validationStatus.getValue();
59
			if (isValid(status)) {
63
			if (!validationStatus.isStale() && isValid(status)) {
60
				if (stale) {
64
				// Update the staleness state. Note that we do not care at this
61
					// this.stale means we are out of sync with target,
65
				// point whether that state changes from stale to non-stale
66
				// since we are always firing a change event below anyway.
67
				stale = target.isStale() || validationStatus.isStale();
68
69
				if (dirty) {
70
					// this.dirty means we are out of sync with target,
62
					// so reset wrapped list to exactly mirror target
71
					// so reset wrapped list to exactly mirror target
63
					stale = false;
72
					dirty = false;
64
					updateWrappedList(new ArrayList(target));
73
					updateWrappedList(new ArrayList(target));
65
				} else {
74
				} else {
66
					ListDiff diff = event.diff;
75
					ListDiff diff = event.diff;
Lines 72-109 Link Here
72
					fireListChange(diff);
81
					fireListChange(diff);
73
				}
82
				}
74
			} else {
83
			} else {
84
				// We are not propagating a received change from the target list
85
				// so we become dirty and stale.
86
				dirty = true;
75
				makeStale();
87
				makeStale();
76
			}
88
			}
77
		}
89
		}
78
	};
90
	};
79
91
80
	private static boolean isValid(IStatus status) {
81
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
82
	}
83
84
	private IStaleListener targetStaleListener = new IStaleListener() {
85
		public void handleStale(StaleEvent staleEvent) {
86
			fireStale();
87
		}
88
	};
89
90
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
92
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
91
		public void handleValueChange(ValueChangeEvent event) {
93
		public void handleValueChange(ValueChangeEvent event) {
92
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
93
			IStatus newStatus = (IStatus) event.diff.getNewValue();
94
			IStatus newStatus = (IStatus) event.diff.getNewValue();
94
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
95
			if (!validationStatus.isStale() && isValid(newStatus)) {
95
				// this.stale means we are out of sync with target,
96
				// Update the staleness state and remember whether we were stale
96
				// reset wrapped list to exactly mirror target
97
				// before.
97
				stale = false;
98
				boolean wasStale = stale;
98
				updateWrappedList(new ArrayList(target));
99
				stale = target.isStale() || validationStatus.isStale();
99
100
100
				// If the validation status becomes valid because of a change in
101
				if (dirty) {
101
				// target observable
102
					// this.dirty means we are out of sync with target,
102
				computeNextDiff = true;
103
					// reset wrapped list to exactly mirror target
104
					dirty = false;
105
					updateWrappedList(new ArrayList(target));
106
107
					// If the validation status becomes valid because of a
108
					// change in target observable
109
					computeNextDiff = true;
110
				} else {
111
					// If we are becoming unstale, we must fire a change event
112
					// to signal this.
113
					if (wasStale && !stale) {
114
						fireListChange(Diffs
115
								.createListDiff(new ListDiffEntry[0]));
116
					}
117
				}
103
			}
118
			}
104
		}
119
		}
105
	};
120
	};
106
121
122
	private IStaleListener staleListener = new IStaleListener() {
123
		public void handleStale(StaleEvent staleEvent) {
124
			makeStale();
125
		}
126
	};
127
128
	private static boolean isValid(IStatus status) {
129
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
130
	}
131
107
	/**
132
	/**
108
	 * @param target
133
	 * @param target
109
	 * @param validationStatus
134
	 * @param validationStatus
Lines 118-126 Link Here
118
						"Target and validation status observables must be on the same realm"); //$NON-NLS-1$
143
						"Target and validation status observables must be on the same realm"); //$NON-NLS-1$
119
		this.target = target;
144
		this.target = target;
120
		this.validationStatus = validationStatus;
145
		this.validationStatus = validationStatus;
146
		this.stale = target.isStale() || validationStatus.isStale();
121
		target.addListChangeListener(targetChangeListener);
147
		target.addListChangeListener(targetChangeListener);
122
		target.addStaleListener(targetStaleListener);
148
		target.addStaleListener(staleListener);
123
		validationStatus.addValueChangeListener(validationStatusChangeListener);
149
		validationStatus.addValueChangeListener(validationStatusChangeListener);
150
		validationStatus.addStaleListener(staleListener);
124
	}
151
	}
125
152
126
	private void makeStale() {
153
	private void makeStale() {
Lines 133-144 Link Here
133
	private void updateTargetList(ListDiff diff) {
160
	private void updateTargetList(ListDiff diff) {
134
		updatingTarget = true;
161
		updatingTarget = true;
135
		try {
162
		try {
136
			if (stale) {
163
			if (dirty) {
137
				stale = false;
164
				dirty = false;
138
				applyDiff(Diffs.computeListDiff(target, wrappedList), target);
165
				applyDiff(Diffs.computeListDiff(target, wrappedList), target);
139
			} else {
166
			} else {
140
				applyDiff(diff, target);
167
				applyDiff(diff, target);
141
			}
168
			}
169
			stale = target.isStale() || validationStatus.isStale();
142
		} finally {
170
		} finally {
143
			updatingTarget = false;
171
			updatingTarget = false;
144
		}
172
		}
Lines 162-169 Link Here
162
	}
190
	}
163
191
164
	public boolean isStale() {
192
	public boolean isStale() {
165
		ObservableTracker.getterCalled(this);
193
		getterCalled();
166
		return stale || target.isStale();
194
		return stale;
167
	}
195
	}
168
196
169
	public void add(int index, Object element) {
197
	public void add(int index, Object element) {
Lines 385-393 Link Here
385
413
386
	public synchronized void dispose() {
414
	public synchronized void dispose() {
387
		target.removeListChangeListener(targetChangeListener);
415
		target.removeListChangeListener(targetChangeListener);
388
		target.removeStaleListener(targetStaleListener);
416
		target.removeStaleListener(staleListener);
389
		validationStatus
417
		validationStatus
390
				.removeValueChangeListener(validationStatusChangeListener);
418
				.removeValueChangeListener(validationStatusChangeListener);
419
		validationStatus.removeStaleListener(staleListener);
391
		super.dispose();
420
		super.dispose();
392
	}
421
	}
393
}
422
}
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableValue.java (-29 / +34 lines)
Lines 7-12 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     Matthew Hall - initial API and implementation (bug 218269)
9
 *     Matthew Hall - initial API and implementation (bug 218269)
10
 *     Matthew Hall - bug 248868
11
 *     Ovidio Mallo - bug 248868
10
 ******************************************************************************/
12
 ******************************************************************************/
11
13
12
package org.eclipse.core.internal.databinding.observable;
14
package org.eclipse.core.internal.databinding.observable;
Lines 27-50 Link Here
27
29
28
/**
30
/**
29
 * An {@link IObservableValue} wrapper that stays in sync with the target
31
 * An {@link IObservableValue} wrapper that stays in sync with the target
30
 * observable as long as a given validation status is valid.
32
 * observable as long as a given validation status is valid and not stale.
31
 * <ul>
33
 * <ul>
32
 * <li>While status is valid, ValidatedObservableValue stays in sync with its
34
 * <li>While status is valid and not stale, ValidatedObservableValue stays in
33
 * target.
35
 * sync with its target.
34
 * <li>When status becomes invalid, ValidatedObservableValue will retain the
36
 * <li>When status becomes invalid or stale, ValidatedObservableValue will
35
 * last valid value of its target.
37
 * retain the last valid value of its target.
36
 * <li>While status is invalid, changes in the target observable cause
38
 * <li>While status is invalid or stale, changes in the target observable cause
37
 * ValidatedObservableValue to fire a stale event, to indicate that changes are
39
 * ValidatedObservableValue to fire a stale event, to indicate that changes are
38
 * pending.
40
 * pending.
39
 * <li>When status becomes valid, pending value changes are performed (if any)
41
 * <li>When status becomes valid and not stale, pending value changes are
40
 * and synchronization resumes.
42
 * performed (if any) and synchronization resumes.
41
 * </ul>
43
 * </ul>
42
 * <p>
44
 * <p>
43
 * Note:
45
 * Note:
44
 * <ul>
46
 * <ul>
45
 * <li>By default, a status is valid if its
47
 * <li>By default, a status is valid if its {@link IStatus#getSeverity()
46
 * {@link IStatus#getSeverity() severity} is {@link IStatus#OK OK},
48
 * severity} is {@link IStatus#OK OK}, {@link IStatus#INFO INFO}, or
47
 * {@link IStatus#INFO INFO}, or {@link IStatus#WARNING WARNING}
49
 * {@link IStatus#WARNING WARNING}
48
 * <li>Calls to {@link #setValue(Object)} on the validated observable changes
50
 * <li>Calls to {@link #setValue(Object)} on the validated observable changes
49
 * the value regardless of the validation status.
51
 * the value regardless of the validation status.
50
 * <li>This class will not forward {@link ValueChangingEvent} events from a
52
 * <li>This class will not forward {@link ValueChangingEvent} events from a
Lines 66-98 Link Here
66
			if (updatingTarget)
68
			if (updatingTarget)
67
				return;
69
				return;
68
			IStatus status = (IStatus) validationStatus.getValue();
70
			IStatus status = (IStatus) validationStatus.getValue();
69
			if (isValid(status))
71
			if (!validationStatus.isStale() && isValid(status))
70
				internalSetValue(event.diff.getNewValue(), false);
72
				internalSetValue(event.diff.getNewValue(), false);
71
			else
73
			else
72
				makeStale();
74
				makeStale();
73
		}
75
		}
74
	};
76
	};
75
77
76
	private static boolean isValid(IStatus status) {
77
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
78
	}
79
80
	private IStaleListener targetStaleListener = new IStaleListener() {
81
		public void handleStale(StaleEvent staleEvent) {
82
			fireStale();
83
		}
84
	};
85
86
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
78
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
87
		public void handleValueChange(ValueChangeEvent event) {
79
		public void handleValueChange(ValueChangeEvent event) {
88
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
89
			IStatus newStatus = (IStatus) event.diff.getNewValue();
80
			IStatus newStatus = (IStatus) event.diff.getNewValue();
90
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
81
			if (!validationStatus.isStale() && isValid(newStatus)) {
91
				internalSetValue(target.getValue(), false);
82
				internalSetValue(target.getValue(), false);
92
			}
83
			}
93
		}
84
		}
94
	};
85
	};
95
86
87
	private IStaleListener staleListener = new IStaleListener() {
88
		public void handleStale(StaleEvent staleEvent) {
89
			makeStale();
90
		}
91
	};
92
93
	private static boolean isValid(IStatus status) {
94
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
95
	}
96
96
	/**
97
	/**
97
	 * Constructs an observable value
98
	 * Constructs an observable value
98
	 * 
99
	 * 
Lines 113-122 Link Here
113
		this.target = target;
114
		this.target = target;
114
		this.validationStatus = validationStatus;
115
		this.validationStatus = validationStatus;
115
		this.cachedValue = target.getValue();
116
		this.cachedValue = target.getValue();
117
		this.stale = target.isStale() || validationStatus.isStale();
116
118
117
		target.addValueChangeListener(targetChangeListener);
119
		target.addValueChangeListener(targetChangeListener);
118
		target.addStaleListener(targetStaleListener);
120
		target.addStaleListener(staleListener);
119
		validationStatus.addValueChangeListener(validationStatusChangeListener);
121
		validationStatus.addValueChangeListener(validationStatusChangeListener);
122
		validationStatus.addStaleListener(staleListener);
120
	}
123
	}
121
124
122
	private void makeStale() {
125
	private void makeStale() {
Lines 128-134 Link Here
128
131
129
	public boolean isStale() {
132
	public boolean isStale() {
130
		ObservableTracker.getterCalled(this);
133
		ObservableTracker.getterCalled(this);
131
		return stale || target.isStale();
134
		return stale;
132
	}
135
	}
133
136
134
	protected Object doGetValue() {
137
	protected Object doGetValue() {
Lines 136-141 Link Here
136
	}
139
	}
137
140
138
	private void internalSetValue(Object value, boolean updateTarget) {
141
	private void internalSetValue(Object value, boolean updateTarget) {
142
		boolean wasStale = this.stale;
139
		Object oldValue = cachedValue;
143
		Object oldValue = cachedValue;
140
		cachedValue = value;
144
		cachedValue = value;
141
		if (updateTarget) {
145
		if (updateTarget) {
Lines 147-154 Link Here
147
				updatingTarget = false;
151
				updatingTarget = false;
148
			}
152
			}
149
		}
153
		}
150
		stale = false;
154
		stale = target.isStale() || validationStatus.isStale();
151
		if (!Util.equals(oldValue, cachedValue))
155
		if ((wasStale && !stale) || !Util.equals(oldValue, cachedValue))
152
			fireValueChange(Diffs.createValueDiff(oldValue, cachedValue));
156
			fireValueChange(Diffs.createValueDiff(oldValue, cachedValue));
153
	}
157
	}
154
158
Lines 162-170 Link Here
162
166
163
	public synchronized void dispose() {
167
	public synchronized void dispose() {
164
		target.removeValueChangeListener(targetChangeListener);
168
		target.removeValueChangeListener(targetChangeListener);
165
		target.removeStaleListener(targetStaleListener);
169
		target.removeStaleListener(staleListener);
166
		validationStatus
170
		validationStatus
167
				.removeValueChangeListener(validationStatusChangeListener);
171
				.removeValueChangeListener(validationStatusChangeListener);
172
		validationStatus.removeStaleListener(staleListener);
168
		super.dispose();
173
		super.dispose();
169
	}
174
	}
170
}
175
}

Return to bug 248868