altosui/altosuilib/altoslib: Move more stuff out of autosui. Reduce site map memory
[fw/altos] / altosuilib / AltosSiteMapCache.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.altosuilib_2;
19
20 import javax.swing.*;
21 import javax.imageio.ImageIO;
22 import java.awt.image.*;
23 import java.awt.*;
24 import java.io.*;
25 import java.net.URL;
26 import java.net.URLConnection;
27
28
29 class AltosCacheImage {
30         Component       component;
31         File            file;
32         VolatileImage   image;
33         int             width;
34         int             height;
35         long            used;
36
37         public void load_image() throws IOException {
38                 BufferedImage   bimg = ImageIO.read(file);
39                 Graphics2D      g = image.createGraphics();
40                 g.drawImage(bimg, 0, 0, null);
41                 bimg.flush();
42         }
43
44         public Image validate() {
45                 int     returnCode;
46
47                 if (image != null)
48                         returnCode = image.validate(component.getGraphicsConfiguration());
49                 else
50                         returnCode = VolatileImage.IMAGE_INCOMPATIBLE;
51                 if (returnCode == VolatileImage.IMAGE_RESTORED) {
52                         try {
53                                 load_image();
54                         } catch (IOException e) {
55                                 return null;
56                         }
57                 } else if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) {
58                         image = component.createVolatileImage(width, height);
59                         try {
60                                 load_image();
61                         } catch (IOException e) {
62                                 return null;
63                         }
64                 }
65                 return image;
66         }
67
68         public void flush() {
69                 image.flush();
70         }
71
72         public AltosCacheImage(Component component, File file, int w, int h) throws IOException {
73                 this.component = component;
74                 this.file = file;
75                 width = w;
76                 height = h;
77                 image = component.createVolatileImage(w, h);
78                 used = 0;
79         }
80 }
81
82 public class AltosSiteMapCache extends JLabel {
83         static final long google_maps_ratelimit_ms = 1200;
84         // Google limits static map queries to 50 per minute per IP, so
85         // each query should take at least 1.2 seconds.
86
87         public static boolean fetchMap(File file, String url) {
88                 if (file.exists())
89                         return true;
90
91                 URL u;
92                 long startTime = System.nanoTime();
93
94                 try {
95                         u = new URL(url);
96                 } catch (java.net.MalformedURLException e) {
97                         return false;
98                 }
99
100                 byte[] data;
101                 try {
102                         URLConnection uc = u.openConnection();
103                         int contentLength = uc.getContentLength();
104                         InputStream in = new BufferedInputStream(uc.getInputStream());
105                         int bytesRead = 0;
106                         int offset = 0;
107                         data = new byte[contentLength];
108                         while (offset < contentLength) {
109                                 bytesRead = in.read(data, offset, data.length - offset);
110                                 if (bytesRead == -1)
111                                         break;
112                                 offset += bytesRead;
113                         }
114                         in.close();
115
116                         if (offset != contentLength) {
117                                 return false;
118                         }
119                 } catch (IOException e) {
120                         return false;
121                 }
122
123                 try {
124                         FileOutputStream out = new FileOutputStream(file);
125                         out.write(data);
126                         out.flush();
127                         out.close();
128                 } catch (FileNotFoundException e) {
129                         return false;
130                 } catch (IOException e) {
131                         if (file.exists()) {
132                                 file.delete();
133                         }
134                         return false;
135                 }
136
137                 long duration_ms = (System.nanoTime() - startTime) / 1000000;
138                 if (duration_ms < google_maps_ratelimit_ms) {
139                         try {
140                                 Thread.sleep(google_maps_ratelimit_ms - duration_ms);
141                         } catch (InterruptedException e) {
142                                 Thread.currentThread().interrupt();
143                         }
144                 }
145
146                 return true;
147         }
148
149         static int                      cache_size = 9;
150
151         static AltosCacheImage[]        images;
152
153         static long                     used;
154
155         public static void set_cache_size(int cache_size) {
156                 AltosSiteMapCache.cache_size = cache_size;
157                 images = null;
158         }
159
160         public static Image get_image(Component component, File file, int width, int height) {
161                 int             oldest = -1;
162                 long            age = used;
163                 AltosCacheImage image;
164                 if (images == null)
165                         images = new AltosCacheImage[cache_size];
166                 for (int i = 0; i < cache_size; i++) {
167                         image = images[i];
168
169                         if (image == null) {
170                                 oldest = i;
171                                 break;
172                         }
173                         if (image.component == component && file.equals(image.file)) {
174                                 image.used = used++;
175                                 return image.validate();
176                         }
177                         if (image.used < age) {
178                                 oldest = i;
179                                 age = image.used;
180                         }
181                 }
182
183                 try {
184                         image = new AltosCacheImage(component, file, width, height);
185                         image.used = used++;
186                         if (images[oldest] != null)
187                                 images[oldest].flush();
188                         images[oldest] = image;
189                         return image.validate();
190                 } catch (IOException e) {
191                         return null;
192                 }
193         }
194 }