]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/map/DiskTileCacher.java
40216f8c1fe532ea1a5f49a3ae3f49c8061019b8
[GpsPrune.git] / tim / prune / gui / map / DiskTileCacher.java
1 package tim.prune.gui.map;
2
3 import java.awt.Image;
4 import java.awt.Toolkit;
5 import java.awt.image.ImageObserver;
6 import java.io.File;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.net.URL;
11
12 /**
13  * Class to control the reading and saving of map tiles
14  * to a cache on disk
15  */
16 public class DiskTileCacher implements Runnable
17 {
18         /** URL to get image from */
19         private URL _url = null;
20         /** File to save image to */
21         private File _file = null;
22         /** Observer to be notified */
23         private ImageObserver _observer = null;
24         /** Time limit to cache images for */
25         private static final long CACHE_TIME_LIMIT = 20 * 24 * 60 * 60 * 1000; // 20 days in ms
26
27         /**
28          * Private constructor
29          * @param inUrl URL to get
30          * @param inFile file to save to
31          */
32         private DiskTileCacher(URL inUrl, File inFile, ImageObserver inObserver)
33         {
34                 _url = inUrl;
35                 _file = inFile;
36                 _observer = inObserver;
37                 new Thread(this).start();
38         }
39
40         /**
41          * Get the specified tile from the disk cache
42          * @param inBasePath base path to whole disk cache
43          * @param inTilePath relative path to requested tile
44          * @param inCheckAge true to check age of file, false to ignore
45          * @return tile image if available, or null if not there
46          */
47         public static Image getTile(String inBasePath, String inTilePath, boolean inCheckAge)
48         {
49                 if (inBasePath == null) {return null;}
50                 File tileFile = new File(inBasePath, inTilePath);
51                 Image image = null;
52                 if (tileFile.exists() && tileFile.canRead() && tileFile.length() > 0) {
53                         long fileStamp = tileFile.lastModified();
54                         if (!inCheckAge || ((System.currentTimeMillis()-fileStamp) < CACHE_TIME_LIMIT))
55                         {
56                                 try {
57                                         image = Toolkit.getDefaultToolkit().createImage(tileFile.getAbsolutePath());
58                                 }
59                                 catch (Exception e) {}
60                         }
61                 }
62                 return image;
63         }
64
65         /**
66          * Save the specified image tile to disk
67          * @param inUrl url to get image from
68          * @param inBasePath base path to disk cache
69          * @param inTilePath relative path to this tile
70          * @param inObserver observer to inform when load complete
71          * @return true if successful, false for failure
72          */
73         public static boolean saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
74         {
75                 // TODO: Check that these are getting blocked properly
76                 if (inBasePath == null || inTilePath == null) {return false;}
77                 // save file if possible
78                 File basePath = new File(inBasePath);
79                 if (!basePath.exists() || !basePath.isDirectory() || !basePath.canWrite()) {
80                         // Can't write to base path
81                         return false;
82                 }
83                 File tileFile = new File(basePath, inTilePath);
84                 // Check if this file is already being loaded
85                 if (isBeingLoaded(tileFile)) {return true;}
86
87                 File dir = tileFile.getParentFile();
88                 // Start a new thread to load the image if necessary
89                 if ((dir.exists() || dir.mkdirs()) && dir.canWrite())
90                 {
91                         new DiskTileCacher(inUrl, tileFile, inObserver);
92                         return true;
93                 }
94                 return false; // couldn't write the file
95         }
96
97         /**
98          * Check whether the given tile is already being loaded
99          * @param inFile desired file
100          * @return true if temporary file with this name exists
101          */
102         private static boolean isBeingLoaded(File inFile)
103         {
104                 File tempFile = new File(inFile.getAbsolutePath() + ".temp");
105                 return tempFile.exists();
106         }
107
108         /**
109          * Run method for loading URL asynchronously and saving to file
110          */
111         public void run()
112         {
113                 boolean finished = false;
114                 InputStream in = null;
115                 FileOutputStream out = null;
116                 File tempFile = new File(_file.getAbsolutePath() + ".temp");
117                 // Use a synchronized block across all threads to make sure this url is only fetched once
118                 synchronized (DiskTileCacher.class) {
119                         if (tempFile.exists()) {return;}
120                         try {
121                                 if (!tempFile.createNewFile()) {return;}
122                         }
123                         catch (Exception e) {return;}
124                 }
125                 try
126                 {
127                         // Open streams from URL and to file
128                         out = new FileOutputStream(tempFile);
129                         in = _url.openStream();
130                         int d = 0;
131                         // Loop over each byte in the stream (maybe buffering is more efficient?)
132                         while ((d = in.read()) >= 0) {
133                                 out.write(d);
134                         }
135                         finished = true;
136                 } catch (IOException e) {}
137                 finally
138                 {
139                         // clean up files
140                         try {in.close();} catch (Exception e) {} // ignore
141                         try {out.close();} catch (Exception e) {} // ignore
142                         if (!finished) {
143                                 tempFile.delete();
144                         }
145                 }
146                 // Move temp file to desired file location
147                 if (!tempFile.renameTo(_file))
148                 {
149                         // File couldn't be moved - delete both to be sure
150                         tempFile.delete();
151                         _file.delete();
152                 }
153                 // Tell parent that load is finished (parameters ignored)
154                 _observer.imageUpdate(null, ImageObserver.ALLBITS, 0, 0, 0, 0);
155         }
156 }