diff --git a/src/main/java/net/openhft/chronicle/map/FilePerKeyMap.java b/src/main/java/net/openhft/chronicle/map/FilePerKeyMap.java new file mode 100644 index 000000000..7c22f0bc9 --- /dev/null +++ b/src/main/java/net/openhft/chronicle/map/FilePerKeyMap.java @@ -0,0 +1,257 @@ +package net.openhft.chronicle.map; + +import net.openhft.chronicle.hash.function.SerializableFunction; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Created by daniel on 22/04/15. + */ +public class FilePerKeyMap implements ChronicleMap { + private String dir; + private Path dirPath; + + public FilePerKeyMap(String dir) { + try { + dirPath = Paths.get(dir); + Files.createDirectories(dirPath); + } catch (IOException e) { + throw new AssertionError(e); + } + this.dir = dir; + } + + @Override + public int size() { + return (int)getFiles(dirPath).count(); + } + + @Override + public boolean isEmpty() { + return size()==0 ? true: false; + } + + @Override + public boolean containsKey(Object key) { + return getFiles(dirPath).anyMatch(p->p.getFileName().toString().equals(key)); + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public V get(Object key) { + checkTypeIsString(key); + + Path path = Paths.get(dir,(String)key); + return (V)getFileContents(path); + } + + @Override + public V put(K key, V value) { + checkTypeIsString(key, value); + + Path path = Paths.get(dir,(String)key); + String existingValue = getFileContents(path); + writeToFile(path, (String) value); + + return (V) existingValue; + } + + private Stream getFiles(Path path){ + try { + return Files.walk(path).filter(p -> !Files.isDirectory(p)); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private String getFileContents(Path path){ + String existingValue = null; + if(Files.exists(path)) { + try { + existingValue = Files.readAllLines(path).stream().collect(Collectors.joining()); + } catch (IOException e) { + throw new AssertionError(e); + } + } + return existingValue; + } + + private void writeToFile(Path path, String value){ + try { + Files.write(path, value.getBytes(), StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.WRITE); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + private void deleteFile(Path path){ + try { + Files.delete(path); + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public V remove(Object key) { + return null; + } + + @Override + public void putAll(Map m) { + + } + + @Override + public void clear() { + getFiles(dirPath).forEach(this::deleteFile); + } + + @NotNull + @Override + public Set keySet() { + return null; + } + + @NotNull + @Override + public Collection values() { + return null; + } + + @NotNull + @Override + public Set> entrySet() { + return null; + } + + @Override + public V getUsing(K key, V usingValue) { + return null; + } + + @Override + public V acquireUsing(@NotNull K key, V usingValue) { + return null; + } + + @NotNull + @Override + public MapKeyContext acquireContext(@NotNull K key, @NotNull V usingValue) { + return null; + } + + @Override + public R getMapped(K key, @NotNull SerializableFunction function) { + return null; + } + + @Override + public V putMapped(K key, @NotNull UnaryOperator unaryOperator) { + return null; + } + + @Override + public void getAll(File toFile) throws IOException { + + } + + @Override + public void putAll(File fromFile) throws IOException { + + } + + @Override + public V newValueInstance() { + return null; + } + + @Override + public K newKeyInstance() { + return null; + } + + @Override + public Class valueClass() { + return null; + } + + @Override + public File file() { + return null; + } + + @Override + public long longSize() { + return 0; + } + + @Override + public MapKeyContext context(K key) { + return null; + } + + @Override + public Class keyClass() { + return null; + } + + @Override + public boolean forEachEntryWhile(Predicate> predicate) { + return false; + } + + @Override + public void forEachEntry(Consumer> action) { + + } + + @Override + public void close() { + + } + + @Override + public V putIfAbsent(K key, V value) { + return null; + } + + @Override + public boolean remove(Object key, Object value) { + return false; + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return false; + } + + @Override + public V replace(K key, V value) { + return null; + } + + private void checkTypeIsString(Object... objs) { + for(Object o : objs){ + if(!(o instanceof String)) + throw new AssertionError("FilePerKeyMap only accepts Key and Values " + + "of type String. Unsupported type: '" + o + "'"); + } + } +} diff --git a/src/test/java/net/openhft/chronicle/map/FilePerMapKeyTest.java b/src/test/java/net/openhft/chronicle/map/FilePerMapKeyTest.java new file mode 100644 index 000000000..21e32d748 --- /dev/null +++ b/src/test/java/net/openhft/chronicle/map/FilePerMapKeyTest.java @@ -0,0 +1,57 @@ +package net.openhft.chronicle.map; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * JUnit test class to support {@link net.openhft.chronicle.map.FilePerMapKeyTest} + */ +public class FilePerMapKeyTest { + /** + * FilePerMapKey only works with Strings + */ + @Test(expected = AssertionError.class) + public void testWrongType(){ + FilePerKeyMap map = new FilePerKeyMap<>("/tmp/filepermaptests"); + map.put(1, "test"); + } + + /** + * Testing the put + */ + @Test + public void testPut(){ + //There are no entries in the map so null should be returned + FilePerKeyMap map = new FilePerKeyMap<>("/tmp/filepermaptests"); + //just in case it hasn't been cleared up last time + map.clear(); + + String val = map.put("one", "test1"); + assertEquals(val, null); + + //After the entry has been written the value returned should be + //the previous value + val = map.put("one", "test2"); + assertEquals(val, "test1"); + + //Check that get returns the latest value + val = map.get("one"); + assertEquals("test2", val); + + assertTrue(map.containsKey("one")); + assertFalse(map.containsKey("two")); + + //Check the size is one + int size = map.size(); + assertEquals(1,size); + assertFalse(map.isEmpty()); + + map.clear(); + //Check the size is now 0 + size = map.size(); + assertEquals(0, size); + + assertTrue(map.isEmpty()); + } +}