001    /*
002     * Copyright 2002-2004 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.springframework.richclient.image;
017    
018    import java.awt.Image;
019    import java.io.IOException;
020    import java.util.HashMap;
021    import java.util.Map;
022    
023    import org.apache.commons.logging.Log;
024    import org.apache.commons.logging.LogFactory;
025    import org.springframework.core.io.Resource;
026    import org.springframework.core.style.StylerUtils;
027    import org.springframework.core.style.ToStringCreator;
028    import org.springframework.util.Assert;
029    import org.springframework.util.CachingMapDecorator;
030    
031    /**
032     * A collection of image resources, each indexed by a common key alias.
033     * <p>
034     * For example, <code>action.edit.copy = /images/edit/copy.gif</code>
035     * <p>
036     * This class by default performs caching of all loaded image resources using
037     * soft references (TODO it just lazy loads them, but it doesn't use
038     * SoftReference).
039     *
040     * <p>
041     * An image {@link Handler} is available that handles the 'image' protocol.
042     * Check the javadocs of the handler to know how to use/register it.
043     * </p>
044     *
045     * @author Keith Donald
046     */
047    public class DefaultImageSource implements ImageSource {
048            protected static final Log logger = LogFactory.getLog(DefaultImageSource.class);
049    
050            private Map imageResources;
051    
052            private ImageCache imageCache;
053    
054            private AwtImageResource brokenImageIndicatorResource;
055    
056            private Image brokenImageIndicator;
057    
058            /**
059             * Creates a image resource bundle containing the specified map of keys to
060             * resource paths.
061             * <p>
062             * A custom URL protocol {@link Handler handler}will be installed for the
063             * "image:" protocol. This allows for images in this image source to be
064             * located using the Java URL classes: <br>
065             * <code>URL imageUrl = new URL("image:the.image.key")</code>
066             *
067             * @param imageResources a map of key-to-image-resources.
068             */
069            public DefaultImageSource(Map imageResources) {
070                    this(true, imageResources);
071            }
072    
073            /**
074             * Creates a image resource bundle containing the specified map of keys to
075             * resource paths.
076             *
077             * @param installUrlHandler should a URL handler be installed.
078             * @param imageResources a map of key-to-image-resources.
079             */
080            public DefaultImageSource(boolean installUrlHandler, Map imageResources) {
081                    Assert.notNull(imageResources);
082                    this.imageResources = new HashMap(imageResources);
083                    debugPrintResources();
084                    this.imageCache = new ImageCache();
085                    if (installUrlHandler) {
086                            Handler.installImageUrlHandler(this);
087                    }
088            }
089    
090            private void debugPrintResources() {
091                    if (logger.isDebugEnabled()) {
092                            logger.debug("Initialing image source with resources: " + StylerUtils.style(this.imageResources));
093                    }
094            }
095    
096            public Image getImage(String key) {
097                    Assert.notNull(key);
098                    AwtImageResource resource = getImageResource(key);
099                    try {
100                            return (Image) imageCache.get(resource);
101                    }
102                    catch (RuntimeException e) {
103                            if (brokenImageIndicator != null) {
104                                    return returnBrokenImageIndicator(resource);
105                            }
106                            throw e;
107                    }
108            }
109    
110            public AwtImageResource getImageResource(String key) {
111                    Assert.notNull(key);
112                    Resource resource = (Resource) imageResources.get(key);
113                    if (resource == null) {
114                            throw new NoSuchImageResourceException(key);
115                    }
116                    try {
117                            resource.getInputStream();
118                            return new AwtImageResource(resource);
119                    }
120                    catch (IOException e) {
121                            if (brokenImageIndicatorResource == null) {
122                                    throw new NoSuchImageResourceException(resource, e);
123                            }
124                            logger.warn("Unable to load image resource at '" + resource + "'; returning the broken image indicator.");
125                            return brokenImageIndicatorResource;
126                    }
127            }
128    
129            public boolean containsKey(Object key) {
130                    return imageResources.containsKey(key);
131            }
132    
133            private Image returnBrokenImageIndicator(Resource resource) {
134                    logger.warn("Unable to load image resource at '" + resource + "'; returning the broken image indicator.");
135                    return brokenImageIndicator;
136            }
137    
138            public Image getImageAtLocation(Resource location) {
139                    try {
140                            return new AwtImageResource(location).getImage();
141                    }
142                    catch (IOException e) {
143                            if (brokenImageIndicator == null) {
144                                    throw new NoSuchImageResourceException(location, e);
145                            }
146                            return returnBrokenImageIndicator(location);
147                    }
148            }
149    
150            public int size() {
151                    return imageResources.size();
152            }
153    
154            public void setBrokenImageIndicator(Resource resource) {
155                    try {
156                            brokenImageIndicatorResource = new AwtImageResource(resource);
157                            brokenImageIndicator = brokenImageIndicatorResource.getImage();
158                    }
159                    catch (IOException e) {
160                            brokenImageIndicatorResource = null;
161                            throw new NoSuchImageResourceException(resource, e);
162                    }
163            }
164    
165            public String toString() {
166                    return new ToStringCreator(this).append("imageResources", imageResources).toString();
167            }
168    
169            private static class ImageCache extends CachingMapDecorator {
170                    public ImageCache() {
171                            super(true);
172                    }
173    
174                    public Object create(Object resource) {
175                            try {
176                                    return ((AwtImageResource) resource).getImage();
177                            }
178                            catch (IOException e) {
179                                    throw new NoSuchImageResourceException("No image found at resource '" + resource + '"', e);
180                            }
181                    }
182            }
183    }