Mercurial > projects > dwt2
comparison org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.d @ 78:0a55d2d5a946
Added file for databinding
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 14 Apr 2009 11:35:29 +0200 |
parents | |
children | 6be48cf9f95c |
comparison
equal
deleted
inserted
replaced
76:f05e6e8b2f2d | 78:0a55d2d5a946 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2008 Matthew Hall and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * Matthew Hall - initial API and implementation (bug 207858) | |
10 * Matthew Hall - bug 226765 | |
11 ******************************************************************************/ | |
12 | |
13 module org.eclipse.jface.internal.databinding.viewers.ObservableCollectionTreeContentProvider; | |
14 | |
15 import java.lang.all; | |
16 | |
17 import java.util.Iterator; | |
18 import java.util.Map; | |
19 import java.util.Set; | |
20 | |
21 import org.eclipse.core.databinding.observable.IObservable; | |
22 import org.eclipse.core.databinding.observable.IObservableCollection; | |
23 import org.eclipse.core.databinding.observable.IObservablesListener; | |
24 import org.eclipse.core.databinding.observable.Observables; | |
25 import org.eclipse.core.databinding.observable.Realm; | |
26 import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; | |
27 import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; | |
28 import org.eclipse.core.databinding.observable.set.IObservableSet; | |
29 import org.eclipse.core.databinding.observable.value.IObservableValue; | |
30 import org.eclipse.core.databinding.observable.value.WritableValue; | |
31 import org.eclipse.core.runtime.Assert; | |
32 import org.eclipse.jface.databinding.swt.SWTObservables; | |
33 import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor; | |
34 import org.eclipse.jface.util.Util; | |
35 import org.eclipse.jface.viewers.AbstractTreeViewer; | |
36 import org.eclipse.jface.viewers.IElementComparer; | |
37 import org.eclipse.jface.viewers.ITreeContentProvider; | |
38 import org.eclipse.jface.viewers.StructuredViewer; | |
39 import org.eclipse.jface.viewers.Viewer; | |
40 import org.eclipse.swt.widgets.Display; | |
41 | |
42 /** | |
43 * NON-API - Abstract base class for {@link ITreeContentProvider}s which use an | |
44 * {@link IObservableFactory observable collection factory} to provide the | |
45 * elements of a tree. Each observable collection obtained from the factory is | |
46 * observed such that changes in the collection are reflected in the viewer. | |
47 * | |
48 * @since 1.2 | |
49 */ | |
50 public abstract class ObservableCollectionTreeContentProvider : | |
51 ITreeContentProvider { | |
52 private Realm realm; | |
53 | |
54 private IObservableValue viewerObservable; | |
55 | |
56 /** | |
57 * Interfaces for sending updates to the viewer. | |
58 */ | |
59 protected TreeViewerUpdater viewerUpdater; | |
60 | |
61 /** | |
62 * Element comparer used by the viewer (may be null). | |
63 */ | |
64 protected IElementComparer comparer; | |
65 | |
66 private IObservableSet knownElements; | |
67 private IObservableSet unmodifiableKnownElements; | |
68 | |
69 private IObservableFactory /* <IObservableCollection> */collectionFactory; | |
70 | |
71 private Map /* <Object element, TreeNode node> */elementNodes; | |
72 | |
73 private TreeStructureAdvisor structureAdvisor; | |
74 | |
75 /** | |
76 * Constructs an ObservableCollectionTreeContentProvider using the given | |
77 * parent provider and collection factory. | |
78 * | |
79 * @param collectionFactory | |
80 * observable factory that produces an IObservableList of | |
81 * children for a given parent element. | |
82 * @param structureAdvisor | |
83 */ | |
84 protected this( | |
85 IObservableFactory collectionFactory, | |
86 TreeStructureAdvisor structureAdvisor) { | |
87 this.structureAdvisor = structureAdvisor; | |
88 realm = SWTObservables.getRealm(Display.getDefault()); | |
89 viewerObservable = new WritableValue(realm); | |
90 viewerUpdater = null; | |
91 | |
92 // Known elements is a detail set of viewerObservable, so that when we | |
93 // get the viewer instance we can swap in a set that uses its | |
94 // IElementComparer, if any. | |
95 IObservableFactory knownElementsFactory = new class() IObservableFactory { | |
96 public IObservable createObservable(Object target) { | |
97 return ObservableViewerElementSet.withComparer(realm, null, | |
98 getElementComparer(cast(Viewer) target)); | |
99 } | |
100 }; | |
101 knownElements = MasterDetailObservables.detailSet(viewerObservable, | |
102 knownElementsFactory, null); | |
103 unmodifiableKnownElements = Observables | |
104 .unmodifiableObservableSet(knownElements); | |
105 | |
106 Assert | |
107 .isNotNull(collectionFactory, | |
108 "Collection factory cannot be null"); //$NON-NLS-1$ | |
109 this.collectionFactory = collectionFactory; | |
110 } | |
111 | |
112 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { | |
113 if (elementNodes !is null && !elementNodes.isEmpty()) { | |
114 // Ensure we flush any observable collection listeners | |
115 TreeNode[] oldNodes = new TreeNode[elementNodes.size()]; | |
116 elementNodes.values().toArray(oldNodes); | |
117 for (int i = 0; i < oldNodes.length; i++) | |
118 oldNodes[i].dispose(); | |
119 elementNodes.clear(); | |
120 elementNodes = null; | |
121 } | |
122 | |
123 setViewer(viewer); | |
124 } | |
125 | |
126 private void setViewer(Viewer viewer) { | |
127 viewerUpdater = createViewerUpdater(viewer); | |
128 comparer = getElementComparer(viewer); | |
129 elementNodes = ViewerElementMap.withComparer(comparer); | |
130 viewerObservable.setValue(viewer); // (clears knownElements) | |
131 } | |
132 | |
133 private static IElementComparer getElementComparer(Viewer viewer) { | |
134 if (null !is cast(StructuredViewer)viewer) | |
135 return (cast(StructuredViewer) viewer).getComparer(); | |
136 return null; | |
137 } | |
138 | |
139 private static TreeViewerUpdater createViewerUpdater(Viewer viewer) { | |
140 if (null !is cast(AbstractTreeViewer)viewer) | |
141 return new TreeViewerUpdater(cast(AbstractTreeViewer) viewer); | |
142 throw new IllegalArgumentException( | |
143 "This content provider only works with AbstractTreeViewer"); //$NON-NLS-1$ | |
144 } | |
145 | |
146 public Object getParent(Object element) { | |
147 if (structureAdvisor !is null) { | |
148 Object parentFromAdvisor = structureAdvisor.getParent(element); | |
149 if (parentFromAdvisor !is null) { | |
150 return parentFromAdvisor; | |
151 } | |
152 } | |
153 TreeNode node = getExistingNode(element); | |
154 if (node !is null) | |
155 return node.getParent(); | |
156 return null; | |
157 } | |
158 | |
159 public Object[] getElements(Object input) { | |
160 return getChildren(input); | |
161 } | |
162 | |
163 public Object[] getChildren(Object element) { | |
164 Object[] children = getOrCreateNode(element).getChildren(); | |
165 for (int i = 0; i < children.length; i++) | |
166 getOrCreateNode(children[i]).addParent(element); | |
167 return children; | |
168 } | |
169 | |
170 public bool hasChildren(Object element) { | |
171 if (structureAdvisor !is null) { | |
172 Boolean hasChildren = structureAdvisor.hasChildren(element); | |
173 if (hasChildren !is null) { | |
174 return hasChildren.booleanValue(); | |
175 } | |
176 } | |
177 return getOrCreateNode(element).hasChildren(); | |
178 } | |
179 | |
180 protected TreeNode getOrCreateNode(Object element) { | |
181 TreeNode node = getExistingNode(element); | |
182 if (node is null) { | |
183 node = new TreeNode(element); | |
184 } | |
185 return node; | |
186 } | |
187 | |
188 protected TreeNode getExistingNode(Object element) { | |
189 TreeNode node = cast(TreeNode) elementNodes.get(element); | |
190 return node; | |
191 } | |
192 | |
193 protected bool isViewerDisposed() { | |
194 Viewer viewer = cast(Viewer) viewerObservable.getValue(); | |
195 return viewer is null || viewer.getControl() is null | |
196 || viewer.getControl().isDisposed(); | |
197 } | |
198 | |
199 public void dispose() { | |
200 if (elementNodes !is null) { | |
201 if (!elementNodes.isEmpty()) { | |
202 TreeNode[] nodes = new TreeNode[elementNodes.size()]; | |
203 elementNodes.values().toArray(nodes); | |
204 for (int i = 0; i < nodes.length; i++) { | |
205 nodes[i].dispose(); | |
206 } | |
207 elementNodes.clear(); | |
208 } | |
209 elementNodes = null; | |
210 } | |
211 if (viewerObservable !is null) { | |
212 viewerObservable.setValue(null); | |
213 viewerObservable.dispose(); | |
214 viewerObservable = null; | |
215 } | |
216 viewerUpdater = null; | |
217 comparer = null; | |
218 knownElements = null; | |
219 unmodifiableKnownElements = null; | |
220 collectionFactory = null; | |
221 } | |
222 | |
223 /** | |
224 * Returns the set of elements known to this content provider. Label | |
225 * providers may track this set if they need to be notified about additions | |
226 * before the viewer sees the added element, and notified about removals | |
227 * after the element was removed from the viewer. This is intended for use | |
228 * by label providers, as it will always return the items that need labels. | |
229 * | |
230 * @return unmodifiable observable set of items that will need labels | |
231 */ | |
232 public IObservableSet getKnownElements() { | |
233 return unmodifiableKnownElements; | |
234 } | |
235 | |
236 /** | |
237 * Returns a listener which, when a collection change event is received, | |
238 * updates the tree viewer through the {@link #viewerUpdater} field, and | |
239 * maintains the adds and removes parents from the appropriate tree nodes. | |
240 * | |
241 * @param parentElement | |
242 * the element that is the parent element of all elements in the | |
243 * observable collection. | |
244 * @return a listener which updates the viewer when change events occur. | |
245 */ | |
246 protected abstract IObservablesListener createCollectionChangeListener( | |
247 Object parentElement); | |
248 | |
249 /** | |
250 * Registers the change listener to receive change events for the specified | |
251 * observable collection. | |
252 * | |
253 * @param collection | |
254 * the collection to observe for changes | |
255 * @param listener | |
256 * the listener that will receive collection change events. | |
257 */ | |
258 protected abstract void addCollectionChangeListener( | |
259 IObservableCollection collection, IObservablesListener listener); | |
260 | |
261 /** | |
262 * Unregisters the change listener from receving change events for the | |
263 * specified observable collection. | |
264 * | |
265 * @param collection | |
266 * the collection to stop observing. | |
267 * @param listener | |
268 * the listener to remove | |
269 */ | |
270 protected abstract void removeCollectionChangeListener( | |
271 IObservableCollection collection, IObservablesListener listener); | |
272 | |
273 protected final class TreeNode { | |
274 private Object element; | |
275 | |
276 private Object parent; | |
277 private Set parentSet; | |
278 | |
279 private IObservableCollection children; | |
280 | |
281 private IObservablesListener listener; | |
282 | |
283 this(Object element) { | |
284 Assert.isNotNull(element, "element cannot be null"); //$NON-NLS-1$ | |
285 this.element = element; | |
286 knownElements.add(element); | |
287 elementNodes.put(element, this); | |
288 } | |
289 | |
290 Object getElement() { | |
291 return element; | |
292 } | |
293 | |
294 private bool equal(Object left, Object right) { | |
295 if (comparer is null) | |
296 return Util.equals(left, right); | |
297 return comparer.equals(left, right); | |
298 } | |
299 | |
300 public void addParent(Object newParent) { | |
301 if (parent is null) { | |
302 parent = newParent; | |
303 } else if (!equal(parent, newParent)) { | |
304 if (parentSet is null) { | |
305 parentSet = ViewerElementSet.withComparer(comparer); | |
306 parentSet.add(parent); | |
307 } | |
308 parentSet.add(newParent); | |
309 } | |
310 } | |
311 | |
312 public void removeParent(Object oldParent) { | |
313 if (parentSet !is null) | |
314 parentSet.remove(oldParent); | |
315 | |
316 if (equal(parent, oldParent)) { | |
317 if (parentSet is null || parentSet.isEmpty()) { | |
318 parent = null; | |
319 } else { | |
320 Iterator iterator = parentSet.iterator(); | |
321 parent = iterator.next(); | |
322 iterator.remove(); | |
323 } | |
324 } | |
325 | |
326 if (parentSet !is null && parentSet.isEmpty()) | |
327 parentSet = null; | |
328 | |
329 if (parent is null) { | |
330 dispose(); | |
331 } | |
332 } | |
333 | |
334 private Object getParent() { | |
335 return parent; | |
336 } | |
337 | |
338 private void initChildren() { | |
339 if (children is null) { | |
340 children = cast(IObservableCollection) collectionFactory | |
341 .createObservable(element); | |
342 if (children is null) { | |
343 listener = null; | |
344 children = Observables.emptyObservableSet(realm); | |
345 } else { | |
346 Assert | |
347 .isTrue(Util.equals(realm, children.getRealm()), | |
348 "Children observable collection must be on the Display realm"); //$NON-NLS-1$ | |
349 listener = createCollectionChangeListener(element); | |
350 addCollectionChangeListener(children, listener); | |
351 } | |
352 } | |
353 } | |
354 | |
355 bool hasChildren() { | |
356 initChildren(); | |
357 return !children.isEmpty(); | |
358 } | |
359 | |
360 Object[] getChildren() { | |
361 initChildren(); | |
362 return children.toArray(); | |
363 } | |
364 | |
365 private void dispose() { | |
366 if (element !is null) { | |
367 elementNodes.remove(element); | |
368 knownElements.remove(element); | |
369 } | |
370 if (children !is null) { | |
371 for (Iterator iterator = children.iterator(); iterator | |
372 .hasNext();) { | |
373 TreeNode child = getExistingNode(iterator.next()); | |
374 if (child !is null) | |
375 child.removeParent(element); | |
376 } | |
377 if (listener !is null) | |
378 removeCollectionChangeListener(children, listener); | |
379 children.dispose(); | |
380 children = null; | |
381 } | |
382 element = null; | |
383 parent = null; | |
384 if (parentSet !is null) { | |
385 parentSet.clear(); | |
386 parentSet = null; | |
387 } | |
388 } | |
389 } | |
390 } |