KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > internal > databinding > provisional > viewers > UnorderedTreeContentProvider


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 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 package org.eclipse.jface.internal.databinding.provisional.viewers;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.LinkedList JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import org.eclipse.core.databinding.observable.Diffs;
24 import org.eclipse.core.databinding.observable.set.AbstractObservableSet;
25 import org.eclipse.core.databinding.observable.set.IObservableSet;
26 import org.eclipse.core.databinding.observable.set.SetDiff;
27 import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider;
28 import org.eclipse.jface.viewers.ITreeContentProvider;
29 import org.eclipse.jface.viewers.ITreePathContentProvider;
30 import org.eclipse.jface.viewers.ITreeViewerListener;
31 import org.eclipse.jface.viewers.TreeExpansionEvent;
32 import org.eclipse.jface.viewers.TreePath;
33 import org.eclipse.jface.viewers.TreeViewer;
34 import org.eclipse.jface.viewers.Viewer;
35
36 /**
37  * NON-API - Generic tree content provider to be used with an AbstractTreeViewer based on a IUnorderedTreeProvider.
38  * @since 1.1
39  *
40  */

41 public class UnorderedTreeContentProvider implements ITreeContentProvider, ITreePathContentProvider {
42
43     private HashMap JavaDoc mapElementToTreeNode = new HashMap JavaDoc();
44     private LinkedList JavaDoc enqueuedPrefetches = new LinkedList JavaDoc();
45     private IParentProvider rootParentProvider = null;
46     private boolean useTreePaths = false;
47     
48     class KnownElementsSet extends AbstractObservableSet {
49         
50         protected KnownElementsSet() {
51             super();
52         }
53
54         /* (non-Javadoc)
55          * @see org.eclipse.jface.internal.databinding.provisional.observable.set.AbstractObservableSet#getWrappedSet()
56          */

57         protected Set JavaDoc getWrappedSet() {
58             return mapElementToTreeNode.keySet();
59         }
60         
61         void doFireDiff(Set JavaDoc added, Set JavaDoc removed) {
62             fireSetChange(Diffs.createSetDiff(added, removed));
63         }
64         
65         public void fireSetChange(SetDiff diff) {
66             super.fireSetChange(diff);
67         }
68
69         void doFireStale(boolean isStale) {
70             if (isStale) {
71                 fireStale();
72             } else {
73                 fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, Collections.EMPTY_SET));
74             }
75         }
76         
77         /* (non-Javadoc)
78          * @see org.eclipse.jface.internal.databinding.provisional.observable.set.IObservableSet#getElementType()
79          */

80         public Object JavaDoc getElementType() {
81             return new Object JavaDoc();
82         }
83     }
84     
85     KnownElementsSet elements = new KnownElementsSet();
86     
87     private ITreeViewerListener expandListener = new ITreeViewerListener() {
88         public void treeCollapsed(TreeExpansionEvent event) {
89         }
90
91         public void treeExpanded(TreeExpansionEvent event) {
92         }
93     };
94
95     private IUnorderedTreeProvider provider;
96     private Object JavaDoc pendingNode;
97
98     private int avoidViewerUpdates;
99
100     private TreeViewer treeViewer;
101
102     private int staleCount = 0;
103     private boolean useRefresh;
104     private int maxPrefetches = 0;
105
106     /**
107      * Constructs a content provider that will render the given tree in a TreeViewer.
108      *
109      * @param provider IObservableTree that provides the contents of the tree
110      * @param pendingNode element to insert whenever a node is being fetched in the background
111      * @param useRefresh true = notify the viewer of changes by calling refresh(...), false =
112      * notify the viewer of changes by calling add(...) and remove(...). Using false
113      * is more efficient, but may not work with TreeViewer subclasses.
114      */

115     public UnorderedTreeContentProvider(IUnorderedTreeProvider provider,
116             Object JavaDoc pendingNode, boolean useRefresh) {
117         this.provider = provider;
118         this.pendingNode = pendingNode;
119         this.useRefresh = useRefresh;
120     }
121  
122     /**
123      * Sets whether this content provider should add/remove elements using
124      * TreePaths (true) or elements (false).
125      *
126      * <p></p>
127      * <p>When using elements:</p>
128      *
129      * <ul>
130      * <li>Cycles are permitted (elements can be their own ancestor)</li>
131      * <li>Addition, removal, and refresh are slightly faster</li>
132      * <li>It is not possible to have more than one content provider per tree</li>
133      * <li>The setRootPath(...) method is ignored</li>
134      * </ul>
135      *
136      * <p></p>
137      * <p>When using TreePaths:</p>
138      *
139      * <ul>
140      * <li>Cycles are not permitted (elements cannot be their own parent)</li>
141      * <li>Addition, removal, and refresh are slightly slower</li>
142      * <li>It is possible to use more than one content provider in the same tree</li>
143      * <li>The setRootPath(...) method can be used to direct the output to a particular
144      * subtree</li>
145      * </ul>
146      *
147      * @param usePaths
148      */

149     public void useTreePaths(boolean usePaths) {
150         this.useTreePaths = usePaths;
151     }
152     
153     /**
154      * @param rootParentProvider
155      */

156     public void setRootPath(IParentProvider rootParentProvider) {
157         this.rootParentProvider = rootParentProvider;
158     }
159     
160     /**
161      * @param maxPrefetches
162      */

163     public void setMaxPrefetches(int maxPrefetches) {
164         this.maxPrefetches = maxPrefetches;
165     }
166     
167     /* package */ IObservableSet createChildSet(Object JavaDoc element) {
168         return provider.createChildSet(element);
169     }
170
171     /* package */ void remove(Object JavaDoc element, Set JavaDoc removals, boolean lastElement) {
172         if (removals.isEmpty()) {
173             return;
174         }
175         if (avoidViewerUpdates == 0) {
176             if (lastElement || useRefresh) {
177                 doRefresh(element);
178             } else {
179                 if (useTreePaths) {
180                     List JavaDoc toRemove = new ArrayList JavaDoc();
181                     TreePath[] parents = getParents(element);
182                     for (int i = 0; i < parents.length; i++) {
183                         TreePath parent = parents[i];
184
185                         for (Iterator JavaDoc iter = removals.iterator(); iter.hasNext();) {
186                             Object JavaDoc elementToRemove = (Object JavaDoc) iter.next();
187                             
188                             toRemove.add(parent.createChildPath(element).createChildPath(elementToRemove));
189                         }
190                     }
191                     
192                     treeViewer.remove((TreePath[]) toRemove.toArray(new TreePath[toRemove.size()]));
193                 } else {
194                     treeViewer.remove(element, removals.toArray());
195                 }
196             }
197             for (Iterator JavaDoc iter = removals.iterator(); iter.hasNext();) {
198                 Object JavaDoc next = (Object JavaDoc) iter.next();
199                 
200                 TreeNode nextNode = (TreeNode)mapElementToTreeNode.get(next);
201                 if (nextNode != null) {
202                     nextNode.removeParent(element);
203                     removeIfUnused(nextNode);
204                 }
205             }
206         }
207     }
208
209     /* package */ void add(Object JavaDoc element, Set JavaDoc additions) {
210         if (additions.isEmpty()) {
211             return;
212         }
213         if (avoidViewerUpdates == 0) {
214             // Handle new parents
215
addParent(element, additions);
216             if (useRefresh) {
217                 doRefresh(element);
218             } else {
219                 if (useTreePaths) {
220                     TreePath[] parents = getParents(element);
221                     for (int i = 0; i < parents.length; i++) {
222                         TreePath parent = parents[i];
223                         
224                         treeViewer.add(parent.createChildPath(element), additions.toArray());
225                     }
226                 } else {
227                     treeViewer.add(element, additions.toArray());
228                 }
229             }
230         }
231     }
232
233     private void doRefresh(Object JavaDoc element) {
234         treeViewer.refresh(element);
235     }
236     
237     /**
238      * Ensures that the given set of children have the given parent as
239      * one of their parents.
240      *
241      * @param parent
242      * @param children
243      */

244     private void addParent(Object JavaDoc parent, Set JavaDoc children) {
245         for (Iterator JavaDoc iter = children.iterator(); iter.hasNext();) {
246             Object JavaDoc next = (Object JavaDoc) iter.next();
247             
248             TreeNode nextNode = getNode(next);
249             nextNode.addParent(parent);
250         }
251     }
252
253     /**
254      * @return saouesnth
255      */

256     public final Object JavaDoc getPendingNode() {
257         return pendingNode;
258     }
259     
260     /**
261      * @param parent
262      * @return aueosnht
263      */

264     public IObservableSet getChildrenSet(Object JavaDoc parent) {
265         IObservableSet result = getNode(parent).getChildrenSet();
266         
267         return result;
268     }
269     
270     public void dispose() {
271         if (treeViewer != null) {
272             try {
273                 avoidViewerUpdates++;
274                 enqueuedPrefetches.clear();
275                 Object JavaDoc[] keys = mapElementToTreeNode.keySet().toArray();
276     
277                 for (int i = 0; i < keys.length; i++) {
278                     Object JavaDoc key = keys[i];
279     
280                     TreeNode result = (TreeNode)mapElementToTreeNode.get(key);
281                     if (result != null) {
282                         result.dispose();
283                     }
284                 }
285                 setViewer(null);
286             } finally {
287                 avoidViewerUpdates--;
288             }
289         }
290     }
291     
292     public void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
293         // This should only ever be called for a single viewer
294
setViewer(viewer);
295         
296         if (oldInput != null && newInput != null && oldInput.equals(newInput)) {
297             return;
298         }
299         
300         try {
301             avoidViewerUpdates++;
302             TreeNode oldNode = (TreeNode)mapElementToTreeNode.get(oldInput);
303             if (oldNode != null) {
304                 removeIfUnused(oldNode);
305             }
306         } finally {
307             avoidViewerUpdates--;
308         }
309     }
310     
311     private void removeIfUnused(TreeNode toRemove) {
312         //TreeNode result = (TreeNode)mapElementToTreeNode.get(element);
313
Object JavaDoc element = toRemove.getElement();
314         if (toRemove.getParent() == null) {
315             mapElementToTreeNode.remove(element);
316             elements.doFireDiff(Collections.EMPTY_SET, Collections.singleton(element));
317             toRemove.dispose();
318         }
319     }
320
321     private void setViewer(Viewer viewer) {
322         if (viewer != null && !(viewer instanceof TreeViewer)) {
323             throw new IllegalArgumentException JavaDoc("This content provider can only be used with TreeViewers"); //$NON-NLS-1$
324
}
325         TreeViewer newTreeViewer = (TreeViewer) viewer;
326         
327         if (newTreeViewer != treeViewer) {
328             if (treeViewer != null) {
329                 treeViewer.removeTreeListener(expandListener);
330             }
331             
332             this.treeViewer = newTreeViewer;
333             if (newTreeViewer != null) {
334                 newTreeViewer.addTreeListener(expandListener);
335             }
336         }
337     }
338
339     public Object JavaDoc[] getChildren(Object JavaDoc parentElement) {
340         Set JavaDoc result = getNode(parentElement).getChildren();
341         
342         addParent(parentElement, result);
343         
344         return result.toArray();
345     }
346
347     private TreeNode getNode(Object JavaDoc parentElement) {
348         TreeNode result = (TreeNode)mapElementToTreeNode.get(parentElement);
349         if (result == null) {
350             result = new TreeNode(parentElement, this);
351             mapElementToTreeNode.put(parentElement, result);
352             elements.fireSetChange(Diffs.createSetDiff(Collections.singleton(parentElement),
353                     Collections.EMPTY_SET));
354         }
355         return result;
356     }
357
358     public Object JavaDoc getParent(Object JavaDoc element) {
359         Object JavaDoc result = getNode(element).getParent();
360         if (result == null && rootParentProvider != null) {
361             result = rootParentProvider.getParent(element);
362         }
363         return result;
364     }
365
366     public boolean hasChildren(Object JavaDoc element) {
367         return getNode(element).shouldShowPlus();
368     }
369
370     public Object JavaDoc[] getElements(Object JavaDoc inputElement) {
371         return getChildren(inputElement);
372     }
373     
374     /**
375      * @return aouesnth
376      */

377     public IObservableSet getKnownElements() {
378         return elements;
379     }
380     
381     /* package */ void changeStale(int staleDelta) {
382         staleCount += staleDelta;
383         processPrefetches();
384         elements.setStale(staleCount != 0);
385     }
386
387     /**
388      * @return aoueesnth
389      */

390     public TreeViewer getViewer() {
391         return treeViewer;
392     }
393
394     /**
395      * @param element
396      * @return aoeusnth
397      */

398     public boolean isDirty(Object JavaDoc element) {
399         return false;
400     }
401
402     /* package */ void enqueuePrefetch(TreeNode node) {
403         if (maxPrefetches > 0 || maxPrefetches == -1) {
404             if (staleCount == 0) {
405                 // Call node.getChildren()... this will cause us to start listening to the
406
// node and will trigger prefetching. Don't call prefetch since this method
407
// is intended to be called inside getters (which will simply return the
408
// fetched nodes) and prefetch() is intended to be called inside an asyncExec,
409
// which will notify the viewer directly of the newly discovered nodes.
410
node.getChildren();
411             } else {
412                 enqueuedPrefetches.add(node);
413                 while (maxPrefetches >= 0 && enqueuedPrefetches.size() > maxPrefetches) {
414                     enqueuedPrefetches.removeFirst();
415                 }
416             }
417         }
418     }
419
420     private void processPrefetches() {
421         while (staleCount == 0 && !enqueuedPrefetches.isEmpty()) {
422             TreeNode next = (TreeNode)enqueuedPrefetches.removeLast();
423             
424             // Note that we don't remove nodes from the prefetch queue when they are disposed,
425
// so we may encounter disposed nodes at this time.
426
if (!next.isDisposed()) {
427                 next.prefetch();
428             }
429         }
430     }
431
432     public Object JavaDoc[] getChildren(TreePath parentPath) {
433         return getChildren(parentPath.getLastSegment());
434     }
435
436     public TreePath[] getParents(Object JavaDoc element) {
437         // Compute all paths that do not contain cycles
438
/**
439          * List of Lists
440          */

441         List JavaDoc parentPaths = computeParents(element, new HashSet JavaDoc());
442         
443         /**
444          * List of TreePath
445          */

446         List JavaDoc result = new ArrayList JavaDoc();
447        
448         for (Iterator JavaDoc iterator = parentPaths.iterator(); iterator.hasNext();) {
449             List JavaDoc nextPath = (List JavaDoc) iterator.next();
450                         
451             LinkedList JavaDoc resultPath = new LinkedList JavaDoc();
452             resultPath.addAll(nextPath);
453             Object JavaDoc nextParent = resultPath.isEmpty() ? element : resultPath.getFirst();
454             for(;nextParent != null;) {
455                 if (rootParentProvider != null) {
456                     nextParent = rootParentProvider.getParent(nextParent);
457                     if (nextParent != null) {
458                         resultPath.addFirst(nextParent);
459                     }
460                 } else {
461                     nextParent = null;
462                 }
463             }
464             
465             result.add(new TreePath(resultPath.toArray()));
466         }
467         
468         if (result.isEmpty() && rootParentProvider != null) {
469             Object JavaDoc nextParent = rootParentProvider.getParent(element);
470             if (nextParent != null) {
471                 LinkedList JavaDoc resultPath = new LinkedList JavaDoc();
472                 while (nextParent != null) {
473                     resultPath.addFirst(nextParent);
474                     nextParent = rootParentProvider.getParent(nextParent);
475                 }
476                 
477                 result.add(new TreePath(resultPath.toArray()));
478             }
479             
480         }
481         
482         return (TreePath[]) result.toArray(new TreePath[result.size()]);
483     }
484     
485     /**
486      *
487      * @param node
488      * @param toIgnore
489      * @return a list of Lists, indicating all known paths to the given node
490      */

491     private List JavaDoc computeParents(Object JavaDoc node, HashSet JavaDoc toIgnore) {
492         List JavaDoc result = new ArrayList JavaDoc();
493         boolean containedNode = toIgnore.add(node);
494         
495         TreeNode tn = getNode(node);
496         
497         HashSet JavaDoc parents = new HashSet JavaDoc();
498         parents.addAll(tn.getParents());
499         parents.removeAll(toIgnore);
500         if (parents.isEmpty()) {
501             ArrayList JavaDoc newPath = new ArrayList JavaDoc();
502             result.add(newPath);
503         } else {
504             for (Iterator JavaDoc iterator = parents.iterator(); iterator.hasNext();) {
505                 Object JavaDoc parent = iterator.next();
506                 
507                 List JavaDoc parentPaths = computeParents(parent, toIgnore);
508
509                 for (Iterator JavaDoc iterator2 = parentPaths.iterator(); iterator2
510                         .hasNext();) {
511                     List JavaDoc parentPath = (List JavaDoc) iterator2.next();
512                     
513                     parentPath.add(parent);
514                     result.add(parentPath);
515                 }
516             }
517         }
518         
519         if (containedNode) {
520             toIgnore.remove(node);
521         }
522         return result;
523     }
524
525     public boolean hasChildren(TreePath path) {
526         return hasChildren(path.getLastSegment());
527     }
528 }
529
Popular Tags