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 }