1 /*
2 * Copyright 2004-2008 the Seasar Foundation and the Others.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 * either express or implied. See the License for the specific language
14 * governing permissions and limitations under the License.
15 */
16 package org.seasar.eclipse.common.util;
17
18 import java.io.InputStream;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Modifier;
21 import java.net.URL;
22 import java.util.Enumeration;
23 import java.util.Locale;
24 import java.util.ResourceBundle;
25
26 import org.eclipse.jface.resource.ImageDescriptor;
27 import org.eclipse.jface.resource.ImageRegistry;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.ImageData;
30 import org.eclipse.swt.widgets.Display;
31 import org.seasar.framework.beans.BeanDesc;
32 import org.seasar.framework.beans.factory.BeanDescFactory;
33 import org.seasar.framework.exception.ResourceNotFoundRuntimeException;
34 import org.seasar.framework.log.Logger;
35 import org.seasar.framework.util.FieldUtil;
36 import org.seasar.framework.util.ResourceUtil;
37
38 /**
39 * {@link Image} オブジェクトを管理するためのユーティリティクラスです。<br />
40 * <p>
41 * 本クラスは、クラスパス上のリソースとして存在するイメージファイルを読み込み、管理する機能を提供します。</br>
42 * また、本クラスのメソッドに対してリソースのパスを指定する場合、先頭にスラッシュ(/)がついていてもいなくても同じパスとして扱います。</br>
43 * たとえば、<code>"org/seasar/uruma/images/xxxImage.png"</code> と
44 * <code>"/org/seasar/uruma/images/xxxImage.png"</code>
45 * は同じものとして扱います。
46 * </p>
47 *
48 * @author y-komori
49 */
50 public class ImageManager {
51 private static ImageRegistry imageRegistry = new ImageRegistry();
52
53 protected static final Logger logger = Logger.getLogger(ImageManager.class);
54
55 private ImageManager() {
56 }
57
58 /**
59 * {@link ImageManager} を初期化します。<br />
60 *
61 * @param display
62 * 本クラスを使用する前に呼び出してください。
63 */
64 public static void init(final Display display) {
65 imageRegistry = new ImageRegistry(display);
66 }
67
68 /**
69 * 指定されたキーで登録された画像の {@link Image} オブジェクトを返します。<br />
70 *
71 * @param key
72 * キー
73 * @return 見つかった {@link Image} オブジェクト。見つからない場合は <code>null</code>。
74 */
75 public static Image getImage(final String key) {
76 return imageRegistry.get(key);
77 }
78
79 /**
80 * 指定されたキーで登録された画像の {@link ImageDescriptor} オブジェクトを返します。<br />
81 *
82 * @param key
83 * キー
84 * @return 見つかった {@link ImageDescriptor} オブジェクト。見つからない場合は <code>null</code>。
85 */
86 public static ImageDescriptor getImageDescriptor(final String key) {
87 return imageRegistry.getDescriptor(key);
88 }
89
90 /**
91 * <code>path</code> で指定された {@link Image} オブジェクトを検索し、存在しなければクラスパスからロードします。<br />
92 * <p>
93 * 本メソッドでは、まず <code>path</code> をキーと見なしてレジストリから {@link Image}
94 * オブジェクトを検索します。 {@link Image} オブジェクトが見つからない場合、<code>path</code>
95 * で示されるリソースをクラスパスからロードして <code>path</code> をキーとしてレジストリに登録します。<br />
96 * この際、<code>path</code> は <code>/</code>(スラッシュ)で始まっていてもいなくても構いません。</br>
97 * </p>
98 * <dl>
99 * <dt>【例】
100 * <dd> loadImage("icons/app.png");
101 * </dl>
102 * <ul>
103 * <li>まず、レジストリに対して <code>icons/app.png</code> というキーで <code>Image</code>
104 * オブジェクトを検索します。
105 * <li>見つからない場合、クラスパスから <code>icons/app.png</code> というリソースをロードします。
106 * <li>ロードに成功すれば、<code>icons/app.png</code> というキーでレジストリに登録します。
107 * <li>ここで見つからない場合は、{@link ResourceNotFoundRuntimeException} をスローします。
108 * </ul>
109 *
110 * @param path
111 * イメージのパス/キー
112 * @return 見つかった {@link Image} オブジェクト
113 * @throws ResourceNotFoundRuntimeException
114 * 指定されたリソースが見つからなかった場合
115 */
116 public static Image loadImage(final String path) {
117 Image image = getImage(path);
118 if (image == null) {
119 image = putImage(path, path);
120 }
121 return image;
122 }
123
124 /**
125 * <code>path</code> で指定された {@link ImageDescriptor}
126 * オブジェクトを検索し、存在しなければクラスパスからロードします。<br />
127 * <p>
128 * {@link Image} オブジェクトではなく {@link ImageDescriptor} オブジェクトを返すという点を除き、本メソッドは
129 * {@link #loadImage(String)} メソッドと同じです。<br />
130 * 詳細は {@link #loadImage(String)} メソッドの説明をご覧ください。
131 * </p>
132 *
133 * @param path
134 * イメージのパス/キー
135 * @return 見つかった {@link ImageDescriptor} オブジェクト
136 * @throws ResourceNotFoundRuntimeException
137 * 指定されたリソースが見つからなかった場合
138 */
139 public static ImageDescriptor loadImageDescriptor(final String path) {
140 ImageDescriptor descriptor = getImageDescriptor(path);
141 if (descriptor == null) {
142 descriptor = putImageDescriptor(path, path);
143 }
144 return descriptor;
145 }
146
147 /**
148 * {@link Image} オブジェクトを登録します。<br />
149 * <p>
150 * <code>path</code> で示されるリソースをクラスパス上から読み込み、<code>key</code>
151 * で示されるキーでレジストリに登録します。<br />
152 * 既に同じキーで {@link Image} オブジェクトが登録されている場合、上書きします。</br>
153 * </p>
154 *
155 * @param key
156 * キー
157 * @param path
158 * イメージのパス
159 * @return 登録した {@link Image} オブジェクト
160 * @throws ResourceNotFoundRuntimeException
161 * 指定されたリソースが見つからなかった場合
162 */
163 public static Image putImage(final String key, final String path) {
164 checkKey(key);
165
166 InputStream is = ResourceUtil.getResourceAsStream(normalizePath(path));
167 Image image = new Image(Display.getCurrent(), is);
168 imageRegistry.put(key, image);
169 return image;
170 }
171
172 /**
173 * {@link ImageData} から生成した {@link Image} オブジェクトを登録します。<br />
174 * <p>
175 * 既に同じキーで {@link Image} オブジェクトが登録されている場合、上書きします。</br>
176 * </p>
177 *
178 * @param key
179 * キー
180 * @param imageData
181 * {@link ImageData} オブジェクト
182 * @return 登録した {@link Image} オブジェクト
183 */
184 public static Image putImage(final String key, final ImageData imageData) {
185 checkKey(key);
186 Image image = new Image(Display.getCurrent(), imageData);
187 imageRegistry.put(key, image);
188 return image;
189 }
190
191 /**
192 * <code>ImageDescriptor</code> オブジェクトを登録します。<br />
193 * <p>
194 * <code>path</code> で示されるリソースをクラスパス上から読み込み、{@link ImageDescriptor}
195 * オブジェクトとして <code>key</code> で示されるキーでレジストリに登録します。<br />
196 * 既に同じキーで {@link ImageDescriptor} オブジェクトが登録されている場合、上書きします。<br />
197 * </p>
198 *
199 * @param key
200 * キー
201 * @param path
202 * リソースのパス
203 * @return 登録した {@link ImageDescriptor} オブジェクト
204 * @throws ResourceNotFoundRuntimeException
205 * 指定されたリソースが見つからなかった場合
206 */
207 public static ImageDescriptor putImageDescriptor(final String key,
208 final String path) {
209 checkKey(key);
210
211 URL url = ResourceUtil.getResource(normalizePath(path));
212 ImageDescriptor descriptor = ImageDescriptor.createFromURL(url);
213 imageRegistry.put(key, descriptor);
214 return descriptor;
215 }
216
217 /**
218 * {@link ResourceBundle} からイメージを読み込み、一括登録します。<br />
219 * <p>
220 * 「key=path」の形式で記述されたプロパティファイルを元にした {@link ResourceBundle} から {@link Image}
221 * オブジェクトを一括して読み込みます。
222 * </p>
223 * <p>
224 * 本メソッドではイメージを {@link ImageDescriptor} として登録します。
225 * </p>
226 *
227 * <p>
228 * コーディング例
229 * </p>
230 *
231 * <pre>
232 * ResourceBundle imageResources = ResourceBundle.getBundle("urumaImages");
233 * ImageManager.loadImages(imageResources);
234 * </pre>
235 *
236 * @param bundle
237 * リソースバンドルの参照
238 */
239 public static void loadImages(final ResourceBundle bundle) {
240 Enumeration keys = bundle.getKeys();
241 while (keys.hasMoreElements()) {
242 String key = (String) keys.nextElement();
243 String path = bundle.getString(key);
244 putImageDescriptor(key, path);
245 }
246 }
247
248 /**
249 * 指定したクラスローダの {@link ResourceBundle} からイメージを読み込み、一括登録します。<br />
250 * <p>
251 * 「key=path」の形式で記述されたプロパティファイルを元にした {@link ResourceBundle} から {@link Image}
252 * オブジェクトを一括して読み込みます。
253 * </p>
254 * <p>
255 * 本メソッドではイメージを {@link ImageDescriptor} として登録します。
256 * </p>
257 *
258 * @param baseName
259 * リソースバンドルの基底名
260 * @param loader
261 * リソースバンドルを読み込むクラスローダ
262 */
263 public static void loadImages(final String baseName,
264 final ClassLoader loader) {
265 ResourceBundle imageResources = ResourceBundle.getBundle(baseName,
266 Locale.getDefault(), loader);
267 loadImages(imageResources);
268 }
269
270 /**
271 * {@link ResourceBundle} からイメージを読み込み、一括登録します。<br />
272 * <p>
273 * 「key=path」の形式で記述されたプロパティファイルを元にした {@link ResourceBundle} から {@link Image}
274 * オブジェクトを一括して読み込みます。
275 * </p>
276 * <p>
277 * 本メソッドではイメージを {@link ImageDescriptor} として登録します。
278 * </p>
279 *
280 * @param baseName
281 * リソースバンドルの基底名
282 */
283 public static void loadImages(final String baseName) {
284 ResourceBundle imageResources = ResourceBundle.getBundle(baseName);
285 loadImages(imageResources);
286 }
287
288 /**
289 * 指定されたクラスの定数フィールドに対して、 {@link ImageManager} が 管理するオブジェクトをインジェクションします。</br>
290 * インジェクション対象となるのは、以下の条件を満たすフィールドです。<br />
291 * <p>
292 * <ol>
293 * <li><code>public static</code> な定数フィールドであること
294 * <li>{@link Image} または {@link ImageDescriptor} 型のフィールドであること
295 * </ol>
296 * </p>
297 * 以上の条件を満たすフィールドに対して、フィールド名をキーとして {@link ImageManager} が登録する {@link Image}
298 * または {@link ImageDescriptor} を検索し、見つかればインジェクションを行います。<br />
299 * 見つからなかった場合は、Warning ログを出力します。
300 * <p>
301 * <b>【例】</b><br />
302 * 以下の例では、<code>ImageHolder</code> クラスの フィールド、<code>IMAGE_A</code> と
303 * <code>IMAGE_B</code> に対して、 {@link ImageManager} が管理するオブジェクトの中から、<code>IMAGE_A</code>、<code>IMAGE_B</code>
304 * という名前のキーで登録されたオブジェクトをインジェクションします。
305 *
306 * <pre>
307 * public class ImageHolder() {
308 * public static Image IMAGE_A;
309 * public static ImageDescriptor IMAGE_B;
310 * }
311 * </pre>
312 * <pre>
313 * ImageManager.injectImages(ImageHolder.class);
314 * </pre>
315 *
316 * </p>
317 *
318 * @param clazz
319 * 対象クラス
320 */
321 public static void injectImages(final Class clazz) {
322 BeanDesc beanDesc = BeanDescFactory.getBeanDesc(clazz);
323 for (int i = 0; i < beanDesc.getFieldSize(); i++) {
324 Field field = beanDesc.getField(i);
325 String key = field.getName();
326 if (!validateMask(field)) {
327 continue;
328 }
329
330 if (isAssignableFrom(Image.class, field)) {
331 injectField(clazz, field, imageRegistry.get(key));
332 } else if (isAssignableFrom(ImageDescriptor.class, field)) {
333 injectField(clazz, field, imageRegistry.getDescriptor(key));
334 }
335 }
336 }
337
338 /**
339 * {@link ImageManager} が管理する {@link ImageRegistry} を破棄します。<br />
340 * <p>
341 * 再び {@link ImageManager} を使用したい場合、{@link #init(Display)} メソッドを呼び出してください。<br />
342 * </p>
343 */
344 public static void dispose() {
345 if (imageRegistry != null) {
346 imageRegistry.dispose();
347 }
348 imageRegistry = null;
349 }
350
351 protected static void injectField(final Class clazz, final Field field,
352 final Object o) {
353 if (o != null) {
354 FieldUtil.set(field, null, o);
355 }
356 }
357
358 protected static boolean validateMask(final Field field) {
359 final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
360 final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
361 return (field.getModifiers() & MOD_MASK) == MOD_EXPECTED;
362 }
363
364 protected static boolean isAssignableFrom(final Class<?> clazz,
365 final Field target) {
366 return clazz.isAssignableFrom(target.getType());
367 }
368
369 protected static void checkKey(final String key) {
370 if (imageRegistry.get(key) != null) {
371 imageRegistry.remove(key);
372 }
373 }
374
375 protected static String normalizePath(final String path) {
376 if ((path != null) && (path.startsWith("/"))) {
377 if (path.length() > 1) {
378 return path.substring(1);
379 }
380 return "";
381 }
382 return path;
383 }
384 }