Mercurial > projects > dwt2
comparison org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/UnorderedTreeContentProvider.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) 2006, 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 * Stefan Xenos, IBM - initial API and implementation | |
11 *******************************************************************************/ | |
12 module org.eclipse.jface.internal.databinding.provisional.viewers.UnorderedTreeContentProvider; | |
13 | |
14 import java.lang.all; | |
15 | |
16 import java.util.ArrayList; | |
17 import java.util.Collections; | |
18 import java.util.HashMap; | |
19 import java.util.HashSet; | |
20 import java.util.Iterator; | |
21 import java.util.LinkedList; | |
22 import java.util.List; | |
23 import java.util.Set; | |
24 | |
25 import org.eclipse.core.databinding.observable.Diffs; | |
26 import org.eclipse.core.databinding.observable.set.AbstractObservableSet; | |
27 import org.eclipse.core.databinding.observable.set.IObservableSet; | |
28 import org.eclipse.core.databinding.observable.set.SetDiff; | |
29 import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider; | |
30 import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; | |
31 import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider; | |
32 import org.eclipse.jface.viewers.ITreeContentProvider; | |
33 import org.eclipse.jface.viewers.ITreePathContentProvider; | |
34 import org.eclipse.jface.viewers.ITreeViewerListener; | |
35 import org.eclipse.jface.viewers.TreeExpansionEvent; | |
36 import org.eclipse.jface.viewers.TreePath; | |
37 import org.eclipse.jface.viewers.TreeViewer; | |
38 import org.eclipse.jface.viewers.Viewer; | |
39 | |
40 /** | |
41 * NON-API - Generic tree content provider to be used with an AbstractTreeViewer based on a IUnorderedTreeProvider. | |
42 * @since 1.1 | |
43 * @deprecated Use {@link ObservableSetTreeContentProvider} or | |
44 * {@link ObservableListTreeContentProvider} instead. | |
45 */ | |
46 public class UnorderedTreeContentProvider : ITreeContentProvider, ITreePathContentProvider { | |
47 | |
48 private HashMap mapElementToTreeNode = new HashMap(); | |
49 private LinkedList enqueuedPrefetches = new LinkedList(); | |
50 private IParentProvider rootParentProvider = null; | |
51 private bool useTreePaths = false; | |
52 | |
53 class KnownElementsSet : AbstractObservableSet { | |
54 | |
55 protected this() { | |
56 super(); | |
57 } | |
58 | |
59 /* (non-Javadoc) | |
60 * @see org.eclipse.jface.internal.databinding.provisional.observable.set.AbstractObservableSet#getWrappedSet() | |
61 */ | |
62 protected Set getWrappedSet() { | |
63 return mapElementToTreeNode.keySet(); | |
64 } | |
65 | |
66 void doFireDiff(Set added, Set removed) { | |
67 fireSetChange(Diffs.createSetDiff(added, removed)); | |
68 } | |
69 | |
70 public void fireSetChange(SetDiff diff) { | |
71 super.fireSetChange(diff); | |
72 } | |
73 | |
74 void doFireStale(bool isStale) { | |
75 if (isStale) { | |
76 fireStale(); | |
77 } else { | |
78 fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, Collections.EMPTY_SET)); | |
79 } | |
80 } | |
81 | |
82 /* (non-Javadoc) | |
83 * @see org.eclipse.jface.internal.databinding.provisional.observable.set.IObservableSet#getElementType() | |
84 */ | |
85 public Object getElementType() { | |
86 return new Object(); | |
87 } | |
88 } | |
89 | |
90 KnownElementsSet elements = new KnownElementsSet(); | |
91 | |
92 private ITreeViewerListener expandListener = new class() ITreeViewerListener { | |
93 public void treeCollapsed(TreeExpansionEvent event) { | |
94 } | |
95 | |
96 public void treeExpanded(TreeExpansionEvent event) { | |
97 } | |
98 }; | |
99 | |
100 private IUnorderedTreeProvider provider; | |
101 private Object pendingNode; | |
102 | |
103 private int avoidViewerUpdates; | |
104 | |
105 private TreeViewer treeViewer; | |
106 | |
107 private int staleCount = 0; | |
108 private bool useRefresh; | |
109 private int maxPrefetches = 0; | |
110 | |
111 /** | |
112 * Constructs a content provider that will render the given tree in a TreeViewer. | |
113 * | |
114 * @param provider IObservableTree that provides the contents of the tree | |
115 * @param pendingNode element to insert whenever a node is being fetched in the background | |
116 * @param useRefresh true = notify the viewer of changes by calling refresh(...), false = | |
117 * notify the viewer of changes by calling add(...) and remove(...). Using false | |
118 * is more efficient, but may not work with TreeViewer subclasses. | |
119 */ | |
120 public this(IUnorderedTreeProvider provider, | |
121 Object pendingNode, bool useRefresh) { | |
122 this.provider = provider; | |
123 this.pendingNode = pendingNode; | |
124 this.useRefresh = useRefresh; | |
125 } | |
126 | |
127 /** | |
128 * Sets whether this content provider should add/remove elements using | |
129 * TreePaths (true) or elements (false). | |
130 * | |
131 * <p></p> | |
132 * <p>When using elements:</p> | |
133 * | |
134 * <ul> | |
135 * <li>Cycles are permitted (elements can be their own ancestor)</li> | |
136 * <li>Addition, removal, and refresh are slightly faster</li> | |
137 * <li>It is not possible to have more than one content provider per tree</li> | |
138 * <li>The setRootPath(...) method is ignored</li> | |
139 * </ul> | |
140 * | |
141 * <p></p> | |
142 * <p>When using TreePaths:</p> | |
143 * | |
144 * <ul> | |
145 * <li>Cycles are not permitted (elements cannot be their own parent)</li> | |
146 * <li>Addition, removal, and refresh are slightly slower</li> | |
147 * <li>It is possible to use more than one content provider in the same tree</li> | |
148 * <li>The setRootPath(...) method can be used to direct the output to a particular | |
149 * subtree</li> | |
150 * </ul> | |
151 * | |
152 * @param usePaths | |
153 */ | |
154 public void useTreePaths(bool usePaths) { | |
155 this.useTreePaths = usePaths; | |
156 } | |
157 | |
158 /** | |
159 * @param rootParentProvider | |
160 */ | |
161 public void setRootPath(IParentProvider rootParentProvider) { | |
162 this.rootParentProvider = rootParentProvider; | |
163 } | |
164 | |
165 /** | |
166 * @param maxPrefetches | |
167 */ | |
168 public void setMaxPrefetches(int maxPrefetches) { | |
169 this.maxPrefetches = maxPrefetches; | |
170 } | |
171 | |
172 /* package */ IObservableSet createChildSet(Object element) { | |
173 return provider.createChildSet(element); | |
174 } | |
175 | |
176 /* package */ void remove(Object element, Set removals, bool lastElement) { | |
177 if (removals.isEmpty()) { | |
178 return; | |
179 } | |
180 if (avoidViewerUpdates is 0) { | |
181 if (lastElement || useRefresh) { | |
182 doRefresh(element); | |
183 } else { | |
184 if (useTreePaths) { | |
185 List toRemove = new ArrayList(); | |
186 TreePath[] parents = getParents(element); | |
187 for (int i = 0; i < parents.length; i++) { | |
188 TreePath parent = parents[i]; | |
189 | |
190 for (Iterator iter = removals.iterator(); iter.hasNext();) { | |
191 Object elementToRemove = iter.next(); | |
192 | |
193 toRemove.add(parent.createChildPath(element).createChildPath(elementToRemove)); | |
194 } | |
195 } | |
196 | |
197 treeViewer.remove(toRemove.toArray(new TreePath[toRemove.size()])); | |
198 } else { | |
199 treeViewer.remove(element, removals.toArray()); | |
200 } | |
201 } | |
202 for (Iterator iter = removals.iterator(); iter.hasNext();) { | |
203 Object next = iter.next(); | |
204 | |
205 TreeNode nextNode = cast(TreeNode)mapElementToTreeNode.get(next); | |
206 if (nextNode !is null) { | |
207 nextNode.removeParent(element); | |
208 removeIfUnused(nextNode); | |
209 } | |
210 } | |
211 } | |
212 } | |
213 | |
214 /* package */ void add(Object element, Set additions) { | |
215 if (additions.isEmpty()) { | |
216 return; | |
217 } | |
218 if (avoidViewerUpdates is 0) { | |
219 // Handle new parents | |
220 addParent(element, additions); | |
221 if (useRefresh) { | |
222 doRefresh(element); | |
223 } else { | |
224 if (useTreePaths) { | |
225 TreePath[] parents = getParents(element); | |
226 for (int i = 0; i < parents.length; i++) { | |
227 TreePath parent = parents[i]; | |
228 | |
229 treeViewer.add(parent.createChildPath(element), additions.toArray()); | |
230 } | |
231 } else { | |
232 treeViewer.add(element, additions.toArray()); | |
233 } | |
234 } | |
235 } | |
236 } | |
237 | |
238 private void doRefresh(Object element) { | |
239 treeViewer.refresh(element); | |
240 } | |
241 | |
242 /** | |
243 * Ensures that the given set of children have the given parent as | |
244 * one of their parents. | |
245 * | |
246 * @param parent | |
247 * @param children | |
248 */ | |
249 private void addParent(Object parent, Set children) { | |
250 for (Iterator iter = children.iterator(); iter.hasNext();) { | |
251 Object next = iter.next(); | |
252 | |
253 TreeNode nextNode = getNode(next); | |
254 nextNode.addParent(parent); | |
255 } | |
256 } | |
257 | |
258 /** | |
259 * @return saouesnth | |
260 */ | |
261 public final Object getPendingNode() { | |
262 return pendingNode; | |
263 } | |
264 | |
265 /** | |
266 * @param parent | |
267 * @return aueosnht | |
268 */ | |
269 public IObservableSet getChildrenSet(Object parent) { | |
270 IObservableSet result = getNode(parent).getChildrenSet(); | |
271 | |
272 return result; | |
273 } | |
274 | |
275 public void dispose() { | |
276 if (treeViewer !is null) { | |
277 try { | |
278 avoidViewerUpdates++; | |
279 enqueuedPrefetches.clear(); | |
280 Object[] keys = mapElementToTreeNode.keySet().toArray(); | |
281 | |
282 for (int i = 0; i < keys.length; i++) { | |
283 Object key = keys[i]; | |
284 | |
285 TreeNode result = cast(TreeNode)mapElementToTreeNode.get(key); | |
286 if (result !is null) { | |
287 result.dispose(); | |
288 } | |
289 } | |
290 setViewer(null); | |
291 } finally { | |
292 avoidViewerUpdates--; | |
293 } | |
294 } | |
295 } | |
296 | |
297 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { | |
298 // This should only ever be called for a single viewer | |
299 setViewer(viewer); | |
300 | |
301 if (oldInput !is null && newInput !is null && oldInput.equals(newInput)) { | |
302 return; | |
303 } | |
304 | |
305 try { | |
306 avoidViewerUpdates++; | |
307 TreeNode oldNode = cast(TreeNode)mapElementToTreeNode.get(oldInput); | |
308 if (oldNode !is null) { | |
309 removeIfUnused(oldNode); | |
310 } | |
311 } finally { | |
312 avoidViewerUpdates--; | |
313 } | |
314 } | |
315 | |
316 private void removeIfUnused(TreeNode toRemove) { | |
317 //TreeNode result = cast(TreeNode)mapElementToTreeNode.get(element); | |
318 Object element = toRemove.getElement(); | |
319 if (toRemove.getParent() is null) { | |
320 mapElementToTreeNode.remove(element); | |
321 elements.doFireDiff(Collections.EMPTY_SET, Collections.singleton(element)); | |
322 toRemove.dispose(); | |
323 } | |
324 } | |
325 | |
326 private void setViewer(Viewer viewer) { | |
327 if (viewer !is null && !(null !is cast(TreeViewer)viewer)) { | |
328 throw new IllegalArgumentException("This content provider can only be used with TreeViewers"); //$NON-NLS-1$ | |
329 } | |
330 TreeViewer newTreeViewer = cast(TreeViewer) viewer; | |
331 | |
332 if (newTreeViewer !is treeViewer) { | |
333 if (treeViewer !is null) { | |
334 treeViewer.removeTreeListener(expandListener); | |
335 } | |
336 | |
337 this.treeViewer = newTreeViewer; | |
338 if (newTreeViewer !is null) { | |
339 newTreeViewer.addTreeListener(expandListener); | |
340 } | |
341 } | |
342 } | |
343 | |
344 public Object[] getChildren(Object parentElement) { | |
345 Set result = getNode(parentElement).getChildren(); | |
346 | |
347 addParent(parentElement, result); | |
348 | |
349 return result.toArray(); | |
350 } | |
351 | |
352 private TreeNode getNode(Object parentElement) { | |
353 TreeNode result = cast(TreeNode)mapElementToTreeNode.get(parentElement); | |
354 if (result is null) { | |
355 result = new TreeNode(parentElement, this); | |
356 mapElementToTreeNode.put(parentElement, result); | |
357 elements.fireSetChange(Diffs.createSetDiff(Collections.singleton(parentElement), | |
358 Collections.EMPTY_SET)); | |
359 } | |
360 return result; | |
361 } | |
362 | |
363 public Object getParent(Object element) { | |
364 Object result = getNode(element).getParent(); | |
365 if (result is null && rootParentProvider !is null) { | |
366 result = rootParentProvider.getParent(element); | |
367 } | |
368 return result; | |
369 } | |
370 | |
371 public bool hasChildren(Object element) { | |
372 return getNode(element).shouldShowPlus(); | |
373 } | |
374 | |
375 public Object[] getElements(Object inputElement) { | |
376 return getChildren(inputElement); | |
377 } | |
378 | |
379 /** | |
380 * @return aouesnth | |
381 */ | |
382 public IObservableSet getKnownElements() { | |
383 return elements; | |
384 } | |
385 | |
386 /* package */ void changeStale(int staleDelta) { | |
387 staleCount += staleDelta; | |
388 processPrefetches(); | |
389 elements.setStale(staleCount !is 0); | |
390 } | |
391 | |
392 /** | |
393 * @return aoueesnth | |
394 */ | |
395 public TreeViewer getViewer() { | |
396 return treeViewer; | |
397 } | |
398 | |
399 /** | |
400 * @param element | |
401 * @return aoeusnth | |
402 */ | |
403 public bool isDirty(Object element) { | |
404 return false; | |
405 } | |
406 | |
407 /* package */ void enqueuePrefetch(TreeNode node) { | |
408 if (maxPrefetches > 0 || maxPrefetches is -1) { | |
409 if (staleCount is 0) { | |
410 // Call node.getChildren()... this will cause us to start listening to the | |
411 // node and will trigger prefetching. Don't call prefetch since this method | |
412 // is intended to be called inside getters (which will simply return the | |
413 // fetched nodes) and prefetch() is intended to be called inside an asyncExec, | |
414 // which will notify the viewer directly of the newly discovered nodes. | |
415 node.getChildren(); | |
416 } else { | |
417 enqueuedPrefetches.add(node); | |
418 while (maxPrefetches >= 0 && enqueuedPrefetches.size() > maxPrefetches) { | |
419 enqueuedPrefetches.removeFirst(); | |
420 } | |
421 } | |
422 } | |
423 } | |
424 | |
425 private void processPrefetches() { | |
426 while (staleCount is 0 && !enqueuedPrefetches.isEmpty()) { | |
427 TreeNode next = cast(TreeNode)enqueuedPrefetches.removeLast(); | |
428 | |
429 // Note that we don't remove nodes from the prefetch queue when they are disposed, | |
430 // so we may encounter disposed nodes at this time. | |
431 if (!next.isDisposed()) { | |
432 next.prefetch(); | |
433 } | |
434 } | |
435 } | |
436 | |
437 public Object[] getChildren(TreePath parentPath) { | |
438 return getChildren(parentPath.getLastSegment()); | |
439 } | |
440 | |
441 public TreePath[] getParents(Object element) { | |
442 // Compute all paths that do not contain cycles | |
443 /** | |
444 * List of Lists | |
445 */ | |
446 List parentPaths = computeParents(element, new HashSet()); | |
447 | |
448 /** | |
449 * List of TreePath | |
450 */ | |
451 List result = new ArrayList(); | |
452 | |
453 for (Iterator iterator = parentPaths.iterator(); iterator.hasNext();) { | |
454 List nextPath = cast(List) iterator.next(); | |
455 | |
456 LinkedList resultPath = new LinkedList(); | |
457 resultPath.addAll(nextPath); | |
458 Object nextParent = resultPath.isEmpty() ? element : resultPath.getFirst(); | |
459 for(;nextParent !is null;) { | |
460 if (rootParentProvider !is null) { | |
461 nextParent = rootParentProvider.getParent(nextParent); | |
462 if (nextParent !is null) { | |
463 resultPath.addFirst(nextParent); | |
464 } | |
465 } else { | |
466 nextParent = null; | |
467 } | |
468 } | |
469 | |
470 result.add(new TreePath(resultPath.toArray())); | |
471 } | |
472 | |
473 if (result.isEmpty() && rootParentProvider !is null) { | |
474 Object nextParent = rootParentProvider.getParent(element); | |
475 if (nextParent !is null) { | |
476 LinkedList resultPath = new LinkedList(); | |
477 while (nextParent !is null) { | |
478 resultPath.addFirst(nextParent); | |
479 nextParent = rootParentProvider.getParent(nextParent); | |
480 } | |
481 | |
482 result.add(new TreePath(resultPath.toArray())); | |
483 } | |
484 | |
485 } | |
486 | |
487 return cast(TreePath[]) result.toArray(new TreePath[result.size()]); | |
488 } | |
489 | |
490 /** | |
491 * | |
492 * @param node | |
493 * @param toIgnore | |
494 * @return a list of Lists, indicating all known paths to the given node | |
495 */ | |
496 private List computeParents(Object node, HashSet toIgnore) { | |
497 List result = new ArrayList(); | |
498 bool containedNode = toIgnore.add(node); | |
499 | |
500 TreeNode tn = getNode(node); | |
501 | |
502 HashSet parents = new HashSet(); | |
503 parents.addAll(tn.getParents()); | |
504 parents.removeAll(toIgnore); | |
505 if (parents.isEmpty()) { | |
506 ArrayList newPath = new ArrayList(); | |
507 result.add(newPath); | |
508 } else { | |
509 for (Iterator iterator = parents.iterator(); iterator.hasNext();) { | |
510 Object parent = iterator.next(); | |
511 | |
512 List parentPaths = computeParents(parent, toIgnore); | |
513 | |
514 for (Iterator iterator2 = parentPaths.iterator(); iterator2 | |
515 .hasNext();) { | |
516 List parentPath = cast(List) iterator2.next(); | |
517 | |
518 parentPath.add(parent); | |
519 result.add(parentPath); | |
520 } | |
521 } | |
522 } | |
523 | |
524 if (containedNode) { | |
525 toIgnore.remove(node); | |
526 } | |
527 return result; | |
528 } | |
529 | |
530 public bool hasChildren(TreePath path) { | |
531 return hasChildren(path.getLastSegment()); | |
532 } | |
533 } |