KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > progress > DeferredTreeContentManager


1 /*******************************************************************************
2  * Copyright (c) 2003, 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  *******************************************************************************/

11 package org.eclipse.ui.progress;
12
13 import org.eclipse.core.runtime.Assert;
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.core.runtime.IStatus;
16 import org.eclipse.core.runtime.Platform;
17 import org.eclipse.core.runtime.Status;
18 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
19 import org.eclipse.core.runtime.jobs.Job;
20 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
21 import org.eclipse.jface.viewers.AbstractTreeViewer;
22 import org.eclipse.jface.viewers.ITreeContentProvider;
23 import org.eclipse.osgi.util.NLS;
24 import org.eclipse.swt.widgets.Control;
25 import org.eclipse.ui.IWorkbenchPartSite;
26 import org.eclipse.ui.PlatformUI;
27 import org.eclipse.ui.internal.progress.ProgressMessages;
28 import org.eclipse.ui.internal.util.Util;
29 import org.eclipse.ui.model.IWorkbenchAdapter;
30
31 /**
32  * The DeferredContentManager is a class that helps an ITreeContentProvider get
33  * its deferred input.
34  *
35  * <b>NOTE</b> AbstractTreeViewer#isExpandable may need to
36  * be implemented in AbstractTreeViewer subclasses with
37  * deferred content that use filtering as a call to
38  * #getChildren may be required to determine the correct
39  * state of the expanding control.
40  *
41  * AbstractTreeViewers which use this class may wish to
42  * sacrifice accuracy of the expandable state indicator for the
43  * performance benefits of deferring content.
44  *
45  * @see IDeferredWorkbenchAdapter
46  * @since 3.0
47  */

48 public class DeferredTreeContentManager {
49     ITreeContentProvider contentProvider;
50
51     AbstractTreeViewer treeViewer;
52
53     IWorkbenchSiteProgressService progressService;
54     
55     /**
56      * The DeferredContentFamily is a class used to keep track of a
57      * manager-object pair so that only jobs scheduled by the receiver
58      * are cancelled by the receiver.
59      * @since 3.1
60      *
61      */

62     class DeferredContentFamily {
63         protected DeferredTreeContentManager manager;
64         protected Object JavaDoc element;
65         
66         /**
67          * Create a new instance of the receiver to define a family
68          * for object in a particular scheduling manager.
69          * @param schedulingManager
70          * @param object
71          */

72         DeferredContentFamily(DeferredTreeContentManager schedulingManager, Object JavaDoc object) {
73             this.manager = schedulingManager;
74             this.element = object;
75         }
76     }
77
78     /**
79      * Create a new instance of the receiver using the supplied content
80      * provider and viewer. Run any jobs using the site.
81      *
82      * @param provider
83      * @param viewer
84      * @param site
85      */

86     public DeferredTreeContentManager(ITreeContentProvider provider,
87             AbstractTreeViewer viewer, IWorkbenchPartSite site) {
88         this(provider, viewer);
89         Object JavaDoc siteService = Util.getAdapter(site,
90                 IWorkbenchSiteProgressService.class);
91         if (siteService != null) {
92             progressService = (IWorkbenchSiteProgressService) siteService;
93         }
94     }
95
96     /**
97      * Create a new instance of the receiver using the supplied content
98      * provider and viewer.
99      *
100      * @param provider The content provider that will be updated
101      * @param viewer The tree viewer that the results are added to
102      */

103     public DeferredTreeContentManager(ITreeContentProvider provider,
104             AbstractTreeViewer viewer) {
105         contentProvider = provider;
106         treeViewer = viewer;
107     }
108
109     /**
110      * Provides an optimized lookup for determining if an element has children.
111      * This is required because elements that are populated lazilly can't
112      * answer <code>getChildren</code> just to determine the potential for
113      * children. Throw an AssertionFailedException if element is null.
114      *
115      * @param element The Object being tested. This should not be
116      * <code>null</code>.
117      * @return boolean <code>true</code> if there are potentially children.
118      * @throws RuntimeException if the element is null.
119      */

120     public boolean mayHaveChildren(Object JavaDoc element) {
121         Assert.isNotNull(element, ProgressMessages.DeferredTreeContentManager_NotDeferred);
122         IDeferredWorkbenchAdapter adapter = getAdapter(element);
123         return adapter != null && adapter.isContainer();
124     }
125
126     /**
127      * Returns the child elements of the given element, or in the case of a
128      * deferred element, returns a placeholder. If a deferred element is used, a
129      * job is created to fetch the children in the background.
130      *
131      * @param parent
132      * The parent object.
133      * @return Object[] or <code>null</code> if parent is not an instance of
134      * IDeferredWorkbenchAdapter.
135      */

136     public Object JavaDoc[] getChildren(final Object JavaDoc parent) {
137         IDeferredWorkbenchAdapter element = getAdapter(parent);
138         if (element == null) {
139             return null;
140         }
141         PendingUpdateAdapter placeholder = createPendingUpdateAdapter();
142         startFetchingDeferredChildren(parent, element, placeholder);
143         return new Object JavaDoc[] { placeholder };
144     }
145
146     /**
147      * Factory method for creating the pending update adapter representing the
148      * placeholder node. Subclasses may override.
149      *
150      * @return a pending update adapter
151      * @since 3.2
152      */

153     protected PendingUpdateAdapter createPendingUpdateAdapter() {
154         return new PendingUpdateAdapter();
155     }
156
157     /**
158      * Return the IDeferredWorkbenchAdapter for element or the element if it is
159      * an instance of IDeferredWorkbenchAdapter. If it does not exist return
160      * null.
161      *
162      * @param element
163      * @return IDeferredWorkbenchAdapter or <code>null</code>
164      */

165     protected IDeferredWorkbenchAdapter getAdapter(Object JavaDoc element) {
166         return (IDeferredWorkbenchAdapter)Util.getAdapter(element, IDeferredWorkbenchAdapter.class);
167     }
168
169     /**
170      * Starts a job and creates a collector for fetching the children of this
171      * deferred adapter. If children are waiting to be retrieved for this parent
172      * already, that job is cancelled and another is started.
173      *
174      * @param parent
175      * The parent object being filled in,
176      * @param adapter
177      * The adapter being used to fetch the children.
178      * @param placeholder
179      * The adapter that will be used to indicate that results are
180      * pending.
181      */

182     protected void startFetchingDeferredChildren(final Object JavaDoc parent,
183             final IDeferredWorkbenchAdapter adapter,
184             final PendingUpdateAdapter placeholder) {
185         final IElementCollector collector = createElementCollector(parent,
186                 placeholder);
187         // Cancel any jobs currently fetching children for the same parent
188
// instance.
189
cancel(parent);
190         String JavaDoc jobName = getFetchJobName(parent, adapter);
191         Job job = new Job(jobName) {
192             /* (non-Javadoc)
193              * @see org.eclipse.core.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
194              */

195             public IStatus run(IProgressMonitor monitor) {
196                 adapter.fetchDeferredChildren(parent, collector, monitor);
197                 if(monitor.isCanceled()) {
198                     return Status.CANCEL_STATUS;
199                 }
200                 return Status.OK_STATUS;
201             }
202
203            
204             /* (non-Javadoc)
205              * @see org.eclipse.core.jobs.Job#belongsTo(java.lang.Object)
206              */

207             public boolean belongsTo(Object JavaDoc family) {
208                 if (family instanceof DeferredContentFamily) {
209                     DeferredContentFamily contentFamily = (DeferredContentFamily) family;
210                     if (contentFamily.manager == DeferredTreeContentManager.this) {
211                         return isParent(contentFamily, parent);
212                     }
213                 }
214                 return false;
215                
216             }
217
218             /**
219              * Check if the parent of element is equal to the parent used in
220              * this job.
221              *
222              * @param family
223              * The DeferredContentFamily that defines a potential
224              * ancestor of the current parent in a particualr manager.
225              * @param child
226              * The object to check against.
227              * @return boolean <code>true</code> if the child or one of its
228              * parents are the same as the element of the family.
229              */

230             private boolean isParent(DeferredContentFamily family, Object JavaDoc child) {
231                 if (family.element.equals(child)) {
232                     return true;
233                 }
234                 IWorkbenchAdapter workbenchAdapter = getWorkbenchAdapter(child);
235                 if (workbenchAdapter == null) {
236                     return false;
237                 }
238                 Object JavaDoc elementParent = workbenchAdapter.getParent(child);
239                 if (elementParent == null) {
240                     return false;
241                 }
242                 return isParent(family, elementParent);
243             }
244
245             /**
246              * Get the workbench adapter for the element.
247              *
248              * @param element
249              * The object we are adapting to.
250              */

251             private IWorkbenchAdapter getWorkbenchAdapter(Object JavaDoc element) {
252                 return (IWorkbenchAdapter) Util.getAdapter(element, IWorkbenchAdapter.class);
253             }
254         };
255         job.addJobChangeListener(new JobChangeAdapter() {
256             /*
257              * (non-Javadoc)
258              *
259              * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
260              */

261             public void done(IJobChangeEvent event) {
262                 runClearPlaceholderJob(placeholder);
263             }
264         });
265         job.setRule(adapter.getRule(parent));
266         if (progressService == null) {
267             job.schedule();
268         } else {
269             progressService.schedule(job);
270         }
271     }
272     
273     /**
274      * Returns a name to use for the job that fetches children of the given parent.
275      * Subclasses may override. Default job name is parent's label.
276      *
277      * @param parent parent that children are to be fetched for
278      * @param adapter parent's deferred adapter
279      * @return job name
280      */

281     protected String JavaDoc getFetchJobName(Object JavaDoc parent, IDeferredWorkbenchAdapter adapter) {
282         return NLS.bind(
283                 ProgressMessages.DeferredTreeContentManager_FetchingName,
284                adapter.getLabel(parent));
285     }
286
287     /**
288      * Create a UIJob to add the children to the parent in the tree viewer.
289      *
290      * @param parent
291      * @param children
292      * @param monitor
293      */

294     protected void addChildren(final Object JavaDoc parent, final Object JavaDoc[] children,
295             IProgressMonitor monitor) {
296         WorkbenchJob updateJob = new WorkbenchJob(
297                 ProgressMessages.DeferredTreeContentManager_AddingChildren) {
298             /*
299              * (non-Javadoc)
300              *
301              * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
302              */

303             public IStatus runInUIThread(IProgressMonitor updateMonitor) {
304                 //Cancel the job if the tree viewer got closed
305
if (treeViewer.getControl().isDisposed() || updateMonitor.isCanceled()) {
306                     return Status.CANCEL_STATUS;
307                 }
308                 treeViewer.add(parent, children);
309                 return Status.OK_STATUS;
310             }
311         };
312         updateJob.setSystem(true);
313         updateJob.schedule();
314
315     }
316
317     /**
318      * Return whether or not the element is or adapts to an
319      * IDeferredWorkbenchAdapter.
320      *
321      * @param element
322      * @return boolean <code>true</code> if the element is an
323      * IDeferredWorkbenchAdapter
324      */

325     public boolean isDeferredAdapter(Object JavaDoc element) {
326         return getAdapter(element) != null;
327     }
328
329     /**
330      * Run a job to clear the placeholder. This is used when the update
331      * for the tree is complete so that the user is aware that no more
332      * updates are pending.
333      *
334      * @param placeholder
335      */

336     protected void runClearPlaceholderJob(final PendingUpdateAdapter placeholder) {
337         if (placeholder.isRemoved() || !PlatformUI.isWorkbenchRunning()) {
338             return;
339         }
340         //Clear the placeholder if it is still there
341
WorkbenchJob clearJob = new WorkbenchJob(ProgressMessages.DeferredTreeContentManager_ClearJob) {
342             /*
343              * (non-Javadoc)
344              *
345              * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
346              */

347             public IStatus runInUIThread(IProgressMonitor monitor) {
348                 if (!placeholder.isRemoved()) {
349                     Control control = treeViewer.getControl();
350                     if (control.isDisposed()) {
351                         return Status.CANCEL_STATUS;
352                     }
353                     treeViewer.remove(placeholder);
354                     placeholder.setRemoved(true);
355                 }
356                 return Status.OK_STATUS;
357             }
358         };
359         clearJob.setSystem(true);
360         clearJob.schedule();
361     }
362
363     /**
364      * Cancel all jobs that are fetching content for the given parent or any of
365      * its children.
366      *
367      * @param parent
368      */

369     public void cancel(Object JavaDoc parent) {
370         if(parent == null) {
371             return;
372         }
373         
374         Platform.getJobManager().cancel(new DeferredContentFamily(this, parent));
375     }
376
377     /**
378      * Create the element collector for the receiver.
379      *@param parent
380      * The parent object being filled in,
381      * @param placeholder
382      * The adapter that will be used to indicate that results are
383      * pending.
384      * @return IElementCollector
385      */

386     protected IElementCollector createElementCollector(final Object JavaDoc parent,
387             final PendingUpdateAdapter placeholder) {
388         return new IElementCollector() {
389             /*
390              * (non-Javadoc)
391              * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object, org.eclipse.core.runtime.IProgressMonitor)
392              */

393             public void add(Object JavaDoc element, IProgressMonitor monitor) {
394                 add(new Object JavaDoc[] { element }, monitor);
395             }
396
397             /*
398              * (non-Javadoc)
399              * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object[], org.eclipse.core.runtime.IProgressMonitor)
400              */

401             public void add(Object JavaDoc[] elements, IProgressMonitor monitor) {
402                 addChildren(parent, elements, monitor);
403             }
404
405             /*
406              * (non-Javadoc)
407              *
408              * @see org.eclipse.jface.progress.IElementCollector#done()
409              */

410             public void done() {
411                 runClearPlaceholderJob(placeholder);
412             }
413         };
414     }
415 }
416
Popular Tags