altoslib: Fix map preloading callbacks, run in separate thread
[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_10;
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                                 System.out.printf("tile fetched, loading image\n");
64                                 load();
65                         }
66                 }
67
68                 public MapCacheElement(AltosMapTile tile) {
69                         this.tile = tile;
70                         this.image = null;
71                         this.used = 0;
72                         tile.add_listener(this);
73                 }
74         }
75
76         int                     min_cache_size;         /* configured minimum cache size */
77         int                     cache_size;             /* current cache size */
78         int                     requested_cache_size;   /* cache size computed by application */
79
80         private Object          fetch_lock = new Object();
81         private Object          cache_lock = new Object();
82
83         AltosMapInterface       map_interface;
84
85         MapCacheElement[]       elements = new MapCacheElement[cache_size];
86
87         long                    used;
88
89         public void set_cache_size(int new_size) {
90
91                 requested_cache_size = new_size;
92
93                 if (new_size < min_cache_size)
94                         new_size = min_cache_size;
95
96                 if (new_size == cache_size)
97                         return;
98
99                 synchronized(cache_lock) {
100                         MapCacheElement[]       new_elements = new MapCacheElement[new_size];
101
102                         for (int i = 0; i < cache_size; i++) {
103                                 if (i < new_size)
104                                         new_elements[i] = elements[i];
105                                 else if (elements[i] != null)
106                                         elements[i].flush();
107                         }
108                         elements = new_elements;
109                         cache_size = new_size;
110                 }
111         }
112
113         public AltosImage get(AltosMapTile tile) {
114                 int             oldest = -1;
115                 long            age = used;
116
117                 synchronized(cache_lock) {
118                         MapCacheElement element = null;
119                         for (int i = 0; i < cache_size; i++) {
120                                 element = elements[i];
121
122                                 if (element == null) {
123                                         oldest = i;
124                                         break;
125                                 }
126                                 if (tile.store.equals(element.tile.store)) {
127                                         element.used = used++;
128                                         return element.image;
129                                 }
130                                 if (element.used < age) {
131                                         oldest = i;
132                                         age = element.used;
133                                 }
134                         }
135
136                         element = new MapCacheElement(tile);
137                         element.used = used++;
138                         if (elements[oldest] != null)
139                                 elements[oldest].flush();
140
141                         elements[oldest] = element;
142                         System.out.printf("AltosMapCache.get image ? %s\n",
143                                           element.image == null ? "false" : "true");
144                         return element.image;
145                 }
146         }
147
148         public void map_cache_changed(int map_cache) {
149                 min_cache_size = map_cache;
150
151                 set_cache_size(requested_cache_size);
152         }
153
154         public void dispose() {
155                 AltosPreferences.unregister_map_cache_listener(this);
156
157                 for (int i = 0; i < cache_size; i++) {
158                         MapCacheElement element = elements[i];
159
160                         if (element != null)
161                             element.flush();
162                 }
163         }
164
165         public AltosMapCache(AltosMapInterface map_interface) {
166                 this.map_interface = map_interface;
167                 min_cache_size = AltosPreferences.map_cache();
168
169                 set_cache_size(0);
170
171                 AltosPreferences.register_map_cache_listener(this);
172         }
173 }