28f6b1163f6323a8286d6f310944cffdb2a60fa3
[fw/altos] / altoslib / AltosMapCache.java
1 /*
2  * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package org.altusmetrum.altoslib_11;
19
20 import java.io.*;
21 import java.net.*;
22
23 public class AltosMapCache implements AltosMapCacheListener {
24
25         /* An entry in the MapCache */
26         class MapCacheElement implements AltosMapTileListener {
27
28                 AltosMapTile            tile;           /* Notify when image has been loaded */
29                 AltosImage              image;
30                 long                    used;
31
32                 class loader implements Runnable {
33                         public void run() {
34                                 if (image == null) {
35                                         try {
36                                                 image = map_interface.load_image(tile.store.file);
37                                         } catch (Exception ex) {
38                                         }
39                                 }
40                                 tile.notify_image(image);
41                         }
42                 }
43
44                 private void load() {
45                         loader  l = new loader();
46                         Thread  lt = new Thread(l);
47                         lt.start();
48                 }
49
50                 public void flush() {
51                         if (image != null) {
52                                 image.flush();
53                                 image = null;
54                         }
55                 }
56
57                 public boolean has_map() {
58                         return tile.status == AltosMapTile.loaded;
59                 }
60
61                 public synchronized void notify_tile(AltosMapTile tile, int status) {
62                         if (status == AltosMapTile.fetched) {
63                                 load();
64                         }
65                 }
66
67                 public MapCacheElement(AltosMapTile tile) {
68                         this.tile = tile;
69                         this.image = null;
70                         this.used = 0;
71                         tile.add_listener(this);
72                 }
73         }
74
75         int                     min_cache_size;         /* configured minimum cache size */
76         int                     cache_size;             /* current cache size */
77         int                     requested_cache_size;   /* cache size computed by application */
78
79         private Object          fetch_lock = new Object();
80         private Object          cache_lock = new Object();
81
82         AltosMapInterface       map_interface;
83
84         MapCacheElement[]       elements = new MapCacheElement[cache_size];
85
86         long                    used;
87
88         public void set_cache_size(int new_size) {
89
90                 requested_cache_size = new_size;
91
92                 if (new_size < min_cache_size)
93                         new_size = min_cache_size;
94
95                 if (new_size == cache_size)
96                         return;
97
98                 synchronized(cache_lock) {
99                         MapCacheElement[]       new_elements = new MapCacheElement[new_size];
100
101                         for (int i = 0; i < cache_size; i++) {
102                                 if (i < new_size)
103                                         new_elements[i] = elements[i];
104                                 else if (elements[i] != null)
105                                         elements[i].flush();
106                         }
107                         elements = new_elements;
108                         cache_size = new_size;
109                 }
110         }
111
112         public AltosImage get(AltosMapTile tile) {
113                 int             oldest = -1;
114                 long            age = used;
115
116                 synchronized(cache_lock) {
117                         MapCacheElement element = null;
118                         for (int i = 0; i < cache_size; i++) {
119                                 element = elements[i];
120
121                                 if (element == null) {
122                                         oldest = i;
123                                         break;
124                                 }
125                                 if (tile.store.equals(element.tile.store)) {
126                                         element.used = used++;
127                                         return element.image;
128                                 }
129                                 if (element.used < age) {
130                                         oldest = i;
131                                         age = element.used;
132                                 }
133                         }
134
135                         element = new MapCacheElement(tile);
136                         element.used = used++;
137                         if (elements[oldest] != null)
138                                 elements[oldest].flush();
139
140                         elements[oldest] = element;
141                         return element.image;
142                 }
143         }
144
145         public void map_cache_changed(int map_cache) {
146                 min_cache_size = map_cache;
147
148                 set_cache_size(requested_cache_size);
149         }
150
151         public void dispose() {
152                 AltosPreferences.unregister_map_cache_listener(this);
153
154                 for (int i = 0; i < cache_size; i++) {
155                         MapCacheElement element = elements[i];
156
157                         if (element != null)
158                             element.flush();
159                 }
160         }
161
162         public AltosMapCache(AltosMapInterface map_interface) {
163                 this.map_interface = map_interface;
164                 min_cache_size = AltosPreferences.map_cache();
165
166                 set_cache_size(0);
167
168                 AltosPreferences.register_map_cache_listener(this);
169         }
170 }