/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package com.bc.ceres.jai.tilecache;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The default implementation of the {@link SwapSpace} interface.
* It creates a file for each swapped tile in the given swap directory.
*
* @author Norman Fomferra
*/
public class DefaultSwapSpace implements SwapSpace {
private final File swapDir;
private final Logger logger;
private final Map<Object, SwappedTile> swappedTiles;
public DefaultSwapSpace(File swapDir) {
this(swapDir, Logger.getLogger(System.getProperty("ceres.context", "ceres")));
}
public DefaultSwapSpace(File swapDir, Logger logger) {
this.swapDir = swapDir;
this.logger = logger;
this.swappedTiles = new HashMap<Object, SwappedTile>(1009); // prime number
}
@Override
protected void finalize() throws Throwable {
super.finalize();
for (Object key : swappedTiles.keySet()) {
swappedTiles.get(key).delete();
}
}
public synchronized boolean storeTile(MemoryTile mt) {
SwappedTile st = swappedTiles.get(mt.getKey());
if (st == null) {
try {
st = new SwappedTile(mt, swapDir);
if (!st.isAvailable()) {
final long t1 = System.currentTimeMillis();
st.storeTile(mt.getTile());
final long t2 = System.currentTimeMillis();
st.getFile().deleteOnExit();
logger.log(Level.FINEST, "Tile stored: " + st.getFile() + " (" + (t2 - t1) + " ms)");
}
swappedTiles.put(mt.getKey(), st);
return true;
} catch (IOException e) {
logger.log(Level.SEVERE, "Tile NOT stored: " + st.getFile(), e);
handleTileStoreFailed(mt, e);
}
}
return false;
}
public synchronized MemoryTile restoreTile(RenderedImage owner, int tileX, int tileY) {
final Object key = hashKey(owner, tileX, tileY);
final SwappedTile st = swappedTiles.get(key);
if (st == null) {
return null;
}
try {
final long t1 = System.currentTimeMillis();
final Raster tile = st.restoreTile();
final long t2 = System.currentTimeMillis();
logger.log(Level.FINEST, "Tile restored: " + st.getFile() + " (" + (t2 - t1) + " ms)");
return new MemoryTile(owner, tileX, tileY, tile, st.getTileCacheMetric());
} catch (IOException e) {
logger.log(Level.SEVERE, "Tile NOT restored: " + st.getFile());
return handleTileRestoreFailed(owner, tileX, tileY, e);
}
}
public synchronized boolean deleteTile(RenderedImage owner, int tileX, int tileY) {
final Object key = hashKey(owner, tileX, tileY);
final SwappedTile st = swappedTiles.remove(key);
if (st == null || !st.getFile().exists()) {
return false;
}
final boolean deleted = st.delete();
if (deleted) {
logger.log(Level.FINEST, "Tile deleted: " + st.getFile());
} else {
logger.log(Level.WARNING, "Tile NOT deleted: " + st.getFile());
}
return deleted;
}
protected void handleTileStoreFailed(MemoryTile mt, IOException e) {
deleteTile(mt.getOwner(), mt.getTileX(), mt.getTileY());
}
protected MemoryTile handleTileRestoreFailed(RenderedImage owner, int tileX, int tileY, IOException e) {
return null;
}
private static Object hashKey(RenderedImage owner, int tileX, int tileY) {
return MemoryTile.hashKey(owner, tileX, tileY);
}
}