package au.com.newint.newinternationalist; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; /** * Created by pix on 27/02/15. */ public class FileCacheStreamFactory extends CacheStreamFactory{ static HashMap<String, FileCacheStreamFactory> instances; static { instances = new HashMap<>(); } private final File cacheFile; FileCacheStreamFactory(File cacheFile, CacheStreamFactory fallback) { this(cacheFile,fallback,null); } FileCacheStreamFactory(File cacheFile, CacheStreamFactory fallback, String name) { super(fallback, name==null?"file":name); this.cacheFile = cacheFile; } static FileCacheStreamFactory createIfNecessary(File cacheFile, CacheStreamFactory fallback) { return FileCacheStreamFactory.createIfNecessary(cacheFile, fallback, null); } // only create one FileCSF for each file url static FileCacheStreamFactory createIfNecessary(File cacheFile, CacheStreamFactory fallback, String name) { String key = null; try { key = cacheFile.getCanonicalPath(); } catch (IOException e) { Log.e("FileCacheStreamFactory", "IOError in cacheFile.getCanonicalPath()"); return null; //e.printStackTrace(); } FileCacheStreamFactory fileCacheStreamFactory = instances.get(key); if (fileCacheStreamFactory==null) { fileCacheStreamFactory = new FileCacheStreamFactory(cacheFile, fallback, name); Helpers.debugLog("FileCacheStreamFactory", "creating new cache ("+fileCacheStreamFactory.hashCode()+") for: "+key); instances.put(key,fileCacheStreamFactory); } return fileCacheStreamFactory; } @Override public String toString() { return "FileCacheStreamFactory["+cacheFile.getName()+(cacheFile.exists()?":"+cacheFile.length():":missing")+"]"; } @Override protected InputStream createCacheInputStream() { //TODO: set a flag on completion, now that FileCSF's should be shared... but what to do when it's incomplete? block? if (cacheFile.exists() && cacheFile.length()>0) { Helpers.debugLog("FileCacheStreamFactory("+cacheFile.getName()+":"+this.hashCode()+")", "createInputStream() cache hit "+cacheFile.length()+" bytes"); try { return new FileInputStream(cacheFile); } catch (FileNotFoundException e) { Helpers.debugLog("FileCacheStreamFactory", "no cache file yet"); } } Helpers.debugLog("FileCacheStreamFactory("+cacheFile.getName()+")", "createInputStream() cache miss"); return null; } class PartialFileOutputStream extends FileOutputStream { final File partialFile; final File completedFile; // workaround to store references to partial/completed file private PartialFileOutputStream(File completedFile, File partialFile) throws FileNotFoundException { super(partialFile); this.partialFile = partialFile; this.completedFile = completedFile; } public PartialFileOutputStream(File file) throws FileNotFoundException { this(file, new File(file.getPath() + ".part")); } @Override public void close() throws IOException { super.close(); // close the partial file output stream if(partialFile.exists()) { if (completedFile.exists()) { Helpers.debugLog("PartialFileOS", "deleting " + completedFile + " before renaming " + partialFile); completedFile.delete(); } if (!partialFile.renameTo(completedFile)) { throw (new IOException("Error renaming '" + partialFile + "' (" + (completedFile.exists() ? "exists" : "missing") + ") to '" + completedFile + "' (" + (completedFile.exists() ? "exists" : "missing") + ")")); } } else { Helpers.debugLog("PartialFileOS","close() called but '"+partialFile+"' does not exist."); } } } @Override protected OutputStream createCacheOutputStream() { Helpers.debugLog("FileCacheStreamFactory("+cacheFile.getName()+")", "createCacheOutputStream()"); try { return new PartialFileOutputStream(cacheFile); } catch (FileNotFoundException e) { Log.e("FileCacheStreamFactory", "error creating cache file"); } return null; } @Override protected void invalidateCache() { try { Helpers.debugLog("FileCacheStreamFactory:"+this.hashCode(), "deleting cache file: "+cacheFile.getCanonicalPath()); } catch (IOException e) { Helpers.debugLog("FileCacheStreamFactory:"+this.hashCode(), "deleting cache file: ...can't get path"); } cacheFile.delete(); } }