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

Collapse All | Expand All

(-)src/org/eclipse/jface/examples/databinding/snippets/Snippet021MultiFieldValidation.java (-31 / +51 lines)
Lines 12-18 Link Here
12
package org.eclipse.jface.examples.databinding.snippets;
12
package org.eclipse.jface.examples.databinding.snippets;
13
13
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.Iterator;
16
15
17
import org.eclipse.core.databinding.DataBindingContext;
16
import org.eclipse.core.databinding.DataBindingContext;
18
import org.eclipse.core.databinding.observable.Realm;
17
import org.eclipse.core.databinding.observable.Realm;
Lines 36-51 Link Here
36
import org.eclipse.jface.wizard.WizardDialog;
35
import org.eclipse.jface.wizard.WizardDialog;
37
import org.eclipse.jface.wizard.WizardPage;
36
import org.eclipse.jface.wizard.WizardPage;
38
import org.eclipse.swt.SWT;
37
import org.eclipse.swt.SWT;
39
import org.eclipse.swt.events.SelectionAdapter;
40
import org.eclipse.swt.events.SelectionEvent;
41
import org.eclipse.swt.layout.GridData;
38
import org.eclipse.swt.layout.GridData;
42
import org.eclipse.swt.layout.GridLayout;
39
import org.eclipse.swt.layout.GridLayout;
43
import org.eclipse.swt.widgets.Button;
40
import org.eclipse.swt.widgets.Button;
44
import org.eclipse.swt.widgets.Composite;
41
import org.eclipse.swt.widgets.Composite;
45
import org.eclipse.swt.widgets.Display;
42
import org.eclipse.swt.widgets.Display;
43
import org.eclipse.swt.widgets.Event;
46
import org.eclipse.swt.widgets.Group;
44
import org.eclipse.swt.widgets.Group;
47
import org.eclipse.swt.widgets.Label;
45
import org.eclipse.swt.widgets.Label;
48
import org.eclipse.swt.widgets.List;
46
import org.eclipse.swt.widgets.List;
47
import org.eclipse.swt.widgets.Listener;
49
import org.eclipse.swt.widgets.Text;
48
import org.eclipse.swt.widgets.Text;
50
49
51
/**
50
/**
Lines 82-88 Link Here
82
	 * @param parent
81
	 * @param parent
83
	 */
82
	 */
84
	public void createControl(Composite parent) {
83
	public void createControl(Composite parent) {
85
		Composite container = new Composite(parent, SWT.NULL);
84
		Composite container = new Composite(parent, SWT.NONE);
86
		final GridLayout gridLayout = new GridLayout();
85
		final GridLayout gridLayout = new GridLayout();
87
		gridLayout.numColumns = 2;
86
		gridLayout.numColumns = 2;
88
		container.setLayout(gridLayout);
87
		container.setLayout(gridLayout);
Lines 202-220 Link Here
202
	}
201
	}
203
202
204
	private void bindEvensAndOddsGroup(DataBindingContext dbc) {
203
	private void bindEvensAndOddsGroup(DataBindingContext dbc) {
205
		IObservableValue targetField1 = SWTObservables.observeText(
206
				field1Target, SWT.Modify);
207
		final IObservableValue middleField1 = new WritableValue(null,
204
		final IObservableValue middleField1 = new WritableValue(null,
208
				Integer.TYPE);
205
				Integer.TYPE);
209
		dbc.bindValue(targetField1, middleField1, null, null);
206
		dbc.bindValue(SWTObservables.observeText(field1Target, SWT.Modify),
207
				middleField1, null, null);
210
208
211
		IObservableValue targetField2 = SWTObservables.observeText(
212
				field2Target, SWT.Modify);
213
		final IObservableValue middleField2 = new WritableValue(null,
209
		final IObservableValue middleField2 = new WritableValue(null,
214
				Integer.TYPE);
210
				Integer.TYPE);
215
		dbc.bindValue(targetField2, middleField2, null, null);
211
		dbc.bindValue(SWTObservables.observeText(field2Target, SWT.Modify),
212
				middleField2, null, null);
216
213
217
		MultiValidator validator = new MultiValidator() {
214
		MultiValidator validator = new MultiValidator() {
215
218
			protected IStatus validate() {
216
			protected IStatus validate() {
219
				Integer field1 = (Integer) middleField1.getValue();
217
				Integer field1 = (Integer) middleField1.getValue();
220
				Integer field2 = (Integer) middleField2.getValue();
218
				Integer field2 = (Integer) middleField2.getValue();
Lines 236-260 Link Here
236
		dbc.bindValue(validator.observeValidatedValue(middleField2),
234
		dbc.bindValue(validator.observeValidatedValue(middleField2),
237
				modelField2, null, null);
235
				modelField2, null, null);
238
236
239
		dbc.bindValue(SWTObservables.observeText(field1ModelValue, SWT.Modify),
237
		dbc.bindValue(SWTObservables.observeText(field1ModelValue, SWT.None),
240
				modelField1, null, null);
238
				modelField1, null, null);
241
		dbc.bindValue(SWTObservables.observeText(field2ModelValue, SWT.Modify),
239
		dbc.bindValue(SWTObservables.observeText(field2ModelValue, SWT.None),
242
				modelField2, null, null);
240
				modelField2, null, null);
243
	}
241
	}
244
242
245
	private void bindSumAndAddendsGroup(DataBindingContext dbc) {
243
	private void bindSumAndAddendsGroup(DataBindingContext dbc) {
246
		IObservableValue targetSum = SWTObservables.observeText(sumTarget,
247
				SWT.Modify);
248
		final IObservableValue middleSum = new WritableValue(null, Integer.TYPE);
244
		final IObservableValue middleSum = new WritableValue(null, Integer.TYPE);
249
		dbc.bindValue(targetSum, middleSum, null, null);
245
		dbc.bindValue(SWTObservables.observeText(sumTarget, SWT.Modify),
246
				middleSum, null, null);
250
247
251
		final IObservableList targetAddends = new WritableList(new ArrayList(),
248
		final IObservableList targetAddends = new WritableList(new ArrayList(),
252
				Integer.TYPE);
249
				Integer.TYPE);
253
		addendsTarget.setContentProvider(new ObservableListContentProvider());
250
		addendsTarget.setContentProvider(new ObservableListContentProvider());
254
		addendsTarget.setInput(targetAddends);
251
		addendsTarget.setInput(targetAddends);
255
252
256
		addAddendButton.addSelectionListener(new SelectionAdapter() {
253
		addAddendButton.addListener(SWT.Selection, new Listener() {
257
			public void widgetSelected(final SelectionEvent e) {
254
			public void handleEvent(Event event) {
258
				InputDialog dialog = new InputDialog(getShell(),
255
				InputDialog dialog = new InputDialog(getShell(),
259
						"Input addend", "Enter an integer addend", "0",
256
						"Input addend", "Enter an integer addend", "0",
260
						new IInputValidator() {
257
						new IInputValidator() {
Lines 275-282 Link Here
275
			}
272
			}
276
		});
273
		});
277
274
278
		removeAddendButton.addSelectionListener(new SelectionAdapter() {
275
		removeAddendButton.addListener(SWT.Selection, new Listener() {
279
			public void widgetSelected(SelectionEvent e) {
276
			public void handleEvent(Event event) {
280
				IStructuredSelection selection = (IStructuredSelection) addendsTarget
277
				IStructuredSelection selection = (IStructuredSelection) addendsTarget
281
						.getSelection();
278
						.getSelection();
282
				if (!selection.isEmpty())
279
				if (!selection.isEmpty())
Lines 286-309 Link Here
286
283
287
		IObservableValue modelSum = new WritableValue(new Integer(5),
284
		IObservableValue modelSum = new WritableValue(new Integer(5),
288
				Integer.TYPE);
285
				Integer.TYPE);
289
		dbc.bindValue(SWTObservables.observeText(sumModelValue, SWT.Modify),
286
		dbc.bindValue(SWTObservables.observeText(sumModelValue, SWT.None),
290
				modelSum, null, null);
287
				modelSum, null, null);
291
288
292
		IObservableList modelAddends = new WritableList(new ArrayList(),
289
		IObservableList modelAddends = new WritableList(new ArrayList(),
293
				Integer.TYPE);
290
				Integer.TYPE);
294
291
295
		MultiValidator validator = new MultiValidator() {
292
		MultiValidator validator = new MultiValidator() {
296
			protected IStatus validate() {
293
			protected void validate(final StatusHandler callback) {
297
				Integer sum = (Integer) middleSum.getValue();
294
				final Integer sum = (Integer) middleSum.getValue();
298
				int actualSum = 0;
295
				final Integer[] addends = (Integer[]) targetAddends
299
				for (Iterator iterator = targetAddends.iterator(); iterator
296
						.toArray(new Integer[targetAddends.size()]);
300
						.hasNext();) {
297
				new Thread(new Runnable() {
301
					actualSum += ((Integer) iterator.next()).intValue();
298
					public void run() {
302
				}
299
						try {
303
				if (sum.intValue() != actualSum)
300
							// artificial delay
304
					return ValidationStatus.error("Sum of addends is "
301
							Thread.sleep(1000);
305
							+ actualSum + ", expecting " + sum);
302
						} catch (InterruptedException e) {
306
				return ValidationStatus.ok();
303
							callback.handleStatus(ValidationStatus.error(e
304
									.getMessage(), e));
305
						}
306
307
						if (sum == null) {
308
							callback.handleStatus(ValidationStatus
309
									.error("sum is null"));
310
							return;
311
						}
312
313
						int actualSum = 0;
314
						for (int i = 0; i < addends.length; i++) {
315
							actualSum += addends[i].intValue();
316
						}
317
318
						if (sum.intValue() != actualSum) {
319
							callback.handleStatus(ValidationStatus
320
									.error("Sum of addends is " + actualSum
321
											+ ", expecting " + sum));
322
						} else {
323
							callback.handleStatus(ValidationStatus.ok());
324
						}
325
					}
326
				}).start();
307
			}
327
			}
308
		};
328
		};
309
		dbc.addValidationStatusProvider(validator);
329
		dbc.addValidationStatusProvider(validator);
(-)src/org/eclipse/core/databinding/validation/MultiValidator.java (-16 / +137 lines)
Lines 14-22 Link Here
14
14
15
import java.util.ArrayList;
15
import java.util.ArrayList;
16
import java.util.Arrays;
16
import java.util.Arrays;
17
import java.util.List;
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 31-36 Link Here
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;
33
import org.eclipse.core.databinding.observable.value.WritableValue;
35
import org.eclipse.core.databinding.observable.value.WritableValue;
36
import org.eclipse.core.internal.databinding.Util;
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;
36
import org.eclipse.core.internal.databinding.observable.ValidatedObservableSet;
39
import org.eclipse.core.internal.databinding.observable.ValidatedObservableSet;
Lines 115-121 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;
Lines 160-167 Link Here
160
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
163
		Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$
161
		this.realm = realm;
164
		this.realm = realm;
162
165
163
		validationStatus = new WritableValue(realm, ValidationStatus.ok(),
166
		validationStatus = new ValidationStatusObservableValue(realm);
164
				IStatus.class);
165
167
166
		targets = new WritableList(realm, new ArrayList(), IObservable.class);
168
		targets = new WritableList(realm, new ArrayList(), IObservable.class);
167
		targets.addListChangeListener(targetsListener);
169
		targets.addListChangeListener(targetsListener);
Lines 195-225 Link Here
195
	}
197
	}
196
198
197
	private void revalidate() {
199
	private void revalidate() {
200
		validationStatus.makeStale();
201
202
		final StatusHandler handler = new StatusHandler() {
203
			public void handleStatus(final IStatus status) {
204
				realm.exec(new Runnable() {
205
					public void run() {
206
						setStatus(status);
207
					}
208
				});
209
			}
210
		};
198
		final IObservable[] dependencies = ObservableTracker.runAndMonitor(
211
		final IObservable[] dependencies = ObservableTracker.runAndMonitor(
199
				new Runnable() {
212
				new Runnable() {
200
					public void run() {
213
					public void run() {
201
						try {
214
						try {
202
							IStatus status = validate();
215
							validate(handler);
203
							if (status == null)
204
								status = ValidationStatus.ok();
205
							validationStatus.setValue(status);
206
						} catch (RuntimeException e) {
216
						} catch (RuntimeException e) {
207
							// Usually an NPE as dependencies are
217
							// Usually an NPE as dependencies are init'ed
208
							// init'ed
218
							setStatus(ValidationStatus.error(e.getMessage(), e));
209
							validationStatus.setValue(ValidationStatus.error(e
210
									.getMessage(), e));
211
						}
219
						}
212
					}
220
					}
213
				}, null, null);
221
				}, null, null);
214
		ObservableTracker.runAndIgnore(new Runnable() {
222
		ObservableTracker.runAndIgnore(new Runnable() {
215
			public void run() {
223
			public void run() {
216
				targets.clear();
224
				List dependencyList = new ArrayList(Arrays.asList(dependencies));
217
				targets.addAll(Arrays.asList(dependencies));
225
				// remove old targets not in the new dependency list
226
				targets.retainAll(dependencyList);
227
				// remove targets in the new dependency list which are already
228
				// known
229
				dependencyList.removeAll(targets);
230
				// add the new dependencies
231
				targets.addAll(dependencyList);
218
			}
232
			}
219
		});
233
		});
220
	}
234
	}
221
235
222
	/**
236
	/**
237
	 * Callback interface for handling statuses
238
	 * 
239
	 * @since 1.2
240
	 */
241
	public interface StatusHandler {
242
		/**
243
		 * Handles the given status object
244
		 * 
245
		 * @param status
246
		 *            the status
247
		 */
248
		void handleStatus(IStatus status);
249
	}
250
251
	/**
252
	 * Calculates and passes the current validation status to the given
253
	 * callback.
254
	 * <p>
255
	 * Note: To ensure that the validation status is kept current, all
256
	 * dependencies used to calculate status should be accessed through
257
	 * {@link IObservable} instances. Each dependency observable must be in the
258
	 * same realm as the MultiValidator.
259
	 * <p>
260
	 * If validation is being performed asynchronously (i.e. in a different
261
	 * thread), then the observables which are used to calculate validation
262
	 * status should be accessed before forking, as follows:
263
	 * 
264
	 * <pre>
265
	 * MultiValidator validator = new MultiValidator() {
266
	 * 	protected void validate(final StatusHandler callback) {
267
	 *      // Observables must be accessed in the calling thread
268
	 * 		final Integer value0 = (Integer) middle0.getValue();
269
	 * 		final Integer value1 = (Integer) middle1.getValue();
270
	 * 		new Thread(new Runnable() {
271
	 * 			public void run() {
272
	 * 		        // Calculate the validation status
273
	 * 				if (Math.abs(value0.intValue()) % 2 != Math.abs(value1
274
	 * 						.intValue()) % 2) {
275
	 *                  callback.handleStatus(ValidationStatus.error(
276
	 *                      &quot;Values must be both even or both odd&quot;);
277
	 *              } else {
278
	 *                  callback.handleStatus(ValidationStatus.ok());
279
	 *              }
280
	 * 			}
281
	 * 		}).start();
282
	 * 	}
283
	 * };
284
	 * </pre>
285
	 * 
286
	 * @param callback
287
	 *            the callback which is to receive the validation status. The
288
	 *            callback may be called from outside the realm.
289
	 */
290
	protected void validate(StatusHandler callback) {
291
		try {
292
			callback.handleStatus(validate());
293
		} catch (RuntimeException e) {
294
			// Usually an NPE as dependencies are
295
			// init'ed
296
			callback.handleStatus(ValidationStatus.error(e.getMessage(), e));
297
		}
298
	}
299
300
	/**
223
	 * Return the current validation status.
301
	 * Return the current validation status.
224
	 * <p>
302
	 * <p>
225
	 * Note: To ensure that the validation status is kept current, all
303
	 * Note: To ensure that the validation status is kept current, all
Lines 229-235 Link Here
229
	 * 
307
	 * 
230
	 * @return the current validation status.
308
	 * @return the current validation status.
231
	 */
309
	 */
232
	protected abstract IStatus validate();
310
	protected IStatus validate() {
311
		throw new IllegalStateException(
312
				"Subclasses must override one of the validate() methods"); //$NON-NLS-1$
313
	}
233
314
234
	/**
315
	/**
235
	 * Returns a wrapper {@link IObservableValue} which stays in sync with the
316
	 * Returns a wrapper {@link IObservableValue} which stays in sync with the
Lines 321-328 Link Here
321
	 * The wrapper behaves as follows with respect to the validation status:
402
	 * The wrapper behaves as follows with respect to the validation status:
322
	 * <ul>
403
	 * <ul>
323
	 * <li>While valid, the wrapper stays in sync with its target observable.
404
	 * <li>While valid, the wrapper stays in sync with its target observable.
324
	 * <li>While invalid, the wrapper's entries are the target observable's
405
	 * <li>While invalid, the wrapper's entries are the target observable's last
325
	 * last valid entries. If the target changes entries, a stale event is fired
406
	 * valid entries. If the target changes entries, a stale event is fired
326
	 * signaling that a change is pending.
407
	 * signaling that a change is pending.
327
	 * <li>When status changes from invalid to valid, the wrapper takes the
408
	 * <li>When status changes from invalid to valid, the wrapper takes the
328
	 * entries of the target observable, and synchronization resumes.
409
	 * entries of the target observable, and synchronization resumes.
Lines 366-369 Link Here
366
		super.dispose();
447
		super.dispose();
367
	}
448
	}
368
449
450
	private void setStatus(final IStatus status) {
451
		ObservableTracker.runAndIgnore(new Runnable() {
452
			public void run() {
453
				validationStatus.setValue(status == null ? ValidationStatus
454
						.ok() : status);
455
			}
456
		});
457
	}
458
459
	private class ValidationStatusObservableValue extends WritableValue {
460
		boolean stale;
461
462
		public ValidationStatusObservableValue(Realm realm) {
463
			super(realm, ValidationStatus.ok(), IStatus.class);
464
		}
465
466
		void makeStale() {
467
			this.stale = true;
468
			fireStale();
469
		}
470
471
		public void doSetValue(Object value) {
472
			boolean wasStale = this.stale;
473
			this.stale = false;
474
			Object oldValue = doGetValue();
475
			super.doSetValue(value);
476
			if (wasStale && Util.equals(oldValue, value)) {
477
				// In order to signal that the observable is not stale anymore,
478
				// we must always fire a value change event, even if the
479
				// validation status has not changed. In the latter case, we
480
				// must do so explicitly.
481
				fireValueChange(Diffs.createValueDiff(oldValue, value));
482
			}
483
		}
484
485
		public boolean isStale() {
486
			ObservableTracker.getterCalled(this);
487
			return stale;
488
		}
489
	}
369
}
490
}
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableSet.java (-16 / +17 lines)
Lines 52-58 Link Here
52
			if (updatingTarget)
52
			if (updatingTarget)
53
				return;
53
				return;
54
			IStatus status = (IStatus) validationStatus.getValue();
54
			IStatus status = (IStatus) validationStatus.getValue();
55
			if (isValid(status)) {
55
			if (!validationStatus.isStale() && isValid(status)) {
56
				if (stale) {
56
				if (stale) {
57
					// this.stale means we are out of sync with target,
57
					// this.stale means we are out of sync with target,
58
					// so reset wrapped list to exactly mirror target
58
					// so reset wrapped list to exactly mirror target
Lines 73-89 Link Here
73
		}
73
		}
74
	};
74
	};
75
75
76
	private IStaleListener targetStaleListener = new IStaleListener() {
77
		public void handleStale(StaleEvent staleEvent) {
78
			fireStale();
79
		}
80
	};
81
82
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
76
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
83
		public void handleValueChange(ValueChangeEvent event) {
77
		public void handleValueChange(ValueChangeEvent event) {
84
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
85
			IStatus newStatus = (IStatus) event.diff.getNewValue();
78
			IStatus newStatus = (IStatus) event.diff.getNewValue();
86
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
79
			if (stale && !validationStatus.isStale() && isValid(newStatus)) {
87
				// this.stale means we are out of sync with target,
80
				// this.stale means we are out of sync with target,
88
				// reset wrapped set to exactly mirror target
81
				// reset wrapped set to exactly mirror target
89
				stale = false;
82
				stale = false;
Lines 96-101 Link Here
96
		}
89
		}
97
	};
90
	};
98
91
92
	private IStaleListener staleListener = new IStaleListener() {
93
		public void handleStale(StaleEvent staleEvent) {
94
			fireStale();
95
		}
96
	};
97
98
	private static boolean isValid(IStatus status) {
99
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
100
	}
101
99
	/**
102
	/**
100
	 * @param target
103
	 * @param target
101
	 * @param validationStatus
104
	 * @param validationStatus
Lines 111-118 Link Here
111
		this.target = target;
114
		this.target = target;
112
		this.validationStatus = validationStatus;
115
		this.validationStatus = validationStatus;
113
		target.addSetChangeListener(targetChangeListener);
116
		target.addSetChangeListener(targetChangeListener);
114
		target.addStaleListener(targetStaleListener);
117
		target.addStaleListener(staleListener);
115
		validationStatus.addValueChangeListener(validationStatusChangeListener);
118
		validationStatus.addValueChangeListener(validationStatusChangeListener);
119
		validationStatus.addStaleListener(staleListener);
116
	}
120
	}
117
121
118
	private void updateWrappedSet(Set newSet) {
122
	private void updateWrappedSet(Set newSet) {
Lines 122-131 Link Here
122
		fireSetChange(diff);
126
		fireSetChange(diff);
123
	}
127
	}
124
128
125
	private static boolean isValid(IStatus status) {
126
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
127
	}
128
129
	private void applyDiff(SetDiff diff, Set set) {
129
	private void applyDiff(SetDiff diff, Set set) {
130
		for (Iterator iterator = diff.getRemovals().iterator(); iterator
130
		for (Iterator iterator = diff.getRemovals().iterator(); iterator
131
				.hasNext();) {
131
				.hasNext();) {
Lines 160-166 Link Here
160
160
161
	public boolean isStale() {
161
	public boolean isStale() {
162
		getterCalled();
162
		getterCalled();
163
		return stale || target.isStale();
163
		return stale || target.isStale() || validationStatus.isStale();
164
	}
164
	}
165
165
166
	public boolean add(Object o) {
166
	public boolean add(Object o) {
Lines 262-270 Link Here
262
262
263
	public synchronized void dispose() {
263
	public synchronized void dispose() {
264
		target.removeSetChangeListener(targetChangeListener);
264
		target.removeSetChangeListener(targetChangeListener);
265
		target.removeStaleListener(targetStaleListener);
265
		target.removeStaleListener(staleListener);
266
		validationStatus
266
		validationStatus
267
				.removeValueChangeListener(validationStatusChangeListener);
267
				.removeValueChangeListener(validationStatusChangeListener);
268
		validationStatus.removeStaleListener(staleListener);
268
		super.dispose();
269
		super.dispose();
269
	}
270
	}
270
}
271
}
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableMap.java (-13 / +13 lines)
Lines 51-57 Link Here
51
			if (updatingTarget)
51
			if (updatingTarget)
52
				return;
52
				return;
53
			IStatus status = (IStatus) validationStatus.getValue();
53
			IStatus status = (IStatus) validationStatus.getValue();
54
			if (isValid(status)) {
54
			if (!validationStatus.isStale() && isValid(status)) {
55
				if (stale) {
55
				if (stale) {
56
					// this.stale means we are out of sync with target,
56
					// this.stale means we are out of sync with target,
57
					// so reset wrapped list to exactly mirror target
57
					// so reset wrapped list to exactly mirror target
Lines 72-88 Link Here
72
		}
72
		}
73
	};
73
	};
74
74
75
	private IStaleListener targetStaleListener = new IStaleListener() {
76
		public void handleStale(StaleEvent staleEvent) {
77
			fireStale();
78
		}
79
	};
80
81
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
75
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
82
		public void handleValueChange(ValueChangeEvent event) {
76
		public void handleValueChange(ValueChangeEvent event) {
83
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
84
			IStatus newStatus = (IStatus) event.diff.getNewValue();
77
			IStatus newStatus = (IStatus) event.diff.getNewValue();
85
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
78
			if (stale && !validationStatus.isStale() && isValid(newStatus)) {
86
				// this.stale means we are out of sync with target,
79
				// this.stale means we are out of sync with target,
87
				// reset wrapped map to exactly mirror target
80
				// reset wrapped map to exactly mirror target
88
				stale = false;
81
				stale = false;
Lines 95-100 Link Here
95
		}
88
		}
96
	};
89
	};
97
90
91
	private IStaleListener staleListener = new IStaleListener() {
92
		public void handleStale(StaleEvent staleEvent) {
93
			fireStale();
94
		}
95
	};
96
98
	/**
97
	/**
99
	 * @param target
98
	 * @param target
100
	 * @param validationStatus
99
	 * @param validationStatus
Lines 110-116 Link Here
110
		this.target = target;
109
		this.target = target;
111
		this.validationStatus = validationStatus;
110
		this.validationStatus = validationStatus;
112
		target.addMapChangeListener(targetChangeListener);
111
		target.addMapChangeListener(targetChangeListener);
113
		target.addStaleListener(targetStaleListener);
112
		target.addStaleListener(staleListener);
114
		validationStatus.addValueChangeListener(validationStatusChangeListener);
113
		validationStatus.addValueChangeListener(validationStatusChangeListener);
115
	}
114
	}
116
115
Lines 122-128 Link Here
122
	}
121
	}
123
122
124
	private static boolean isValid(IStatus status) {
123
	private static boolean isValid(IStatus status) {
125
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
124
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
126
	}
125
	}
127
126
128
	private void applyDiff(MapDiff diff, Map map) {
127
	private void applyDiff(MapDiff diff, Map map) {
Lines 164-170 Link Here
164
163
165
	public boolean isStale() {
164
	public boolean isStale() {
166
		getterCalled();
165
		getterCalled();
167
		return stale || target.isStale();
166
		return stale || target.isStale() || validationStatus.isStale();
168
	}
167
	}
169
168
170
	public void clear() {
169
	public void clear() {
Lines 220-228 Link Here
220
219
221
	public synchronized void dispose() {
220
	public synchronized void dispose() {
222
		target.removeMapChangeListener(targetChangeListener);
221
		target.removeMapChangeListener(targetChangeListener);
223
		target.removeStaleListener(targetStaleListener);
222
		target.removeStaleListener(staleListener);
224
		validationStatus
223
		validationStatus
225
				.removeValueChangeListener(validationStatusChangeListener);
224
				.removeValueChangeListener(validationStatusChangeListener);
225
		validationStatus.removeStaleListener(staleListener);
226
		super.dispose();
226
		super.dispose();
227
	}
227
	}
228
}
228
}
(-)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/observable/ValidatedObservableList.java (-16 / +17 lines)
Lines 56-62 Link Here
56
			if (updatingTarget)
56
			if (updatingTarget)
57
				return;
57
				return;
58
			IStatus status = (IStatus) validationStatus.getValue();
58
			IStatus status = (IStatus) validationStatus.getValue();
59
			if (isValid(status)) {
59
			if (!validationStatus.isStale() && isValid(status)) {
60
				if (stale) {
60
				if (stale) {
61
					// this.stale means we are out of sync with target,
61
					// this.stale means we are out of sync with target,
62
					// so reset wrapped list to exactly mirror target
62
					// so reset wrapped list to exactly mirror target
Lines 77-97 Link Here
77
		}
77
		}
78
	};
78
	};
79
79
80
	private static boolean isValid(IStatus status) {
81
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
82
	}
83
84
	private IStaleListener targetStaleListener = new IStaleListener() {
85
		public void handleStale(StaleEvent staleEvent) {
86
			fireStale();
87
		}
88
	};
89
90
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
80
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
91
		public void handleValueChange(ValueChangeEvent event) {
81
		public void handleValueChange(ValueChangeEvent event) {
92
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
93
			IStatus newStatus = (IStatus) event.diff.getNewValue();
82
			IStatus newStatus = (IStatus) event.diff.getNewValue();
94
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
83
			if (stale && !validationStatus.isStale() && isValid(newStatus)) {
95
				// this.stale means we are out of sync with target,
84
				// this.stale means we are out of sync with target,
96
				// reset wrapped list to exactly mirror target
85
				// reset wrapped list to exactly mirror target
97
				stale = false;
86
				stale = false;
Lines 104-109 Link Here
104
		}
93
		}
105
	};
94
	};
106
95
96
	private IStaleListener staleListener = new IStaleListener() {
97
		public void handleStale(StaleEvent staleEvent) {
98
			fireStale();
99
		}
100
	};
101
102
	private static boolean isValid(IStatus status) {
103
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
104
	}
105
107
	/**
106
	/**
108
	 * @param target
107
	 * @param target
109
	 * @param validationStatus
108
	 * @param validationStatus
Lines 119-126 Link Here
119
		this.target = target;
118
		this.target = target;
120
		this.validationStatus = validationStatus;
119
		this.validationStatus = validationStatus;
121
		target.addListChangeListener(targetChangeListener);
120
		target.addListChangeListener(targetChangeListener);
122
		target.addStaleListener(targetStaleListener);
121
		target.addStaleListener(staleListener);
123
		validationStatus.addValueChangeListener(validationStatusChangeListener);
122
		validationStatus.addValueChangeListener(validationStatusChangeListener);
123
		validationStatus.addStaleListener(staleListener);
124
	}
124
	}
125
125
126
	private void makeStale() {
126
	private void makeStale() {
Lines 163-169 Link Here
163
163
164
	public boolean isStale() {
164
	public boolean isStale() {
165
		ObservableTracker.getterCalled(this);
165
		ObservableTracker.getterCalled(this);
166
		return stale || target.isStale();
166
		return stale || target.isStale() || validationStatus.isStale();
167
	}
167
	}
168
168
169
	public void add(int index, Object element) {
169
	public void add(int index, Object element) {
Lines 385-393 Link Here
385
385
386
	public synchronized void dispose() {
386
	public synchronized void dispose() {
387
		target.removeListChangeListener(targetChangeListener);
387
		target.removeListChangeListener(targetChangeListener);
388
		target.removeStaleListener(targetStaleListener);
388
		target.removeStaleListener(staleListener);
389
		validationStatus
389
		validationStatus
390
				.removeValueChangeListener(validationStatusChangeListener);
390
				.removeValueChangeListener(validationStatusChangeListener);
391
		validationStatus.removeStaleListener(staleListener);
391
		super.dispose();
392
		super.dispose();
392
	}
393
	}
393
}
394
}
(-)src/org/eclipse/core/internal/databinding/observable/ValidatedObservableValue.java (-20 / +22 lines)
Lines 42-50 Link Here
42
 * <p>
42
 * <p>
43
 * Note:
43
 * Note:
44
 * <ul>
44
 * <ul>
45
 * <li>By default, a status is valid if its
45
 * <li>By default, a status is valid if its {@link IStatus#getSeverity()
46
 * {@link IStatus#getSeverity() severity} is {@link IStatus#OK OK},
46
 * severity} is {@link IStatus#OK OK}, {@link IStatus#INFO INFO}, or
47
 * {@link IStatus#INFO INFO}, or {@link IStatus#WARNING WARNING}
47
 * {@link IStatus#WARNING WARNING}
48
 * <li>Calls to {@link #setValue(Object)} on the validated observable changes
48
 * <li>Calls to {@link #setValue(Object)} on the validated observable changes
49
 * the value regardless of the validation status.
49
 * the value regardless of the validation status.
50
 * <li>This class will not forward {@link ValueChangingEvent} events from a
50
 * <li>This class will not forward {@link ValueChangingEvent} events from a
Lines 66-98 Link Here
66
			if (updatingTarget)
66
			if (updatingTarget)
67
				return;
67
				return;
68
			IStatus status = (IStatus) validationStatus.getValue();
68
			IStatus status = (IStatus) validationStatus.getValue();
69
			if (isValid(status))
69
			if (!validationStatus.isStale() && isValid(status))
70
				internalSetValue(event.diff.getNewValue(), false);
70
				internalSetValue(event.diff.getNewValue(), false);
71
			else
71
			else
72
				makeStale();
72
				makeStale();
73
		}
73
		}
74
	};
74
	};
75
75
76
	private static boolean isValid(IStatus status) {
77
		return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING);
78
	}
79
80
	private IStaleListener targetStaleListener = new IStaleListener() {
81
		public void handleStale(StaleEvent staleEvent) {
82
			fireStale();
83
		}
84
	};
85
86
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
76
	private IValueChangeListener validationStatusChangeListener = new IValueChangeListener() {
87
		public void handleValueChange(ValueChangeEvent event) {
77
		public void handleValueChange(ValueChangeEvent event) {
88
			IStatus oldStatus = (IStatus) event.diff.getOldValue();
89
			IStatus newStatus = (IStatus) event.diff.getNewValue();
78
			IStatus newStatus = (IStatus) event.diff.getNewValue();
90
			if (stale && !isValid(oldStatus) && isValid(newStatus)) {
79
			if (stale && !validationStatus.isStale() && isValid(newStatus)) {
91
				internalSetValue(target.getValue(), false);
80
				internalSetValue(target.getValue(), false);
92
			}
81
			}
93
		}
82
		}
94
	};
83
	};
95
84
85
	private IStaleListener staleListener = new IStaleListener() {
86
		public void handleStale(StaleEvent staleEvent) {
87
			fireStale();
88
		}
89
	};
90
91
	private static boolean isValid(IStatus status) {
92
		return !status.matches(IStatus.CANCEL | IStatus.ERROR);
93
	}
94
96
	/**
95
	/**
97
	 * Constructs an observable value
96
	 * Constructs an observable value
98
	 * 
97
	 * 
Lines 115-122 Link Here
115
		this.cachedValue = target.getValue();
114
		this.cachedValue = target.getValue();
116
115
117
		target.addValueChangeListener(targetChangeListener);
116
		target.addValueChangeListener(targetChangeListener);
118
		target.addStaleListener(targetStaleListener);
117
		target.addStaleListener(staleListener);
119
		validationStatus.addValueChangeListener(validationStatusChangeListener);
118
		validationStatus.addValueChangeListener(validationStatusChangeListener);
119
		validationStatus.addStaleListener(staleListener);
120
	}
120
	}
121
121
122
	private void makeStale() {
122
	private void makeStale() {
Lines 128-134 Link Here
128
128
129
	public boolean isStale() {
129
	public boolean isStale() {
130
		ObservableTracker.getterCalled(this);
130
		ObservableTracker.getterCalled(this);
131
		return stale || target.isStale();
131
		return stale || target.isStale() || validationStatus.isStale();
132
	}
132
	}
133
133
134
	protected Object doGetValue() {
134
	protected Object doGetValue() {
Lines 136-141 Link Here
136
	}
136
	}
137
137
138
	private void internalSetValue(Object value, boolean updateTarget) {
138
	private void internalSetValue(Object value, boolean updateTarget) {
139
		boolean wasStale = this.stale;
139
		Object oldValue = cachedValue;
140
		Object oldValue = cachedValue;
140
		cachedValue = value;
141
		cachedValue = value;
141
		if (updateTarget) {
142
		if (updateTarget) {
Lines 148-154 Link Here
148
			}
149
			}
149
		}
150
		}
150
		stale = false;
151
		stale = false;
151
		if (!Util.equals(oldValue, cachedValue))
152
		if (wasStale || !Util.equals(oldValue, cachedValue))
152
			fireValueChange(Diffs.createValueDiff(oldValue, cachedValue));
153
			fireValueChange(Diffs.createValueDiff(oldValue, cachedValue));
153
	}
154
	}
154
155
Lines 162-170 Link Here
162
163
163
	public synchronized void dispose() {
164
	public synchronized void dispose() {
164
		target.removeValueChangeListener(targetChangeListener);
165
		target.removeValueChangeListener(targetChangeListener);
165
		target.removeStaleListener(targetStaleListener);
166
		target.removeStaleListener(staleListener);
166
		validationStatus
167
		validationStatus
167
				.removeValueChangeListener(validationStatusChangeListener);
168
				.removeValueChangeListener(validationStatusChangeListener);
169
		validationStatus.removeStaleListener(staleListener);
168
		super.dispose();
170
		super.dispose();
169
	}
171
	}
170
}
172
}

Return to bug 233191