001 /* 002 * Copyright (C) 2008-2010 by Holger Arndt 003 * 004 * This file is part of the Universal Java Matrix Package (UJMP). 005 * See the NOTICE file distributed with this work for additional 006 * information regarding copyright ownership and licensing. 007 * 008 * UJMP is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation; either version 2 011 * of the License, or (at your option) any later version. 012 * 013 * UJMP is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public 019 * License along with UJMP; if not, write to the 020 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 021 * Boston, MA 02110-1301 USA 022 */ 023 024 package org.ujmp.core.collections; 025 026 import java.io.BufferedInputStream; 027 import java.io.BufferedOutputStream; 028 import java.io.File; 029 import java.io.FileInputStream; 030 import java.io.FileOutputStream; 031 import java.io.IOException; 032 import java.io.InputStream; 033 import java.io.OutputStream; 034 import java.io.Serializable; 035 import java.util.HashSet; 036 import java.util.Set; 037 import java.util.zip.GZIPInputStream; 038 import java.util.zip.GZIPOutputStream; 039 040 import org.ujmp.core.exceptions.MatrixException; 041 import org.ujmp.core.interfaces.Erasable; 042 import org.ujmp.core.util.SerializationUtil; 043 import org.ujmp.core.util.StringUtil; 044 import org.ujmp.core.util.io.FileUtil; 045 046 public abstract class AbstractDiskMap<K, V> extends AbstractMap<K, V> implements Erasable { 047 private static final long serialVersionUID = -8615077389159395747L; 048 049 private File path = null; 050 051 private boolean useGZip = true; 052 053 private int maxDepth = 10; 054 055 public AbstractDiskMap(File path, boolean useGZip) throws IOException { 056 this.useGZip = useGZip; 057 this.path = path; 058 } 059 060 public final File getPath() { 061 if (path == null) { 062 try { 063 path = File.createTempFile("diskmap" + System.nanoTime(), ""); 064 path.delete(); 065 if (!path.exists()) { 066 path.mkdirs(); 067 } 068 } catch (Exception e) { 069 throw new MatrixException(e); 070 } 071 } 072 return path; 073 } 074 075 public synchronized final int size() { 076 return FileUtil.countFiles(getPath()); 077 } 078 079 private final File getFileNameForKey(Object o) throws IOException { 080 String key; 081 String suffix = ".dat"; 082 if (useGZip) { 083 suffix += ".gz"; 084 } 085 if (o instanceof String && StringUtil.isSuitedAsFilename((String) o) 086 && !((String) o).contains(".")) { 087 key = (String) o; 088 } else { 089 suffix = ".obj" + suffix; 090 key = StringUtil.reverse(StringUtil.encodeToHex((Serializable) o)); 091 } 092 StringBuilder result = new StringBuilder(); 093 result.append(getPath().getAbsolutePath()); 094 result.append(File.separator); 095 for (int i = 0; i < maxDepth && i < key.length() - 1; i++) { 096 char c = key.charAt(i); 097 result.append(c); 098 result.append(File.separator); 099 } 100 result.append(key); 101 result.append(suffix); 102 return new File(result.toString()); 103 } 104 105 public synchronized final V remove(Object key) { 106 try { 107 V v = get(key); 108 File file = getFileNameForKey(key); 109 if (file.exists()) { 110 file.delete(); 111 } 112 return v; 113 } catch (Exception e) { 114 throw new RuntimeException(e); 115 } 116 } 117 118 public synchronized final boolean containsKey(Object key) { 119 try { 120 File file = getFileNameForKey(key); 121 if (file == null) { 122 return false; 123 } 124 return file.exists(); 125 } catch (Exception e) { 126 throw new RuntimeException(e); 127 } 128 } 129 130 // TODO: better with an Iterator 131 public final Set<K> keySet() { 132 try { 133 Set<K> set = new HashSet<K>(); 134 listFilesToSet(getPath(), set); 135 return set; 136 } catch (Exception e) { 137 throw new RuntimeException(e); 138 } 139 } 140 141 private void listFilesToSet(File path, Set<K> set) throws ClassNotFoundException, IOException { 142 File[] files = path.listFiles(); 143 if (files != null) { 144 for (File f : files) { 145 if (f.isDirectory()) { 146 listFilesToSet(f, set); 147 } else { 148 set.add(getKeyForFile(f)); 149 } 150 } 151 } 152 } 153 154 @SuppressWarnings("unchecked") 155 private K getKeyForFile(File file) throws ClassNotFoundException, IOException { 156 String filename = file.getName(); 157 if (filename.endsWith(".gz")) { 158 filename = filename.substring(0, filename.length() - 3); 159 } 160 if (filename.endsWith(".dat")) { 161 filename = filename.substring(0, filename.length() - 4); 162 } 163 if (filename.endsWith(".obj")) { 164 filename = filename.substring(0, filename.length() - 4); 165 filename = StringUtil.reverse(filename); 166 return (K) SerializationUtil.deserialize(StringUtil.decodeFromHex(filename)); 167 } else { 168 return (K) filename; 169 } 170 } 171 172 public final synchronized void clear() { 173 try { 174 erase(); 175 } catch (Exception e) { 176 e.printStackTrace(); 177 } 178 } 179 180 public final void erase() throws IOException { 181 FileUtil.deleteRecursive(path); 182 } 183 184 public final void setPath(File path) { 185 this.path = path; 186 } 187 188 public final synchronized V put(K key, V value) { 189 try { 190 if (key == null) { 191 return null; 192 } 193 194 File file = getFileNameForKey(key); 195 196 if (value == null && file.exists()) { 197 file.delete(); 198 return null; 199 } 200 201 if (!file.getParentFile().exists()) { 202 file.getParentFile().mkdirs(); 203 } 204 205 FileOutputStream fo = new FileOutputStream(file); 206 207 OutputStream bo = new BufferedOutputStream(fo); 208 if (useGZip) { 209 bo = new GZIPOutputStream(bo, 8192); 210 } 211 212 writeValue(bo, value); 213 214 bo.close(); 215 fo.close(); 216 217 return null; 218 } catch (Exception e) { 219 throw new MatrixException("could not put object " + key, e); 220 } 221 } 222 223 public final synchronized V get(Object key) { 224 try { 225 File file = getFileNameForKey(key); 226 if (file == null || !file.exists()) { 227 return null; 228 } 229 230 V o = null; 231 232 FileInputStream fi = new FileInputStream(file); 233 InputStream bi = new BufferedInputStream(fi); 234 if (useGZip) { 235 bi = new GZIPInputStream(bi, 8192); 236 } 237 238 o = readValue(bi); 239 240 bi.close(); 241 fi.close(); 242 243 return o; 244 } catch (Exception e) { 245 throw new MatrixException("could not get object " + key, e); 246 } 247 } 248 249 public abstract void writeValue(OutputStream os, V value); 250 251 public abstract V readValue(InputStream is); 252 253 }