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

Collapse All | Expand All

(-)src/org/eclipse/core/tests/databinding/validation/MultiValidatorTest.java (-3 / +51 lines)
Lines 7-18 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 233191
10
 ******************************************************************************/
11
 ******************************************************************************/
11
12
12
package org.eclipse.core.tests.databinding.validation;
13
package org.eclipse.core.tests.databinding.validation;
13
14
14
import org.eclipse.core.databinding.DataBindingContext;
15
import org.eclipse.core.databinding.DataBindingContext;
16
import org.eclipse.core.databinding.observable.IStaleListener;
15
import org.eclipse.core.databinding.observable.Realm;
17
import org.eclipse.core.databinding.observable.Realm;
18
import org.eclipse.core.databinding.observable.StaleEvent;
16
import org.eclipse.core.databinding.observable.value.IObservableValue;
19
import org.eclipse.core.databinding.observable.value.IObservableValue;
17
import org.eclipse.core.databinding.observable.value.WritableValue;
20
import org.eclipse.core.databinding.observable.value.WritableValue;
18
import org.eclipse.core.databinding.validation.MultiValidator;
21
import org.eclipse.core.databinding.validation.MultiValidator;
Lines 25-37 Link Here
25
28
26
public class MultiValidatorTest extends AbstractDefaultRealmTestCase {
29
public class MultiValidatorTest extends AbstractDefaultRealmTestCase {
27
	private WritableValue dependency;
30
	private WritableValue dependency;
28
	private MultiValidator validator;
31
	private TestMultiValidator validator;
29
	private IObservableValue validationStatus;
32
	private IObservableValue validationStatus;
30
33
31
	protected void setUp() throws Exception {
34
	protected void setUp() throws Exception {
32
		super.setUp();
35
		super.setUp();
33
		dependency = new WritableValue(null, IStatus.class);
36
		dependency = new WritableValue(null, IStatus.class);
34
		validator = new MultiValidator() {
37
		validator = new TestMultiValidator() {
35
			protected IStatus validate() {
38
			protected IStatus validate() {
36
				return (IStatus) dependency.getValue();
39
				return (IStatus) dependency.getValue();
37
			}
40
			}
Lines 58-64 Link Here
58
61
59
	public void testGetValidationStatus_ExceptionThrownYieldsErrorStatus() {
62
	public void testGetValidationStatus_ExceptionThrownYieldsErrorStatus() {
60
		final RuntimeException e = new RuntimeException("message");
63
		final RuntimeException e = new RuntimeException("message");
61
		validator = new MultiValidator() {
64
		validator = new TestMultiValidator() {
62
			protected IStatus validate() {
65
			protected IStatus validate() {
63
				throw e;
66
				throw e;
64
			}
67
			}
Lines 115-118 Link Here
115
		assertEquals(target.getValue(), validated.getValue());
118
		assertEquals(target.getValue(), validated.getValue());
116
		assertFalse(validated.isStale());
119
		assertFalse(validated.isStale());
117
	}
120
	}
121
122
	public void testEnterExitStale_ValidationStatusStaleness() {
123
		StaleCounter staleCounter = new StaleCounter();
124
		validationStatus.addStaleListener(staleCounter);
125
126
		assertEquals(0, staleCounter.count);
127
128
		validator.enterStaleDelegate();
129
		assertEquals(1, staleCounter.count);
130
		assertTrue(validationStatus.isStale());
131
132
		validator.exitStaleDelegate((IStatus) validationStatus.getValue());
133
		assertFalse(validationStatus.isStale());
134
	}
135
136
	public void testEnterExitStale_ValidationStatusValue() {
137
		validator.enterStaleDelegate();
138
		IStatus status = ValidationStatus.error("done");
139
		validator.exitStaleDelegate(status);
140
		assertSame(status, validationStatus.getValue());
141
	}
142
143
	/**
144
	 * Simple extension of the MultiValidator class which is functionally
145
	 * equivalent while making some methods accessible to the unit tests.
146
	 */
147
	private static abstract class TestMultiValidator extends MultiValidator {
148
149
		void enterStaleDelegate() {
150
			enterStale();
151
		}
152
153
		void exitStaleDelegate(IStatus status) {
154
			exitStale(status);
155
		}
156
	}
157
158
	private static class StaleCounter implements IStaleListener {
159
160
		int count;
161
162
		public void handleStale(StaleEvent event) {
163
			count++;
164
		}
165
	}
118
}
166
}
(-)src/org/eclipse/core/tests/internal/databinding/QueueTest.java (-1 / +10 lines)
Lines 64-68 Link Here
64
		assertEquals("moo", queue.dequeue());
64
		assertEquals("moo", queue.dequeue());
65
		assertTrue(queue.isEmpty());
65
		assertTrue(queue.isEmpty());
66
	}
66
	}
67
	
67
68
	public void testClear() {
69
		assertTrue(queue.isEmpty());
70
		queue.enqueue("foo");
71
		assertFalse(queue.isEmpty());
72
		queue.clear();
73
		assertTrue(queue.isEmpty());
74
		queue.clear();
75
		assertTrue(queue.isEmpty());
76
	}
68
}
77
}
(-)src/org/eclipse/jface/tests/databinding/scenarios/PropertyScenarios.java (+8 lines)
Lines 203-208 Link Here
203
                String remainingChars = modelValue.substring(1);
203
                String remainingChars = modelValue.substring(1);
204
                return firstChar.toUpperCase() + remainingChars.toLowerCase();
204
                return firstChar.toUpperCase() + remainingChars.toLowerCase();
205
            }
205
            }
206
207
            public boolean isAsync() {
208
            	return false;
209
            }
206
        };
210
        };
207
        IConverter converter2 = new IConverter() {
211
        IConverter converter2 = new IConverter() {
208
            public Object getFromType() {
212
            public Object getFromType() {
Lines 216-221 Link Here
216
            public Object convert(Object fromObject) {
220
            public Object convert(Object fromObject) {
217
                return ((String) fromObject).toUpperCase();
221
                return ((String) fromObject).toUpperCase();
218
            }
222
            }
223
224
            public boolean isAsync() {
225
            	return false;
226
            }
219
        };
227
        };
220
228
221
        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
229
        getDbc().bindValue(SWTObservables.observeText(text, SWT.Modify),
(-)src/org/eclipse/jface/tests/databinding/BindingTestSuite.java (-1 / +9 lines)
Lines 61-66 Link Here
61
import org.eclipse.core.tests.internal.databinding.BindingStatusTest;
61
import org.eclipse.core.tests.internal.databinding.BindingStatusTest;
62
import org.eclipse.core.tests.internal.databinding.QueueTest;
62
import org.eclipse.core.tests.internal.databinding.QueueTest;
63
import org.eclipse.core.tests.internal.databinding.RandomAccessListIteratorTest;
63
import org.eclipse.core.tests.internal.databinding.RandomAccessListIteratorTest;
64
import org.eclipse.core.tests.internal.databinding.UpdateExecutorTest;
65
import org.eclipse.core.tests.internal.databinding.UpdateValidationObservableValueTest;
64
import org.eclipse.core.tests.internal.databinding.beans.BeanObservableListDecoratorTest;
66
import org.eclipse.core.tests.internal.databinding.beans.BeanObservableListDecoratorTest;
65
import org.eclipse.core.tests.internal.databinding.beans.BeanObservableSetDecoratorTest;
67
import org.eclipse.core.tests.internal.databinding.beans.BeanObservableSetDecoratorTest;
66
import org.eclipse.core.tests.internal.databinding.beans.BeanObservableValueDecoratorTest;
68
import org.eclipse.core.tests.internal.databinding.beans.BeanObservableValueDecoratorTest;
Lines 132-137 Link Here
132
import org.eclipse.jface.tests.databinding.viewers.ObservableSetContentProviderTest;
134
import org.eclipse.jface.tests.databinding.viewers.ObservableSetContentProviderTest;
133
import org.eclipse.jface.tests.databinding.viewers.ObservableSetTreeContentProviderTest;
135
import org.eclipse.jface.tests.databinding.viewers.ObservableSetTreeContentProviderTest;
134
import org.eclipse.jface.tests.databinding.viewers.ViewersObservablesTest;
136
import org.eclipse.jface.tests.databinding.viewers.ViewersObservablesTest;
137
import org.eclipse.jface.tests.databinding.wizard.WizardPageSupportTest;
135
import org.eclipse.jface.tests.examples.databinding.mask.internal.EditMaskLexerAndTokenTest;
138
import org.eclipse.jface.tests.examples.databinding.mask.internal.EditMaskLexerAndTokenTest;
136
import org.eclipse.jface.tests.examples.databinding.mask.internal.EditMaskParserTest;
139
import org.eclipse.jface.tests.examples.databinding.mask.internal.EditMaskParserTest;
137
import org.eclipse.jface.tests.internal.databinding.swt.ButtonObservableValueTest;
140
import org.eclipse.jface.tests.internal.databinding.swt.ButtonObservableValueTest;
Lines 242-247 Link Here
242
		addTestSuite(BindingStatusTest.class);
245
		addTestSuite(BindingStatusTest.class);
243
		addTestSuite(RandomAccessListIteratorTest.class);
246
		addTestSuite(RandomAccessListIteratorTest.class);
244
		addTestSuite(QueueTest.class);
247
		addTestSuite(QueueTest.class);
248
		addTestSuite(UpdateExecutorTest.class);
249
		addTest(UpdateValidationObservableValueTest.suite());
245
250
246
		// org.eclipse.core.tests.internal.databinding.conversion
251
		// org.eclipse.core.tests.internal.databinding.conversion
247
		addTestSuite(DateConversionSupportTest.class);
252
		addTestSuite(DateConversionSupportTest.class);
Lines 332-338 Link Here
332
		addTestSuite(ObservableSetContentProviderTest.class);
337
		addTestSuite(ObservableSetContentProviderTest.class);
333
		addTestSuite(ObservableSetTreeContentProviderTest.class);
338
		addTestSuite(ObservableSetTreeContentProviderTest.class);
334
		addTestSuite(ViewersObservablesTest.class);
339
		addTestSuite(ViewersObservablesTest.class);
335
		
340
341
		// org.eclipse.jface.tests.databinding.wizard
342
		addTestSuite(WizardPageSupportTest.class);
343
336
		//org.eclipse.jface.tests.example.databinding.mask.internal
344
		//org.eclipse.jface.tests.example.databinding.mask.internal
337
		addTestSuite(EditMaskLexerAndTokenTest.class);
345
		addTestSuite(EditMaskLexerAndTokenTest.class);
338
		addTestSuite(EditMaskParserTest.class);
346
		addTestSuite(EditMaskParserTest.class);
(-)src/org/eclipse/core/tests/databinding/UpdateValueStrategyTest.java (-1 / +45 lines)
Lines 16-24 Link Here
16
import java.util.Date;
16
import java.util.Date;
17
17
18
import org.eclipse.core.databinding.UpdateValueStrategy;
18
import org.eclipse.core.databinding.UpdateValueStrategy;
19
import org.eclipse.core.databinding.conversion.Converter;
20
import org.eclipse.core.databinding.conversion.IConverter;
19
import org.eclipse.core.databinding.observable.value.IObservableValue;
21
import org.eclipse.core.databinding.observable.value.IObservableValue;
20
import org.eclipse.core.databinding.observable.value.WritableValue;
22
import org.eclipse.core.databinding.observable.value.WritableValue;
21
import org.eclipse.core.databinding.validation.IValidator;
23
import org.eclipse.core.databinding.validation.IValidator;
24
import org.eclipse.core.databinding.validation.IValidator2;
25
import org.eclipse.core.databinding.validation.ValidationStatus;
22
import org.eclipse.core.internal.databinding.validation.NumberToByteValidator;
26
import org.eclipse.core.internal.databinding.validation.NumberToByteValidator;
23
import org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator;
27
import org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator;
24
import org.eclipse.core.internal.databinding.validation.NumberToFloatValidator;
28
import org.eclipse.core.internal.databinding.validation.NumberToFloatValidator;
Lines 33-38 Link Here
33
import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator;
37
import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator;
34
import org.eclipse.core.internal.databinding.validation.StringToLongValidator;
38
import org.eclipse.core.internal.databinding.validation.StringToLongValidator;
35
import org.eclipse.core.internal.databinding.validation.StringToShortValidator;
39
import org.eclipse.core.internal.databinding.validation.StringToShortValidator;
40
import org.eclipse.core.runtime.IStatus;
36
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
41
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
37
42
38
/**
43
/**
Lines 138-144 Link Here
138
		
143
		
139
		assertSame(validator,strategy.validator);
144
		assertSame(validator,strategy.validator);
140
	}
145
	}
141
	
146
147
	public void testIsAsync_DefaultDelegatesToValidatorsAndConverter() throws Exception {
148
		IValidator2 asyncValidator = new IValidator2() {
149
			public IStatus validate(Object value) {
150
				return ValidationStatus.ok();
151
			}
152
153
			public boolean isAsync() {
154
				return true;
155
			}
156
		};
157
158
		IConverter asyncConverter = new Converter(null, null) {
159
			public Object convert(Object fromObject) {
160
				return null;
161
			}
162
163
			public boolean isAsync() {
164
				return true;
165
			}
166
		};
167
168
		UpdateValueStrategy strategy;
169
170
		strategy = new UpdateValueStrategy();
171
		assertFalse(strategy.isAsync());
172
173
		strategy = new UpdateValueStrategy().setAfterGetValidator(asyncValidator);
174
		assertTrue(strategy.isAsync());
175
176
		strategy = new UpdateValueStrategy().setConverter(asyncConverter);
177
		assertTrue(strategy.isAsync());
178
179
		strategy = new UpdateValueStrategy().setAfterConvertValidator(asyncValidator);
180
		assertTrue(strategy.isAsync());
181
182
		strategy = new UpdateValueStrategy().setBeforeSetValidator(asyncValidator);
183
		assertTrue(strategy.isAsync());
184
	}
185
142
	private void assertDefaultValidator(Class fromType, Class toType, Class validatorType) {
186
	private void assertDefaultValidator(Class fromType, Class toType, Class validatorType) {
143
		WritableValue source = WritableValue.withValueType(fromType);
187
		WritableValue source = WritableValue.withValueType(fromType);
144
		WritableValue destination = WritableValue.withValueType(toType);
188
		WritableValue destination = WritableValue.withValueType(toType);
(-)src/org/eclipse/core/tests/databinding/ValueBindingTest.java (-1 / +150 lines)
Lines 9-24 Link Here
9
 *     Brad Reynolds - initial API and implementation
9
 *     Brad Reynolds - initial API and implementation
10
 *     Brad Reynolds - bug 116920
10
 *     Brad Reynolds - bug 116920
11
 *     Brad Reynolds - bug 164653, 159768
11
 *     Brad Reynolds - bug 164653, 159768
12
 *     Ovidio Mallo - bug 233191
12
 ******************************************************************************/
13
 ******************************************************************************/
13
14
14
package org.eclipse.core.tests.databinding;
15
package org.eclipse.core.tests.databinding;
15
16
17
import java.util.Random;
18
19
import org.eclipse.core.databinding.AggregateValidationStatus;
16
import org.eclipse.core.databinding.Binding;
20
import org.eclipse.core.databinding.Binding;
17
import org.eclipse.core.databinding.DataBindingContext;
21
import org.eclipse.core.databinding.DataBindingContext;
18
import org.eclipse.core.databinding.UpdateValueStrategy;
22
import org.eclipse.core.databinding.UpdateValueStrategy;
23
import org.eclipse.core.databinding.conversion.Converter;
24
import org.eclipse.core.databinding.conversion.IConverter;
19
import org.eclipse.core.databinding.observable.Diffs;
25
import org.eclipse.core.databinding.observable.Diffs;
20
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
26
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
21
import org.eclipse.core.databinding.observable.value.IObservableValue;
27
import org.eclipse.core.databinding.observable.value.IObservableValue;
28
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
29
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
22
import org.eclipse.core.databinding.observable.value.ValueDiff;
30
import org.eclipse.core.databinding.observable.value.ValueDiff;
23
import org.eclipse.core.databinding.observable.value.WritableValue;
31
import org.eclipse.core.databinding.observable.value.WritableValue;
24
import org.eclipse.core.databinding.validation.IValidator;
32
import org.eclipse.core.databinding.validation.IValidator;
Lines 27-32 Link Here
27
import org.eclipse.core.runtime.IStatus;
35
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.core.runtime.MultiStatus;
36
import org.eclipse.core.runtime.MultiStatus;
29
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
37
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
38
import org.eclipse.swt.widgets.Display;
30
39
31
/**
40
/**
32
 * @since 1.1
41
 * @since 1.1
Lines 228-234 Link Here
228
		target.fireValueChange(Diffs.createValueDiff("", ""));
237
		target.fireValueChange(Diffs.createValueDiff("", ""));
229
		assertEquals("update does not occur", count, strategy.afterGetCount);
238
		assertEquals("update does not occur", count, strategy.afterGetCount);
230
	}
239
	}
231
	
240
241
	public void testIsAsync() throws Exception {
242
		UpdateValueStrategy asyncStrategy = new UpdateValueStrategy() {
243
			public boolean isAsync() {
244
				return true;
245
			}
246
		};
247
248
		assertFalse(dbc.bindValue(target, model, null, null).isAsync());
249
		assertTrue(dbc.bindValue(target, model, asyncStrategy, null).isAsync());
250
		assertTrue(dbc.bindValue(target, model, null, asyncStrategy).isAsync());
251
		assertTrue(dbc.bindValue(target, model, asyncStrategy, asyncStrategy)
252
				.isAsync());
253
	}
254
255
	public void testAsyncExecutesInSeparateThread() throws Exception {
256
		final Thread realmThread = Thread.currentThread();
257
258
		IConverter asyncConverter = new Converter(null, null) {
259
			public Object convert(Object fromObject) {
260
				assertNotSame(Thread.currentThread(), realmThread);
261
				return null;
262
			}
263
264
			public boolean isAsync() {
265
				return true;
266
			}
267
		};
268
269
		UpdateValueStrategy targetToModel = new UpdateValueStrategy();
270
		targetToModel.setConverter(asyncConverter);
271
		UpdateValueStrategy modelToTarget = new UpdateValueStrategy();
272
		modelToTarget.setConverter(asyncConverter);
273
274
		Binding binding = dbc.bindValue(target, model, targetToModel, modelToTarget);
275
276
		assertTrue(binding.isAsync());
277
278
		binding.updateTargetToModel();
279
		binding.updateModelToTarget();
280
	}
281
282
	public void testAsyncUpdateSerialization() throws Exception {
283
		UpdateValueStrategy targetToModel = new UpdateValueStrategy();
284
		targetToModel.setConverter(asyncConverter());
285
286
		UpdateValueStrategy modelToTarget = new UpdateValueStrategy();
287
		modelToTarget.setConverter(asyncConverter());
288
289
		dbc.bindValue(target, model, targetToModel, modelToTarget);
290
291
		// Write to the target.
292
		for (int i = 0; i <= 10; i++) {
293
			target.setValue(String.valueOf(i));
294
		}
295
		awaitPendingValidations(dbc);
296
		// Check that the updates to the model have been correctly serialized.
297
		assertEquals("10", model.getValue());
298
299
		// Write to the model.
300
		for (int i = 0; i <= 10; i++) {
301
			model.setValue(String.valueOf(i));
302
		}
303
		awaitPendingValidations(dbc);
304
		// Check that the updates to the target have been correctly serialized.
305
		assertEquals("10", target.getValue());
306
307
		// Alternatively write to the target and model.
308
		for (int i = 0; i <= 10; i++) {
309
			if (i % 2 == 0) {
310
				target.setValue(String.valueOf(i));
311
			} else {
312
				model.setValue(String.valueOf(i));
313
			}
314
		}
315
		awaitPendingValidations(dbc);
316
		// Check that target and model end up having the correct value.
317
		assertEquals("10", target.getValue());
318
		assertEquals("10", model.getValue());
319
	}
320
321
	public void testDisposeCancelsPendingUpdates() throws Exception {
322
		UpdateValueStrategy targetToModel = new UpdateValueStrategy();
323
		targetToModel.setConverter(asyncConverter());
324
325
		final Binding binding = dbc.bindValue(target, model, targetToModel,
326
				null);
327
328
		model.addValueChangeListener(new IValueChangeListener() {
329
			public void handleValueChange(ValueChangeEvent event) {
330
				fail("No update should get to the model if we dispose the Binding.");
331
			}
332
		});
333
334
		// Note that neither v1 nor v2 should make their way to the model since
335
		// we only dispatch the events on the UI thread by calling the method
336
		// awaitPendingValidations(...) below after having disposed the binding.
337
		target.setValue("v1");
338
		binding.dispose();
339
		target.setValue("v2");
340
341
		awaitPendingValidations(dbc);
342
	}
343
344
	private void awaitPendingValidations(DataBindingContext dbc) {
345
		AggregateValidationStatus validation = new AggregateValidationStatus(
346
				dbc, AggregateValidationStatus.MERGED);
347
348
		while (validation.isStale()) {
349
			// By dispatching on the Display, we get the pending runnables
350
			// executed on the Realm belonging to the Display.
351
			Display display = Display.getCurrent();
352
			while (display.readAndDispatch()) {
353
				// just dispatch
354
			}
355
			if (validation.isStale()) {
356
				display.sleep();
357
			}
358
		}
359
	}
360
232
	private IValidator warningValidator() {
361
	private IValidator warningValidator() {
233
		return new IValidator() {
362
		return new IValidator() {
234
			public IStatus validate(Object value) {
363
			public IStatus validate(Object value) {
Lines 261-266 Link Here
261
		};
390
		};
262
	}
391
	}
263
392
393
	private static IConverter asyncConverter() {
394
		return new Converter(String.class, String.class) {
395
396
			private final Random random = new Random(System.currentTimeMillis());
397
398
			public Object convert(Object fromObject) {
399
				try {
400
					Thread.sleep(random.nextInt(100));
401
				} catch (InterruptedException e) {
402
					// do nothing
403
				}
404
				return fromObject;
405
			}
406
407
			public boolean isAsync() {
408
				return true;
409
			}
410
		};
411
	}
412
264
	private static class ObservableValueStub extends AbstractObservableValue {
413
	private static class ObservableValueStub extends AbstractObservableValue {
265
		protected Object doGetValue() {
414
		protected Object doGetValue() {
266
			// do nothing
415
			// do nothing
(-)src/org/eclipse/core/tests/internal/databinding/UpdateValidationObservableValueTest.java (+134 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2007 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.core.tests.internal.databinding;
13
14
import junit.framework.Test;
15
import junit.framework.TestSuite;
16
17
import org.eclipse.core.databinding.observable.IObservable;
18
import org.eclipse.core.databinding.observable.IStaleListener;
19
import org.eclipse.core.databinding.observable.ObservableTracker;
20
import org.eclipse.core.databinding.observable.Realm;
21
import org.eclipse.core.databinding.observable.StaleEvent;
22
import org.eclipse.core.databinding.observable.value.IObservableValue;
23
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
24
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
25
import org.eclipse.core.databinding.validation.ValidationStatus;
26
import org.eclipse.core.internal.databinding.UpdateValidationObservableValue;
27
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.jface.databinding.conformance.MutableObservableValueContractTest;
29
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableValueContractDelegate;
30
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
31
32
/**
33
 * @since 1.2
34
 */
35
public class UpdateValidationObservableValueTest extends
36
		AbstractDefaultRealmTestCase {
37
38
	public static Test suite() {
39
		TestSuite suite = new TestSuite(
40
				UpdateValidationObservableValueTest.class.getName());
41
		suite.addTestSuite(UpdateValidationObservableValueTest.class);
42
		suite.addTest(MutableObservableValueContractTest.suite(new Delegate()));
43
		return suite;
44
	}
45
46
	public void testStaleness() {
47
		UpdateValidationObservableValueStub observable = new UpdateValidationObservableValueStub(
48
				Realm.getDefault());
49
50
		ValueChangeCounter valueChangeCounter = new ValueChangeCounter();
51
		observable.addValueChangeListener(valueChangeCounter);
52
53
		StaleCounter staleCounter = new StaleCounter();
54
		observable.addStaleListener(staleCounter);
55
56
		assertEquals(0, valueChangeCounter.count);
57
		assertEquals(0, staleCounter.count);
58
		assertFalse(observable.isStale());
59
60
		observable.setStale(true);
61
		assertEquals(0, valueChangeCounter.count);
62
		assertEquals(1, staleCounter.count);
63
		assertTrue(observable.isStale());
64
65
		observable.setStale(false);
66
		assertEquals(1, valueChangeCounter.count);
67
		assertEquals(1, staleCounter.count);
68
		assertFalse(observable.isStale());
69
	}
70
71
	/* package */static class Delegate extends
72
			AbstractObservableValueContractDelegate {
73
74
		public IObservableValue createObservableValue(Realm realm) {
75
			return new UpdateValidationObservableValueStub(realm);
76
		}
77
78
		public void change(IObservable observable) {
79
			IObservableValue observableValue = (IObservableValue) observable;
80
			observableValue.setValue(createValue(observableValue));
81
		}
82
83
		public void setStale(IObservable observable, boolean stale) {
84
			((UpdateValidationObservableValueStub) observable).setStale(stale);
85
		}
86
87
		public Object getValueType(IObservableValue observable) {
88
			return IStatus.class;
89
		}
90
91
		public Object createValue(IObservableValue observable) {
92
			IStatus status = (IStatus) observable.getValue();
93
			return ValidationStatus.error(status.getMessage() + "a");
94
		}
95
	}
96
97
	private static class UpdateValidationObservableValueStub extends
98
			UpdateValidationObservableValue {
99
100
		private boolean stale = false;
101
102
		public UpdateValidationObservableValueStub(Realm realm) {
103
			super(realm);
104
		}
105
106
		public boolean isStale() {
107
			ObservableTracker.getterCalled(this);
108
			return stale;
109
		}
110
111
		public void setStale(boolean stale) {
112
			this.stale = stale;
113
			updateStaleness();
114
		}
115
	}
116
117
	private static class ValueChangeCounter implements IValueChangeListener {
118
119
		int count;
120
121
		public void handleValueChange(ValueChangeEvent event) {
122
			count++;
123
		}
124
	}
125
126
	private static class StaleCounter implements IStaleListener {
127
128
		int count;
129
130
		public void handleStale(StaleEvent event) {
131
			count++;
132
		}
133
	}
134
}
(-)src/org/eclipse/core/tests/internal/databinding/UpdateExecutorTest.java (+250 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2007 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.core.tests.internal.databinding;
13
14
import java.util.ArrayList;
15
16
import junit.framework.TestCase;
17
18
import org.eclipse.core.internal.databinding.UpdateExecutor;
19
import org.eclipse.core.internal.databinding.UpdateRunnable;
20
21
/**
22
 * @since 1.2
23
 */
24
public class UpdateExecutorTest extends TestCase {
25
26
	public void testSyncExecutesInSameThread() throws Throwable {
27
		UpdateExecutor executor = new UpdateExecutor(false, null);
28
29
		final Thread mainThread = Thread.currentThread();
30
		TestUpdateRunnable update = new TestUpdateRunnable() {
31
			public void doRun() {
32
				assertSame(mainThread, Thread.currentThread());
33
				notifyDone();
34
			}
35
		};
36
37
		executor.execute(update, false);
38
39
		awaitPendingUpdates(executor);
40
		assertNotFailed(update);
41
	}
42
43
	public void testAsyncExecutesInSeparateThread() throws Throwable {
44
		UpdateExecutor executor = new UpdateExecutor(true, null);
45
46
		final Thread mainThread = Thread.currentThread();
47
		TestUpdateRunnable update = new TestUpdateRunnable() {
48
			public void doRun() {
49
				assertNotSame(mainThread, Thread.currentThread());
50
				notifyDone();
51
			}
52
		};
53
54
		executor.execute(update, false);
55
56
		awaitPendingUpdates(executor);
57
		assertNotFailed(update);
58
	}
59
60
	public void testHasPendingUpdates() throws Throwable {
61
		// Test for both, synchronous and asynchronous executions.
62
		testHasPendingUpdates(false);
63
		testHasPendingUpdates(true);
64
	}
65
66
	private static void testHasPendingUpdates(boolean isAsync) throws Throwable {
67
		final UpdateExecutor executor = new UpdateExecutor(isAsync, null);
68
69
		TestUpdateRunnable update = new TestUpdateRunnable() {
70
			public void doRun() {
71
				assertTrue(executor.hasPendingUpdates());
72
				notifyDone();
73
				assertFalse(executor.hasPendingUpdates());
74
			}
75
		};
76
77
		assertFalse(executor.hasPendingUpdates());
78
		executor.execute(update, false);
79
80
		awaitPendingUpdates(executor);
81
		assertNotFailed(update);
82
	}
83
84
	public void testSchedulingEventCallbackInvoked() throws Throwable {
85
		// Test for both, synchronous and asynchronous executions.
86
		testSchedulingEventCallbackInvoked(false);
87
		testSchedulingEventCallbackInvoked(true);
88
	}
89
90
	private static void testSchedulingEventCallbackInvoked(boolean isAsync)
91
			throws Throwable {
92
		final SchedulingEventCallbackCounter callbackCounter = new SchedulingEventCallbackCounter();
93
		UpdateExecutor executor = new UpdateExecutor(isAsync, callbackCounter);
94
95
		assertEquals(0, callbackCounter.count);
96
97
		TestUpdateRunnable update = new TestUpdateRunnable() {
98
			public void doRun() {
99
				// At this point, we must already have received the scheduling
100
				// event of having started this update.
101
				assertEquals(1, callbackCounter.count);
102
				notifyDone();
103
				// At this point, we must have received the scheduling event
104
				// about the update having terminated.
105
				assertEquals(2, callbackCounter.count);
106
			}
107
		};
108
109
		executor.execute(update, false);
110
111
		awaitPendingUpdates(executor);
112
		assertNotFailed(update);
113
	}
114
115
	public void testAsyncExecuteInOrder() throws Throwable {
116
		UpdateExecutor executor = new UpdateExecutor(true, null);
117
118
		final ArrayList updateList = new ArrayList();
119
		for (int i = 0; i < 10; i++) {
120
			updateList.add(new TestUpdateRunnable() {
121
				public void doRun() {
122
					assertSame(updateList.remove(0), this);
123
					notifyDone();
124
				}
125
			});
126
		}
127
128
		// The original list is modified by the updates, so we make a copy.
129
		ArrayList updateListCopy = new ArrayList(updateList);
130
		for (int i = 0; i < updateListCopy.size(); i++) {
131
			UpdateRunnable update = (UpdateRunnable) updateListCopy.get(i);
132
			executor.execute(update, false);
133
		}
134
135
		awaitPendingUpdates(executor);
136
		for (int i = 0; i < updateListCopy.size(); i++) {
137
			assertNotFailed((TestUpdateRunnable) updateListCopy.get(i));
138
		}
139
	}
140
141
	public void testCancelPendingUpdates() throws Throwable {
142
		// Test for both, canceling and not canceling pending updates.
143
		testCancelPendingUpdates(false);
144
		testCancelPendingUpdates(true);
145
	}
146
147
	private void testCancelPendingUpdates(boolean doCancel) throws Throwable {
148
		UpdateExecutor executor = new UpdateExecutor(true, null);
149
150
		// In order to reliably ensure that update1 has not already terminated
151
		// when we request its canceling, we start the two updates while holding
152
		// a lock which is also tried to be acquired by update1.
153
		synchronized (this) {
154
			TestUpdateRunnable update1 = new TestUpdateRunnable() {
155
				public void doRun() {
156
					synchronized (UpdateExecutorTest.this) {
157
						// just try to acquire the lock
158
					}
159
					notifyDone();
160
				}
161
			};
162
163
			executor.execute(update1, false);
164
165
			assertFalse(update1.isCanceled());
166
			executor.execute(new TestUpdateRunnable(), doCancel);
167
			assertEquals(doCancel, update1.isCanceled());
168
		}
169
	}
170
171
	public void testTerminateCancelsPendingUpdates() throws Throwable {
172
		UpdateExecutor executor = new UpdateExecutor(true, null);
173
174
		// In order to reliably ensure that the update has not already
175
		// terminated when we request its canceling, we start the two updates
176
		// while holding a lock which is also tried to be acquired by the
177
		// update.
178
		synchronized (this) {
179
			TestUpdateRunnable update = new TestUpdateRunnable() {
180
				public void doRun() {
181
					synchronized (UpdateExecutorTest.this) {
182
						// just try to acquire the lock
183
					}
184
					notifyDone();
185
				}
186
			};
187
188
			executor.execute(update, false);
189
190
			assertFalse(update.isCanceled());
191
			executor.terminate();
192
			assertTrue(update.isCanceled());
193
		}
194
	}
195
196
	private static void awaitPendingUpdates(UpdateExecutor executor) {
197
		while (executor.hasPendingUpdates()) {
198
			try {
199
				// avoid continuous polling
200
				Thread.sleep(20);
201
			} catch (InterruptedException e) {
202
				// just go ahead
203
			}
204
		}
205
	}
206
207
	private static void assertNotFailed(TestUpdateRunnable update)
208
			throws Throwable {
209
		if (update.throwable != null) {
210
			throw update.throwable;
211
		}
212
	}
213
214
	/**
215
	 * Simple extension of an UpdateRunnable which catches any Throwable thrown
216
	 * during the execution of the {@link #run()} method and stores it in a
217
	 * class field for later querying. We do this to be able to use JUnit
218
	 * assertions from within the run method since when running an
219
	 * UpdateRunnable asynchronously, the UpdateExecutor class executes it from
220
	 * within a Job which would otherwise swallow all Throwables.
221
	 * 
222
	 * TODO Maybe, this could be done more elegantly?
223
	 */
224
	private static class TestUpdateRunnable extends UpdateRunnable {
225
226
		public Throwable throwable = null;
227
228
		public void run() {
229
			try {
230
				doRun();
231
			} catch (Throwable t) {
232
				throwable = t;
233
				notifyDone();
234
			}
235
		}
236
237
		public void doRun() {
238
			// do nothing
239
		}
240
	}
241
242
	private static class SchedulingEventCallbackCounter implements Runnable {
243
244
		public int count = 0;
245
246
		public void run() {
247
			count++;
248
		}
249
	}
250
}
(-)src/org/eclipse/jface/tests/databinding/wizard/WizardPageSupportTest.java (+138 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.jface.tests.databinding.wizard;
13
14
import org.eclipse.core.databinding.DataBindingContext;
15
import org.eclipse.core.databinding.ValidationStatusProvider;
16
import org.eclipse.core.databinding.observable.Diffs;
17
import org.eclipse.core.databinding.observable.Observables;
18
import org.eclipse.core.databinding.observable.Realm;
19
import org.eclipse.core.databinding.observable.list.IObservableList;
20
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
21
import org.eclipse.core.databinding.observable.value.IObservableValue;
22
import org.eclipse.core.databinding.validation.ValidationStatus;
23
import org.eclipse.core.internal.commands.util.Util;
24
import org.eclipse.core.runtime.IStatus;
25
import org.eclipse.jface.databinding.wizard.WizardPageSupport;
26
import org.eclipse.jface.tests.databinding.AbstractSWTTestCase;
27
import org.eclipse.jface.wizard.IWizardPage;
28
import org.eclipse.jface.wizard.Wizard;
29
import org.eclipse.jface.wizard.WizardDialog;
30
import org.eclipse.jface.wizard.WizardPage;
31
import org.eclipse.swt.widgets.Composite;
32
33
/**
34
 * @since 1.2
35
 */
36
public class WizardPageSupportTest extends AbstractSWTTestCase {
37
38
	public void testPageCompleteOnValidationStaleness() {
39
		IWizardPage page = new WizardPage("Page") {
40
			public void createControl(Composite parent) {
41
				setControl(parent);
42
43
				ValidationObservable validation = new ValidationObservable();
44
45
				DataBindingContext dbc = new DataBindingContext();
46
				dbc.addValidationStatusProvider(new ValidationProvider(
47
						validation));
48
49
				WizardPageSupport.create(this, dbc);
50
51
				assertTrue(isPageComplete());
52
53
				validation.setStale(true);
54
				assertFalse(isPageComplete());
55
56
				validation.setStale(false);
57
				assertTrue(isPageComplete());
58
			}
59
		};
60
61
		loadWizardPage(page);
62
	}
63
64
	private void loadWizardPage(IWizardPage page) {
65
		Wizard wizard = new Wizard() {
66
			public boolean performFinish() {
67
				return true;
68
			}
69
		};
70
		wizard.addPage(page);
71
72
		WizardDialog dialog = new WizardDialog(getShell(), wizard);
73
		dialog.create();
74
	}
75
76
	private static class ValidationObservable extends AbstractObservableValue {
77
78
		private Object value = ValidationStatus.ok();
79
80
		private boolean stale = false;
81
82
		public ValidationObservable() {
83
			super(Realm.getDefault());
84
		}
85
86
		protected Object doGetValue() {
87
			return value;
88
		}
89
90
		protected void doSetValue(Object value) {
91
			Object oldValue = this.value;
92
			this.value = value;
93
			if (!Util.equals(oldValue, value)) {
94
				fireValueChange(Diffs.createValueDiff(oldValue, value));
95
			}
96
		}
97
98
		public boolean isStale() {
99
			return stale;
100
		}
101
102
		public void setStale(boolean stale) {
103
			if (this.stale != stale) {
104
				this.stale = stale;
105
				if (stale) {
106
					fireStale();
107
				} else {
108
					fireValueChange(Diffs.createValueDiff(value, value));
109
				}
110
			}
111
		}
112
113
		public Object getValueType() {
114
			return IStatus.class;
115
		}
116
	}
117
118
	private static class ValidationProvider extends ValidationStatusProvider {
119
120
		private final IObservableValue validation;
121
122
		public ValidationProvider(IObservableValue validation) {
123
			this.validation = validation;
124
		}
125
126
		public IObservableValue getValidationStatus() {
127
			return validation;
128
		}
129
130
		public IObservableList getTargets() {
131
			return Observables.emptyObservableList();
132
		}
133
134
		public IObservableList getModels() {
135
			return Observables.emptyObservableList();
136
		}
137
	}
138
}
(-)src/org/eclipse/jface/databinding/wizard/WizardPageSupport.java (-3 / +23 lines)
Lines 9-15 Link Here
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Boris Bokowski - bug 218269
10
 *     Boris Bokowski - bug 218269
11
 *     Matthew Hall - bug 218269
11
 *     Matthew Hall - bug 218269
12
 *     Ashley Cambrell - bug 199179 
12
 *     Ashley Cambrell - bug 199179
13
 *     Ovidio Mallo - bug 233191
13
 *******************************************************************************/
14
 *******************************************************************************/
14
package org.eclipse.jface.databinding.wizard;
15
package org.eclipse.jface.databinding.wizard;
15
16
Lines 21-26 Link Here
21
import org.eclipse.core.databinding.observable.ChangeEvent;
22
import org.eclipse.core.databinding.observable.ChangeEvent;
22
import org.eclipse.core.databinding.observable.IChangeListener;
23
import org.eclipse.core.databinding.observable.IChangeListener;
23
import org.eclipse.core.databinding.observable.IObservable;
24
import org.eclipse.core.databinding.observable.IObservable;
25
import org.eclipse.core.databinding.observable.IStaleListener;
26
import org.eclipse.core.databinding.observable.StaleEvent;
24
import org.eclipse.core.databinding.observable.list.IListChangeListener;
27
import org.eclipse.core.databinding.observable.list.IListChangeListener;
25
import org.eclipse.core.databinding.observable.list.IObservableList;
28
import org.eclipse.core.databinding.observable.list.IObservableList;
26
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
29
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
Lines 41-46 Link Here
41
 * given wizard page, updating the wizard page's completion state and its error
44
 * given wizard page, updating the wizard page's completion state and its error
42
 * message accordingly.
45
 * message accordingly.
43
 * 
46
 * 
47
 * <p>
48
 * The completion state of the wizard page will only be set to <code>true</code>
49
 * if <i>all</i> of the following conditions are met:
50
 * <ul>
51
 * <li>The validation result from the data binding context has none of the
52
 * severities {@link IStatus#ERROR} and {@link IStatus#CANCEL}.</li>
53
 * <li>None of the validation status observables of the data binding context is
54
 * stale.</li>
55
 * </ul>
56
 * </p>
57
 * 
44
 * @noextend This class is not intended to be subclassed by clients.
58
 * @noextend This class is not intended to be subclassed by clients.
45
 *
59
 *
46
 * @since 1.1
60
 * @since 1.1
Lines 137-142 Link Here
137
				handleStatusChanged();
151
				handleStatusChanged();
138
			}
152
			}
139
		});
153
		});
154
		aggregateStatus.addStaleListener(new IStaleListener() {
155
			public void handleStale(StaleEvent staleEvent) {
156
				handleStatusChanged();
157
			}
158
		});
140
		currentStatus = (IStatus) aggregateStatus.getValue();
159
		currentStatus = (IStatus) aggregateStatus.getValue();
141
		handleStatusChanged();
160
		handleStatusChanged();
142
		dbc.getValidationStatusProviders().addListChangeListener(
161
		dbc.getValidationStatusProviders().addListChangeListener(
Lines 188-194 Link Here
188
		} else if (currentStatus != null
207
		} else if (currentStatus != null
189
				&& currentStatus.getSeverity() != IStatus.OK) {
208
				&& currentStatus.getSeverity() != IStatus.OK) {
190
			int severity = currentStatus.getSeverity();
209
			int severity = currentStatus.getSeverity();
191
			wizardPage.setPageComplete((severity & IStatus.CANCEL) != 0);
210
			wizardPage.setPageComplete(((severity & IStatus.CANCEL) == 0)
211
					&& !aggregateStatus.isStale());
192
			int type;
212
			int type;
193
			switch (severity) {
213
			switch (severity) {
194
			case IStatus.OK:
214
			case IStatus.OK:
Lines 213-219 Link Here
213
			wizardPage.setErrorMessage(null);
233
			wizardPage.setErrorMessage(null);
214
			wizardPage.setMessage(currentStatus.getMessage(), type);
234
			wizardPage.setMessage(currentStatus.getMessage(), type);
215
		} else {
235
		} else {
216
			wizardPage.setPageComplete(true);
236
			wizardPage.setPageComplete(!aggregateStatus.isStale());
217
			wizardPage.setMessage(null);
237
			wizardPage.setMessage(null);
218
			wizardPage.setErrorMessage(null);
238
			wizardPage.setErrorMessage(null);
219
		}
239
		}
(-)src/org/eclipse/core/databinding/Binding.java (-1 / +21 lines)
Lines 10-15 Link Here
10
 *     Brad Reynolds - bug 159768
10
 *     Brad Reynolds - bug 159768
11
 *     Boris Bokowski - bug 218269
11
 *     Boris Bokowski - bug 218269
12
 *     Matthew Hall - bug 218269
12
 *     Matthew Hall - bug 218269
13
 *     Ovidio Mallo - bug 233191
13
 *******************************************************************************/
14
 *******************************************************************************/
14
15
15
package org.eclipse.core.databinding;
16
package org.eclipse.core.databinding;
Lines 110-116 Link Here
110
	 * by the time this call returns.
111
	 * by the time this call returns.
111
	 */
112
	 */
112
	public abstract void validateModelToTarget();
113
	public abstract void validateModelToTarget();
113
	
114
115
	/**
116
	 * Returns whether the updates performed by this binding are executed
117
	 * asynchronously.
118
	 * 
119
	 * <p>
120
	 * By default, this method returns <code>false</code>. Subclasses will
121
	 * typically want to overwrite this method to return whether <i>any</i> of
122
	 * the binding's update strategies is intended to be run asynchronously.
123
	 * However, subclasses may always decide not to execute the updates
124
	 * asynchronously.
125
	 * </p>
126
	 * 
127
	 * @return whether the updates performed by this binding are executed
128
	 *         asynchronously.
129
	 */
130
	public boolean isAsync() {
131
		return false;
132
	}
133
114
	/**
134
	/**
115
	 * Disposes of this Binding. Subclasses may extend, but must call super.dispose().
135
	 * Disposes of this Binding. Subclasses may extend, but must call super.dispose().
116
	 */
136
	 */
(-)src/org/eclipse/core/databinding/UpdateListStrategy.java (+33 lines)
Lines 229-232 Link Here
229
		}
229
		}
230
		return Status.OK_STATUS;
230
		return Status.OK_STATUS;
231
	}
231
	}
232
233
	/**
234
	 * Returns whether the list update defined by this strategy is intended to
235
	 * be executed asynchronously.
236
	 * 
237
	 * <p>
238
	 * By default, this method returns <code>true</code> if and only if the
239
	 * converter associated to this strategy is intended to be executed
240
	 * asynchronously. Clients may extend without having to call the super
241
	 * implementation.
242
	 * </p>
243
	 * 
244
	 * <p>
245
	 * Note that even if this method returns <code>true</code>, there is no
246
	 * guarantee as of whether the list update will indeed by executed
247
	 * asynchronously or not.
248
	 * </p>
249
	 * 
250
	 * @return whether the list update defined by this strategy is intended to
251
	 *         be executed asynchronously.
252
	 * 
253
	 * @see IConverter#isAsync()
254
	 */
255
	public boolean isAsync() {
256
		return isAsyncConverter(converter);
257
	}
258
259
	private boolean isAsyncConverter(IConverter converter) {
260
		if (converter != null) {
261
			return converter.isAsync();
262
		}
263
		return false;
264
	}
232
}
265
}
(-)src/org/eclipse/core/databinding/UpdateStrategy.java (+19 lines)
Lines 68-73 Link Here
68
68
69
	private static Map converterMap;
69
	private static Map converterMap;
70
70
71
	/**
72
	 * Returns whether the update defined by this strategy is intended to be
73
	 * executed asynchronously.
74
	 * 
75
	 * <p>
76
	 * By default, this method returns <code>false</code>.
77
	 * </p>
78
	 * 
79
	 * @return whether the update defined by this strategy is intended to be
80
	 *         executed asynchronously.
81
	 */
82
	public boolean isAsync() {
83
		return false;
84
	}
85
71
	private static Class autoboxed(Class clazz) {
86
	private static Class autoboxed(Class clazz) {
72
		if (clazz == Float.TYPE)
87
		if (clazz == Float.TYPE)
73
			return Float.class;
88
			return Float.class;
Lines 705-710 Link Here
705
		public Object getToType() {
720
		public Object getToType() {
706
			return toType;
721
			return toType;
707
		}
722
		}
723
724
		public boolean isAsync() {
725
			return false;
726
		}
708
	}
727
	}
709
728
710
}
729
}
(-)src/org/eclipse/core/databinding/UpdateValueStrategy.java (+46 lines)
Lines 9-14 Link Here
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Matt Carter - Character support completed (bug 197679)
10
 *     Matt Carter - Character support completed (bug 197679)
11
 *     Tom Schindl<tom.schindl@bestsolution.at> - bugfix for 217940
11
 *     Tom Schindl<tom.schindl@bestsolution.at> - bugfix for 217940
12
 *     Ovidio Mallo - bug 233191
12
 *******************************************************************************/
13
 *******************************************************************************/
13
14
14
package org.eclipse.core.databinding;
15
package org.eclipse.core.databinding;
Lines 19-24 Link Here
19
import org.eclipse.core.databinding.conversion.IConverter;
20
import org.eclipse.core.databinding.conversion.IConverter;
20
import org.eclipse.core.databinding.observable.value.IObservableValue;
21
import org.eclipse.core.databinding.observable.value.IObservableValue;
21
import org.eclipse.core.databinding.validation.IValidator;
22
import org.eclipse.core.databinding.validation.IValidator;
23
import org.eclipse.core.databinding.validation.IValidator2;
22
import org.eclipse.core.databinding.validation.ValidationStatus;
24
import org.eclipse.core.databinding.validation.ValidationStatus;
23
import org.eclipse.core.internal.databinding.BindingMessages;
25
import org.eclipse.core.internal.databinding.BindingMessages;
24
import org.eclipse.core.internal.databinding.Pair;
26
import org.eclipse.core.internal.databinding.Pair;
Lines 490-495 Link Here
490
		return Status.OK_STATUS;
492
		return Status.OK_STATUS;
491
	}
493
	}
492
494
495
	/**
496
	 * Returns whether the value update defined by this strategy is intended to
497
	 * be executed asynchronously.
498
	 * 
499
	 * <p>
500
	 * By default, this method returns <code>true</code> if and only if
501
	 * <i>any</i> of the validators or the converter associated to this strategy
502
	 * is intended to be executed asynchronously. Clients may extend without
503
	 * having to call the super implementation.
504
	 * </p>
505
	 * 
506
	 * <p>
507
	 * Note that even if this method returns <code>true</code>, there is no
508
	 * guarantee as of whether the value update will indeed by executed
509
	 * asynchronously or not.
510
	 * </p>
511
	 * 
512
	 * @return whether the value update defined by this strategy is intended to
513
	 *         be executed asynchronously.
514
	 * 
515
	 * @see IValidator2#isAsync()
516
	 * @see IConverter#isAsync()
517
	 */
518
	public boolean isAsync() {
519
		return isAsyncValidator(afterGetValidator)
520
				|| isAsyncConverter(converter)
521
				|| isAsyncValidator(afterConvertValidator)
522
				|| isAsyncValidator(beforeSetValidator);
523
	}
524
525
	private boolean isAsyncValidator(IValidator validator) {
526
		if (validator instanceof IValidator2) {
527
			return ((IValidator2) validator).isAsync();
528
		}
529
		return false;
530
	}
531
532
	private boolean isAsyncConverter(IConverter converter) {
533
		if (converter != null) {
534
			return converter.isAsync();
535
		}
536
		return false;
537
	}
538
493
	private static class ValidatorRegistry {
539
	private static class ValidatorRegistry {
494
540
495
		private HashMap validators = new HashMap();
541
		private HashMap validators = new HashMap();
(-)src/org/eclipse/core/databinding/ListBinding.java (-17 / +73 lines)
Lines 14-30 Link Here
14
import java.util.Collections;
14
import java.util.Collections;
15
15
16
import org.eclipse.core.databinding.observable.Diffs;
16
import org.eclipse.core.databinding.observable.Diffs;
17
import org.eclipse.core.databinding.observable.ObservableTracker;
17
import org.eclipse.core.databinding.observable.list.IListChangeListener;
18
import org.eclipse.core.databinding.observable.list.IListChangeListener;
18
import org.eclipse.core.databinding.observable.list.IObservableList;
19
import org.eclipse.core.databinding.observable.list.IObservableList;
19
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
20
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
20
import org.eclipse.core.databinding.observable.list.ListDiff;
21
import org.eclipse.core.databinding.observable.list.ListDiff;
21
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
22
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
22
import org.eclipse.core.databinding.observable.value.IObservableValue;
23
import org.eclipse.core.databinding.observable.value.IObservableValue;
23
import org.eclipse.core.databinding.observable.value.WritableValue;
24
import org.eclipse.core.internal.databinding.BindingStatus;
24
import org.eclipse.core.internal.databinding.BindingStatus;
25
import org.eclipse.core.internal.databinding.UpdateExecutor;
26
import org.eclipse.core.internal.databinding.UpdateRunnable;
27
import org.eclipse.core.internal.databinding.UpdateValidationObservableValue;
25
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.core.runtime.IStatus;
26
import org.eclipse.core.runtime.MultiStatus;
29
import org.eclipse.core.runtime.MultiStatus;
27
import org.eclipse.core.runtime.Status;
28
30
29
/**
31
/**
30
 * @since 1.0
32
 * @since 1.0
Lines 34-42 Link Here
34
36
35
	private UpdateListStrategy targetToModel;
37
	private UpdateListStrategy targetToModel;
36
	private UpdateListStrategy modelToTarget;
38
	private UpdateListStrategy modelToTarget;
37
	private IObservableValue validationStatusObservable;
39
	private UpdateValidationObservableValue validationStatusObservable;
38
	private boolean updatingTarget;
40
	private boolean updatingTarget;
39
	private boolean updatingModel;
41
	private boolean updatingModel;
42
	private UpdateExecutor updateExecutor;
40
43
41
	private IListChangeListener targetChangeListener = new IListChangeListener() {
44
	private IListChangeListener targetChangeListener = new IListChangeListener() {
42
		public void handleListChange(ListChangeEvent event) {
45
		public void handleListChange(ListChangeEvent event) {
Lines 86-93 Link Here
86
	}
89
	}
87
90
88
	protected void preInit() {
91
	protected void preInit() {
89
		validationStatusObservable = new WritableValue(context
92
		updateExecutor = new UpdateExecutor(isAsync(), new Runnable() {
90
				.getValidationRealm(), Status.OK_STATUS, IStatus.class);
93
			public void run() {
94
				// If we are in asynchronous mode, we must update the staleness
95
				// of the validation observable upon every scheduling event.
96
				if (isAsync()) {
97
					validationStatusObservable.getRealm().exec(new Runnable() {
98
						public void run() {
99
							validationStatusObservable.updateStaleness();
100
						}
101
					});
102
				}
103
			}
104
		});
105
106
		validationStatusObservable = new UpdateValidationObservableValue(
107
				context.getValidationRealm()) {
108
			public boolean isStale() {
109
				ObservableTracker.getterCalled(this);
110
				// If not in asynchronous mode, we never set the validation
111
				// observable to be stale.
112
				return ListBinding.this.isAsync()
113
						&& updateExecutor.hasPendingUpdates();
114
			}
115
		};
91
	}
116
	}
92
117
93
	protected void postInit() {
118
	protected void postInit() {
Lines 131-136 Link Here
131
		// nothing for now
156
		// nothing for now
132
	}
157
	}
133
158
159
	public boolean isAsync() {
160
		return targetToModel.isAsync() || modelToTarget.isAsync();
161
	}
162
134
	/*
163
	/*
135
	 * This method may be moved to UpdateListStrategy in the future if clients
164
	 * This method may be moved to UpdateListStrategy in the future if clients
136
	 * need more control over how the two lists are kept in sync.
165
	 * need more control over how the two lists are kept in sync.
Lines 140-149 Link Here
140
			final UpdateListStrategy updateListStrategy,
169
			final UpdateListStrategy updateListStrategy,
141
			final boolean explicit, final boolean clearDestination) {
170
			final boolean explicit, final boolean clearDestination) {
142
		final int policy = updateListStrategy.getUpdatePolicy();
171
		final int policy = updateListStrategy.getUpdatePolicy();
143
		if (policy != UpdateListStrategy.POLICY_NEVER) {
172
		if (policy == UpdateListStrategy.POLICY_NEVER)
144
			if (policy != UpdateListStrategy.POLICY_ON_REQUEST || explicit) {
173
			return;
174
		if (policy == UpdateListStrategy.POLICY_ON_REQUEST && !explicit)
175
			return;
176
177
		UpdateRunnable update = new UpdateRunnable() {
178
			public void run() {
179
				final ListDiffEntry[] diffEntries = diff.getDifferences();
180
				for (int i = 0; i < diffEntries.length; i++) {
181
					ListDiffEntry entry = diffEntries[i];
182
					if (entry.isAddition()) {
183
						diffEntries[i] = Diffs.createListDiffEntry(entry
184
								.getPosition(), entry.isAddition(),
185
								updateListStrategy.convert(entry.getElement()));
186
					}
187
				}
188
145
				destination.getRealm().exec(new Runnable() {
189
				destination.getRealm().exec(new Runnable() {
146
					public void run() {
190
					public void run() {
191
						// If the update has been canceled before writing to the
192
						// destination observable, we do not set the validation
193
						// status, so we return outside the below try block.
194
						if (isCanceled()) {
195
							notifyDone();
196
							return;
197
						}
198
147
						if (destination == getTarget()) {
199
						if (destination == getTarget()) {
148
							updatingTarget = true;
200
							updatingTarget = true;
149
						} else {
201
						} else {
Lines 155-183 Link Here
155
							if (clearDestination) {
207
							if (clearDestination) {
156
								destination.clear();
208
								destination.clear();
157
							}
209
							}
158
							ListDiffEntry[] diffEntries = diff.getDifferences();
159
							for (int i = 0; i < diffEntries.length; i++) {
210
							for (int i = 0; i < diffEntries.length; i++) {
160
								ListDiffEntry listDiffEntry = diffEntries[i];
211
								ListDiffEntry listDiffEntry = diffEntries[i];
161
								if (listDiffEntry.isAddition()) {
212
								if (listDiffEntry.isAddition()) {
162
									IStatus setterStatus = updateListStrategy
213
									IStatus setterStatus = updateListStrategy
163
											.doAdd(
214
											.doAdd(destination, listDiffEntry
164
													destination,
215
													.getElement(),
165
													updateListStrategy
166
															.convert(listDiffEntry
167
																	.getElement()),
168
													listDiffEntry.getPosition());
216
													listDiffEntry.getPosition());
169
217
170
									mergeStatus(multiStatus, setterStatus);
218
									mergeStatus(multiStatus, setterStatus);
171
									// TODO - at this point, the two lists
219
									// TODO - at this point, the two lists
172
									// will be out of sync if an error occurred...
220
									// will be out of sync if an error
221
									// occurred...
173
								} else {
222
								} else {
174
									IStatus setterStatus = updateListStrategy
223
									IStatus setterStatus = updateListStrategy
175
											.doRemove(destination,
224
											.doRemove(destination,
176
													listDiffEntry.getPosition());
225
													listDiffEntry.getPosition());
177
									
226
178
									mergeStatus(multiStatus, setterStatus);
227
									mergeStatus(multiStatus, setterStatus);
179
									// TODO - at this point, the two lists
228
									// TODO - at this point, the two lists
180
									// will be out of sync if an error occurred...
229
									// will be out of sync if an error
230
									// occurred...
181
								}
231
								}
182
							}
232
							}
183
						} finally {
233
						} finally {
Lines 188-198 Link Here
188
							} else {
238
							} else {
189
								updatingModel = false;
239
								updatingModel = false;
190
							}
240
							}
241
242
							notifyDone();
191
						}
243
						}
192
					}
244
					}
193
				});
245
				});
194
			}
246
			}
195
		}
247
		};
248
249
		updateExecutor.execute(update, false);
196
	}
250
	}
197
251
198
	/**
252
	/**
Lines 209-214 Link Here
209
	}
263
	}
210
264
211
	public void dispose() {
265
	public void dispose() {
266
		updateExecutor.terminate();
267
212
		if (targetChangeListener != null) {
268
		if (targetChangeListener != null) {
213
			((IObservableList)getTarget()).removeListChangeListener(targetChangeListener);
269
			((IObservableList)getTarget()).removeListChangeListener(targetChangeListener);
214
			targetChangeListener = null;
270
			targetChangeListener = null;
(-)src/org/eclipse/core/databinding/ValueBinding.java (-72 / +150 lines)
Lines 8-23 Link Here
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     Matthew Hall - bug 220700
10
 *     Matthew Hall - bug 220700
11
 *     Ovidio Mallo - bug 233191
11
 *******************************************************************************/
12
 *******************************************************************************/
12
13
13
package org.eclipse.core.databinding;
14
package org.eclipse.core.databinding;
14
15
16
import org.eclipse.core.databinding.observable.ObservableTracker;
15
import org.eclipse.core.databinding.observable.value.IObservableValue;
17
import org.eclipse.core.databinding.observable.value.IObservableValue;
16
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
18
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
17
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
19
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
18
import org.eclipse.core.databinding.observable.value.WritableValue;
19
import org.eclipse.core.databinding.util.Policy;
20
import org.eclipse.core.databinding.util.Policy;
20
import org.eclipse.core.internal.databinding.BindingStatus;
21
import org.eclipse.core.internal.databinding.BindingStatus;
22
import org.eclipse.core.internal.databinding.UpdateExecutor;
23
import org.eclipse.core.internal.databinding.UpdateRunnable;
24
import org.eclipse.core.internal.databinding.UpdateValidationObservableValue;
21
import org.eclipse.core.internal.databinding.Util;
25
import org.eclipse.core.internal.databinding.Util;
22
import org.eclipse.core.runtime.IStatus;
26
import org.eclipse.core.runtime.IStatus;
23
import org.eclipse.core.runtime.MultiStatus;
27
import org.eclipse.core.runtime.MultiStatus;
Lines 30-38 Link Here
30
class ValueBinding extends Binding {
34
class ValueBinding extends Binding {
31
	private final UpdateValueStrategy targetToModel;
35
	private final UpdateValueStrategy targetToModel;
32
	private final UpdateValueStrategy modelToTarget;
36
	private final UpdateValueStrategy modelToTarget;
33
	private WritableValue validationStatusObservable;
37
	private UpdateValidationObservableValue validationStatusObservable;
34
	private IObservableValue target;
38
	private IObservableValue target;
35
	private IObservableValue model;
39
	private IObservableValue model;
40
	private UpdateExecutor updateExecutor;
36
41
37
	private boolean updatingTarget;
42
	private boolean updatingTarget;
38
	private boolean updatingModel;
43
	private boolean updatingModel;
Lines 78-85 Link Here
78
	}
83
	}
79
84
80
	protected void preInit() {
85
	protected void preInit() {
81
		validationStatusObservable = new WritableValue(context
86
		updateExecutor = new UpdateExecutor(isAsync(), new Runnable() {
82
				.getValidationRealm(), Status.OK_STATUS, IStatus.class);
87
			public void run() {
88
				// If we are in asynchronous mode, we must update the staleness
89
				// of the validation observable upon every scheduling event.
90
				if (isAsync()) {
91
					validationStatusObservable.getRealm().exec(new Runnable() {
92
						public void run() {
93
							validationStatusObservable.updateStaleness();
94
						}
95
					});
96
				}
97
			}
98
		});
99
100
		validationStatusObservable = new UpdateValidationObservableValue(
101
				context.getValidationRealm()) {
102
			public boolean isStale() {
103
				ObservableTracker.getterCalled(this);
104
				// If not in asynchronous mode, we never set the validation
105
				// observable to be stale.
106
				return ValueBinding.this.isAsync()
107
						&& updateExecutor.hasPendingUpdates();
108
			}
109
		};
83
	}
110
	}
84
111
85
	protected void postInit() {
112
	protected void postInit() {
Lines 104-109 Link Here
104
	}
131
	}
105
132
106
	/**
133
	/**
134
	 * Returns whether the updates performed by this binding are executed
135
	 * asynchronously.
136
	 * 
137
	 * <p>
138
	 * This method returns <code>true</code> if and only if <i>any</i> of the
139
	 * binding's update strategies is intended to be run asynchronously.
140
	 * Thereby, the individual updates of this binding are always guaranteed to
141
	 * be executed in the correct order, thus ensuring that the target and model
142
	 * values will never get out of sync due to the asynchronous nature of the
143
	 * updates.
144
	 * </p>
145
	 * 
146
	 * <p>
147
	 * In case the updates are executed asynchronously, the staleness state of
148
	 * the validation status observable can be used to determine whether there
149
	 * are any pending updates. This allows for awaiting the termination of
150
	 * those updates, if desired, by tracking the observable's staleness state.
151
	 * </p>
152
	 * 
153
	 * @return whether the updates performed by this binding are executed
154
	 *         asynchronously.
155
	 * 
156
	 * @see UpdateValueStrategy#isAsync()
157
	 */
158
	public boolean isAsync() {
159
		return targetToModel.isAsync() || modelToTarget.isAsync();
160
	}
161
162
	/**
107
	 * Incorporates the provided <code>newStats</code> into the
163
	 * Incorporates the provided <code>newStats</code> into the
108
	 * <code>multieStatus</code>.
164
	 * <code>multieStatus</code>.
109
	 * 
165
	 * 
Lines 128-134 Link Here
128
			final IObservableValue destination,
184
			final IObservableValue destination,
129
			final UpdateValueStrategy updateValueStrategy,
185
			final UpdateValueStrategy updateValueStrategy,
130
			final boolean explicit, final boolean validateOnly) {
186
			final boolean explicit, final boolean validateOnly) {
131
132
		final int policy = updateValueStrategy.getUpdatePolicy();
187
		final int policy = updateValueStrategy.getUpdatePolicy();
133
		if (policy == UpdateValueStrategy.POLICY_NEVER)
188
		if (policy == UpdateValueStrategy.POLICY_NEVER)
134
			return;
189
			return;
Lines 137-214 Link Here
137
192
138
		source.getRealm().exec(new Runnable() {
193
		source.getRealm().exec(new Runnable() {
139
			public void run() {
194
			public void run() {
140
				boolean destinationRealmReached = false;
195
				// Get value
141
				final MultiStatus multiStatus = BindingStatus.ok();
196
				final Object value = source.getValue();
142
				try {
197
143
					// Get value
198
				UpdateRunnable update = new UpdateRunnable() {
144
					Object value = source.getValue();
199
					public void run() {
145
200
						boolean destinationRealmReached = false;
146
					// Validate after get
201
						final MultiStatus multiStatus = BindingStatus.ok();
147
					IStatus status = updateValueStrategy
202
						try {
148
							.validateAfterGet(value);
203
							// Validate after get
149
					if (!mergeStatus(multiStatus, status))
204
							IStatus status = updateValueStrategy
150
						return;
205
									.validateAfterGet(value);
151
206
							if (!mergeStatus(multiStatus, status))
152
					// Convert value
207
								return;
153
					final Object convertedValue = updateValueStrategy
208
154
							.convert(value);
209
							// Convert value
155
210
							final Object convertedValue = updateValueStrategy
156
					// Validate after convert
211
									.convert(value);
157
					status = updateValueStrategy
212
158
							.validateAfterConvert(convertedValue);
213
							// Validate after convert
159
					if (!mergeStatus(multiStatus, status))
214
							status = updateValueStrategy
160
						return;
215
									.validateAfterConvert(convertedValue);
161
					if (policy == UpdateValueStrategy.POLICY_CONVERT
216
							if (!mergeStatus(multiStatus, status))
162
							&& !explicit)
217
								return;
163
						return;
218
							if (policy == UpdateValueStrategy.POLICY_CONVERT
164
219
									&& !explicit)
165
					// Validate before set
220
								return;
166
					status = updateValueStrategy
221
167
							.validateBeforeSet(convertedValue);
222
							// Validate before set
168
					if (!mergeStatus(multiStatus, status))
223
							status = updateValueStrategy
169
						return;
224
									.validateBeforeSet(convertedValue);
170
					if (validateOnly)
225
							if (!mergeStatus(multiStatus, status))
171
						return;
226
								return;
172
227
							if (validateOnly)
173
					// Set value
228
								return;
174
					destinationRealmReached = true;
229
175
					destination.getRealm().exec(new Runnable() {
230
							// Set value
176
						public void run() {
231
							destinationRealmReached = true;
177
							if (destination == target) {
232
							destination.getRealm().exec(new Runnable() {
178
								updatingTarget = true;
233
								public void run() {
179
							} else {
234
									// If the update has been canceled before
180
								updatingModel = true;
235
									// writing to the destination observable, we
181
							}
236
									// do not set the validation status, so we
182
							try {
237
									// return outside the below try block.
183
								IStatus setterStatus = updateValueStrategy
238
									if (isCanceled()) {
184
										.doSet(destination, convertedValue);
239
										notifyDone();
185
240
										return;
186
								mergeStatus(multiStatus, setterStatus);
241
									}
187
							} finally {
242
188
								if (destination == target) {
243
									if (destination == target) {
189
									updatingTarget = false;
244
										updatingTarget = true;
190
								} else {
245
									} else {
191
									updatingModel = false;
246
										updatingModel = true;
247
									}
248
									try {
249
										IStatus setterStatus = updateValueStrategy
250
												.doSet(destination,
251
														convertedValue);
252
253
										mergeStatus(multiStatus, setterStatus);
254
									} finally {
255
										if (destination == target) {
256
											updatingTarget = false;
257
										} else {
258
											updatingModel = false;
259
										}
260
										setValidationStatus(multiStatus);
261
										notifyDone();
262
									}
263
								}
264
							});
265
						} catch (Exception ex) {
266
							// This check is necessary as in 3.2.2 Status
267
							// doesn't accept a null message (bug 177264).
268
							String message = (ex.getMessage() != null) ? ex
269
									.getMessage() : ""; //$NON-NLS-1$
270
271
							mergeStatus(multiStatus, new Status(IStatus.ERROR,
272
									Policy.JFACE_DATABINDING, IStatus.ERROR,
273
									message, ex));
274
						} finally {
275
							if (!destinationRealmReached) {
276
								// Set the validation status unless the update
277
								// has been canceled in the meanwhile.
278
								if (!isCanceled()) {
279
									setValidationStatus(multiStatus);
192
								}
280
								}
193
								setValidationStatus(multiStatus);
281
								notifyDone();
194
							}
282
							}
195
						}
283
						}
196
					});
197
				} catch (Exception ex) {
198
					// This check is necessary as in 3.2.2 Status
199
					// doesn't accept a null message (bug 177264).
200
					String message = (ex.getMessage() != null) ? ex
201
							.getMessage() : ""; //$NON-NLS-1$
202
203
					mergeStatus(multiStatus, new Status(IStatus.ERROR,
204
							Policy.JFACE_DATABINDING, IStatus.ERROR, message,
205
							ex));
206
				} finally {
207
					if (!destinationRealmReached) {
208
						setValidationStatus(multiStatus);
209
					}
284
					}
285
				};
210
286
211
				}
287
				updateExecutor.execute(update, !explicit);
212
			}
288
			}
213
		});
289
		});
214
	}
290
	}
Lines 230-235 Link Here
230
	}
306
	}
231
	
307
	
232
	public void dispose() {
308
	public void dispose() {
309
		updateExecutor.terminate();
310
233
		if (targetChangeListener != null) {
311
		if (targetChangeListener != null) {
234
			target.removeValueChangeListener(targetChangeListener);
312
			target.removeValueChangeListener(targetChangeListener);
235
			targetChangeListener = null;
313
			targetChangeListener = null;
(-)src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.java (+3 lines)
Lines 53-56 Link Here
53
		return String.class;
53
		return String.class;
54
	}
54
	}
55
55
56
	public boolean isAsync() {
57
		return false;
58
	}
56
}
59
}
(-)src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.java (+3 lines)
Lines 85-88 Link Here
85
		return Boolean.TYPE;
85
		return Boolean.TYPE;
86
	}
86
	}
87
87
88
	public boolean isAsync() {
89
		return false;
90
	}
88
}
91
}
(-)src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.java (-1 / +5 lines)
Lines 32-36 Link Here
32
32
33
	public Object getToType() {
33
	public Object getToType() {
34
		return Date.class;
34
		return Date.class;
35
	}	
35
	}
36
37
	public boolean isAsync() {
38
		return false;
39
	}
36
}
40
}
(-)src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.java (-1 / +5 lines)
Lines 35-39 Link Here
35
35
36
	public Object getToType() {
36
	public Object getToType() {
37
		return String.class;
37
		return String.class;
38
	}	
38
	}
39
40
	public boolean isAsync() {
41
		return false;
42
	}
39
}
43
}
(-)src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.java (+3 lines)
Lines 107-110 Link Here
107
		return toType;
107
		return toType;
108
	}
108
	}
109
109
110
	public boolean isAsync() {
111
		return false;
112
	}
110
}
113
}
(-)src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.java (+4 lines)
Lines 70-75 Link Here
70
		return primitiveTarget ? Character.TYPE : Character.class;
70
		return primitiveTarget ? Character.TYPE : Character.class;
71
	}
71
	}
72
72
73
	public boolean isAsync() {
74
		return false;
75
	}
76
73
	/**
77
	/**
74
	 * @param primitive
78
	 * @param primitive
75
	 * @return converter
79
	 * @return converter
(-)src/org/eclipse/core/internal/databinding/Queue.java (+8 lines)
Lines 72-75 Link Here
72
	public boolean isEmpty() {
72
	public boolean isEmpty() {
73
		return first == null;
73
		return first == null;
74
	}
74
	}
75
76
	/**
77
	 * Removes all elements from this queue.
78
	 */
79
	public void clear() {
80
		first = null;
81
		last = null;
82
	}
75
}
83
}
(-)META-INF/MANIFEST.MF (-1 / +2 lines)
Lines 22-28 Link Here
22
 org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding",
22
 org.eclipse.core.internal.databinding.observable.masterdetail;x-friends:="org.eclipse.jface.tests.databinding",
23
 org.eclipse.core.internal.databinding.observable.tree;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
23
 org.eclipse.core.internal.databinding.observable.tree;x-friends:="org.eclipse.jface.databinding,org.eclipse.jface.tests.databinding",
24
 org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding"
24
 org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding"
25
Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)"
25
Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)",
26
 org.eclipse.core.jobs
26
Import-Package-Comment: see http://wiki.eclipse.org/
27
Import-Package-Comment: see http://wiki.eclipse.org/
27
Import-Package: com.ibm.icu.text,
28
Import-Package: com.ibm.icu.text,
28
 org.osgi.framework;version="[1.4.0,2.0.0)";resolution:=optional,
29
 org.osgi.framework;version="[1.4.0,2.0.0)";resolution:=optional,
(-)src/org/eclipse/core/databinding/conversion/Converter.java (+6 lines)
Lines 40-43 Link Here
40
		return toType;
40
		return toType;
41
	}
41
	}
42
42
43
	/**
44
	 * By default, this method returns <code>false</code>.
45
	 */
46
	public boolean isAsync() {
47
		return false;
48
	}
43
}
49
}
(-)src/org/eclipse/core/databinding/conversion/IConverter.java (+15 lines)
Lines 50-53 Link Here
50
	 * @return the converted object, of type {@link #getToType()}
50
	 * @return the converted object, of type {@link #getToType()}
51
	 */
51
	 */
52
	public Object convert(Object fromObject);
52
	public Object convert(Object fromObject);
53
54
	/**
55
	 * Returns whether the {@link #convert(Object)} method of this converter is
56
	 * intended to be executed asynchronously.
57
	 * 
58
	 * <p>
59
	 * Note that even if this method returns <code>true</code>, there is no
60
	 * guarantee as of whether the conversion will indeed by executed
61
	 * asynchronously or not.
62
	 * </p>
63
	 * 
64
	 * @return whether the {@link #convert(Object)} method of this converter is
65
	 *         intended to be executed asynchronously.
66
	 */
67
	public boolean isAsync();
53
}
68
}
(-)src/org/eclipse/core/databinding/validation/MultiValidator.java (-3 / +96 lines)
Lines 8-13 Link Here
8
 * Contributors:
8
 * Contributors:
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
 *     Ovidio Mallo - bug 233191
11
 ******************************************************************************/
12
 ******************************************************************************/
12
13
13
package org.eclipse.core.databinding.validation;
14
package org.eclipse.core.databinding.validation;
Lines 17-22 Link Here
17
18
18
import org.eclipse.core.databinding.ValidationStatusProvider;
19
import org.eclipse.core.databinding.ValidationStatusProvider;
19
import org.eclipse.core.databinding.observable.ChangeEvent;
20
import org.eclipse.core.databinding.observable.ChangeEvent;
21
import org.eclipse.core.databinding.observable.Diffs;
20
import org.eclipse.core.databinding.observable.IChangeListener;
22
import org.eclipse.core.databinding.observable.IChangeListener;
21
import org.eclipse.core.databinding.observable.IObservable;
23
import org.eclipse.core.databinding.observable.IObservable;
22
import org.eclipse.core.databinding.observable.ObservableTracker;
24
import org.eclipse.core.databinding.observable.ObservableTracker;
Lines 30-35 Link Here
30
import org.eclipse.core.databinding.observable.map.IObservableMap;
32
import org.eclipse.core.databinding.observable.map.IObservableMap;
31
import org.eclipse.core.databinding.observable.set.IObservableSet;
33
import org.eclipse.core.databinding.observable.set.IObservableSet;
32
import org.eclipse.core.databinding.observable.value.IObservableValue;
34
import org.eclipse.core.databinding.observable.value.IObservableValue;
35
import org.eclipse.core.databinding.observable.value.ValueDiff;
33
import org.eclipse.core.databinding.observable.value.WritableValue;
36
import org.eclipse.core.databinding.observable.value.WritableValue;
34
import org.eclipse.core.internal.databinding.observable.ValidatedObservableList;
37
import org.eclipse.core.internal.databinding.observable.ValidatedObservableList;
35
import org.eclipse.core.internal.databinding.observable.ValidatedObservableMap;
38
import org.eclipse.core.internal.databinding.observable.ValidatedObservableMap;
Lines 115-125 Link Here
115
 */
118
 */
116
public abstract class MultiValidator extends ValidationStatusProvider {
119
public abstract class MultiValidator extends ValidationStatusProvider {
117
	private Realm realm;
120
	private Realm realm;
118
	private IObservableValue validationStatus;
121
	private ValidationStatusObservableValue validationStatus;
119
	private IObservableValue unmodifiableValidationStatus;
122
	private IObservableValue unmodifiableValidationStatus;
120
	private WritableList targets;
123
	private WritableList targets;
121
	private IObservableList unmodifiableTargets;
124
	private IObservableList unmodifiableTargets;
122
	private IObservableList models;
125
	private IObservableList models;
126
	private boolean stale = false;
123
127
124
	IListChangeListener targetsListener = new IListChangeListener() {
128
	IListChangeListener targetsListener = new IListChangeListener() {
125
		public void handleListChange(ListChangeEvent event) {
129
		public void handleListChange(ListChangeEvent event) {
Lines 160-167 Link Here
160
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
164
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
161
		this.realm = realm;
165
		this.realm = realm;
162
166
163
		validationStatus = new WritableValue(realm, ValidationStatus.ok(),
167
		validationStatus = new ValidationStatusObservableValue(realm);
164
				IStatus.class);
165
168
166
		targets = new WritableList(realm, new ArrayList(), IObservable.class);
169
		targets = new WritableList(realm, new ArrayList(), IObservable.class);
167
		targets.addListChangeListener(targetsListener);
170
		targets.addListChangeListener(targetsListener);
Lines 182-187 Link Here
182
	 * validation status of this MultiValidator. The returned observable is in
185
	 * validation status of this MultiValidator. The returned observable is in
183
	 * the same realm as this MultiValidator.
186
	 * the same realm as this MultiValidator.
184
	 * 
187
	 * 
188
	 * <p>
189
	 * In case the validation of this MultiValidator is performed
190
	 * asynchronously, the staleness state of the validation status observable
191
	 * can be used to determine whether there are any pending validations. This
192
	 * allows for awaiting the termination of those validations, if desired, by
193
	 * tracking the observable's staleness state.
194
	 * 
185
	 * @return an {@link IObservableValue} whose value is always the current
195
	 * @return an {@link IObservableValue} whose value is always the current
186
	 *         validation status of this MultiValidator.
196
	 *         validation status of this MultiValidator.
187
	 */
197
	 */
Lines 232-237 Link Here
232
	protected abstract IStatus validate();
242
	protected abstract IStatus validate();
233
243
234
	/**
244
	/**
245
	 * Notifies the MultiValidator that the current validation status is being
246
	 * computed asynchronously and is not available yet.
247
	 * 
248
	 * <p>
249
	 * Subclasses should invoke this method whenever the {@link #validate()
250
	 * validation} is performed asynchronously. Calling this method guarantees
251
	 * that the {@link #getValidationStatus() validation status observable} is
252
	 * correctly set to be stale.
253
	 * 
254
	 * <p>
255
	 * Typically, this method should be called from within the
256
	 * {@link #validate()} method when the asynchronous validation is started
257
	 * while a reasonable validation status must always be returned to be used
258
	 * while the pending validation has not terminated. Once the validation is
259
	 * available, it can be passed to the MultiValidator by calling the
260
	 * {@link #exitStale(IStatus)} method.
261
	 * 
262
	 * <p>
263
	 * Note: This method must be called from within the MultiValidator's realm.
264
	 * 
265
	 * @see #exitStale(IStatus)
266
	 * @see IObservableValue#isStale()
267
	 */
268
	protected final void enterStale() {
269
		stale = true;
270
		validationStatus.fireStale();
271
	}
272
273
	/**
274
	 * Notifies the MultiValidator that an asynchronous validation has
275
	 * terminated which resulted in the given validation status.
276
	 * 
277
	 * <p>
278
	 * This method sets the given <code>status</code> as the current validation
279
	 * status and sets the {@link #getValidationStatus() validation status
280
	 * observable} to not be stale anymore.
281
	 * 
282
	 * <p>
283
	 * Note: This method must be called from within the MultiValidator's realm.
284
	 * 
285
	 * @param status
286
	 *            the resulting status of the terminated validation.
287
	 * 
288
	 * @see #enterStale()
289
	 * @see IObservableValue#isStale()
290
	 */
291
	protected final void exitStale(IStatus status) {
292
		stale = false;
293
		Object oldStatus = validationStatus.getValue();
294
		validationStatus.setValue(status);
295
296
		// In order to signal that the validation status observable is not stale
297
		// anymore, we must always fire a value change event, even if the
298
		// validation status has not changed. In the latter case, we must do so
299
		// explicitly.
300
		if (oldStatus.equals(status)) {
301
			validationStatus.fireValueChange(Diffs.createValueDiff(oldStatus,
302
					status));
303
		}
304
	}
305
306
	/**
235
	 * Returns a wrapper {@link IObservableValue} which stays in sync with the
307
	 * Returns a wrapper {@link IObservableValue} which stays in sync with the
236
	 * given target observable only when the validation status is valid.
308
	 * given target observable only when the validation status is valid.
237
	 * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
309
	 * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or
Lines 366-369 Link Here
366
		super.dispose();
438
		super.dispose();
367
	}
439
	}
368
440
441
	private class ValidationStatusObservableValue extends WritableValue {
442
443
		public ValidationStatusObservableValue(Realm realm) {
444
			super(realm, ValidationStatus.ok(), IStatus.class);
445
		}
446
447
		public boolean isStale() {
448
			ObservableTracker.getterCalled(this);
449
			return stale;
450
		}
451
452
		protected void fireStale() {
453
			// Make the method accessible to the MultiValidator class.
454
			super.fireStale();
455
		}
456
457
		protected void fireValueChange(ValueDiff diff) {
458
			// Make the method accessible to the MultiValidator class.
459
			super.fireValueChange(diff);
460
		}
461
	}
369
}
462
}
(-)src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.java (+4 lines)
Lines 55-58 Link Here
55
	public Object getValueType() {
55
	public Object getValueType() {
56
		return wrappedValue.getValueType();
56
		return wrappedValue.getValueType();
57
	}
57
	}
58
59
	public boolean isStale() {
60
		return wrappedValue.isStale();
61
	}
58
}
62
}
(-)src/org/eclipse/core/internal/databinding/UpdateExecutor.java (+234 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding;
13
14
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.Set;
17
18
import org.eclipse.core.runtime.IProgressMonitor;
19
import org.eclipse.core.runtime.IStatus;
20
import org.eclipse.core.runtime.Status;
21
import org.eclipse.core.runtime.jobs.Job;
22
23
/**
24
 * Simple class to manage the execution of {@link UpdateRunnable}s intended to
25
 * be used for performing the updates of a binding.
26
 * 
27
 * <p>
28
 * This class is thread safe and supports the synchronous as well as
29
 * asynchronous execution of a set of UpdateRunnables where consecutive updates
30
 * are guaranteed to be executed in the correct order. In addition, this class
31
 * allows for tracking the set of pending updates which can eventually be
32
 * canceled upon scheduling a new update which makes previous updates obsolete.
33
 * </p>
34
 * 
35
 * @see #execute(UpdateRunnable, boolean)
36
 * @see #hasPendingUpdates()
37
 */
38
public class UpdateExecutor {
39
40
	private final boolean isAsync;
41
42
	private final Runnable schedulingEventCallback;
43
44
	private final Set pendingUpdates = new HashSet();
45
46
	private final UpdateJob updateJob;
47
48
	/**
49
	 * Creates a new UpdateExecutor which will run the individual updates either
50
	 * synchronously or asynchronously.
51
	 * 
52
	 * @param isAsync
53
	 *            whether the UpdateRunnables passed to this UpdateExecutor
54
	 *            should be executed asynchronously.
55
	 * @param schedulingEventCallback
56
	 *            a callback Runnable which will be executed whenever some
57
	 *            scheduling event in the execution of the updates happened. The
58
	 *            code in the runnable can then use this classes API to retrieve
59
	 *            the relevant information. May be <code>null</code>.
60
	 */
61
	public UpdateExecutor(boolean isAsync, Runnable schedulingEventCallback) {
62
		this.isAsync = isAsync;
63
		this.schedulingEventCallback = schedulingEventCallback;
64
65
		// Only instantiate the UpdateJob if we are in asynchronous mode.
66
		if (isAsync) {
67
			this.updateJob = new UpdateJob();
68
			this.updateJob.setSystem(true);
69
		} else {
70
			this.updateJob = null;
71
		}
72
	}
73
74
	/**
75
	 * Executes the given {@link UpdateRunnable update} and eventually tries to
76
	 * cancel previous, still pending updates, if desired.
77
	 * 
78
	 * @param update
79
	 *            the new update to execute.
80
	 * @param cancelPendingUpdates
81
	 *            whether previous, still pending updates, should be requested
82
	 *            to be {@link UpdateRunnable#cancel() canceled} before
83
	 *            executing the new update.
84
	 * 
85
	 * @see UpdateRunnable#run()
86
	 * @see UpdateRunnable#cancel()
87
	 */
88
	public void execute(UpdateRunnable update, boolean cancelPendingUpdates) {
89
		// If requested, we try to cancel previous updates.
90
		if (cancelPendingUpdates) {
91
			cancelPendingUpdates();
92
		}
93
94
		// The executor must be set on the UpdateRunnable in order to get
95
		// notified of the update's termination.
96
		update.setExecutor(this);
97
98
		// Add the new update to the set of pending updates.
99
		synchronized (pendingUpdates) {
100
			pendingUpdates.add(update);
101
		}
102
103
		// Right before starting the update, we notify about the new update.
104
		if (schedulingEventCallback != null) {
105
			schedulingEventCallback.run();
106
		}
107
108
		if (isAsync) {
109
			synchronized (updateJob.updateQueue) {
110
				// Whenever we are adding a new update to an empty queue, we
111
				// must re-schedule the UpdateJob. Note that it is OK to do this
112
				// before actually adding the update to the queue since we are
113
				// already holding the lock on the queue at this point.
114
				if (updateJob.updateQueue.isEmpty()) {
115
					updateJob.schedule();
116
				}
117
				updateJob.updateQueue.enqueue(update);
118
			}
119
		} else {
120
			// In synchronous mode, we simply execute the run method.
121
			update.run();
122
		}
123
	}
124
125
	/**
126
	 * Indicates that the given update was running and has now terminated.
127
	 * 
128
	 * @param update
129
	 *            the update which has terminated.
130
	 * 
131
	 * @see #hasPendingUpdates()
132
	 * @see UpdateRunnable#notifyDone()
133
	 */
134
	/* package */void endUpdate(UpdateRunnable update) {
135
		synchronized (pendingUpdates) {
136
			pendingUpdates.remove(update);
137
		}
138
139
		if (schedulingEventCallback != null) {
140
			schedulingEventCallback.run();
141
		}
142
	}
143
144
	/**
145
	 * Returns whether there are any pending updates to be executed on behalf of
146
	 * this UpdateExecutor.
147
	 * 
148
	 * <p>
149
	 * Note that an update is defined to be pending as soon as it has been
150
	 * passed to this UpdateExecutor for
151
	 * {@link #execute(UpdateRunnable, boolean) execution}, regardless of
152
	 * whether the update is already running or not.
153
	 * </p>
154
	 * 
155
	 * @return whether there are any pending updates to be executed on behalf of
156
	 *         this UpdateExecutor.
157
	 */
158
	public boolean hasPendingUpdates() {
159
		synchronized (pendingUpdates) {
160
			return !pendingUpdates.isEmpty();
161
		}
162
	}
163
164
	/**
165
	 * Requests all the pending updates to be canceled.
166
	 * 
167
	 * <p>
168
	 * This method guarantees that by the end of its execution, all the pending
169
	 * updates have been requested to be {@link UpdateRunnable#cancel()
170
	 * canceled} and that updates which have not been started yet (if in
171
	 * asynchronous mode) will never be run.
172
	 * </p>
173
	 * 
174
	 * @see UpdateRunnable#cancel()
175
	 */
176
	private void cancelPendingUpdates() {
177
		synchronized (pendingUpdates) {
178
			// Cancel all the pending updates.
179
			for (Iterator iter = pendingUpdates.iterator(); iter.hasNext();) {
180
				UpdateRunnable update = (UpdateRunnable) iter.next();
181
				update.cancel();
182
			}
183
			pendingUpdates.clear();
184
		}
185
186
		if (isAsync) {
187
			// If in asynchronous mode, we must also clear the queue of updates
188
			// waiting to be run.
189
			synchronized (updateJob.updateQueue) {
190
				updateJob.updateQueue.clear();
191
			}
192
		}
193
	}
194
195
	/**
196
	 * Terminates the UpdateExecutor by canceling any eventual pending update.
197
	 * 
198
	 * <p>
199
	 * Note that once this method has been called, no further updates should be
200
	 * run on behalf of this UpdateExecutor.
201
	 * </p>
202
	 */
203
	public void terminate() {
204
		cancelPendingUpdates();
205
206
		if (schedulingEventCallback != null) {
207
			schedulingEventCallback.run();
208
		}
209
	}
210
211
	private class UpdateJob extends Job {
212
213
		private final Queue updateQueue = new Queue();
214
215
		public UpdateJob() {
216
			super("Update Job"); //$NON-NLS-1$
217
		}
218
219
		protected IStatus run(IProgressMonitor monitor) {
220
			while (true) {
221
				UpdateRunnable update;
222
				synchronized (updateQueue) {
223
					// As soon as we get out of work, we return in order to
224
					// release the Thread on whose behalf the job is running.
225
					if (updateQueue.isEmpty()) {
226
						return Status.OK_STATUS;
227
					}
228
					update = (UpdateRunnable) updateQueue.dequeue();
229
				}
230
				update.run();
231
			}
232
		}
233
	}
234
}
(-)src/org/eclipse/core/databinding/validation/IValidator2.java (+34 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.validation;
13
14
/**
15
 * Extension of the {@link IValidator} interface which adds API for expressing
16
 * the intend of a validator to be executed asynchronously.
17
 */
18
public interface IValidator2 extends IValidator {
19
20
	/**
21
	 * Returns whether the {@link #validate(Object)} method of this validator is
22
	 * intended to be executed asynchronously.
23
	 * 
24
	 * <p>
25
	 * Note that even if this method returns <code>true</code>, there is no
26
	 * guarantee as of whether the validation will indeed by executed
27
	 * asynchronously or not.
28
	 * </p>
29
	 * 
30
	 * @return whether the {@link #validate(Object)} method of this validator is
31
	 *         intended to be executed asynchronously.
32
	 */
33
	public boolean isAsync();
34
}
(-)src/org/eclipse/core/internal/databinding/UpdateValidationObservableValue.java (+84 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding;
13
14
import org.eclipse.core.databinding.observable.Diffs;
15
import org.eclipse.core.databinding.observable.Realm;
16
import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
17
import org.eclipse.core.databinding.observable.value.IObservableValue;
18
import org.eclipse.core.runtime.IStatus;
19
import org.eclipse.core.runtime.Status;
20
21
/**
22
 * Simple {@link IObservableValue} of type {@link IStatus} intended to be used
23
 * for the validation resulting from the update process of a binding.
24
 * 
25
 * <p>
26
 * This observable provides some convenience API to signal a possible change in
27
 * its staleness state. Whenever the staleness of the validation observable
28
 * changes (or may have changed), {@link #updateStaleness()} should be called
29
 * which either fires a stale event in case the method {@link #isStale()}
30
 * returns <code>true</code>, or else, a value change event is fired in order to
31
 * signal that the observable is not stale anymore. Subclasses must implement
32
 * the {@link #isStale()} method.
33
 * </p>
34
 */
35
public abstract class UpdateValidationObservableValue extends
36
		AbstractObservableValue {
37
38
	private Object value = Status.OK_STATUS;
39
40
	/**
41
	 * Creates a new observable with the initial value
42
	 * <code>Status.OK_STATUS</code> in the given realm.
43
	 * 
44
	 * @param realm
45
	 *            the observable's realm.
46
	 */
47
	public UpdateValidationObservableValue(Realm realm) {
48
		super(realm);
49
	}
50
51
	protected Object doGetValue() {
52
		return value;
53
	}
54
55
	protected void doSetValue(Object value) {
56
		Object oldValue = this.value;
57
		this.value = value;
58
		if (!Util.equals(oldValue, value)) {
59
			fireValueChange(Diffs.createValueDiff(oldValue, value));
60
		}
61
	}
62
63
	/**
64
	 * Updates the staleness of this validation by either firing a stale event
65
	 * in case the method {@link #isStale()} returns <code>true</code>, or else,
66
	 * by firing a value change event in order to signal that the observable is
67
	 * not stale anymore.
68
	 */
69
	public void updateStaleness() {
70
		if (isStale()) {
71
			fireStale();
72
		} else {
73
			// This synthetic value change event is merely used to signal that
74
			// the observable is not stale anymore.
75
			fireValueChange(Diffs.createValueDiff(getValue(), getValue()));
76
		}
77
	}
78
79
	public abstract boolean isStale();
80
81
	public Object getValueType() {
82
		return IStatus.class;
83
	}
84
}
(-)src/org/eclipse/core/internal/databinding/UpdateRunnable.java (+92 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.core.internal.databinding;
13
14
/**
15
 * Simple implementation of a <code>Runnable</code> which can be used to have a
16
 * binding update executed by an {@link UpdateExecutor}.
17
 * 
18
 * <p>
19
 * This thread safe class adds methods to support a collaborative
20
 * {@link #cancel() canceling} of a running update. In addition, in order to
21
 * allow for an <code>UpdateExecutor</code> to keep track of the updates
22
 * currently running on its behalf, every <code>UpdateRunnable</code> is
23
 * responsible for {@link #notifyDone() notifying} the executor about its
24
 * completion.
25
 * </p>
26
 * 
27
 * @see #cancel()
28
 * @see #notifyDone()
29
 */
30
public abstract class UpdateRunnable implements Runnable {
31
32
	private UpdateExecutor executor;
33
34
	private volatile boolean canceled = false;
35
36
	/**
37
	 * Sets the {@link UpdateExecutor} on whose behalf this update is executed.
38
	 * 
39
	 * @param executor
40
	 *            the <code>UpdateExecutor</code> on whose behalf this update is
41
	 *            executed.
42
	 */
43
	/* package */final synchronized void setExecutor(UpdateExecutor executor) {
44
		this.executor = executor;
45
	}
46
47
	/**
48
	 * Signals that this update has completed.
49
	 * 
50
	 * <p>
51
	 * Calling this method is typically the responsibility of the running update
52
	 * itself and is used to allow for the {@link UpdateExecutor executor} of
53
	 * this update to keep track of the pending updates.
54
	 * </p>
55
	 * 
56
	 * <p>
57
	 * Note that if an update terminates asynchronously, this method should only
58
	 * be called as soon as the update really terminates and not already when
59
	 * reaching the end of the {@link #run()} method.
60
	 * </p>
61
	 * 
62
	 * @see #setExecutor(UpdateExecutor)
63
	 * @see UpdateExecutor#endUpdate(UpdateRunnable)
64
	 */
65
	protected final synchronized void notifyDone() {
66
		if (executor != null) {
67
			executor.endUpdate(this);
68
		}
69
	}
70
71
	/**
72
	 * Returns whether this update has been requested to be canceled.
73
	 * 
74
	 * @return whether this update has been requested to be canceled.
75
	 * 
76
	 * @see #cancel()
77
	 */
78
	public final boolean isCanceled() {
79
		return canceled;
80
	}
81
82
	/**
83
	 * Requests this update to be canceled while it is up to the running update
84
	 * to decide if and when the update can be canceled before its natural
85
	 * completion.
86
	 * 
87
	 * @see #isCanceled()
88
	 */
89
	public final void cancel() {
90
		this.canceled = true;
91
	}
92
}
(-)src/org/eclipse/jface/examples/databinding/nestedselection/TestMasterDetail.java (+4 lines)
Lines 252-257 Link Here
252
			public Object getToType() {
252
			public Object getToType() {
253
				return String.class;
253
				return String.class;
254
			}
254
			}
255
256
            public boolean isAsync() {
257
            	return false;
258
            }
255
		};
259
		};
256
		IValidator vowelValidator = new IValidator() {
260
		IValidator vowelValidator = new IValidator() {
257
			public IStatus validate(Object value) {
261
			public IStatus validate(Object value) {
(-)src/org/eclipse/jface/examples/databinding/snippets/Snippet022AsyncUpdate.java (+320 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.jface.examples.databinding.snippets;
13
14
import java.util.Random;
15
16
import org.eclipse.core.databinding.DataBindingContext;
17
import org.eclipse.core.databinding.UpdateListStrategy;
18
import org.eclipse.core.databinding.UpdateValueStrategy;
19
import org.eclipse.core.databinding.conversion.Converter;
20
import org.eclipse.core.databinding.conversion.IConverter;
21
import org.eclipse.core.databinding.observable.Realm;
22
import org.eclipse.core.databinding.observable.list.IObservableList;
23
import org.eclipse.core.databinding.observable.list.WritableList;
24
import org.eclipse.core.databinding.observable.value.IObservableValue;
25
import org.eclipse.core.databinding.observable.value.WritableValue;
26
import org.eclipse.jface.databinding.swt.SWTObservables;
27
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
28
import org.eclipse.jface.layout.GridDataFactory;
29
import org.eclipse.jface.layout.GridLayoutFactory;
30
import org.eclipse.jface.viewers.IStructuredSelection;
31
import org.eclipse.jface.viewers.LabelProvider;
32
import org.eclipse.jface.viewers.ListViewer;
33
import org.eclipse.swt.SWT;
34
import org.eclipse.swt.events.KeyAdapter;
35
import org.eclipse.swt.events.KeyEvent;
36
import org.eclipse.swt.events.ModifyEvent;
37
import org.eclipse.swt.events.ModifyListener;
38
import org.eclipse.swt.events.SelectionAdapter;
39
import org.eclipse.swt.events.SelectionEvent;
40
import org.eclipse.swt.widgets.Button;
41
import org.eclipse.swt.widgets.Composite;
42
import org.eclipse.swt.widgets.Display;
43
import org.eclipse.swt.widgets.Group;
44
import org.eclipse.swt.widgets.Label;
45
import org.eclipse.swt.widgets.Shell;
46
import org.eclipse.swt.widgets.Spinner;
47
import org.eclipse.swt.widgets.Text;
48
49
/**
50
 * Snippet that demonstrates the usage of asynchronous bindings by defining an
51
 * asynchronous converter which introduces some artificial delay during
52
 * conversion. The example allows to manually change the target or model of a
53
 * ValueBinding/ListBinding and to see how the counterpart gets updated
54
 * asynchronously. In addition, automatic updates of the target and/or model can
55
 * be triggered.
56
 */
57
public class Snippet022AsyncUpdate {
58
59
	private int updateDelay = 1000;
60
61
	private boolean randomizeUpdateDelay = true;
62
63
	private DataBindingContext dbc;
64
65
	private WritableValue valueTarget;
66
67
	private WritableValue valueModel;
68
69
	private WritableList listTarget;
70
71
	private WritableList listModel;
72
73
	public void createControl(Composite parent) {
74
		dbc = new DataBindingContext();
75
76
		parent.setLayout(GridLayoutFactory.fillDefaults().margins(5, 5)
77
				.spacing(15, 10).create());
78
79
		createSettingsGroup(parent);
80
		createValueBindingGroup(parent);
81
		createListBindingGroup(parent);
82
		createAutomaticUpdatesGroup(parent);
83
	}
84
85
	private void createSettingsGroup(Composite parent) {
86
		Group group = new Group(parent, SWT.NONE);
87
		group.setText("Settings");
88
		group.setLayout(GridLayoutFactory.fillDefaults().numColumns(2).margins(
89
				5, 7).spacing(15, 10).create());
90
		GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
91
92
		new Label(group, SWT.NONE).setText("Update delay");
93
		final Spinner updateDelaySpinner = new Spinner(group, SWT.BORDER);
94
		GridDataFactory.fillDefaults().grab(true, false).applyTo(
95
				updateDelaySpinner);
96
		updateDelaySpinner.setMinimum(1);
97
		updateDelaySpinner.setMaximum(Integer.MAX_VALUE);
98
		updateDelaySpinner.setSelection(updateDelay);
99
		updateDelaySpinner.addModifyListener(new ModifyListener() {
100
			public void modifyText(ModifyEvent e) {
101
				updateDelay = updateDelaySpinner.getSelection();
102
			}
103
		});
104
105
		final Button randomizeUpdateDelayButton = new Button(group, SWT.CHECK);
106
		randomizeUpdateDelayButton.setText("Randomize update delay");
107
		GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(
108
				randomizeUpdateDelayButton);
109
		randomizeUpdateDelayButton.setSelection(randomizeUpdateDelay);
110
		randomizeUpdateDelayButton.addSelectionListener(new SelectionAdapter() {
111
			public void widgetSelected(SelectionEvent e) {
112
				randomizeUpdateDelay = randomizeUpdateDelayButton
113
						.getSelection();
114
			}
115
		});
116
	}
117
118
	private void createValueBindingGroup(Composite parent) {
119
		Group group = new Group(parent, SWT.NONE);
120
		group.setText("Value Binding");
121
		group.setLayout(GridLayoutFactory.fillDefaults().numColumns(2).margins(
122
				5, 7).spacing(15, 10).create());
123
		GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
124
125
		valueTarget = WritableValue.withValueType(String.class);
126
		createValueComponent(group, "Target", valueTarget);
127
128
		valueModel = WritableValue.withValueType(String.class);
129
		createValueComponent(group, "Model", valueModel);
130
131
		dbc.bindValue(valueTarget, valueModel, new UpdateValueStrategy()
132
				.setConverter(asyncConverter()), new UpdateValueStrategy()
133
				.setConverter(asyncConverter()));
134
	}
135
136
	private void createValueComponent(Composite parent, String title,
137
			IObservableValue observableValue) {
138
		new Label(parent, SWT.NONE).setText(title);
139
		Text text = new Text(parent, SWT.BORDER);
140
		GridDataFactory.fillDefaults().grab(true, false).applyTo(text);
141
142
		dbc.bindValue(SWTObservables.observeText(text, SWT.Modify),
143
				observableValue, null, null);
144
	}
145
146
	private void createListBindingGroup(Composite parent) {
147
		Group group = new Group(parent, SWT.NONE);
148
		group.setText("List Binding");
149
		group.setLayout(GridLayoutFactory.fillDefaults().numColumns(2).margins(
150
				5, 7).spacing(15, 10).create());
151
		GridDataFactory.fillDefaults().grab(true, true).applyTo(group);
152
153
		listTarget = WritableList.withElementType(String.class);
154
		createListComponent(group, "Target", listTarget);
155
156
		listModel = WritableList.withElementType(String.class);
157
		createListComponent(group, "Model", listModel);
158
159
		dbc.bindList(listTarget, listModel, new UpdateListStrategy()
160
				.setConverter(asyncConverter()), new UpdateListStrategy()
161
				.setConverter(asyncConverter()));
162
	}
163
164
	private void createListComponent(Composite parent, String title,
165
			final IObservableList observableList) {
166
		new Label(parent, SWT.NONE).setText(title);
167
		final Text targetText = new Text(parent, SWT.BORDER);
168
		GridDataFactory.fillDefaults().grab(true, false).applyTo(targetText);
169
		targetText.addKeyListener(new KeyAdapter() {
170
			public void keyPressed(KeyEvent e) {
171
				if (e.keyCode == SWT.CR) {
172
					observableList.add(targetText.getText());
173
					targetText.selectAll();
174
				}
175
			}
176
		});
177
		new Label(parent, SWT.NONE);
178
		final ListViewer targetList = new ListViewer(parent, SWT.BORDER
179
				| SWT.MULTI | SWT.V_SCROLL);
180
		GridDataFactory.fillDefaults().minSize(SWT.DEFAULT, 150).grab(true,
181
				true).applyTo(targetList.getList());
182
		targetList.setContentProvider(new ObservableListContentProvider());
183
		targetList.setLabelProvider(new LabelProvider());
184
		targetList.setInput(observableList);
185
		targetList.getList().addKeyListener(new KeyAdapter() {
186
			public void keyPressed(KeyEvent e) {
187
				if (e.keyCode == SWT.DEL) {
188
					IStructuredSelection selection = (IStructuredSelection) targetList
189
							.getSelection();
190
					observableList.removeAll(selection.toList());
191
				}
192
			}
193
		});
194
	}
195
196
	private void createAutomaticUpdatesGroup(Composite parent) {
197
		Group group = new Group(parent, SWT.NONE);
198
		group.setText("Automatic Updates");
199
		group.setLayout(GridLayoutFactory.fillDefaults().numColumns(3)
200
				.equalWidth(true).margins(5, 5).create());
201
		GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
202
203
		Button targetButton = new Button(group, SWT.PUSH);
204
		targetButton.setText("Target");
205
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true,
206
				false).applyTo(targetButton);
207
		targetButton.addSelectionListener(new SelectionAdapter() {
208
			public void widgetSelected(SelectionEvent e) {
209
				valueModel.setValue("");
210
				listTarget.clear();
211
				for (int i = 0; i < 10; i++) {
212
					String value = String.valueOf(i);
213
					valueTarget.setValue(value);
214
					listTarget.add(value);
215
216
					Display.getCurrent().update();
217
					sleep(100);
218
				}
219
			}
220
		});
221
222
		Button modelButton = new Button(group, SWT.PUSH);
223
		modelButton.setText("Model");
224
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true,
225
				false).applyTo(modelButton);
226
		modelButton.addSelectionListener(new SelectionAdapter() {
227
			public void widgetSelected(SelectionEvent e) {
228
				valueTarget.setValue("");
229
				listModel.clear();
230
				for (int i = 0; i < 10; i++) {
231
					String value = String.valueOf(i);
232
					valueModel.setValue(value);
233
					listModel.add(value);
234
235
					Display.getCurrent().update();
236
					sleep(100);
237
				}
238
			}
239
		});
240
241
		Button twowayButton = new Button(group, SWT.PUSH);
242
		twowayButton.setText("Twoway");
243
		GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true,
244
				false).applyTo(twowayButton);
245
		twowayButton.addSelectionListener(new SelectionAdapter() {
246
			public void widgetSelected(SelectionEvent e) {
247
				valueTarget.setValue("");
248
				valueModel.setValue("");
249
				listTarget.clear();
250
				listModel.clear();
251
				for (int i = 0; i < 10; i++) {
252
					String value = String.valueOf(i);
253
					if (i % 2 == 0) {
254
						valueTarget.setValue(value);
255
						listTarget.add(value);
256
					} else {
257
						valueModel.setValue(value);
258
						listModel.add(value);
259
					}
260
261
					Display.getCurrent().update();
262
					sleep(100);
263
				}
264
			}
265
		});
266
	}
267
268
	private IConverter asyncConverter() {
269
		return new Converter(null, null) {
270
			private final Random random = new Random(System.currentTimeMillis());
271
272
			public Object convert(Object fromObject) {
273
				if (randomizeUpdateDelay) {
274
					sleep(random.nextInt(updateDelay));
275
				} else {
276
					sleep(updateDelay);
277
				}
278
279
				return fromObject;
280
			}
281
282
			public boolean isAsync() {
283
				return true;
284
			}
285
		};
286
	}
287
288
	private static void sleep(long millis) {
289
		try {
290
			Thread.sleep(millis);
291
		} catch (InterruptedException e1) {
292
			// go ahead
293
		}
294
	}
295
296
	public static void main(String[] args) {
297
		Display display = new Display();
298
299
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
300
			public void run() {
301
				Display display = Display.getCurrent();
302
				final Shell shell = new Shell(display);
303
				shell.setText("Asynchronous Bindings");
304
				new Snippet022AsyncUpdate().createControl(shell);
305
306
				shell.setMinimumSize(shell.computeSize(350, SWT.DEFAULT));
307
				shell.pack();
308
				shell.open();
309
310
				// The SWT event loop
311
				while (!shell.isDisposed()) {
312
					if (!display.readAndDispatch()) {
313
						display.sleep();
314
					}
315
				}
316
				display.dispose();
317
			}
318
		});
319
	}
320
}
(-)src/org/eclipse/jface/examples/databinding/snippets/Snippet023AsyncUpdateWizard.java (+225 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 233191)
10
 ******************************************************************************/
11
12
package org.eclipse.jface.examples.databinding.snippets;
13
14
import java.util.Random;
15
16
import org.eclipse.core.databinding.DataBindingContext;
17
import org.eclipse.core.databinding.UpdateValueStrategy;
18
import org.eclipse.core.databinding.observable.Realm;
19
import org.eclipse.core.databinding.observable.value.WritableValue;
20
import org.eclipse.core.databinding.validation.IValidator2;
21
import org.eclipse.core.databinding.validation.MultiValidator;
22
import org.eclipse.core.databinding.validation.ValidationStatus;
23
import org.eclipse.core.runtime.IProgressMonitor;
24
import org.eclipse.core.runtime.IStatus;
25
import org.eclipse.core.runtime.Status;
26
import org.eclipse.core.runtime.jobs.Job;
27
import org.eclipse.jface.databinding.swt.SWTObservables;
28
import org.eclipse.jface.databinding.wizard.WizardPageSupport;
29
import org.eclipse.jface.examples.databinding.decoration.ControlDecorator;
30
import org.eclipse.jface.layout.GridDataFactory;
31
import org.eclipse.jface.layout.GridLayoutFactory;
32
import org.eclipse.jface.resource.ImageDescriptor;
33
import org.eclipse.jface.util.Util;
34
import org.eclipse.jface.wizard.Wizard;
35
import org.eclipse.jface.wizard.WizardDialog;
36
import org.eclipse.jface.wizard.WizardPage;
37
import org.eclipse.swt.SWT;
38
import org.eclipse.swt.graphics.Image;
39
import org.eclipse.swt.widgets.Composite;
40
import org.eclipse.swt.widgets.Display;
41
import org.eclipse.swt.widgets.Label;
42
import org.eclipse.swt.widgets.Shell;
43
import org.eclipse.swt.widgets.Text;
44
45
/**
46
 * Snippet that demonstrates the usage of asynchronous binding as well as cross
47
 * field validations in the context of a wizard page. The example mainly tries
48
 * to illustrate how the WizardPageSupport class tracks the staleness state of
49
 * all the ValidationStatusProviders of the given data binding context, thus
50
 * waiting for pending validations before allowing the user to flip to the next
51
 * wizard page.
52
 */
53
public class Snippet023AsyncUpdateWizard {
54
55
	private static class AsyncBindingWizardPage extends WizardPage {
56
57
		public AsyncBindingWizardPage() {
58
			super("Asynchronous Bindings", "Asynchronous Bindings",
59
					ImageDescriptor.createFromImage(new Image(Display
60
							.getCurrent(), 16, 16)));
61
		}
62
63
		public void createControl(Composite parent) {
64
			Composite container = new Composite(parent, SWT.NONE);
65
			container.setLayout(GridLayoutFactory.fillDefaults().numColumns(2)
66
					.margins(5, 5).spacing(15, 10).create());
67
			GridDataFactory.fillDefaults().grab(true, false).applyTo(container);
68
69
			final DataBindingContext dbc = new DataBindingContext();
70
71
			// We use a control decorator to better illustrate when validations
72
			// are pending on the individual bindings.
73
			ControlDecorator controlDecorator = new ControlDecorator();
74
			controlDecorator.addDbc(dbc);
75
76
			WizardPageSupport.create(this, dbc);
77
78
			new Label(container, SWT.NONE).setText("Value 1");
79
			Text v1Text = new Text(container, SWT.BORDER);
80
			GridDataFactory.fillDefaults().grab(true, false).applyTo(v1Text);
81
82
			final WritableValue value1 = new WritableValue();
83
			dbc.bindValue(SWTObservables.observeText(v1Text, SWT.Modify),
84
					value1, asyncStrategy(), null);
85
86
			new Label(container, SWT.NONE).setText("Value 2");
87
			Text v2Text = new Text(container, SWT.BORDER);
88
			GridDataFactory.fillDefaults().grab(true, false).applyTo(v2Text);
89
90
			final WritableValue value2 = new WritableValue();
91
			dbc.bindValue(SWTObservables.observeText(v2Text, SWT.Modify),
92
					value2, asyncStrategy(), null);
93
94
			// Define the cross field validator.
95
			MultiValidator multiValidator = new MultiValidator() {
96
				private Job validationJob;
97
98
				protected IStatus validate() {
99
					// Cancel any previous job when re-evaluating the
100
					// validation.
101
					if (validationJob != null) {
102
						validationJob.cancel();
103
					}
104
105
					// The observables cannot be accessed from within a
106
					// different thread so we must extract their containing
107
					// values at this point.
108
					final Object v1 = value1.getValue();
109
					final Object v2 = value2.getValue();
110
					validationJob = new Job("Equality validation") {
111
						protected IStatus run(final IProgressMonitor monitor) {
112
							// Do the actual validation.
113
							final IStatus validation;
114
							if (!Util.equals(v1, v2)) {
115
								validation = ValidationStatus
116
										.error("The two values must be equal.");
117
							} else {
118
								validation = ValidationStatus.ok();
119
							}
120
121
							// Take it easy :-).
122
							try {
123
								Thread.sleep(1000);
124
							} catch (InterruptedException e) {
125
								// go ahead
126
							}
127
128
							// Notify the MultiValidator about the completed
129
							// validation. This must be done from within the
130
							// MultiValidator's realm (here the validation
131
							// realm).
132
							dbc.getValidationRealm().exec(new Runnable() {
133
								public void run() {
134
									// Check if this validation has been
135
									// canceled in the meanwhile.
136
									if (!monitor.isCanceled()) {
137
										exitStale(validation);
138
									}
139
								}
140
							});
141
142
							return Status.OK_STATUS;
143
						}
144
					};
145
					validationJob.setSystem(true);
146
					validationJob.schedule();
147
148
					// Notify the MultiValidator about the validation being
149
					// performed asynchronously.
150
					enterStale();
151
152
					// Return a temporary validation to be shown to the user
153
					// while the actual validation has not completed.
154
					return ValidationStatus
155
							.info("Cross field validation is pending...");
156
				}
157
158
				public void dispose() {
159
					if (validationJob != null) {
160
						validationJob.cancel();
161
					}
162
163
					super.dispose();
164
				}
165
			};
166
			dbc.addValidationStatusProvider(multiValidator);
167
168
			setControl(container);
169
		}
170
	}
171
172
	private static UpdateValueStrategy asyncStrategy() {
173
		IValidator2 asyncValidator = new IValidator2() {
174
			private final Random random = new Random(System.currentTimeMillis());
175
176
			public IStatus validate(Object value) {
177
				try {
178
					Thread.sleep(random.nextInt(1000));
179
				} catch (InterruptedException e) {
180
					// go ahead
181
				}
182
183
				String input = (String) value;
184
				if (input != null) {
185
					if (input.length() > 15) {
186
						return ValidationStatus.error("Input is too long.");
187
					}
188
				}
189
190
				return ValidationStatus.ok();
191
			}
192
193
			public boolean isAsync() {
194
				return true;
195
			}
196
		};
197
198
		return new UpdateValueStrategy().setBeforeSetValidator(asyncValidator);
199
	}
200
201
	public static void main(String[] args) {
202
		Display display = new Display();
203
204
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
205
			public void run() {
206
				Display display = Display.getCurrent();
207
				final Shell shell = new Shell(display);
208
				shell.setText("Asynchronous Bindings");
209
210
				Wizard wizard = new Wizard() {
211
					public boolean performFinish() {
212
						return true;
213
					}
214
				};
215
				wizard.setWindowTitle("Asynchronous Bindings");
216
				wizard.addPage(new AsyncBindingWizardPage());
217
218
				WizardDialog dialog = new WizardDialog(shell, wizard);
219
				dialog.open();
220
221
				display.dispose();
222
			}
223
		});
224
	}
225
}
(-)src/org/eclipse/jface/examples/databinding/decoration/ControlDecorator.java (+153 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
10
 ******************************************************************************/
11
12
package org.eclipse.jface.examples.databinding.decoration;
13
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Map;
17
18
import org.eclipse.core.databinding.Binding;
19
import org.eclipse.core.databinding.DataBindingContext;
20
import org.eclipse.core.databinding.observable.IStaleListener;
21
import org.eclipse.core.databinding.observable.StaleEvent;
22
import org.eclipse.core.databinding.observable.list.IListChangeListener;
23
import org.eclipse.core.databinding.observable.list.IObservableList;
24
import org.eclipse.core.databinding.observable.list.ListChangeEvent;
25
import org.eclipse.core.databinding.observable.list.ListDiff;
26
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
27
import org.eclipse.core.databinding.observable.value.IObservableValue;
28
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
29
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
30
import org.eclipse.core.runtime.IStatus;
31
import org.eclipse.jface.databinding.swt.ISWTObservable;
32
import org.eclipse.jface.fieldassist.ControlDecoration;
33
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
34
import org.eclipse.swt.SWT;
35
import org.eclipse.swt.graphics.Image;
36
import org.eclipse.swt.widgets.Control;
37
38
/**
39
 * Simple control decorator which visualizes the validation status of a binding
40
 * on a control assuming the binding's target observable is of type
41
 * {@link ISWTObservable} and {@link ISWTObservable#getWidget()} returns a
42
 * {@link Control} instance to be decorated. In addition, the staleness state of
43
 * a binding's validation status observable is also visualized on the control.
44
 */
45
public class ControlDecorator {
46
47
	private static final int DECORATION_POSITION = SWT.LEFT | SWT.TOP;
48
49
	private final Map bindingToDecorationMap = new HashMap();
50
51
	private IListChangeListener bindingsListener = new IListChangeListener() {
52
		public void handleListChange(ListChangeEvent event) {
53
			ListDiff diff = event.diff;
54
			ListDiffEntry[] differences = diff.getDifferences();
55
			for (int i = 0; i < differences.length; i++) {
56
				ListDiffEntry listDiffEntry = differences[i];
57
				Binding binding = (Binding) listDiffEntry.getElement();
58
				if (listDiffEntry.isAddition()) {
59
					addBinding(binding);
60
				} else {
61
					removeBinding(binding);
62
				}
63
			}
64
		}
65
	};
66
67
	public void addDbc(DataBindingContext dbc) {
68
		IObservableList bindings = dbc.getBindings();
69
		for (Iterator iterator = bindings.iterator(); iterator.hasNext();) {
70
			Binding binding = (Binding) iterator.next();
71
			addBinding(binding);
72
		}
73
		bindings.addListChangeListener(bindingsListener);
74
	}
75
76
	private void addBinding(final Binding binding) {
77
		if (binding.getTarget() instanceof ISWTObservable) {
78
			ISWTObservable target = (ISWTObservable) binding.getTarget();
79
			if (target.getWidget() instanceof Control) {
80
				Control control = (Control) target.getWidget();
81
				final ControlDecoration decoration = new ControlDecoration(
82
						control, DECORATION_POSITION);
83
84
				binding.getValidationStatus().addValueChangeListener(
85
						new IValueChangeListener() {
86
							public void handleValueChange(ValueChangeEvent event) {
87
								updateDecoration(decoration, binding);
88
							}
89
						});
90
91
				binding.getValidationStatus().addStaleListener(
92
						new IStaleListener() {
93
							public void handleStale(StaleEvent staleEvent) {
94
								updateDecoration(decoration, binding);
95
							}
96
						});
97
98
				bindingToDecorationMap.put(binding, decoration);
99
				updateDecoration(decoration, binding);
100
			}
101
		}
102
	}
103
104
	private void removeBinding(Binding binding) {
105
		ControlDecoration decoration = (ControlDecoration) bindingToDecorationMap
106
				.get(binding);
107
		if (decoration != null) {
108
			decoration.hide();
109
			decoration.setDescriptionText(null);
110
111
			bindingToDecorationMap.remove(binding);
112
		}
113
	}
114
115
	private void updateDecoration(ControlDecoration decoration, Binding binding) {
116
		IObservableValue validation = binding.getValidationStatus();
117
		if (validation.isStale()) {
118
			showPendingUpdate(decoration);
119
			return;
120
		}
121
122
		IStatus validationStatus = (IStatus) validation.getValue();
123
		if (validationStatus.isOK()) {
124
			decoration.hide();
125
		} else {
126
			decoration.show();
127
			decoration.setImage(getStatusImage(validationStatus));
128
			decoration.setDescriptionText(validationStatus.getMessage());
129
		}
130
	}
131
132
	private void showPendingUpdate(final ControlDecoration decoration) {
133
		decoration.show();
134
		decoration.setImage(getImage(FieldDecorationRegistry.DEC_INFORMATION));
135
		decoration.setDescriptionText("Pending update...");
136
	}
137
138
	private Image getImage(String key) {
139
		return FieldDecorationRegistry.getDefault().getFieldDecoration(key)
140
				.getImage();
141
	}
142
143
	private Image getStatusImage(IStatus status) {
144
		if (status.matches(IStatus.ERROR)) {
145
			return getImage(FieldDecorationRegistry.DEC_ERROR);
146
		} else if (status.matches(IStatus.WARNING)) {
147
			return getImage(FieldDecorationRegistry.DEC_WARNING);
148
		} else if (status.matches(IStatus.INFO)) {
149
			return getImage(FieldDecorationRegistry.DEC_INFORMATION);
150
		}
151
		return null;
152
	}
153
}

Return to bug 233191