OSDN Git Service

e4e0fe9562d70d37d44fc948cd74916472c0c87f
[charactermanaj/CharacterManaJ.git] / src / charactermanaj / graphics / io / ImageCache.java
1 package charactermanaj.graphics.io;\r
2 \r
3 import java.awt.image.BufferedImage;\r
4 import java.lang.ref.Reference;\r
5 import java.lang.ref.ReferenceQueue;\r
6 import java.lang.ref.SoftReference;\r
7 import java.util.HashMap;\r
8 import java.util.logging.Level;\r
9 import java.util.logging.Logger;\r
10 \r
11 \r
12 /**\r
13  * 画像のキャッシュ.<br>\r
14  * キャッシュは自動的にガベージコレクタにより回収されます.<br>\r
15  * ただし、{@link #unlockImages()}が呼び出されるまで、{@link #set(Object, BufferedImage)}されたイメージは\r
16  * ガベージコレクトの対象にはなりません。\r
17  * @author seraphy\r
18  *\r
19  * @param <K>\r
20  */\r
21 public class ImageCache<K> {\r
22 \r
23         private static final Logger logger = Logger.getLogger(ImageCache.class.getName());\r
24 \r
25         private static final ImageCacheMBeanImpl imageCacheMBean = ImageCacheMBeanImpl.getSingleton();\r
26 \r
27         private HashMap<K, BufferedImageWithKeyReference<K>> lockedImages\r
28                 = new HashMap<K, BufferedImageWithKeyReference<K>>();\r
29 \r
30         private ReferenceQueue<LoadedImage> queue = new ReferenceQueue<LoadedImage>();\r
31 \r
32         private HashMap<K, BufferedImageWithKeyReference<K>> caches\r
33                 = new HashMap<K, BufferedImageWithKeyReference<K>>();\r
34 \r
35         public ImageCache() {\r
36             imageCacheMBean.incrementInstance();\r
37         }\r
38 \r
39         @Override\r
40         protected void finalize() throws Throwable {\r
41             clear();\r
42         imageCacheMBean.decrementInstance();\r
43             super.finalize();\r
44         }\r
45 \r
46         public LoadedImage get(K key) {\r
47                 if (key == null) {\r
48                         return null;\r
49                 }\r
50                 synchronized (caches) {\r
51                         BufferedImageWithKeyReference<K> ref = caches.get(key);\r
52                         LoadedImage img = null;\r
53                         if (ref != null) {\r
54                                 img = ref.get();\r
55                         }\r
56                         imageCacheMBean.incrementReadCount(img != null);\r
57                         sweep();\r
58                         return img;\r
59                 }\r
60         }\r
61 \r
62         public void set(K key, LoadedImage img) {\r
63                 if (key == null) {\r
64                         return;\r
65                 }\r
66                 synchronized (caches) {\r
67                     // 現在キャッシュされているものがあれば、いったん解放する.\r
68             BufferedImageWithKeyReference<K> ref = caches.get(key);\r
69             if (ref != null) {\r
70                 ref.enqueue();\r
71             }\r
72 \r
73             if (img == null) {\r
74                                 if (logger.isLoggable(Level.FINE)) {\r
75                                         logger.log(Level.FINE, "remove cache: " + key);\r
76                                 }\r
77                                 caches.remove(key);\r
78 \r
79                         } else {\r
80                                 BufferedImageWithKeyReference<K> cacheData = new BufferedImageWithKeyReference<K>(key, img, queue);\r
81                                 lockedImages.put(key, cacheData);\r
82                                 caches.put(key, cacheData);\r
83 \r
84                                 imageCacheMBean.cacheIn(cacheData.getImageSize());\r
85                         }\r
86 \r
87             // 解放済みのアイテムエントリを除去する.\r
88             sweep();\r
89                 }\r
90         }\r
91 \r
92         public void unlockImages() {\r
93                 synchronized (caches) {\r
94                         lockedImages.clear();\r
95                         sweep();\r
96                 }\r
97         }\r
98 \r
99         /**\r
100          * すべてのエントリをキャッシュアウトしてクリアする.\r
101          */\r
102         public void clear() {\r
103             synchronized (caches) {\r
104             lockedImages.clear();\r
105                 for (BufferedImageWithKeyReference<K> ref : caches.values()) {\r
106                     ref.enqueue();\r
107                 }\r
108             sweep();\r
109             caches.clear();\r
110             }\r
111         }\r
112 \r
113         public void sweep() {\r
114                 synchronized (caches) {\r
115                         // ガベージコレクト済みアイテムを除去する\r
116                         Reference<?> ref = null;\r
117                         boolean removed = false;\r
118                         while ((ref = queue.poll()) != null) {\r
119                                 @SuppressWarnings("unchecked")\r
120                                 BufferedImageWithKeyReference<K> r =\r
121                                     (BufferedImageWithKeyReference<K>) ref;\r
122                                 K key = r.getKey();\r
123                                 if (key != null) {\r
124                                         if (caches.get(key).get() == null) {\r
125                                                 if (logger.isLoggable(Level.FINE)) {\r
126                                                         logger.log(Level.FINE, "removed cache: " + key);\r
127                                                 }\r
128                                                 removed = true;\r
129                                                 caches.remove(key);\r
130                                         }\r
131                                 }\r
132 \r
133                                 int imageSize = r.getImageSize();\r
134                                 imageCacheMBean.cacheOut(imageSize);\r
135                         }\r
136                         if (removed) {\r
137                                 if (logger.isLoggable(Level.FINE)) {\r
138                                         logger.log(Level.FINE,\r
139                                                         "cache[" + Integer.toHexString(this.hashCode())\r
140                                                                         + "] size:" + caches.size());\r
141                                 }\r
142                         }\r
143                 }\r
144         }\r
145 }\r
146 \r
147 /**\r
148  * キー情報つきSoftReference\r
149  * @author seraphy\r
150  *\r
151  * @param <K> キー\r
152  */\r
153 class BufferedImageWithKeyReference<K> extends SoftReference<LoadedImage> {\r
154 \r
155         private final K key;\r
156 \r
157         private final int imageSize;\r
158 \r
159         public BufferedImageWithKeyReference(K key, LoadedImage img, ReferenceQueue<? super LoadedImage> queue) {\r
160                 super(img, queue);\r
161                 this.key = key;\r
162                 this.imageSize = (img == null) ? 0 : img.getImageSize();\r
163         }\r
164 \r
165         public K getKey() {\r
166                 return key;\r
167         }\r
168 \r
169         public int getImageSize() {\r
170         return imageSize;\r
171     }\r
172 }\r