001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.io; 031 032import java.io.File; 033import java.io.IOException; 034import java.io.PrintWriter; 035import java.util.Scanner; 036 037 038import cern.colt.Arrays; 039 040 041/** 042 * In its simplest form this function constructs an object using the construct found for the objects 043 * specified. This works with all java classes. If the class also happens to be cachable, the property 044 * org.openimaj.cache.dir is use (or $HOME/.OIcache) to save the class. The object instance is saved in 045 * $CACHE_DIR/classpackage/classname/unique-name where unique-name is taken from cachable. 046 * 047 * If cachable objects already exist they are read in using {@link IOUtils#read(java.io.File)}. 048 * 049 * Once created, cachable objects are saved using either {@link IOUtils#writeASCII(java.io.File, WriteableASCII)} or 050 * {@link IOUtils#writeBinary(java.io.File, WriteableBinary)} 051 * 052 * 053 * @author Sina Samangooei (ss@ecs.soton.ac.uk) 054 * 055 */ 056public class Cache { 057 058 private final static String cacheProp = "org.openimaj.cache.dir"; 059 private static final String CACHE_DIR_NAME = ".OIcache"; 060 061 /** 062 * load an instance using {@link #load(Object, Class, boolean)} with class as instance#getClass. The cache lookup is not 063 * skipped. 064 * @param <T> 065 * @param instance 066 * @return a loaded instance 067 */ 068 public static <T> T load(T instance){ 069 @SuppressWarnings("unchecked") 070 T ret = (T) load(instance,instance.getClass(),false); 071 return ret; 072 } 073 074 /** 075 * Create an instance of the clazz for the objects (for the constructor). 076 * If the class creates {@link Cachable} instances, an attempt is made to load 077 * the instance from the Cache. 078 * 079 * @param <T> The type of the object returned 080 * @param clazz the class which to get a cached instance of 081 * @param objects the parameters used to instantiate and index the cached object 082 * @return an instance of the clazz 083 */ 084 public static <T> T load(Class<? extends T> clazz, Object ... objects ) { 085 return load(clazz,false,objects); 086 } 087 088 /** 089 * Create an instance of the clazz for the objects (for the constructor). 090 * If the class creates {@link Cachable} instances, an attempt is made to load 091 * the instance from the Cache. 092 * 093 * @param <T> The type of the object returned 094 * @param clazz the class which to get a cached instance of 095 * @param objects the parameters used to instantiate and index the cached object 096 * @return an instance of the clazz 097 */ 098 public static <T> T loadSkipCache(Class<? extends T> clazz, Object ... objects ) { 099 return load(clazz,true,objects); 100 } 101 102 /** 103 * Clear the cache entry for a given clazz and a set of constructors 104 * @param <T> 105 * @param clazz 106 * @param objects 107 */ 108 public static <T extends Cachable> void clear(Class<T> clazz, Object ... objects) { 109 T instance = createInstance(clazz,objects); 110 File location = constructCachedLocation(instance, clazz); 111 FileUtils.deleteRecursive(location); 112 } 113 114 /** 115 * Clear the cache entry for a given cachable object 116 * @param instance 117 */ 118 public static void clear(Cachable instance) { 119 File location = constructCachedLocation(instance, instance.getClass()); 120 FileUtils.deleteRecursive(location); 121 } 122 123 /** 124 * @param <T> The type of the object returned 125 * @param clazz the class which to get a cached instance of 126 * @param skipcache force the cache to be skipped, for a new instance to be returned 127 * @param objects the parameters used to instantiate and index the cached object 128 * @return an instance of the clazz 129 */ 130 private static <T> T load(Class<? extends T> clazz, boolean skipcache, Object ... objects ) { 131 T instance = createInstance(clazz,objects); 132 return load(instance,clazz,skipcache); 133 } 134 135 /** 136 * @param <T> The type of the object returned 137 * @param instance the instance to attempt to load from the cache if the instance is {@link Cachable} 138 * @param clazz the class which to get a cached instance of 139 * @param skipcache force the cache to be skipped, for a new instance to be returned 140 * @return an instance of the clazz 141 */ 142 @SuppressWarnings("unchecked") 143 public static <T> T load(T instance, Class<? extends T> clazz, boolean skipcache) { 144 if(instance instanceof Cachable && !skipcache){ 145 Cachable cinstance = (Cachable)instance; 146 File cachedLocation = constructCachedLocation(cinstance,clazz); 147 try{ 148 // if it exists load it! 149 if(cachedLocation.exists()){ 150 if(cinstance instanceof ReadWriteableASCII){ 151 ReadWriteableASCII rwinstance = cacheFromASCII((ReadWriteableASCII)cinstance,cachedLocation); 152 instance = (T) rwinstance; 153 } 154 else if(cinstance instanceof ReadWriteableBinary){ 155 ReadWriteableBinary rwinstance = cacheFromBinary((ReadWriteableBinary)cinstance,cachedLocation); 156 instance = (T) rwinstance; 157 } 158 } 159 // if not, create the parent directory and save it! 160 else{ 161 if(!cachedLocation.getParentFile().exists() && !cachedLocation.getParentFile().mkdirs()){ 162 throw new IOException("Couldn't create cache directory!"); 163 } 164 if(cinstance instanceof ReadWriteableASCII){ 165 IOUtils.writeASCII(cachedLocation, (ReadWriteableASCII) instance); 166 } 167 else if(cinstance instanceof ReadWriteableBinary){ 168 IOUtils.writeBinary(cachedLocation, (ReadWriteableBinary) instance); 169 } 170 } 171 172 } 173 catch(Exception e){ 174 e.printStackTrace(); 175 System.err.println("Error reading or writing object from cache"); 176 } 177 } 178 return instance; 179 } 180 181 private static File constructCachedLocation(Cachable cinstance, Class<?> clazz) { 182 String cdir = System.getProperty(cacheProp); 183 if(cdir == null || cdir.equals("")) 184 cdir = System.getProperty("user.home") + "/" + CACHE_DIR_NAME; 185 186 String pname = clazz.getPackage().getName(); 187 String cname = clazz.getName(); 188 String uname = cinstance.identifier(); 189 File cachedLocation = new File(cdir,String.format("%s/%s/%s",pname,cname,uname)); 190 return cachedLocation; 191 } 192 193 private static <T> T createInstance(Class<T> clazz, Object ... objects) { 194 Class<?>[] classes = new Class<?>[objects.length]; 195 int i = 0; 196 for (Object object : objects) { 197 classes[i++] = object.getClass(); 198 } 199 T instance = null; 200 try { 201 instance = clazz.getConstructor(classes).newInstance(objects); 202 } catch (Exception e) { 203 System.err.format("Error finding constructor for class %s with classes %s\n",clazz.toString(),Arrays.toString(classes)); 204 } 205 return instance; 206 } 207 208 @SuppressWarnings("unchecked") 209 private static <T extends ReadWriteableASCII> T cacheFromASCII(T rwascii,File f) throws IOException { 210 return IOUtils.read(f, (Class<T>)rwascii.getClass()); 211 } 212 213 @SuppressWarnings("unchecked") 214 private static <T extends ReadWriteableBinary > T cacheFromBinary(T rwbin,File f) throws IOException { 215 return IOUtils.read(f, (Class<T>)rwbin.getClass()); 216 } 217 218 static class CachableStringInteger implements CachableASCII{ 219 int integer; 220 String string; 221 222 public CachableStringInteger(){ 223 224 } 225 226 public CachableStringInteger(Integer integer, String string) { 227 this.integer = integer; 228 this.string = string; 229 } 230 231 @Override 232 public void readASCII(Scanner in) throws IOException { 233 this.integer = Integer.parseInt(in.nextLine()); 234 this.string = in.nextLine(); 235 } 236 237 @Override 238 public String asciiHeader() { 239 return "STRING_INT\n"; 240 } 241 242 @Override 243 public void writeASCII(PrintWriter out) throws IOException { 244 out.println(integer); 245 out.println(string); 246 } 247 248 @Override 249 public String identifier() { 250 return String.format("%d-%s",integer,string); 251 } 252 253 @Override 254 public String toString() { 255 return identifier(); 256 } 257 } 258 259 /** 260 * @param args 261 */ 262 public static void main(String[] args) { 263 String a = load(String.class,"wang"); 264 System.out.println("This is the created string: " + a); 265 CachableStringInteger csi = load(CachableStringInteger.class,1,"wang"); 266 System.out.println("Got this from the cache.create():" + csi); 267 } 268 269}