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

Collapse All | Expand All

(-)src/org/eclipse/core/databinding/observable/set/DeferredObservableSet.java (+98 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation 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
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.core.databinding.observable.set;
13
14
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.Set;
17
18
import org.eclipse.core.databinding.observable.Diffs;
19
import org.eclipse.core.runtime.Assert;
20
21
/**
22
 * A read-only observable set that can defer changes to its underlying
23
 * observable set.
24
 * 
25
 * @since 1.1
26
 * 
27
 */
28
public class DeferredObservableSet extends ObservableSet {
29
	
30
	private IObservableSet target;
31
	private ISetChangeListener listener = new ISetChangeListener(){
32
		public void handleSetChange(SetChangeEvent event) {
33
			if (deferChanges == 0) {
34
				fireSetChange(Diffs.createSetDiff(event.diff.getAdditions(), event.diff.getRemovals()));
35
			} else {
36
				for (Iterator it = event.diff.getAdditions().iterator(); it
37
						.hasNext();) {
38
					Object added = it.next();
39
					if (!deferredRemovals.remove(added)) {
40
						deferredAdditions.add(added);
41
					}
42
				}
43
				for (Iterator it = event.diff.getRemovals().iterator(); it
44
				.hasNext();) {
45
					Object removed = it.next();
46
					if (!deferredAdditions.remove(removed)) {
47
						deferredRemovals.add(removed);
48
					}
49
				}
50
			}
51
		}
52
	};
53
	
54
	private int deferChanges;
55
	
56
	private Set deferredAdditions = new HashSet();
57
	private Set deferredRemovals = new HashSet();
58
59
	/**
60
	 * @param target
61
	 * @param elementType 
62
	 */
63
	public DeferredObservableSet(IObservableSet target, Object elementType) {
64
		super(new HashSet(), elementType);
65
		this.target = target;
66
		target.addSetChangeListener(listener);
67
		wrappedSet.addAll(target);
68
	}
69
	
70
	/**
71
	 * @param defer
72
	 */
73
	public void deferChanges(boolean defer) {
74
		checkRealm();
75
		if (defer) {
76
			deferChanges++;
77
		} else {
78
			--deferChanges;
79
		}
80
		Assert.isTrue(deferChanges >= 0);
81
		if (deferChanges == 0 && (deferredRemovals.size() > 0 || deferredAdditions.size() > 0)) {
82
			wrappedSet.removeAll(deferredRemovals);
83
			wrappedSet.addAll(deferredAdditions);
84
			SetDiff diff = Diffs.createSetDiff(new HashSet(deferredAdditions), new HashSet(deferredRemovals));
85
			deferredAdditions.clear();
86
			deferredRemovals.clear();
87
			fireSetChange(diff);
88
		}
89
	}
90
	
91
	public synchronized void dispose() {
92
		target.removeSetChangeListener(listener);
93
		listener = null;
94
		target = null;
95
		super.dispose();
96
	}
97
98
}
(-)src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.java (+7 lines)
Lines 159-162 Link Here
159
	public IObservableSet getKnownElements() {
159
	public IObservableSet getKnownElements() {
160
		return impl.getKnownElements();
160
		return impl.getKnownElements();
161
	}
161
	}
162
163
	/**
164
	 * @return bla
165
	 */
166
	public IObservableSet getDeferredKnownElements() {
167
		return impl.getDeferredKnownElements();
168
	}
162
}
169
}
(-)src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.java (+13 lines)
Lines 64-73 Link Here
64
				final Set removals = ViewerElementSet.withComparer(comparer);
64
				final Set removals = ViewerElementSet.withComparer(comparer);
65
				event.diff.accept(new ListDiffVisitor() {
65
				event.diff.accept(new ListDiffVisitor() {
66
					public void handleAdd(int index, Object child) {
66
					public void handleAdd(int index, Object child) {
67
68
						deferredKnownElements.deferChanges(true);
67
						// adds to known elements if new element
69
						// adds to known elements if new element
68
						getOrCreateNode(child).addParent(parentElement);
70
						getOrCreateNode(child).addParent(parentElement);
69
71
70
						viewerUpdater.insert(parentElement, child, index);
72
						viewerUpdater.insert(parentElement, child, index);
73
						
74
						deferredKnownElements.deferChanges(false);
71
					}
75
					}
72
76
73
					public void handleRemove(int index, Object child) {
77
					public void handleRemove(int index, Object child) {
Lines 78-88 Link Here
78
82
79
					public void handleReplace(int index, Object oldChild,
83
					public void handleReplace(int index, Object oldChild,
80
							Object newChild) {
84
							Object newChild) {
85
						deferredKnownElements.deferChanges(true);
81
						getOrCreateNode(newChild).addParent(parentElement);
86
						getOrCreateNode(newChild).addParent(parentElement);
82
87
83
						viewerUpdater.replace(parentElement, oldChild,
88
						viewerUpdater.replace(parentElement, oldChild,
84
								newChild, index);
89
								newChild, index);
85
90
91
						deferredKnownElements.deferChanges(false);
86
						removals.add(oldChild);
92
						removals.add(oldChild);
87
					}
93
					}
88
94
Lines 183-186 Link Here
183
	public IObservableSet getKnownElements() {
189
	public IObservableSet getKnownElements() {
184
		return impl.getKnownElements();
190
		return impl.getKnownElements();
185
	}
191
	}
192
193
	/**
194
	 * @return bla
195
	 */
196
	public IObservableSet getDeferredKnownElements() {
197
		return impl.getDeferredKnownElements();
198
	}
186
}
199
}
(-)src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.java (-2 / +42 lines)
Lines 23-28 Link Here
23
import org.eclipse.core.databinding.observable.Realm;
23
import org.eclipse.core.databinding.observable.Realm;
24
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
24
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
25
import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
25
import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables;
26
import org.eclipse.core.databinding.observable.set.DeferredObservableSet;
26
import org.eclipse.core.databinding.observable.set.IObservableSet;
27
import org.eclipse.core.databinding.observable.set.IObservableSet;
27
import org.eclipse.core.databinding.observable.value.IObservableValue;
28
import org.eclipse.core.databinding.observable.value.IObservableValue;
28
import org.eclipse.core.databinding.observable.value.WritableValue;
29
import org.eclipse.core.databinding.observable.value.WritableValue;
Lines 62-67 Link Here
62
	protected IElementComparer comparer;
63
	protected IElementComparer comparer;
63
64
64
	private IObservableSet knownElements;
65
	private IObservableSet knownElements;
66
	protected DeferredObservableSet deferredKnownElements;
65
	private IObservableSet unmodifiableKnownElements;
67
	private IObservableSet unmodifiableKnownElements;
66
68
67
	private IObservableFactory /* <IObservableCollection> */collectionFactory;
69
	private IObservableFactory /* <IObservableCollection> */collectionFactory;
Lines 70-75 Link Here
70
72
71
	private TreeStructureAdvisor structureAdvisor;
73
	private TreeStructureAdvisor structureAdvisor;
72
74
75
	protected ObservableViewerElementSet internalCurrentKnownElements;
76
77
	private IObservableSet unmodifiableDeferredKnownElements;
78
73
	/**
79
	/**
74
	 * Constructs an ObservableCollectionTreeContentProvider using the given
80
	 * Constructs an ObservableCollectionTreeContentProvider using the given
75
	 * parent provider and collection factory.
81
	 * parent provider and collection factory.
Lines 100-105 Link Here
100
				knownElementsFactory, null);
106
				knownElementsFactory, null);
101
		unmodifiableKnownElements = Observables
107
		unmodifiableKnownElements = Observables
102
				.unmodifiableObservableSet(knownElements);
108
				.unmodifiableObservableSet(knownElements);
109
		deferredKnownElements = new DeferredObservableSet(knownElements, null);
110
		unmodifiableDeferredKnownElements = Observables
111
				.unmodifiableObservableSet(deferredKnownElements);
103
112
104
		Assert
113
		Assert
105
				.isNotNull(collectionFactory,
114
				.isNotNull(collectionFactory,
Lines 158-165 Link Here
158
		return getChildren(input);
167
		return getChildren(input);
159
	}
168
	}
160
169
170
	private boolean deferringKnownElements;
171
	
161
	public Object[] getChildren(Object element) {
172
	public Object[] getChildren(Object element) {
162
		Object[] children = getOrCreateNode(element).getChildren();
173
		if (!deferringKnownElements) {
174
			deferringKnownElements = true;
175
			deferredKnownElements.deferChanges(true);
176
			realm.asyncExec(new Runnable(){
177
				public void run() {
178
					deferringKnownElements = false;
179
					deferredKnownElements.deferChanges(false);
180
				}
181
			});
182
		}
183
		TreeNode node = getOrCreateNode(element);
184
		Object[] children = node.getChildren();
163
		for (int i = 0; i < children.length; i++)
185
		for (int i = 0; i < children.length; i++)
164
			getOrCreateNode(children[i]).addParent(element);
186
			getOrCreateNode(children[i]).addParent(element);
165
		return children;
187
		return children;
Lines 172-180 Link Here
172
				return hasChildren.booleanValue();
194
				return hasChildren.booleanValue();
173
			}
195
			}
174
		}
196
		}
197
		if (!deferringKnownElements) {
198
			deferringKnownElements = true;
199
			deferredKnownElements.deferChanges(true);
200
			realm.asyncExec(new Runnable(){
201
				public void run() {
202
					deferringKnownElements = false;
203
					deferredKnownElements.deferChanges(false);
204
				}
205
			});
206
		}
175
		return getOrCreateNode(element).hasChildren();
207
		return getOrCreateNode(element).hasChildren();
176
	}
208
	}
177
209
	
178
	protected TreeNode getOrCreateNode(Object element) {
210
	protected TreeNode getOrCreateNode(Object element) {
179
		TreeNode node = getExistingNode(element);
211
		TreeNode node = getExistingNode(element);
180
		if (node == null) {
212
		if (node == null) {
Lines 214-219 Link Here
214
		viewerUpdater = null;
246
		viewerUpdater = null;
215
		comparer = null;
247
		comparer = null;
216
		knownElements = null;
248
		knownElements = null;
249
		deferredKnownElements = null;
217
		unmodifiableKnownElements = null;
250
		unmodifiableKnownElements = null;
218
		collectionFactory = null;
251
		collectionFactory = null;
219
	}
252
	}
Lines 232-237 Link Here
232
	}
265
	}
233
266
234
	/**
267
	/**
268
	 * @return bla
269
	 */
270
	public IObservableSet getDeferredKnownElements() {
271
		return unmodifiableDeferredKnownElements;
272
	}
273
	
274
	/**
235
	 * Returns a listener which, when a collection change event is received,
275
	 * Returns a listener which, when a collection change event is received,
236
	 * updates the tree viewer through the {@link #viewerUpdater} field, and
276
	 * updates the tree viewer through the {@link #viewerUpdater} field, and
237
	 * maintains the adds and removes parents from the appropriate tree nodes.
277
	 * maintains the adds and removes parents from the appropriate tree nodes.
(-)src/org/eclipse/jface/examples/databinding/snippets/Snippet024AsyncAndCheckAndFilter.java (+342 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2008 IBM Corporation 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
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
12
package org.eclipse.jface.examples.databinding.snippets;
13
14
import java.util.ArrayList;
15
import java.util.Collections;
16
import java.util.Iterator;
17
import java.util.List;
18
19
import org.eclipse.core.databinding.observable.IObservable;
20
import org.eclipse.core.databinding.observable.Observables;
21
import org.eclipse.core.databinding.observable.Realm;
22
import org.eclipse.core.databinding.observable.list.ComputedList;
23
import org.eclipse.core.databinding.observable.list.IObservableList;
24
import org.eclipse.core.databinding.observable.list.WritableList;
25
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
26
import org.eclipse.core.databinding.observable.set.IObservableSet;
27
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
28
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
29
import org.eclipse.core.databinding.observable.set.WritableSet;
30
import org.eclipse.jface.databinding.swt.SWTObservables;
31
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
32
import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor;
33
import org.eclipse.jface.layout.GridDataFactory;
34
import org.eclipse.jface.viewers.CheckStateChangedEvent;
35
import org.eclipse.jface.viewers.CheckboxTreeViewer;
36
import org.eclipse.jface.viewers.ICheckStateListener;
37
import org.eclipse.swt.layout.GridLayout;
38
import org.eclipse.swt.widgets.Display;
39
import org.eclipse.swt.widgets.Shell;
40
41
public class Snippet024AsyncAndCheckAndFilter {
42
43
	public static void main(String[] args) {
44
		final Display display = new Display();
45
		final Shell shell = new Shell(display);
46
		shell.setLayout(new GridLayout(1, false));
47
48
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
49
			public void run() {
50
				new Snippet024AsyncAndCheckAndFilter().createControls(shell);
51
52
				shell.pack();
53
				shell.open();
54
				while (!shell.isDisposed()) {
55
					if (!display.readAndDispatch())
56
						display.sleep();
57
				}
58
			}
59
		});
60
		display.dispose();
61
	}
62
63
	static abstract class Node {
64
		private final String name;
65
		private final Node parent;
66
67
		public Node getParent() {
68
			return parent;
69
		}
70
71
		Node(Node parent, String name) {
72
			this.parent = parent;
73
			this.name = name;
74
		}
75
76
		public String toString() {
77
			return name;
78
		}
79
80
		public abstract IObservableList createChildren();
81
	}
82
83
	static class Pending extends Node {
84
		Pending(Node parent) {
85
			super(parent, "Pending...");
86
		}
87
88
		public IObservableList createChildren() {
89
			return null;
90
		}
91
	}
92
93
	static class Root extends Node {
94
		private final IObservableSet checked;
95
96
		Root(IObservableSet checkedElements) {
97
			super(null, "root");
98
			checked = checkedElements;
99
		}
100
101
		public IObservableList createChildren() {
102
			List repositories = new ArrayList();
103
			repositories.add(new Repository(this, "Eclipse SDK Update Site",
104
					checked));
105
			repositories.add(new Repository(this, "EMF Update Site", checked));
106
			repositories.add(new Repository(this, "Ganymede Update Site",
107
					checked));
108
			repositories
109
					.add(new Repository(this, "Mylyn Update Site", checked));
110
			return Observables.staticObservableList(repositories);
111
		}
112
	}
113
114
	static class Repository extends Node {
115
		WritableList iuList;
116
		protected Pending pendingNode;
117
		private final IObservableSet checked;
118
119
		Repository(Root root, String name, IObservableSet checkedElements) {
120
			super(root, name);
121
			checked = checkedElements;
122
		}
123
124
		public IObservableList getIUList() {
125
			if (iuList == null) {
126
				iuList = new WritableList();
127
				iuList.setStale(true);
128
				Display.getDefault().timerExec(3000, new Runnable() {
129
					public void run() {
130
						List bulkAdd = new ArrayList();
131
						Category c1 = new Category(Repository.this,
132
								"Alpha Category");
133
						bulkAdd.add(new IU(c1, "Bar"));
134
						bulkAdd.add(new IU(c1, "Bas"));
135
						bulkAdd.add(new IU(c1, "Foo"));
136
						bulkAdd.add(new IU(c1, "Meh"));
137
						bulkAdd.add(new IU(c1, "Moo"));
138
						Category c2 = new Category(Repository.this,
139
								"Beta Category");
140
						bulkAdd.add(new IU(c2, "Bar"));
141
						bulkAdd.add(new IU(c2, "Bas"));
142
						bulkAdd.add(new IU(c2, "Foo"));
143
						bulkAdd.add(new IU(c2, "Meh"));
144
						bulkAdd.add(new IU(c2, "Moo"));
145
						Category c3 = new Category(Repository.this,
146
								"Gamma Category");
147
						bulkAdd.add(new IU(c3, "Bar"));
148
						bulkAdd.add(new IU(c3, "Bas"));
149
						bulkAdd.add(new IU(c3, "Foo"));
150
						bulkAdd.add(new IU(c3, "Meh"));
151
						bulkAdd.add(new IU(c3, "Moo"));
152
						iuList.setStale(false);
153
						System.out.println("finished retrieval, pendingNode="
154
								+ pendingNode);
155
						if (pendingNode != null) {
156
							if (checked.contains(pendingNode)) {
157
								System.out
158
										.println("pending node was checked, adding nodes to checked set:"
159
												+ bulkAdd.size());
160
								checked.remove(pendingNode);
161
								checked.add(c1);
162
								checked.add(c2);
163
								checked.add(c3);
164
								checked.addAll(bulkAdd);
165
							}
166
							pendingNode = null;
167
						}
168
						iuList.addAll(bulkAdd);
169
					}
170
				});
171
			}
172
			return iuList;
173
		}
174
175
		public IObservableList createChildren() {
176
			return new ComputedList() {
177
				protected List calculate() {
178
					if (getIUList().isStale()) {
179
						if (pendingNode == null) {
180
							pendingNode = new Pending(Repository.this);
181
						}
182
						return Collections.singletonList(pendingNode);
183
					}
184
					List result = new ArrayList();
185
					for (Iterator it = getIUList().iterator(); it.hasNext();) {
186
						IU iu = (IU) it.next();
187
						if (!result.contains(iu.getParent())) {
188
							result.add(iu.getParent());
189
						}
190
					}
191
					return result;
192
				}
193
			};
194
		}
195
	}
196
197
	static class Category extends Node {
198
		Category(Repository repo, String name) {
199
			super(repo, name);
200
		}
201
202
		public IObservableList createChildren() {
203
			return new ComputedList() {
204
				protected List calculate() {
205
					List result = new ArrayList();
206
					for (Iterator it = ((Repository) getParent()).getIUList()
207
							.iterator(); it.hasNext();) {
208
						IU iu = (IU) it.next();
209
						if (iu.getParent() == Category.this) {
210
							result.add(iu);
211
						}
212
					}
213
					System.out.println("returning children, " + result.size());
214
					return result;
215
				}
216
			};
217
		}
218
	}
219
220
	static class IU extends Node {
221
		IU(Category category, String name) {
222
			super(category, name);
223
		}
224
225
		public IObservableList createChildren() {
226
			return null;
227
		}
228
	}
229
230
	private CheckboxTreeViewer viewer;
231
	private IObservableSet knownElements;
232
	private IObservableSet checkedElements;
233
	private IObservableSet grayedElements;
234
	private ObservableListTreeContentProvider contentProvider;
235
	private TreeStructureAdvisor treeStructureAdvisor;
236
237
	protected void createControls(Shell shell) {
238
		viewer = new CheckboxTreeViewer(shell);
239
		GridDataFactory.defaultsFor(viewer.getControl()).hint(400, 600)
240
				.applyTo(viewer.getControl());
241
		treeStructureAdvisor = new TreeStructureAdvisor() {
242
			public Object getParent(Object element) {
243
				if (element instanceof Node) {
244
					return ((Node) element).getParent();
245
				}
246
				return null;
247
			}
248
249
			public Boolean hasChildren(Object element) {
250
				return (element instanceof IU || element instanceof Pending) ? Boolean.FALSE
251
						: Boolean.TRUE;
252
			}
253
		};
254
		IObservableFactory listFactory = new IObservableFactory() {
255
			public IObservable createObservable(Object target) {
256
				return ((Node) target).createChildren();
257
			}
258
		};
259
		contentProvider = new ObservableListTreeContentProvider(listFactory,
260
				treeStructureAdvisor);
261
		viewer.setContentProvider(contentProvider);
262
		knownElements = contentProvider.getDeferredKnownElements();
263
		checkedElements = new WritableSet();
264
		grayedElements = new WritableSet();
265
		viewer.addCheckStateListener(new ICheckStateListener() {
266
			public void checkStateChanged(CheckStateChangedEvent event) {
267
				handleChecked(event.getElement(), event.getChecked());
268
			}
269
		});
270
		knownElements.addSetChangeListener(new ISetChangeListener() {
271
			public void handleSetChange(SetChangeEvent event) {
272
				for (Iterator it = event.diff.getAdditions().iterator(); it
273
						.hasNext();) {
274
					Object added = it.next();
275
					System.out.println("added element: " + added + ", checked="
276
							+ checkedElements.contains(added));
277
					if (checkedElements.contains(added)) {
278
						viewer.setChecked(added, true);
279
					}
280
				}
281
			}
282
		});
283
		viewer.setInput(new Root(checkedElements));
284
	}
285
286
	protected void handleChecked(Object element, boolean checked) {
287
		checkSubtree(element, checked);
288
		checkParentPath(element, checked, false);
289
	}
290
291
	private void checkParentPath(Object element, boolean checked, boolean grayed) {
292
		if (element == null) {
293
			return;
294
		}
295
		if (grayed) {
296
			checked = true;
297
		} else {
298
			Object[] children = contentProvider.getChildren(element);
299
			for (int i = 0; i < children.length; i++) {
300
				Object child = children[i];
301
				if (grayedElements.contains(child)
302
						|| checked != checkedElements.contains(child)) {
303
					checked = grayed = true;
304
					break;
305
				}
306
			}
307
		}
308
		updateCheckedGrayed(element, checked, grayed);
309
		checkParentPath(treeStructureAdvisor.getParent(element), checked,
310
				grayed);
311
	}
312
313
	private void checkSubtree(Object element, boolean checked) {
314
		updateCheckedGrayed(element, checked, false);
315
		if (!(element instanceof Pending)) {
316
			Object[] children = contentProvider.getChildren(element);
317
			for (int i = 0; i < children.length; i++) {
318
				Object child = children[i];
319
				checkSubtree(child, checked);
320
			}
321
		}
322
	}
323
324
	private void updateCheckedGrayed(Object element, boolean checked,
325
			boolean grayed) {
326
		if (checked) {
327
			checkedElements.add(element);
328
		} else {
329
			checkedElements.remove(element);
330
		}
331
		if (grayed) {
332
			grayedElements.add(element);
333
		} else {
334
			grayedElements.remove(element);
335
		}
336
		if (knownElements.contains(element)) {
337
			viewer.setChecked(element, checked);
338
			viewer.setGrayed(element, grayed);
339
		}
340
	}
341
342
}

Return to bug 237359