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 }