// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 by R. Pito Salas // // 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: Cache.java,v 1.19 2008/04/04 14:35:44 spyromus Exp $ // package com.salas.bb.utils.uif.images; import java.awt.*; import java.io.File; import java.net.URL; import java.util.Arrays; import java.util.Comparator; import java.util.logging.Logger; /** * Cache of images. The cache is disk-based, but it also has memory map of images which are * currently loaded by someone. The cache uses separate thread for flushing of images to disk. * The maximum disk usage amount can be specified and the writer thread will check if current * saved data is not exceeding the limit after writing the next image. If the limits are exceeded * it will remove several last-used images to get back into the limits. */ public class Cache { private static final Logger LOG = Logger.getLogger(Cache.class.getName()); private File cacheFolder; private long sizeLimit; /** * Creates cache. * * @param aCacheFolder folder to use for caching. * @param aSizeLimit maximum folder contents size. */ public Cache(File aCacheFolder, long aSizeLimit) { cacheFolder = aCacheFolder; sizeLimit = aSizeLimit; if (!cacheFolder.exists()) cacheFolder.mkdir(); } /** * Gets the image from cache. * * @param url URL of the image. * * @return image or <code>NULL</code>. */ public Image get(URL url) { if (url == null || isLocal(url)) return null; Image image = null; File file = new File(cacheFolder, urlToFilename(url)); if (file.exists()) { image = ImageFetcher.loadImage(file); file.setLastModified(System.currentTimeMillis()); } return image; } private static boolean isLocal(URL url) { return url.getProtocol().equals("file"); } public File getCachedFile(URL url) { return new File(cacheFolder, urlToFilename(url)); } /** * Converts URL into cached file name. * * @param url URL. * * @return file name. */ public String urlToFilename(URL url) { int hashCode = url.toString().toLowerCase().hashCode(); return Integer.toHexString(hashCode).toUpperCase(); } /** Checks if we still in limits and removes some old files if we aren't. */ public void verifyLimits() { long size = calcSize(); if (size > sizeLimit) { removeOldEntries(size - sizeLimit); } } /** * Removes some entries to free minimum <code>size</code> number of bytes. * * @param size amount to free. */ private void removeOldEntries(long size) { File[] files = cacheFolder.listFiles(); Arrays.sort(files, new FileAccessComparator()); long leftToFree = size; for (int i = 0; leftToFree > 0 && i < files.length - 1; i++) { File file = files[i]; if (file.isFile() && file.exists()) { if (file.delete()) leftToFree -= file.length(); } } } /** * Returns the size of directory contents. * * @return size. */ private long calcSize() { long size = 0; File[] files = cacheFolder.listFiles(); for (File file : files) size += file.length(); return size; } /** * Compares files by their modification times. */ private static class FileAccessComparator implements Comparator<File> { /** * Compares two files by their modification times. * * @param f1 first file. * @param f2 second file. * * @return result. */ public int compare(File f1, File f2) { long l1 = f1.lastModified(); long l2 = f2.lastModified(); return l1 == l2 ? 0 : l1 < l2 ? -1 : 1; } } }