--- /dev/null
+package tim.prune.gui.map;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Class to control the reading and saving of map tiles
+ * to a cache on disk
+ */
+public class DiskTileCacher implements Runnable
+{
+ /** URL to get image from */
+ private URL _url = null;
+ /** File to save image to */
+ private File _file = null;
+ /** Observer to be notified */
+ private ImageObserver _observer = null;
+ /** Time limit to cache images for */
+ private static final long CACHE_TIME_LIMIT = 20 * 24 * 60 * 60 * 1000; // 20 days in ms
+
+ /**
+ * Private constructor
+ * @param inUrl URL to get
+ * @param inFile file to save to
+ */
+ private DiskTileCacher(URL inUrl, File inFile, ImageObserver inObserver)
+ {
+ _url = inUrl;
+ _file = inFile;
+ _observer = inObserver;
+ new Thread(this).start();
+ }
+
+ /**
+ * Get the specified tile from the disk cache
+ * @param inBasePath base path to whole disk cache
+ * @param inTilePath relative path to requested tile
+ * @param inCheckAge true to check age of file, false to ignore
+ * @return tile image if available, or null if not there
+ */
+ public static Image getTile(String inBasePath, String inTilePath, boolean inCheckAge)
+ {
+ if (inBasePath == null) {return null;}
+ File tileFile = new File(inBasePath, inTilePath);
+ Image image = null;
+ if (tileFile.exists() && tileFile.canRead() && tileFile.length() > 0) {
+ long fileStamp = tileFile.lastModified();
+ if (!inCheckAge || ((System.currentTimeMillis()-fileStamp) < CACHE_TIME_LIMIT))
+ {
+ try {
+ image = Toolkit.getDefaultToolkit().createImage(tileFile.getAbsolutePath());
+ }
+ catch (Exception e) {}
+ }
+ }
+ return image;
+ }
+
+ /**
+ * Save the specified image tile to disk
+ * @param inUrl url to get image from
+ * @param inBasePath base path to disk cache
+ * @param inTilePath relative path to this tile
+ * @param inObserver observer to inform when load complete
+ */
+ public static void saveTile(URL inUrl, String inBasePath, String inTilePath, ImageObserver inObserver)
+ {
+ if (inBasePath == null || inTilePath == null) {return;}
+ // save file if possible
+ File basePath = new File(inBasePath);
+ if (!basePath.exists() || !basePath.isDirectory() || !basePath.canWrite()) {
+ //System.err.println("Can't write");
+ // Can't write to base path
+ return;
+ }
+ File tileFile = new File(basePath, inTilePath);
+ // Check if this file is already being loaded
+ if (!isBeingLoaded(tileFile))
+ {
+ File dir = tileFile.getParentFile();
+ // Start a new thread to load the image if necessary
+ if (dir.exists() || dir.mkdirs())
+ {
+ new DiskTileCacher(inUrl, tileFile, inObserver);
+ }
+ }
+ }
+
+ /**
+ * Check whether the given tile is already being loaded
+ * @param inFile desired file
+ * @return true if temporary file with this name exists
+ */
+ private static boolean isBeingLoaded(File inFile)
+ {
+ File tempFile = new File(inFile.getAbsolutePath() + ".temp");
+ return tempFile.exists();
+ }
+
+ /**
+ * Run method for loading URL asynchronously and saving to file
+ */
+ public void run()
+ {
+ boolean finished = false;
+ InputStream in = null;
+ FileOutputStream out = null;
+ File tempFile = new File(_file.getAbsolutePath() + ".temp");
+ // Use a synchronized block across all threads to make sure this url is only fetched once
+ synchronized (DiskTileCacher.class) {
+ if (tempFile.exists()) {return;}
+ try {
+ if (!tempFile.createNewFile()) {return;}
+ }
+ catch (Exception e) {return;}
+ }
+ try {
+ // Open streams from URL and to file
+ out = new FileOutputStream(tempFile);
+ in = _url.openStream();
+ int d = 0;
+ // Loop over each byte in the stream (maybe buffering is more efficient?)
+ while ((d = in.read()) >= 0) {
+ out.write(d);
+ }
+ finished = true;
+ } catch (IOException e) {}
+ finally {
+ try {
+ in.close();
+ out.close();
+ if (!finished) {tempFile.delete();}
+ }
+ catch (Exception e) {} // ignore
+ }
+ // Move temp file to desired file location
+ if (!tempFile.renameTo(_file)) {
+ // File couldn't be moved - delete both to be sure
+ tempFile.delete();
+ _file.delete();
+ }
+ // Tell parent that load is finished (parameters ignored)
+ _observer.imageUpdate(null, ImageObserver.ALLBITS, 0, 0, 0, 0);
+ }
+}