blob: 5cec9a02187fb5fe0e4acac47d28bd12b07efd8b [file] [log] [blame]
/*
* Copyright (c) 2007, 2008, 2011, 2012, 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.util.io;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* @author Eike Stepper
*/
public abstract class SortedFileMap<K extends Comparable<K>, V> implements Closeable
{
private File file;
private RandomAccessFile randomAccessFile;
private ExtendedDataInput input;
private ExtendedDataOutput output;
private long entrySize;
private long entryCount;
/**
* @see RandomAccessFile#RandomAccessFile(File, String)
*/
public SortedFileMap(File file, String mode)
{
try
{
this.file = file;
randomAccessFile = new RandomAccessFile(file, mode);
input = new DataInputExtender(randomAccessFile);
output = new DataOutputExtender(randomAccessFile);
entrySize = getKeySize() + getValueSize();
entryCount = randomAccessFile.length() / entrySize;
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
}
public void close() throws IOException
{
IOUtil.close(randomAccessFile);
}
public File getFile()
{
return file;
}
public RandomAccessFile getRandomAccessFile()
{
return randomAccessFile;
}
public long getEntryCount()
{
return entryCount;
}
public int getEntrySize()
{
return (int)entrySize;
}
public long getPosition(long index)
{
return index * entrySize;
}
public long getValuePosition(long index)
{
return getPosition(index) + getKeySize();
}
public K getMaxKey()
{
if (entryCount == 0)
{
return null;
}
return getKey(entryCount - 1);
}
public K getKey(long index)
{
try
{
long pos = getPosition(index);
randomAccessFile.seek(pos);
return readKey(input);
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
}
public V getValue(long index)
{
try
{
long pos = getValuePosition(index);
randomAccessFile.seek(pos);
return readValue(input);
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
}
public V get(K key)
{
try
{
long index = search(key);
if (index < 0)
{
return null;
}
return readValue(input);
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
}
public V put(K key, V value)
{
try
{
long index = search(key);
if (index >= 0)
{
long pos = getValuePosition(index);
randomAccessFile.seek(pos);
V oldValue = readValue(input);
randomAccessFile.seek(pos);
writeValue(output, value);
return oldValue;
}
index = -index - 1;
for (long i = entryCount; i > index; --i)
{
randomAccessFile.seek(getPosition(i - 1));
K k = readKey(input);
randomAccessFile.seek(getValuePosition(i - 1));
V v = readValue(input);
randomAccessFile.seek(getPosition(i));
writeKey(output, k);
randomAccessFile.seek(getValuePosition(i));
writeValue(output, v);
}
++entryCount;
randomAccessFile.seek(getPosition(index));
writeKey(output, key);
randomAccessFile.seek(getValuePosition(index));
writeValue(output, value);
return null;
}
catch (IOException ex)
{
throw new IORuntimeException(ex);
}
}
/**
* @return The index of the entry with the given key if the key exists, <code>-(insertionIndex + 1)</code> otherwise.
*/
protected long search(K key) throws IOException
{
long low = 0;
long high = entryCount - 1;
while (low <= high)
{
long mid = low + high >> 1;
randomAccessFile.seek(getPosition(mid));
Comparable<K> midVal = readKey(input);
int cmp = midVal.compareTo(key);
if (cmp < 0)
{
low = mid + 1;
}
else if (cmp > 0)
{
high = mid - 1;
}
else
{
return mid; // key found
}
}
return -(low + 1); // key not found.
}
public abstract int getKeySize();
protected abstract K readKey(ExtendedDataInput in) throws IOException;
protected abstract void writeKey(ExtendedDataOutput out, K key) throws IOException;
public abstract int getValueSize();
protected abstract V readValue(ExtendedDataInput in) throws IOException;
protected abstract void writeValue(ExtendedDataOutput out, V value) throws IOException;
}