/*
 * Copyright (C) 2008-2015 by Holger Arndt
 *
 * This file is part of the Universal Java Matrix Package (UJMP).
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * UJMP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * UJMP 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with UJMP; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package org.ujmp.core.collections.map;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.ujmp.core.interfaces.Erasable;
import org.ujmp.core.util.SerializationUtil;
import org.ujmp.core.util.StringUtil;
import org.ujmp.core.util.io.FileUtil;

public abstract class AbstractDiskMap extends AbstractMap implements Erasable {
	private static final long serialVersionUID = -8615077389159395747L;

	private File path = null;

	private boolean useGZip = true;

	private int maxDepth = 10;

	public AbstractDiskMap(File path, boolean useGZip) throws IOException {
		this.useGZip = useGZip;
		this.path = path;
	}

	public final File getPath() {
		if (path == null) {
			try {
				path = File.createTempFile("diskmap" + System.nanoTime(), "");
				path.delete();
				if (!path.exists()) {
					path.mkdirs();
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		return path;
	}

	public synchronized final int size() {
		return FileUtil.countFiles(getPath());
	}

	private final File getFileNameForKey(Object o) throws IOException {
		String key;
		String suffix = ".dat";
		if (useGZip) {
			suffix += ".gz";
		}
		if (o instanceof String && StringUtil.isSuitedAsFilename((String) o)
				&& !((String) o).contains(".")) {
			key = (String) o;
		} else {
			suffix = ".obj" + suffix;
			key = StringUtil.reverse(StringUtil.encodeToHex((Serializable) o));
		}
		StringBuilder result = new StringBuilder();
		result.append(getPath().getAbsolutePath());
		result.append(File.separator);
		for (int i = 0; i < maxDepth && i < key.length() - 1; i++) {
			char c = key.charAt(i);
			result.append(c);
			result.append(File.separator);
		}
		result.append(key);
		result.append(suffix);
		return new File(result.toString());
	}

	public synchronized final V remove(Object key) {
		try {
			V v = get(key);
			File file = getFileNameForKey(key);
			if (file.exists()) {
				file.delete();
			}
			return v;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public synchronized final boolean containsKey(Object key) {
		try {
			File file = getFileNameForKey(key);
			if (file == null) {
				return false;
			}
			return file.exists();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// TODO: better with an Iterator
	public final Set keySet() {
		try {
			Set set = new HashSet();
			listFilesToSet(getPath(), set);
			return set;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void listFilesToSet(File path, Set set) throws ClassNotFoundException, IOException {
		File[] files = path.listFiles();
		if (files != null) {
			for (File f : files) {
				if (f.isDirectory()) {
					listFilesToSet(f, set);
				} else {
					set.add(getKeyForFile(f));
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	private K getKeyForFile(File file) throws ClassNotFoundException, IOException {
		String filename = file.getName();
		if (filename.endsWith(".gz")) {
			filename = filename.substring(0, filename.length() - 3);
		}
		if (filename.endsWith(".dat")) {
			filename = filename.substring(0, filename.length() - 4);
		}
		if (filename.endsWith(".obj")) {
			filename = filename.substring(0, filename.length() - 4);
			filename = StringUtil.reverse(filename);
			return (K) SerializationUtil.deserialize(StringUtil.decodeFromHex(filename));
		} else {
			return (K) filename;
		}
	}

	public final synchronized void clear() {
		try {
			erase();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public final void erase() throws IOException {
		FileUtil.deleteRecursive(path);
	}

	public final void setPath(File path) {
		this.path = path;
	}

	public final synchronized V put(K key, V value) {
		try {
			if (key == null) {
				return null;
			}

			File file = getFileNameForKey(key);

			if (value == null && file.exists()) {
				file.delete();
				return null;
			}

			if (!file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}

			FileOutputStream fo = new FileOutputStream(file);

			OutputStream bo = new BufferedOutputStream(fo);
			if (useGZip) {
				bo = new GZIPOutputStream(bo, 8192);
			}

			writeValue(bo, value);

			bo.close();
			fo.close();

			return null;
		} catch (Exception e) {
			throw new RuntimeException("could not put object " + key, e);
		}
	}

	public final synchronized V get(Object key) {
		try {
			File file = getFileNameForKey(key);
			if (file == null || !file.exists()) {
				return null;
			}

			V o = null;

			FileInputStream fi = new FileInputStream(file);
			InputStream bi = new BufferedInputStream(fi);
			if (useGZip) {
				bi = new GZIPInputStream(bi, 8192);
			}

			o = readValue(bi);

			bi.close();
			fi.close();

			return o;
		} catch (Exception e) {
			throw new RuntimeException("could not get object " + key, e);
		}
	}

	public abstract void writeValue(OutputStream os, V value);

	public abstract V readValue(InputStream is);

}

 

Ads help maintain this website.