KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > views > markers > internal > TableContentProvider


1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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
12 package org.eclipse.ui.views.markers.internal;
13
14 import java.lang.reflect.InvocationTargetException JavaDoc;
15 import java.util.Collection JavaDoc;
16
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Platform;
20 import org.eclipse.core.runtime.Status;
21 import org.eclipse.core.runtime.SubProgressMonitor;
22 import org.eclipse.core.runtime.jobs.ILock;
23 import org.eclipse.core.runtime.jobs.Job;
24 import org.eclipse.jface.operation.IRunnableWithProgress;
25 import org.eclipse.jface.viewers.IStructuredContentProvider;
26 import org.eclipse.jface.viewers.TableViewer;
27 import org.eclipse.jface.viewers.Viewer;
28 import org.eclipse.swt.widgets.Control;
29 import org.eclipse.ui.PlatformUI;
30 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
31 import org.eclipse.ui.progress.WorkbenchJob;
32
33 /**
34  * Provides a threaded content provider for efficiently handling large tables.
35  * The owner of this object is responsible for adding and removing the items in the
36  * table (by invoking the add, remove, set, and change methods, respectively). However,
37  * the changes are buffered and applied to the actual table incrementally
38  * using a background thread. This keeps the UI responsive when manipulating very
39  * large tables.
40  *
41  * <p>
42  * Other objects should set the contents of the table by manipulating
43  * this object rather than manipulating the table itself. Threading issues
44  * are encapsulated internally.
45  * </p>
46  *
47  */

48 class TableContentProvider implements IStructuredContentProvider {
49
50     // NLS strings
51
private static final String JavaDoc TABLE_SYNCHRONIZATION = Messages
52             .getString("TableContentProvider.TableSynchronization"); //$NON-NLS-1$
53

54     private static final String JavaDoc UPDATING_TABLE_WIDGET = Messages
55             .getString("TableContentProvider.Updating"); //$NON-NLS-1$
56

57     /**
58      * Currently running update job.
59      */

60     private String JavaDoc description = ""; //$NON-NLS-1$
61

62     /**
63      * Comparator to use for sorting the view
64      */

65     private TableSorter sortOrder = null;
66
67     // Pending changes
68
private DeferredQueue queues;
69
70     /**
71      * This job disables redraw on the table
72      */

73     private Job disableUpdatesJob = new WorkbenchJob(TABLE_SYNCHRONIZATION) {
74         public IStatus runInUIThread(IProgressMonitor monitor) {
75             if (controlExists()) {
76                 getViewer().getTable().setRedraw(false);
77             }
78
79             return Status.OK_STATUS;
80         }
81     };
82
83     /**
84      * This job re-enables redraw on the table
85      */

86     private Job enableUpdatesJob = new WorkbenchJob(TABLE_SYNCHRONIZATION) {
87         public IStatus runInUIThread(IProgressMonitor monitor) {
88             if (controlExists()) {
89                 getViewer().getTable().setRedraw(true);
90             }
91
92             return Status.OK_STATUS;
93         }
94     };
95
96     /**
97      * This job is responsible for performing a single incremental update to the
98      * viewer. It will either add, remove, or change items in the viewer depending
99      * on the contents of the pending* sets, above. It is scheduled repeatedly by
100      * the OverallUpdateJob, below.
101      */

102     private class WidgetRefreshJob extends WorkbenchJob {
103
104         /**
105          * Number of items modified in the last update
106          */

107         int lastWorked = 0;
108
109         /**
110          * Remembers whether the viewer existed the last time this job was run
111          */

112         boolean controlExists = true;
113
114         WidgetRefreshJob(String JavaDoc title) {
115             super(title);
116         }
117
118         /**
119          * Will be executed each time the update thread wakes up. This makes
120          * a single incremental update to the viewer (ie: adds or removes a few items)
121          */

122         public IStatus runInUIThread(IProgressMonitor monitor) {
123
124             // If we can't get the lock, terminate without blocking the UI thread.
125
if (lock.getDepth() > 0) {
126                 lastWorked = 0;
127                 return Status.OK_STATUS;
128             }
129
130             lock.acquire();
131             try {
132                 if (!PlatformUI.isWorkbenchRunning()) {
133                     controlExists = false;
134                 } else {
135                     controlExists = controlExists();
136                     if (controlExists) {
137                         lastWorked = updateViewer();
138                     }
139                 }
140             } finally {
141                 lock.release();
142             }
143
144             return Status.OK_STATUS;
145         }
146     }
147
148     /**
149      * Job that does the real work for individual updates
150      */

151     WidgetRefreshJob uiJob;
152
153     /**
154      * This job incrementally updates the viewer until all pending changes have
155      * been applied. It repeatedly schedules WidgetRefreshJobs until there are no more
156      * changes to apply. This job doesn't actually do any real work -- it simply
157      * schedules updates and updates the progress bar.
158      */

159     RestartableJob updateJob;
160
161     private ILock lock;
162
163     /**
164      * Creates a new TableContentProvider that will control the contents of the given
165      * viewer.
166      *
167      * @param viewer
168      * @param description user-readable string that will be included in progress monitors
169      * @param service IWorkbenchSiteProgressService or <code>null</null>
170      * the service that this content provider will inform of
171      * updates.
172      */

173     public TableContentProvider(TableViewer viewer, String JavaDoc description,
174             IWorkbenchSiteProgressService service) {
175         this.queues = new DeferredQueue(viewer);
176         this.description = description;
177
178         uiJob = new WidgetRefreshJob(UPDATING_TABLE_WIDGET);
179         uiJob.setPriority(Job.LONG);
180         uiJob.setSystem(true);
181
182         updateJob = new RestartableJob(TABLE_SYNCHRONIZATION,
183                 new IRunnableWithProgress() {
184                     public void run(IProgressMonitor monitor)
185                             throws InvocationTargetException JavaDoc,
186                             InterruptedException JavaDoc {
187                         doUpdate(monitor);
188                     }
189                 }, service);
190
191         lock = Platform.getJobManager().newLock();
192     }
193
194     /**
195      * Sets the view's sorter (or null if no sorting is to be used)
196      *
197      * @param c comparator that controls the view's sort order (or null if no sorting)
198      */

199     public void setSorter(TableSorter c) {
200         if (sortOrder != c) {
201             sortOrder = c;
202             scheduleUpdate();
203         }
204     }
205
206     /* (non-Javadoc)
207      * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
208      */

209     public Object JavaDoc[] getElements(Object JavaDoc inputElement) {
210         return queues.getVisibleItems();
211     }
212
213     /* (non-Javadoc)
214      * @see org.eclipse.jface.viewers.IContentProvider#dispose()
215      */

216     public void dispose() {
217         //No state to dispose here
218
}
219
220     /* (non-Javadoc)
221      * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
222      */

223     public void inputChanged(Viewer inputViewer, Object JavaDoc oldInput,
224             Object JavaDoc newInput) {
225         scheduleUpdate();
226     }
227
228     /**
229      * Sets the contents of the table. Note that the changes may not become visible
230      * immediately, as the viewer will actually be updated in a background thread.
231      *
232      * @param newVisibleItems
233      */

234     public void set(Collection JavaDoc newVisibleItems, IProgressMonitor mon) {
235         lock.acquire();
236
237         try {
238             queues.set(newVisibleItems, mon);
239
240             scheduleUpdate();
241         } finally {
242             lock.release();
243         }
244     }
245
246     /**
247      * If the contents of the viewer have somehow become out-of-sync with the "visibleItems"
248      * set, this method will restore synchronization.
249      */

250     private void resync() {
251         if (controlExists()) {
252             int count = queues.getViewer().getTable().getItemCount();
253             if (count != queues.countVisibleItems()) {
254                 queues.getViewer().refresh();
255             }
256         }
257     }
258
259     /**
260      * Causes the given collection of items to be refreshed
261      *
262      * @param changes collection of objects that have changed
263      */

264     void change(Collection JavaDoc changes) {
265
266         lock.acquire();
267         try {
268             // Ensure that this is never done in the user interface thread.
269
//Assert.isTrue(Display.getCurrent() == null);
270

271             queues.change(changes);
272             scheduleUpdate();
273         } finally {
274             lock.release();
275         }
276     }
277
278     /**
279      * Returns the TableViewer being populated by this content provider
280      *
281      * @return the TableViewer being populated by this content provider
282      */

283     private TableViewer getViewer() {
284         return queues.getViewer();
285     }
286
287     /**
288      * Returns true iff the control exists and has not yet been disposed
289      *
290      * @return
291      */

292     private boolean controlExists() {
293         Control control = getViewer().getControl();
294
295         if (control == null || control.isDisposed()) {
296             return false;
297         }
298
299         return true;
300     }
301
302     /**
303      * Returns true iff this content provider contains changes that are not yet
304      * reflected in the viewer.
305      *
306      * @return true iff the reciever has unprocessed changes
307      */

308     public boolean hasPendingChanges() {
309         return queues.hasPendingChanges() || sortOrder != queues.getSorter();
310     }
311
312     /**
313      * Returns an estimate of the total work remaining (used for progress monitors)
314      *
315      * @return
316      */

317     private int totalWork() {
318         return queues.workRemaining() + 1;
319     }
320
321     /**
322      * Starts the update thread... this will continue to make incremental changes
323      * to the viewer until the pending* sets become empty. Does nothing if the
324      * update thread is already running or if there are no changes to process.
325      */

326     private void scheduleUpdate() {
327         if (hasPendingChanges()) {
328             updateJob.schedule();
329         }
330     }
331
332     /**
333      * Cancels any pending changes to the viewer. The contents of the viewer
334      * will be left in whatever state they are in at the time. Any changes that
335      * have not yet been applied will be lost. It is a good idea to call this
336      * method before performing a long computation that will ultimately invalidate
337      * the contents of the viewer.
338      */

339     public void cancelPendingChanges() {
340         updateJob.cancel();
341
342         lock.acquire();
343         try {
344             queues.cancelPending();
345         } finally {
346             lock.release();
347         }
348     }
349
350     private void doUpdate(IProgressMonitor monitor) throws InterruptedException JavaDoc {
351
352         //Do not update if the workbench is shutdown
353
if (!PlatformUI.isWorkbenchRunning())
354             return;
355
356         // This counter represents how many work units remain unused in the progress monitor.
357
int remainingWorkUnits = 100000;
358
359         monitor.beginTask(description, remainingWorkUnits);
360
361         disableUpdatesJob.schedule();
362         disableUpdatesJob.join();
363         try {
364
365             // Loop until there are no more changes to apply, the control is destroyed, the monitor is cancelled,
366
// or another job takes over.
367
while (hasPendingChanges() && !monitor.isCanceled()) {
368
369                 // Ensure that we aren't running in the UI thread
370
//Assert.isTrue(Display.getCurrent() == null);
371

372                 try {
373
374                     int totalWork;
375                     lock.acquire();
376                     try {
377                         totalWork = totalWork();
378                         if (sortOrder != queues.getSorter()) {
379                             queues.setComparator(sortOrder);
380                         }
381
382                         SubProgressMonitor sub = new SubProgressMonitor(
383                                 monitor, 0);
384                         queues.refreshQueues(sub);
385                     } finally {
386                         lock.release();
387                     }
388
389                     try {
390                         uiJob.schedule();
391                         // Wait for the current update job to complete before scheduling another
392
uiJob.join();
393                     } catch (IllegalStateException JavaDoc e) {
394                         // Ignore this exception -- it means that the Job manager was shut down, which is expected
395
// at the end of the application. Note that we need to check for this by catching the exception
396
// rather than using something like if (jobManagerIsShutDown())... since the job manager might
397
// be shut down in another thread between the time we evaluate the if statement and when
398
// we schedule the job.
399
}
400
401                     // Estimate how much of the remaining work we'll be doing in this update,
402
// and update the progress bar appropriately.
403
int consumedUnits = uiJob.lastWorked * remainingWorkUnits
404                             / totalWork;
405                     monitor.worked(consumedUnits);
406                     remainingWorkUnits -= consumedUnits;
407
408                 } catch (InterruptedException JavaDoc e) {
409                     monitor.setCanceled(true);
410                 }
411
412                 if (!uiJob.controlExists) {
413                     break;
414                 }
415             }
416         } finally {
417             //Only send updates if we can send them to the workbench
418
if (PlatformUI.isWorkbenchRunning()) {
419                 enableUpdatesJob.schedule();
420                 enableUpdatesJob.join();
421             }
422
423             monitor.done();
424         }
425     }
426
427     /**
428      * Performs a single update to the viewer. Based on the contents of the pending* queues,
429      * items will either be removed, added, or refreshed in the viewer (in that order). This
430      * should only be called within a synchronized block, since the various queues shouldn't
431      * be modified during an update. This method is invoked repeatedly by jobs to gradually
432      * apply the pending changes.
433      */

434     private int updateViewer() {
435
436         int result;
437
438         // Note that this method is only called when we have the lock so acquiring it here
439
// does nothing... but we re-acquire it anyway in case future refactoring causes
440
// this to be called when we don't own the lock.
441
lock.acquire();
442         try {
443             if (getViewer().getSorter() != null) {
444                 getViewer().setSorter(null);
445             }
446
447             resync();
448
449             result = queues.nextUpdate();
450         } finally {
451             lock.release();
452         }
453
454         return result;
455     }
456 }
457
Popular Tags