001: /*******************************************************************************
002: * Copyright (c) 2000, 2004 IBM Corporation and others. All rights reserved.
003: * This program and the accompanying materials are made available under the
004: * terms of the Common Public License v1.0 which accompanies this distribution,
005: * and is available at http://www.eclipse.org/legal/cpl-v10.html
006: *
007: * Contributors: IBM Corporation - initial API and implementation
008: ******************************************************************************/package net.refractions.udig.ui;
009:
010: import java.lang.ref.Reference;
011: import java.lang.ref.ReferenceQueue;
012: import java.lang.ref.WeakReference;
013: import java.util.ArrayList;
014: import java.util.Collections;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Set;
020:
021: import org.eclipse.jface.resource.ImageDescriptor;
022: import org.eclipse.swt.SWT;
023: import org.eclipse.swt.graphics.Image;
024: import org.eclipse.swt.widgets.Display;
025:
026: /**
027: * A weakly referenced cache of image descriptors to arrays of image instances
028: * (representing normal, gray and disabled images). This is used to hold images
029: * in memory while their descriptors are defined. When the image descriptors
030: * become weakly referred to, the corresponding images in the array of images
031: * will be disposed.
032: *
033: * Weak references of equivalent image descriptors are mapped to the same array
034: * of images (where equivalent descriptors are <code>equals(Object)</code>).
035: *
036: * It is recommended to use this class as a singleton, since it creates a thread
037: * for cleaning out images.
038: *
039: * It is the responsibility of the user to ensure that the image descriptors are
040: * kept around as long as the images are needed. The users of this cache should
041: * not explicitly dispose the images.
042: *
043: * Upon request of a disabled or gray image, the normal image will be created as
044: * well (if it was not already in the cache) in order to create the disabled or
045: * gray version of the image.
046: *
047: * This cache makes no guarantees on how long the cleaning process will take, or
048: * when exactly it will occur.
049: *
050: *
051: * This class may be instantiated; it is not intended to be subclassed.
052: *
053: * @since 3.1
054: */
055: public final class ImageCache {
056:
057: /**
058: * An equivalent set of weak references to equivalent descriptors. The
059: * equivalence of image descriptors is determined through
060: * <code>equals(Object)</code>.
061: *
062: * @since 3.1
063: */
064: private static final class EquivalenceSet {
065:
066: /**
067: * The equivalence set's hash code is the hash code of the first weak
068: * reference added to the set.
069: */
070: private final int equivalenceHashCode;
071:
072: /**
073: * A list of weak references to equivalent image descriptors.
074: */
075: private final ArrayList imageCacheWeakReferences;
076:
077: /**
078: * Create an equivalence set and add the weak reference to the list.
079: *
080: * @param referenceToAdd
081: * The weak reference to add to the list of weak references.
082: */
083: private EquivalenceSet(ImageCacheWeakReference referenceToAdd) {
084: imageCacheWeakReferences = new ArrayList();
085: imageCacheWeakReferences.add(referenceToAdd);
086: // The equivalence hash code will be the hash code of the first
087: // inserted weak reference
088: equivalenceHashCode = referenceToAdd.getCachedHashCode();
089: }
090:
091: /**
092: * Add a weak refrence to the equivalence set. This method assumes that
093: * the reference to add does belong in this set.
094: *
095: * @param referenceToAdd
096: * The weak reference to add.
097: * @return true if the weak reference was added to the set, and false if
098: * the reference already exists in the set.
099: */
100: public boolean addWeakReference(
101: ImageCacheWeakReference referenceToAdd) {
102: // Only add the weak reference if it does not already exist
103: ImageCacheWeakReference weakReference = null;
104: for (Iterator i = imageCacheWeakReferences.iterator(); i
105: .hasNext();) {
106: weakReference = (ImageCacheWeakReference) i.next();
107: // "referenceToAdd.get()" will not be null, but
108: // "weakReference.get()" could be null, which is ok since we
109: // should add the element since its "identity" will be removed
110: // shortly.
111: if (referenceToAdd.get() == weakReference.get()) {
112: return false;
113: }
114: }
115: imageCacheWeakReferences.add(referenceToAdd);
116: return true;
117: }
118:
119: /**
120: * Clear the weak references in this equivalence set.
121: *
122: */
123: public void clear() {
124: ImageCacheWeakReference currentReference = null;
125: for (Iterator i = imageCacheWeakReferences.iterator(); i
126: .hasNext();) {
127: currentReference = (ImageCacheWeakReference) i.next();
128: // Cleaner thread could've have cleared the reference
129: if (currentReference != null) {
130: currentReference.clear();
131: }
132: }
133: imageCacheWeakReferences.clear();
134: }
135:
136: /*
137: * (non-Javadoc)
138: *
139: * @see java.lang.Object#equals(java.lang.Object)
140: */
141: public boolean equals(Object object) {
142: // Two sets are equivalent if their descriptors
143: // are "equal"
144: ImageDescriptor reachableDescriptor = null;
145: if (!(object instanceof EquivalenceSet)) {
146: return false;
147: }
148:
149: // Retrieve an image descriptor in the set of weak references
150: // that has not been enqueued
151: reachableDescriptor = ((EquivalenceSet) object)
152: .getFirstReachableDescriptor();
153: if (reachableDescriptor == null) {
154: return false;
155: }
156: // Manipulating descriptors themselves just in case the referent
157: // gets cleaned by the time we reach this part.
158: return reachableDescriptor
159: .equals(getFirstReachableDescriptor());
160:
161: }
162:
163: /**
164: * Get a non-null image descriptor from the list of weak references to
165: * image descriptors.
166: *
167: * @return a non null image descriptor, or null if none could be found.
168: */
169: public ImageDescriptor getFirstReachableDescriptor() {
170: ImageDescriptor referent = null;
171: for (Iterator i = imageCacheWeakReferences.iterator(); i
172: .hasNext();) {
173: referent = (ImageDescriptor) ((ImageCacheWeakReference) i
174: .next()).get();
175: if (referent != null) {
176: // return descriptor itself. This way, we have a reference
177: // to it and it won't be cleared by the time we return from
178: // this method
179: return referent;
180: }
181: }
182: // no reachable descriptors found
183: return null;
184: }
185:
186: /**
187: * Return the number of items in the list of weak references.
188: *
189: * @return the number of items in the list of weak references.
190: */
191: public int getSize() {
192: return imageCacheWeakReferences.size();
193: }
194:
195: /*
196: * (non-Javadoc)
197: *
198: * @see java.lang.Object#hashCode()
199: */
200: public int hashCode() {
201: return equivalenceHashCode;
202: }
203:
204: /**
205: * Remove a hashable weak reference from the list. This method makes no
206: * assumptions as to whether the reference to remove belongs in this
207: * equivalence set or not.
208: *
209: * @param referenceToRemove
210: * The weak reference to remove.
211: * @return true if the reference was removed succesfully.
212: */
213: public boolean removeReference(
214: ImageCacheWeakReference referenceToRemove) {
215: return imageCacheWeakReferences.remove(referenceToRemove);
216: }
217:
218: }
219:
220: /**
221: * A wrapper around the weak reference to imae descriptors in order to be
222: * able to store the referrent's hash code since it will be null when
223: * enqueued.
224: *
225: * @since 3.1
226: */
227: private static final class ImageCacheWeakReference extends
228: WeakReference {
229: /**
230: * Referent's hash code since it will not be available once the
231: * reference has been enqueued.
232: */
233: private final int referentHashCode;
234:
235: /**
236: * Creates a weak reference for an image descriptor.
237: *
238: * @param referent
239: * The image descriptor. Will not be <code>null</code>.
240: * @param queue
241: * The reference queue.
242: */
243: public ImageCacheWeakReference(Object referent,
244: ReferenceQueue queue) {
245: super (referent, queue);
246: referentHashCode = referent.hashCode();
247: }
248:
249: /**
250: * The referent's cached hash code value.
251: *
252: * @return the referent's cached hash code value.
253: */
254: public int getCachedHashCode() {
255: return referentHashCode;
256: }
257:
258: }
259:
260: /**
261: * An entry in the image map, which consists of the array of images (the
262: * value), as well as the key. This allows to retrieve BOTH the key (the
263: * equivalence set) and the value (the array of images) from the map
264: * directly.
265: *
266: * @since 3.1
267: */
268: private static final class ImageMapEntry {
269: /**
270: * The array of images.
271: */
272: private final Image[] entryImages;
273:
274: /**
275: * The equivalence set.
276: */
277: private final EquivalenceSet entrySet;
278:
279: /**
280: * Create an entry that consists of the equivalence set (key) as well as
281: * the array of images.
282: *
283: * @param equivalenceSet
284: * The equivalence set.
285: * @param images
286: * The array of images.
287: */
288: public ImageMapEntry(EquivalenceSet equivalenceSet,
289: Image[] images) {
290: this .entrySet = equivalenceSet;
291: this .entryImages = images;
292: }
293:
294: /**
295: * Return the equivalence set in this entry. Should not be
296: * <code>null</code>.
297: *
298: * @return the entry set.
299: */
300: public EquivalenceSet getEquivalenceSet() {
301: return entrySet;
302: }
303:
304: /**
305: * Return the array of images in this entry. Should not be
306: * <code>null</code>.
307: *
308: * @return the array of images.
309: */
310: public Image[] getImages() {
311: return entryImages;
312: }
313:
314: }
315:
316: /**
317: * A thread for cleaning up the reference queues as the garbage collector
318: * fills them. It takes an image map and a reference queue. When an item
319: * appears in the reference queue, it uses it as a key to remove values from
320: * the map. If the value is an array of images, then the defined images in
321: * that array are is disposed. To shutdown the thread, call
322: * <code>stopCleaning()</code>.
323: *
324: * @since 3.1
325: */
326: private static class ReferenceCleanerThread extends Thread {
327:
328: /**
329: * The number of reference cleaner threads created.
330: */
331: private static int threads = 0;
332:
333: /**
334: * A marker indicating that the reference cleaner thread should exit.
335: * This is enqueued when the thread is told to stop. Any referenced
336: * enqueued after the thread is told to stop will not be cleaned up.
337: */
338: private final ImageCacheWeakReference endMarker;
339:
340: /**
341: * A map of equivalence sets to ImageMapEntry (Image[3],
342: * EquivalenceSet).
343: */
344: private final Map imageMap;
345:
346: /**
347: * The reference queue to check.
348: */
349: private final ReferenceQueue referenceQueue;
350:
351: private final StaleImages staleImages;
352:
353: /**
354: * Constructs a new instance of <code>ReferenceCleanerThread</code>.
355: *
356: * @param referenceQueue
357: * The reference queue to check for garbage.
358: * @param map
359: * Map of equivalence sets to ImageMapEntry (Image[3],
360: * EquivalenceSet).
361: */
362: private ReferenceCleanerThread(final ImageCache imageCache) {
363: super ("Reference Cleaner: " + ++threads); //$NON-NLS-1$
364:
365: this .referenceQueue = imageCache.imageReferenceQueue;
366: this .imageMap = imageCache.imageMap;
367: this .endMarker = new ImageCacheWeakReference(
368: referenceQueue, referenceQueue);
369: this .staleImages = imageCache.staleImages;
370: }
371:
372: /**
373: * Remove the reference enqueued by iterating through the set of keys in
374: * the map.
375: *
376: * @param currentReference
377: * The current reference.
378: */
379: private void removeReferenceEnqueued(
380: final ImageCacheWeakReference currentReference) {
381: EquivalenceSet currentSet = null;
382: Set keySet = imageMap.keySet();
383: Image[] images = null;
384:
385: // Ensure that the image map is locked until the removal of the
386: // reference has finished
387: synchronized (imageMap) {
388: // Traverse the set of keys to find corresponding
389: // equivalence set
390: for (Iterator i = keySet.iterator(); i.hasNext();) {
391: currentSet = (EquivalenceSet) i.next();
392: boolean removed = currentSet
393: .removeReference(currentReference);
394: if (removed) {
395: // Clean up needed since the set is now empty
396: if (currentSet.getSize() == 0) {
397: images = ((ImageMapEntry) imageMap
398: .remove(currentSet)).getImages();
399: if (images == null) {
400: throw new NullPointerException(
401: "The array of images removed from the map on clean up should not be null."); //$NON-NLS-1$
402: }
403: }
404: // break out of for loop since the reference has
405: // been removed
406: break;
407: }
408: }
409: }
410:
411: // Images need disposal
412: if (images != null) {
413: staleImages.addImagesToDispose(images);
414: // Run async to avoid deadlock from dispose
415: Display display = Display.getDefault();
416: if (display != null) {
417: display.asyncExec(new Runnable() {
418: public void run() {
419: staleImages.disposeStaleImages();
420: }
421: });
422: }
423: }
424: }
425:
426: /**
427: * Wait for new garbage. When new garbage arrives, remove it, clear it,
428: * and dispose of any corresponding images.
429: */
430: public final void run() {
431: while (true) {
432: // Get the next reference to dispose.
433: Reference reference = null;
434: // Block until a reference becomes available in the queue
435: try {
436: reference = referenceQueue.remove();
437: } catch (final InterruptedException e) {
438: // Reference will be null.
439: }
440:
441: // Check to see if we've been told to stop.
442: if (reference == endMarker) {
443: // Clean up the image map
444: break;
445: }
446:
447: // Image disposal - need to traverse the set of keys, since the
448: // image descriptor has been cleaned. No way to directly
449: // retrieve the equivalence set from the map . This could be
450: // improved (with better search/sort).
451: if (reference instanceof ImageCacheWeakReference) {
452: removeReferenceEnqueued((ImageCacheWeakReference) reference);
453: }
454:
455: // Clear the reference.
456: if (reference != null) {
457: reference.clear();
458: }
459:
460: }
461: }
462:
463: /**
464: * Tells this thread to stop trying to clean up. This is usually run
465: * when the cache is shutting down.
466: */
467: private final void stopCleaning() {
468: endMarker.enqueue();
469: }
470: }
471:
472: /**
473: * A container class to hold a list of array of images that have been
474: * identified as requiring disposal. This class was added to ensure that if
475: * the image cache's dispose method is called while the cleaner thread is in
476: * the process of cleaning images, stopping the thread will not prevent
477: * those images from being disposed. They will be disposed by the image
478: * cache's dispose method.
479: *
480: */
481: private static class StaleImages {
482: /**
483: * List of array of images the require disposal.
484: */
485: private final List staleImages;
486:
487: /**
488: * Create the list of stale images.
489: *
490: */
491: public StaleImages() {
492: staleImages = Collections.synchronizedList(new ArrayList());
493: }
494:
495: /**
496: * Add the array of images to the list of images to dispose. This is
497: * called only from the cleaner thread.
498: *
499: * @param images
500: * The array of images.
501: */
502: public void addImagesToDispose(final Image[] images) {
503: staleImages.add(images);
504: }
505:
506: /**
507: * Dispose images that require disposal.
508: *
509: */
510: public void disposeStaleImages() {
511: Image[] imagesToDispose = null;
512: // Ensure only one thread at a time accesses the stale images list
513: synchronized (staleImages) {
514: for (Iterator i = staleImages.iterator(); i.hasNext();) {
515: imagesToDispose = (Image[]) i.next();
516: for (int j = 0; j < imagesToDispose.length; j++) {
517: final Image image = imagesToDispose[j];
518: if ((image != null) && (!image.isDisposed())) {
519: image.dispose();
520: }
521: }
522: }
523: staleImages.clear();
524: }
525: }
526:
527: }
528:
529: /**
530: * Types of images supported by the image cache.
531: */
532: public static final int DISABLE = 0;
533:
534: public static final int GRAY = 1;
535:
536: public static final int REGULAR = 2;
537:
538: private static final int TYPES_OF_IMAGES = 3;
539:
540: /**
541: * The thread responsible for cleaning out images that are no longer needed.
542: * The images in Image[3] will be cleaned if the corresponding equivalence
543: * set contains no more weak references to image descriptor.
544: */
545: private final ReferenceCleanerThread imageCleaner;
546:
547: /**
548: * A map of equivalence sets to ImageMapEntry (Image[3], EquivalenceSet).
549: * The equivalence set represents a list of weakly referenced image
550: * descriptors that are equivalent ("equal"). The equivalence set will
551: * contain no duplicate image descriptor references (check for identical
552: * descriptors on addition using "==").
553: */
554: private final Map imageMap;
555:
556: /**
557: * A queue of references (<code>HashableWeakReference</code>) waiting to
558: * be garbage collected. This value is never <code>null</code>. This is
559: * the queue for <code>imageMap</code>.
560: */
561: private final ReferenceQueue imageReferenceQueue;
562:
563: /**
564: * The image to display when no image is available. This value is
565: * <code>null</code> until it is first used, and will not get disposed
566: * until the image cache itself is disposed.
567: */
568: private Image missingImage = null;
569:
570: /**
571: * Stale images that the cleaner thread might not have the opportunity to
572: * dispose. The latter images will be disposed by the image cache's dispose.
573: */
574: private StaleImages staleImages;
575:
576: /**
577: * Constructs a new instance of <code>ImageCache</code>, and starts a
578: * thread to monitor the reference queue for image clean up.
579: */
580: public ImageCache() {
581: imageMap = Collections.synchronizedMap(new HashMap());
582:
583: staleImages = new StaleImages();
584: imageReferenceQueue = new ReferenceQueue();
585: imageCleaner = new ReferenceCleanerThread(this );
586: imageCleaner.start();
587: }
588:
589: /**
590: * Constructs a new instance of <code>ImageCache</code>, and starts a
591: * thread to monitor the reference queue for image clean up. If the passed
592: * initial load capacity is negative, the image map is created with the
593: * default <code>HashMap</code> constructor.
594: *
595: * @param initialLoadCapacity
596: * Initial load capacity for the image hash map.
597: */
598: public ImageCache(final int initialLoadCapacity) {
599: if (initialLoadCapacity < 0) {
600: imageMap = Collections.synchronizedMap(new HashMap());
601: } else {
602: imageMap = Collections.synchronizedMap(new HashMap(
603: initialLoadCapacity));
604: }
605:
606: staleImages = new StaleImages();
607: imageReferenceQueue = new ReferenceQueue();
608: imageCleaner = new ReferenceCleanerThread(this );
609: imageCleaner.start();
610: }
611:
612: /**
613: * Constructs a new instance of <code>ImageCache</code>, and starts a
614: * thread to monitor the reference queue for image clean up. If the passed
615: * initial load capacity is negative or if the load factor is nonpositive,
616: * the image map is created with the default <code>HashMap</code>
617: * constructor.
618: *
619: * @param initialLoadCapacity
620: * Initial load capacity for the image hash map.
621: * @param loadFactor
622: * Load factor for the image hash map.
623: */
624: public ImageCache(final int initialLoadCapacity,
625: final float loadFactor) {
626: if (initialLoadCapacity < 0 || loadFactor <= 0) {
627: imageMap = Collections.synchronizedMap(new HashMap());
628: } else {
629: imageMap = Collections.synchronizedMap(new HashMap(
630: initialLoadCapacity, loadFactor));
631: }
632:
633: staleImages = new StaleImages();
634: imageReferenceQueue = new ReferenceQueue();
635: imageCleaner = new ReferenceCleanerThread(this );
636: imageCleaner.start();
637: }
638:
639: /**
640: * Add a new equivalence set to the imag map.
641: *
642: * @param imageDescriptor
643: * The image descriptor.
644: * @param temporaryKey
645: * The temporary key.
646: * @param typeOfImage
647: * The type of image requested.
648: * @return the requested image, or the missing image if an error occurs in
649: * the creation of the image.
650: */
651: private Image addNewEquivalenceSet(
652: final ImageDescriptor imageDescriptor,
653: EquivalenceSet equivalenceKey, int typeOfImage) {
654:
655: // Create the array of images, as well as the regular image
656: // since it will be need to create gray or disable
657: final Image[] images = new Image[TYPES_OF_IMAGES];
658: images[REGULAR] = imageDescriptor.createImage(false);
659:
660: // If the image creation fails, returns the missing image
661: if (images[REGULAR] == null) {
662: // clear the key (this will also clear the reference created)
663: equivalenceKey.clear();
664: return getMissingImage();
665:
666: }
667: if (typeOfImage == DISABLE) {
668: images[typeOfImage] = new Image(null, images[REGULAR],
669: SWT.IMAGE_DISABLE);
670: } else if (typeOfImage == GRAY) {
671: images[typeOfImage] = new Image(null, images[REGULAR],
672: SWT.IMAGE_GRAY);
673: }
674: // Add the entry to the map
675: final ImageMapEntry mapEntry = new ImageMapEntry(
676: equivalenceKey, images);
677: imageMap.put(equivalenceKey, mapEntry);
678: return images[typeOfImage];
679: }
680:
681: /**
682: * Cleans up all images in the cache. This disposes of all of the images,
683: * and drops references to them. This should only be called when the images
684: * and the image cache are no longer needed (i.e.: shutdown). Note that the
685: * image disposal is handled by the cleaner thread.
686: */
687: public final void dispose() {
688: // Clean up the missing image.
689: if ((missingImage != null) && (!missingImage.isDisposed())) {
690: missingImage.dispose();
691: missingImage = null;
692: }
693:
694: // Stop the image cleaner thread
695: imageCleaner.stopCleaning();
696: try {
697: imageCleaner.join();
698: } catch (InterruptedException e) {
699: // Interrupted
700: }
701:
702: // Clear all the references in the equivalence sets and
703: // dispose the corresponding images
704: for (Iterator imageItr = imageMap.entrySet().iterator(); imageItr
705: .hasNext();) {
706: final Map.Entry entry = (Map.Entry) imageItr.next();
707: final EquivalenceSet key = (EquivalenceSet) entry.getKey();
708: // Dispose the images if they have been created and have
709: // not been disposed yet
710: final Image[] images = ((ImageMapEntry) entry.getValue())
711: .getImages();
712: for (int i = 0; i < images.length; i++) {
713: final Image image = images[i];
714: if ((image != null) && (!image.isDisposed())) {
715: image.dispose();
716: }
717: }
718:
719: // Clear all the references in the equivalence set
720: key.clear();
721: }
722: // Clear map
723: imageMap.clear();
724:
725: // Clean up the stale images that the cleaner thread might have missed
726: staleImages.disposeStaleImages();
727: }
728:
729: /**
730: * Returns the regular image for the given image descriptor. This caches the
731: * result so that future attempts to get the image for an equivalent or
732: * identical image descriptor will only access the cache. When all
733: * references to equivalent image descriptors are dropped, the images
734: * (regular, gray and disabled) will be cleaned up if they have been
735: * created. This clean up makes no guarantees about how long or when it will
736: * take place.
737: *
738: * @param descriptor
739: * The image descriptor with which a regular image should be
740: * created; may be <code>null</code>.
741: * @return The regular image, either newly created or from the cache. This
742: * value is <code>null</code> if the image descriptor passed in is
743: * <code>null</code>. Note that a missing image will be returned
744: * if a problem occurs in the creation of the image.
745: */
746: public final Image getImage(final ImageDescriptor imageDescriptor) {
747: return getImage(imageDescriptor, REGULAR);
748: }
749:
750: /**
751: * Returns the requested image for the given image descriptor and image
752: * type. This caches the result so that future attempts to get the image for
753: * an equivalent or identical image descriptor will only access the cache.
754: * When all references to equivalent image descriptors are dropped, the
755: * images (regular, gray and disabled) will be cleaned up if they have been
756: * created. This clean up makes no guarantees about how long or when it will
757: * take place.
758: *
759: * @param descriptor
760: * The image descriptor with which the requested image should be
761: * created; may be <code>null</code>.
762: * @param typeOfImage
763: * The type of the desired image:
764: * <code>ImageCache.DISABLED</code>,
765: * <code>ImageCache.GRAY</code> or
766: * <code>ImageCache.NORMAL</code>.
767: * @return The image for the requested image type, either newly created or
768: * from the cache. This value is <code>null</code> if the image
769: * descriptor passed in is <code>null</code>, or if the image
770: * type is invalid. Note that a missing image will be returned if a
771: * problem occurs in the creation of the image.
772: */
773: public final Image getImage(final ImageDescriptor imageDescriptor,
774: final int typeOfImage) {
775: // Invalid descriptor
776: if (imageDescriptor == null) {
777: return null;
778: }
779: // Invalid type of image
780: if (typeOfImage < 0 || !(typeOfImage < TYPES_OF_IMAGES)) {
781: return null;
782: }
783: // Created a temporary key to query the image map
784: ImageCacheWeakReference referencedToAdd = new ImageCacheWeakReference(
785: imageDescriptor, imageReferenceQueue);
786: EquivalenceSet temporaryKey = new EquivalenceSet(
787: referencedToAdd);
788:
789: Image imageToReturn = null;
790:
791: // Ensure that the image map is locked until the retrieving of the image
792: // process is finished
793: synchronized (imageMap) {
794: // Retrieve the corresponding entry in the map
795: ImageMapEntry mapEntry = (ImageMapEntry) imageMap
796: .get(temporaryKey);
797: if (mapEntry != null) {
798: // The entry was found, retrieve the image from cache, or
799: // create it if it has not been created yet
800: imageToReturn = getImageFromEquivalenceSet(
801: imageDescriptor, mapEntry, referencedToAdd,
802: typeOfImage);
803: } else {
804: // The entry was not found, create it.
805: imageToReturn = addNewEquivalenceSet(imageDescriptor,
806: temporaryKey, typeOfImage);
807: }
808: }
809: return imageToReturn;
810:
811: }
812:
813: /**
814: * Retrieve the image from the cache, or create it if it has not been
815: * created yet.
816: *
817: * @param imageDescriptor
818: * The image descriptor.
819: * @param mapEntry
820: * The mape entry.
821: * @param referenceToAdd
822: * The weak reference to add.
823: * @param typeOfImage
824: * The type of image to create.
825: * @return the requested image, or the missing image if an error occurs in
826: * the creation of the image.
827: */
828: private Image getImageFromEquivalenceSet(
829: ImageDescriptor imageDescriptor, ImageMapEntry mapEntry,
830: ImageCacheWeakReference referenceToAdd, int typeOfImage) {
831:
832: final Image[] images = mapEntry.getImages();
833: final EquivalenceSet equivalenceKey = mapEntry
834: .getEquivalenceSet();
835:
836: // Add the weak reference to the equivalence set
837: boolean added = equivalenceKey.addWeakReference(referenceToAdd);
838: if (!added) {
839: // The identical reference already exists in the set, clear it
840: referenceToAdd.clear();
841: }
842: // If the type of image requested is cached
843: if (images[typeOfImage] != null) {
844: return images[typeOfImage];
845: }
846:
847: // Regular image shoudl not be null, since it gets created when the set
848: // is created
849: if (images[REGULAR] == null) {
850: throw new NullPointerException(
851: "The normal image from the equivalence set should not be null.");//$NON-NLS-1$
852: }
853:
854: if (typeOfImage == GRAY) {
855: images[typeOfImage] = new Image(null, images[REGULAR],
856: SWT.IMAGE_GRAY);
857: } else if (typeOfImage == DISABLE) {
858: images[typeOfImage] = new Image(null, images[REGULAR],
859: SWT.IMAGE_DISABLE);
860: }
861: return images[typeOfImage];
862: }
863:
864: /**
865: * Returns the image to display when no image can be found, or none is
866: * specified. This image is only disposed when the cache is disposed.
867: *
868: * @return The image to display for missing images. This value will never be
869: * <code>null</code>.
870: */
871: public final Image getMissingImage() {
872: // Ensure that the missing image is not being accessed by another thread
873: if (missingImage == null) {
874: missingImage = ImageDescriptor.getMissingImageDescriptor()
875: .createImage();
876: }
877:
878: return missingImage;
879: }
880:
881: }
|