Mercurial > projects > dwt-addons
diff dwtx/jface/resource/ResourceManager.d @ 8:a3ff22a98bef
Dialog
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 29 Mar 2008 01:25:27 +0100 |
parents | |
children | 6c14e54dfc11 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/resource/ResourceManager.d Sat Mar 29 01:25:27 2008 +0100 @@ -0,0 +1,406 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.jface.resource.ResourceManager; + +import dwtx.jface.resource.DeviceResourceDescriptor; +import dwtx.jface.resource.ImageDescriptor; +import dwtx.jface.resource.ColorDescriptor; +import dwtx.jface.resource.FontDescriptor; +import dwtx.jface.resource.DeviceResourceException; +import dwtx.jface.resource.RGBColorDescriptor; + +// import java.util.ArrayList; +import tango.util.collection.model.Seq; +import tango.util.collection.ArraySeq; + +import dwt.DWTException; +import dwt.graphics.Color; +import dwt.graphics.Device; +import dwt.graphics.Font; +import dwt.graphics.Image; +import dwt.graphics.RGB; +import dwtx.core.runtime.Assert; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.Status; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * This class manages DWT resources. It manages reference-counted instances of resources + * such as Fonts, Images, and Colors, and allows them to be accessed using descriptors. + * Everything allocated through the registry should also be disposed through the registry. + * Since the resources are shared and reference counted, they should never be disposed + * directly. + * <p> + * ResourceManager handles correct allocation and disposal of resources. It differs from + * the various JFace *Registry classes, which also map symbolic IDs onto resources. In + * general, you should use a *Registry class to map IDs onto descriptors, and use a + * ResourceManager to convert the descriptors into real Images/Fonts/etc. + * </p> + * + * @since 3.1 + */ +public abstract class ResourceManager { + + /** + * List of Runnables scheduled to run when the ResourceManager is disposed. + * null if empty. + */ + private Seq!(Runnable) disposeExecs = null; + + /** + * Returns the Device for which this ResourceManager will create resources + * + * @since 3.1 + * + * @return the Device associated with this ResourceManager + */ + public abstract Device getDevice(); + + /** + * Returns the resource described by the given descriptor. If the resource already + * exists, the reference count is incremented and the exiting resource is returned. + * Otherwise, a new resource is allocated. Every call to this method should have + * a corresponding call to {@link #destroy(DeviceResourceDescriptor)}. + * + * <p>If the resource is intended to live for entire lifetime of the resource manager, + * a subsequent call to {@link #destroy(DeviceResourceDescriptor)} may be omitted and the + * resource will be cleaned up when the resource manager is disposed. This pattern + * is useful for short-lived {@link LocalResourceManager}s, but should never be used + * with the global resource manager since doing so effectively leaks the resource.</p> + * + * <p>The resources returned from this method are reference counted and may be shared + * internally with other resource managers. They should never be disposed outside of the + * ResourceManager framework, or it will cause exceptions in other code that shares + * them. For example, never call {@link dwt.graphics.Resource#dispose()} + * on anything returned from this method.</p> + * + * <p>Callers may safely downcast the result to the resource type associated with + * the descriptor. For example, when given an ImageDescriptor, the return + * value of this method will always be an Image.</p> + * + * @since 3.1 + * + * @param descriptor descriptor for the resource to allocate + * @return the newly allocated resource (not null) + * @throws DeviceResourceException if unable to allocate the resource + */ + public abstract Object create(DeviceResourceDescriptor descriptor); + + /** + * Deallocates a resource previously allocated by {@link #create(DeviceResourceDescriptor)}. + * Descriptors are compared by equality, not identity. If the same resource was + * created multiple times, this may decrement a reference count rather than + * disposing the actual resource. + * + * @since 3.1 + * + * @param descriptor identifier for the resource + */ + public abstract void destroy(DeviceResourceDescriptor descriptor); + + /** + * <p>Returns a previously-allocated resource or allocates a new one if none + * exists yet. The resource will remain allocated for at least the lifetime + * of this resource manager. If necessary, the resource will be deallocated + * automatically when the resource manager is disposed.</p> + * + * <p>The resources returned from this method are reference counted and may be shared + * internally with other resource managers. They should never be disposed outside of the + * ResourceManager framework, or it will cause exceptions in other code that shares + * them. For example, never call {@link dwt.graphics.Resource#dispose()} + * on anything returned from this method.</p> + * + * <p> + * Callers may safely downcast the result to the resource type associated with + * the descriptor. For example, when given an ImageDescriptor, the return + * value of this method may be downcast to Image. + * </p> + * + * <p> + * This method should only be used for resources that should remain + * allocated for the lifetime of the resource manager. To allocate shorter-lived + * resources, manage them with <code>create</code>, and <code>destroy</code> + * rather than this method. + * </p> + * + * <p> + * This method should never be called on the global resource manager, + * since all resources will remain allocated for the lifetime of the app and + * will be effectively leaked. + * </p> + * + * @param descriptor identifier for the requested resource + * @return the requested resource. Never null. + * @throws DeviceResourceException if the resource does not exist yet and cannot + * be created for any reason. + * + * @since 3.3 + */ + public final Object get(DeviceResourceDescriptor descriptor) { + Object result = find(descriptor); + + if (result is null) { + result = create(descriptor); + } + + return result; + } + + /** + * <p>Creates an image, given an image descriptor. Images allocated in this manner must + * be disposed by {@link #destroyImage(ImageDescriptor)}, and never by calling + * {@link Image#dispose()}.</p> + * + * <p> + * If the image is intended to remain allocated for the lifetime of the ResourceManager, + * the call to destroyImage may be omitted and the image will be cleaned up automatically + * when the ResourceManager is disposed. This should only be done with short-lived ResourceManagers, + * as doing so with the global manager effectively leaks the resource. + * </p> + * + * @since 3.1 + * + * @param descriptor descriptor for the image to create + * @return the Image described by this descriptor (possibly shared by other equivalent + * ImageDescriptors) + * @throws DeviceResourceException if unable to allocate the Image + */ + public final Image createImage(ImageDescriptor descriptor) { + // Assertion added to help diagnose client bugs. See bug #83711 and bug #90454. + Assert.isNotNull(descriptor); + + return cast(Image)create(descriptor); + } + + /** + * Creates an image, given an image descriptor. Images allocated in this manner must + * be disposed by {@link #destroyImage(ImageDescriptor)}, and never by calling + * {@link Image#dispose()}. + * + * @since 3.1 + * + * @param descriptor descriptor for the image to create + * @return the Image described by this descriptor (possibly shared by other equivalent + * ImageDescriptors) + */ + public final Image createImageWithDefault(ImageDescriptor descriptor) { + if (descriptor is null) { + return getDefaultImage(); + } + + try { + return cast(Image) create(descriptor); + } catch (DeviceResourceException e) { + Policy.getLog().log( + new Status(IStatus.WARNING, "dwtx.jface", 0, //$NON-NLS-1$ + "The image could not be loaded: " ~ descriptor.toString, //$NON-NLS-1$ + e)); + return getDefaultImage(); + } catch (DWTException e) { + Policy.getLog().log( + new Status(IStatus.WARNING, "dwtx.jface", 0, //$NON-NLS-1$ + "The image could not be loaded: " ~ descriptor.toString, //$NON-NLS-1$ + e)); + return getDefaultImage(); + } + } + + /** + * Returns the default image that will be returned in the event that the intended + * image is missing. + * + * @since 3.1 + * + * @return a default image that will be returned in the event that the intended + * image is missing. + */ + protected abstract Image getDefaultImage(); + + /** + * Undoes everything that was done by {@link #createImage(ImageDescriptor)}. + * + * @since 3.1 + * + * @param descriptor identifier for the image to dispose + */ + public final void destroyImage(ImageDescriptor descriptor) { + destroy(descriptor); + } + + /** + * Allocates a color, given a color descriptor. Any color allocated in this + * manner must be disposed by calling {@link #destroyColor(ColorDescriptor)}, + * or by an eventual call to {@link #dispose()}. {@link Color#dispose()} must + * never been called directly on the returned color. + * + * @since 3.1 + * + * @param descriptor descriptor for the color to create + * @return the Color described by the given ColorDescriptor (not null) + * @throws DeviceResourceException if unable to create the color + */ + public final Color createColor(ColorDescriptor descriptor) { + return cast(Color)create(descriptor); + } + + /** + * Allocates a color, given its RGB value. Any color allocated in this + * manner must be disposed by calling {@link #destroyColor(RGB)}, + * or by an eventual call to {@link #dispose()}. {@link Color#dispose()} must + * never been called directly on the returned color. + * + * @since 3.1 + * + * @param descriptor descriptor for the color to create + * @return the Color described by the given ColorDescriptor (not null) + * @throws DeviceResourceException if unable to create the color + */ + public final Color createColor(RGB descriptor) { + return createColor(new RGBColorDescriptor(descriptor)); + } + + /** + * Undoes everything that was done by a call to {@link #createColor(RGB)}. + * + * @since 3.1 + * + * @param descriptor RGB value of the color to dispose + */ + public final void destroyColor(RGB descriptor) { + destroyColor(new RGBColorDescriptor(descriptor)); + } + + /** + * Undoes everything that was done by a call to {@link #createColor(ColorDescriptor)}. + * + * + * @since 3.1 + * + * @param descriptor identifier for the color to dispose + */ + public final void destroyColor(ColorDescriptor descriptor) { + destroy(descriptor); + } + + /** + * Returns the Font described by the given FontDescriptor. Any Font + * allocated in this manner must be deallocated by calling disposeFont(...), + * or by an eventual call to {@link #dispose()}. The method {@link Font#dispose()} + * must never be called directly on the returned font. + * + * @since 3.1 + * + * @param descriptor description of the font to create + * @return the Font described by the given descriptor + * @throws DeviceResourceException if unable to create the font + */ + public final Font createFont(FontDescriptor descriptor) { + return cast(Font)create(descriptor); + } + + /** + * Undoes everything that was done by a previous call to {@link #createFont(FontDescriptor)}. + * + * @since 3.1 + * + * @param descriptor description of the font to destroy + */ + public final void destroyFont(FontDescriptor descriptor) { + destroy(descriptor); + } + + /** + * Disposes any remaining resources allocated by this manager. + */ + public void dispose() { + if (disposeExecs is null) { + return; + } + + // If one of the runnables throws an exception, we need to propagate it. + // However, this should not prevent the remaining runnables from being + // notified. If any runnables throw an exception, we remember one of them + // here and throw it at the end of the method. + RuntimeException foundException = null; + + Runnable[] execs = disposeExecs.toArray(); + for (int i = 0; i < execs.length; i++) { + Runnable exec = execs[i]; + + try { + exec.run(); + } catch (RuntimeException e) { + // Ensure that we propagate an exception, but don't stop notifying + // the remaining runnables. + foundException = e; + } + } + + if (foundException !is null) { + // If any runnables threw an exception, propagate one of them. + throw foundException; + } + } + + /** + * Returns a previously allocated resource associated with the given descriptor, or + * null if none exists yet. + * + * @since 3.1 + * + * @param descriptor descriptor to find + * @return a previously allocated resource for the given descriptor or null if none. + */ + public abstract Object find(DeviceResourceDescriptor descriptor); + + /** + * Causes the <code>run()</code> method of the runnable to + * be invoked just before the receiver is disposed. The runnable + * can be subsequently canceled by a call to <code>cancelDisposeExec</code>. + * + * @param r runnable to execute. + */ + public void disposeExec(Runnable r) { + Assert.isNotNull(cast(Object)r); + + if (disposeExecs is null) { + disposeExecs = new ArraySeq!(Runnable); + } + + disposeExecs.append(r); + } + + /** + * Cancels a runnable that was previously scheduled with <code>disposeExec</code>. + * Has no effect if the given runnable was not previously registered with + * disposeExec. + * + * @param r runnable to cancel + */ + public void cancelDisposeExec(Runnable r) { + Assert.isNotNull(cast(Object)r); + + if (disposeExecs is null) { + return; + } + + disposeExecs.remove(r); + + if (disposeExecs.drained()) { + disposeExecs = null; + } + } +}