KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > decorators > DecorationScheduler


1 /*******************************************************************************
2  * Copyright (c) 2000, 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.internal.decorators;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Collection JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Status;
25 import org.eclipse.core.runtime.jobs.Job;
26 import org.eclipse.jface.viewers.DecorationContext;
27 import org.eclipse.jface.viewers.IDecorationContext;
28 import org.eclipse.jface.viewers.ILabelProviderListener;
29 import org.eclipse.jface.viewers.LabelProviderChangedEvent;
30 import org.eclipse.swt.graphics.Color;
31 import org.eclipse.swt.graphics.Font;
32 import org.eclipse.swt.graphics.Image;
33 import org.eclipse.ui.PlatformUI;
34 import org.eclipse.ui.internal.WorkbenchMessages;
35 import org.eclipse.ui.progress.UIJob;
36 import org.eclipse.ui.progress.WorkbenchJob;
37
38 /**
39  * The DecorationScheduler is the class that handles the decoration of elements
40  * using a background thread.
41  */

42 public class DecorationScheduler {
43
44     static final ILabelProviderListener[] EMPTY_LISTENER_LIST = new ILabelProviderListener[0];
45
46     // When decorations are computed they are added to this cache via
47
// decorated() method
48
Map JavaDoc resultCache = new HashMap JavaDoc();
49
50     // Objects that need an icon and text computed for display to the user
51
List JavaDoc awaitingDecoration = new ArrayList JavaDoc();
52
53     // Objects that are awaiting a label update.
54
Set JavaDoc pendingUpdate = new HashSet JavaDoc();
55
56     // Key to lock write access to the pending update set
57
Object JavaDoc pendingKey = new Object JavaDoc();
58
59     Map JavaDoc awaitingDecorationValues = new HashMap JavaDoc();
60
61     DecoratorManager decoratorManager;
62
63     boolean shutdown = false;
64
65     Job decorationJob;
66
67     UIJob updateJob;
68
69     private Collection JavaDoc removedListeners = Collections
70             .synchronizedSet(new HashSet JavaDoc());
71
72     private Job clearJob;
73
74     // Static used for the updates to indicate an update is required
75
static final int NEEDS_INIT = -1;
76
77     /** Amount of time to delay the update notification when max reached. */
78     static final int UPDATE_DELAY = 100;
79
80     /**
81      * Return a new instance of the receiver configured for the supplied
82      * DecoratorManager.
83      *
84      * @param manager
85      */

86     DecorationScheduler(DecoratorManager manager) {
87         decoratorManager = manager;
88         createDecorationJob();
89     }
90
91     /**
92      * Decorate the text for the receiver. If it has already been done then
93      * return the result, otherwise queue it for decoration.
94      *
95      * @return String
96      * @param text
97      * @param element
98      * @param adaptedElement
99      * The adapted value of element. May be null.
100      * @param context
101      * the decoration context
102      */

103
104     public String JavaDoc decorateWithText(String JavaDoc text, Object JavaDoc element,
105             Object JavaDoc adaptedElement, IDecorationContext context) {
106
107         DecorationResult decoration = getResult(element, adaptedElement,
108                 context);
109
110         if (decoration == null) {
111             return text;
112         }
113
114         return decoration.decorateWithText(text);
115
116     }
117
118     /**
119      * Queue the element and its adapted value if it has not been already.
120      *
121      * @param element
122      * @param adaptedElement
123      * The adapted value of element. May be null.
124      * @param forceUpdate
125      * If true then a labelProviderChanged is fired whether
126      * decoration occurred or not.
127      * @param undecoratedText
128      * The original text for the element if it is known.
129      * @param context
130      * The decoration context
131      */

132
133     synchronized void queueForDecoration(Object JavaDoc element, Object JavaDoc adaptedElement,
134             boolean forceUpdate, String JavaDoc undecoratedText,
135             IDecorationContext context) {
136
137         DecorationReference reference = (DecorationReference) awaitingDecorationValues
138                 .get(element);
139         if (reference != null) {
140             if (forceUpdate) {// Make sure we don't loose a force
141
reference.setForceUpdate(forceUpdate);
142             }
143             reference.addContext(context);
144         } else {
145             reference = new DecorationReference(element, adaptedElement,
146                     context);
147             reference.setForceUpdate(forceUpdate);
148             reference.setUndecoratedText(undecoratedText);
149             awaitingDecorationValues.put(element, reference);
150             awaitingDecoration.add(element);
151             if (shutdown) {
152                 return;
153             }
154             if (decorationJob.getState() == Job.SLEEPING) {
155                 decorationJob.wakeUp();
156             }
157             decorationJob.schedule();
158         }
159
160     }
161
162     /**
163      * Decorate the supplied image, element and its adapted value.
164      *
165      * @return Image
166      * @param image
167      * @param element
168      * @param adaptedElement
169      * The adapted value of element. May be null.
170      * @param context
171      * the decoration context
172      *
173      */

174     public Image decorateWithOverlays(Image image, Object JavaDoc element,
175             Object JavaDoc adaptedElement, IDecorationContext context) {
176
177         DecorationResult decoration = getResult(element, adaptedElement,
178                 context);
179
180         if (decoration == null) {
181             return image;
182         }
183         return decoration.decorateWithOverlays(image, decoratorManager
184                 .getLightweightManager().getOverlayCache());
185     }
186
187     /**
188      * Return the DecorationResult for element. If there isn't one queue for
189      * decoration and return <code>null</code>.
190      *
191      * @param element
192      * The element to be decorated. If it is <code>null</code>
193      * return <code>null</code>.
194      * @param adaptedElement
195      * It's adapted value.
196      * @param context
197      * The deocration context
198      * @return DecorationResult or <code>null</code>
199      */

200     private DecorationResult getResult(Object JavaDoc element, Object JavaDoc adaptedElement,
201             IDecorationContext context) {
202
203         // We do not support decoration of null
204
if (element == null) {
205             return null;
206         }
207
208         DecorationResult decoration = internalGetResult(element, context);
209
210         if (decoration == null) {
211             queueForDecoration(element, adaptedElement, false, null, context);
212             return null;
213         }
214         return decoration;
215
216     }
217
218     private DecorationResult internalGetResult(Object JavaDoc element,
219             IDecorationContext context) {
220         Map JavaDoc results = (Map JavaDoc) resultCache.get(context);
221         if (results != null) {
222             return (DecorationResult) results.get(element);
223         }
224         return null;
225     }
226
227     protected void internalPutResult(Object JavaDoc element,
228             IDecorationContext context, DecorationResult result) {
229         Map JavaDoc results = (Map JavaDoc) resultCache.get(context);
230         if (results == null) {
231             results = new HashMap JavaDoc();
232             resultCache.put(context, results);
233         }
234         results.put(element, result);
235     }
236
237     /**
238      * Execute a label update using the pending decorations.
239      */

240     synchronized void decorated() {
241
242         // Don't bother if we are shutdown now
243
if (shutdown) {
244             return;
245         }
246
247         // Lazy initialize the job
248
if (updateJob == null) {
249             updateJob = getUpdateJob();
250         }
251
252         // Give it a bit of a lag for other updates to occur
253
updateJob.schedule(UPDATE_DELAY);
254     }
255
256     /**
257      * Shutdown the decoration.
258      */

259     synchronized void shutdown() {
260         shutdown = true;
261     }
262
263     /**
264      * Get the next resource to be decorated.
265      *
266      * @return IResource
267      */

268     synchronized DecorationReference nextElement() {
269
270         if (shutdown || awaitingDecoration.isEmpty()) {
271             return null;
272         }
273         Object JavaDoc element = awaitingDecoration.remove(0);
274
275         return (DecorationReference) awaitingDecorationValues.remove(element);
276     }
277
278     /**
279      * Create the Thread used for running decoration.
280      */

281     private void createDecorationJob() {
282         decorationJob = new Job(
283                 WorkbenchMessages.DecorationScheduler_CalculationJobName) {
284             /*
285              * (non-Javadoc)
286              *
287              * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
288              */

289             public IStatus run(IProgressMonitor monitor) {
290
291                 synchronized (DecorationScheduler.this) {
292                     if (shutdown) {
293                         return Status.CANCEL_STATUS;
294                     }
295                 }
296
297                 while (updatesPending()) {
298
299                     try {
300                         Thread.sleep(100);
301                     } catch (InterruptedException JavaDoc e) {
302                         // Cancel and try again if there was an error
303
schedule();
304                         return Status.CANCEL_STATUS;
305                     }
306                 }
307
308                 monitor.beginTask(
309                         WorkbenchMessages.DecorationScheduler_CalculatingTask,
310                         100);
311                 // will block if there are no resources to be decorated
312
DecorationReference reference;
313                 monitor.worked(5);
314                 int workCount = 5;
315                 while ((reference = nextElement()) != null) {
316
317                     // Count up to 90 to give the appearance of updating
318
if (workCount < 90) {
319                         monitor.worked(1);
320                         workCount++;
321                     }
322
323                     monitor.subTask(reference.getSubTask());
324                     Object JavaDoc element = reference.getElement();
325                     boolean force = reference.shouldForceUpdate();
326                     IDecorationContext[] contexts = reference.getContexts();
327                     for (int i = 0; i < contexts.length; i++) {
328                         IDecorationContext context = contexts[i];
329                         ensureResultCached(element, force, context);
330                     }
331
332                     // Only notify listeners when we have exhausted the
333
// queue of decoration requests.
334
synchronized (DecorationScheduler.this) {
335                         if (awaitingDecoration.isEmpty()) {
336                             decorated();
337                         }
338                     }
339                 }
340                 monitor.worked(100 - workCount);
341                 monitor.done();
342                 return Status.OK_STATUS;
343             }
344
345             /**
346              * Ensure that a result is cached for the given element and context
347              *
348              * @param element
349              * the elements
350              * @param force
351              * whether an update should be forced
352              * @param context
353              * the decoration context
354              */

355             private void ensureResultCached(Object JavaDoc element, boolean force,
356                     IDecorationContext context) {
357                 boolean elementIsCached = internalGetResult(element, context) != null;
358                 if (elementIsCached) {
359                     synchronized (pendingKey) {
360                         pendingUpdate.add(element);
361                     }
362
363                 }
364
365                 if (!elementIsCached) {
366                     DecorationBuilder cacheResult = new DecorationBuilder(
367                             context);
368                     // Calculate the decoration
369
decoratorManager.getLightweightManager().getDecorations(
370                             element, cacheResult);
371
372                     // If we should update regardless then put a result
373
// anyways
374
if (cacheResult.hasValue() || force) {
375
376                         // Synchronize on the result lock as we want to
377
// be sure that we do not try and decorate during
378
// label update servicing.
379
// Note: resultCache and pendingUpdate modifications
380
// must be done atomically.
381

382                         // Add the decoration even if it's empty in
383
// order to indicate that the decoration is
384
// ready
385
internalPutResult(element, context, cacheResult
386                                 .createResult());
387
388                         // Add an update for only the original element
389
// to
390
// prevent multiple updates and clear the cache.
391
pendingUpdate.add(element);
392
393                     }
394                 }
395             }
396
397             /*
398              * (non-Javadoc)
399              *
400              * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
401              */

402             public boolean belongsTo(Object JavaDoc family) {
403                 return DecoratorManager.FAMILY_DECORATE == family;
404             }
405
406             /*
407              * (non-Javadoc)
408              *
409              * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
410              */

411             public boolean shouldRun() {
412                 return PlatformUI.isWorkbenchRunning();
413             }
414         };
415
416         decorationJob.setSystem(true);
417         decorationJob.setPriority(Job.DECORATE);
418         decorationJob.schedule();
419     }
420
421     /**
422      * Return whether or not we are waiting on updated
423      *
424      * @return <code>true</code> if there are updates waiting to be served
425      */

426     protected boolean updatesPending() {
427         if (updateJob != null && updateJob.getState() != Job.NONE) {
428             return true;
429         }
430         if (clearJob != null && clearJob.getState() != Job.NONE) {
431             return true;
432         }
433         return false;
434     }
435
436     /**
437      * An external update request has been made. Clear the results as they are
438      * likely obsolete now.
439      */

440     void clearResults() {
441         if (clearJob == null) {
442             clearJob = getClearJob();
443         }
444         clearJob.schedule();
445     }
446
447     private Job getClearJob() {
448         Job clear = new Job(
449                 WorkbenchMessages.DecorationScheduler_ClearResultsJob) {
450
451             /*
452              * (non-Javadoc)
453              *
454              * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
455              */

456             protected IStatus run(IProgressMonitor monitor) {
457                 resultCache.clear();
458                 return Status.OK_STATUS;
459             }
460
461             /*
462              * (non-Javadoc)
463              *
464              * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
465              */

466             public boolean shouldRun() {
467                 return PlatformUI.isWorkbenchRunning();
468             }
469
470         };
471         clear.setSystem(true);
472
473         return clear;
474     }
475
476     /**
477      * Get the update WorkbenchJob.
478      *
479      * @return WorkbenchJob
480      */

481     private WorkbenchJob getUpdateJob() {
482         WorkbenchJob job = new WorkbenchJob(
483                 WorkbenchMessages.DecorationScheduler_UpdateJobName) {
484
485             int currentIndex = NEEDS_INIT;
486
487             LabelProviderChangedEvent labelProviderChangedEvent;
488
489             ILabelProviderListener[] listeners;
490
491             public IStatus runInUIThread(IProgressMonitor monitor) {
492
493                 synchronized (DecorationScheduler.this) {
494                     if (shutdown) {
495                         return Status.CANCEL_STATUS;
496                     }
497                 }
498
499                 // If this is the first one check again in case
500
// someone has already cleared it out.
501
if (currentIndex == NEEDS_INIT) {
502                     if (hasPendingUpdates()) {
503                         // If the removal came in while we were waiting clear it
504
// anyways
505
removedListeners.clear();
506                         return Status.OK_STATUS;
507                     }
508                     setUpUpdates();
509                 }
510
511                 if (listeners.length == 0) {
512                     return Status.OK_STATUS;
513                 }
514
515                 monitor.beginTask(
516                         WorkbenchMessages.DecorationScheduler_UpdatingTask,
517                         IProgressMonitor.UNKNOWN);
518
519                 long startTime = System.currentTimeMillis();
520                 while (currentIndex < listeners.length) {
521                     ILabelProviderListener listener = listeners[currentIndex];
522                     currentIndex++;
523
524                     // If it was removed in the meantime then skip it.
525
if (!removedListeners.contains(listener)) {
526                         decoratorManager.fireListener(
527                                 labelProviderChangedEvent, listener);
528                     }
529
530                     // If it is taking long enough for the user to notice then
531
// cancel the
532
// updates.
533
if ((System.currentTimeMillis() - startTime) >= UPDATE_DELAY / 2) {
534                         break;
535                     }
536                 }
537
538                 monitor.done();
539
540                 if (currentIndex >= listeners.length) {
541                     // Other decoration requests may have occurred due to
542
// updates or we may have timed out updating listeners.
543
// Only clear the results if there are none pending.
544
if (awaitingDecoration.isEmpty()) {
545                         resultCache.clear();
546                     }
547
548                     if (!hasPendingUpdates()) {
549                         decorated();
550                     }
551                     currentIndex = NEEDS_INIT;// Reset
552
labelProviderChangedEvent = null;
553                     removedListeners.clear();
554                     listeners = EMPTY_LISTENER_LIST;
555                 } else {
556                     schedule(UPDATE_DELAY);// Reschedule if we are not done
557
}
558                 return Status.OK_STATUS;
559             }
560
561             private void setUpUpdates() {
562                 // Get the elements awaiting update and then
563
// clear the list
564
removedListeners.clear();
565                 currentIndex = 0;
566                 synchronized (pendingKey) {
567                     Object JavaDoc[] elements = pendingUpdate
568                             .toArray(new Object JavaDoc[pendingUpdate.size()]);
569                     pendingUpdate.clear();
570                     labelProviderChangedEvent = new LabelProviderChangedEvent(
571                             decoratorManager, elements);
572                 }
573                 listeners = decoratorManager.getListeners();
574             }
575
576             /*
577              * (non-Javadoc)
578              *
579              * @see org.eclipse.core.runtime.jobs.Job#belongsTo(java.lang.Object)
580              */

581             public boolean belongsTo(Object JavaDoc family) {
582                 return DecoratorManager.FAMILY_DECORATE == family;
583             }
584
585             /*
586              * (non-Javadoc)
587              *
588              * @see org.eclipse.core.runtime.jobs.Job#shouldRun()
589              */

590             public boolean shouldRun() {
591                 return PlatformUI.isWorkbenchRunning();
592             }
593         };
594
595         job.setSystem(true);
596         return job;
597     }
598
599     /**
600      * Return whether or not there is a decoration for this element ready.
601      *
602      * @param element
603      * @param context
604      * The decoration context
605      * @return boolean true if the element is ready.
606      */

607     public boolean isDecorationReady(Object JavaDoc element, IDecorationContext context) {
608         return internalGetResult(element, context) != null;
609     }
610
611     /**
612      * Return the background Color for element. If there is no result cue for
613      * decoration and return null, otherwise return the value in the result.
614      *
615      * @param element
616      * The Object to be decorated
617      * @param adaptedElement
618      * @return Color or <code>null</code> if there is no value or if it is has
619      * not been decorated yet.
620      */

621     public Color getBackgroundColor(Object JavaDoc element, Object JavaDoc adaptedElement) {
622         DecorationResult decoration = getResult(element, adaptedElement,
623                 DecorationContext.DEFAULT_CONTEXT);
624
625         if (decoration == null) {
626             return null;
627         }
628         return decoration.getBackgroundColor();
629     }
630
631     /**
632      * Return the font for element. If there is no result cue for decoration and
633      * return null, otherwise return the value in the result.
634      *
635      * @param element
636      * The Object to be decorated
637      * @param adaptedElement
638      * @return Font or <code>null</code> if there is no value or if it is has
639      * not been decorated yet.
640      */

641     public Font getFont(Object JavaDoc element, Object JavaDoc adaptedElement) {
642         DecorationResult decoration = getResult(element, adaptedElement,
643                 DecorationContext.DEFAULT_CONTEXT);
644
645         if (decoration == null) {
646             return null;
647         }
648         return decoration.getFont();
649     }
650
651     /**
652      * Return the foreground Color for element. If there is no result cue for
653      * decoration and return null, otherwise return the value in the result.
654      *
655      * @param element
656      * The Object to be decorated
657      * @param adaptedElement
658      * @return Color or <code>null</code> if there is no value or if it is has
659      * not been decorated yet.
660      */

661     public Color getForegroundColor(Object JavaDoc element, Object JavaDoc adaptedElement) {
662         DecorationResult decoration = getResult(element, adaptedElement,
663                 DecorationContext.DEFAULT_CONTEXT);
664
665         if (decoration == null) {
666             return null;
667         }
668         return decoration.getForegroundColor();
669     }
670
671     /**
672      * Return whether or not any updates are being processed/
673      *
674      * @return boolean
675      */

676     public boolean processingUpdates() {
677         return !hasPendingUpdates() && !awaitingDecoration.isEmpty();
678     }
679
680     /**
681      * A listener has been removed. If we are updating then skip it.
682      *
683      * @param listener
684      */

685     void listenerRemoved(ILabelProviderListener listener) {
686         if (updatesPending()) {// Only keep track of them if there are updates
687
// pending
688
removedListeners.add(listener);
689         }
690         if (!updatesPending()) {
691             removedListeners.remove(listener);
692         }
693
694     }
695
696     /**
697      * Return whether or not there are any updates pending.
698      *
699      * @return
700      */

701     boolean hasPendingUpdates() {
702         synchronized (pendingKey) {
703             return pendingUpdate.isEmpty();
704         }
705
706     }
707 }
708
Popular Tags