package ninja.ugly.prevail.chunk; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.Maps; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Map; import ninja.ugly.prevail.exception.DeleteException; import ninja.ugly.prevail.exception.InsertException; import ninja.ugly.prevail.exception.QueryException; import ninja.ugly.prevail.exception.UpdateException; import static com.google.common.base.Preconditions.checkNotNull; /** * A simple extension of DefaultChunk that stores data in memory. */ public class VolatileChunk<K, V> extends DefaultChunk<K, V> { private final Map<K, V> mMap; private final KeyFactory<K, V> mKeyFactory; /** * Constructs a new HashMapChunk that uses the given KeyFactory to generate keys * during insertion. * * @param keyFactory A KeyFactory used to create keys to insert objects under. */ public VolatileChunk(final KeyFactory<K, V> keyFactory) { this(Maps.<K,V>newHashMap(), keyFactory); } /** * Constructs a new HashMapChunk that uses the given backing Map and the given KeyFactory * to generate keys during insertion. * <p> * This implementation uses the given Map directly for storage. Changes to this Chunk * will be reflected in the given Map, and vice-versa. * * @param map The Map to use as backing storage. * @param keyFactory A KeyFactory used to create keys to insert objects under. */ public VolatileChunk(final Map<K, V> map, final KeyFactory<K, V> keyFactory) { mMap = checkNotNull(map); mKeyFactory = checkNotNull(keyFactory); } /** * Insert the given value into the backing storage. * @return The key at which the given value can be obtained. */ @Override protected K doInsert(final V value, final OnProgressUpdateListener onProgressUpdateListener) throws InsertException { final K key = mKeyFactory.createKey(value); mMap.put(key, value); return key; } /** * Query the given key from the backing storage. * @return The results */ @Override protected QueryResult<V> doQuery(final K key, final OnProgressUpdateListener onProgressUpdateListener) throws QueryException { final QueryResult<V> result; if (mMap.containsKey(key)) { result = new QueryResult.SingletonQueryResult<>(mMap.get(key)); } else { result = new QueryResult.EmptyQueryResult<>(); } return result; } /** * Update the given key in backing storage with the given value. * <p> * Some Chunk implementations might allow update of multiple values with a non-specific key. * Not this one. * @return The number of elements updated. Either 0 or 1. */ @Override protected int doUpdate(final K key, final V value, final OnProgressUpdateListener progressUpdateListener) throws UpdateException { int numUpdates = 0; if (mMap.containsKey(key)) { mMap.put(key, value); numUpdates = 1; } return numUpdates; } /** * Delete the given key from the backing storage. * <p> * Some Chunk implementations might allow update of multiple values with a non-specific key. * Not this one. * @return The number of values deleted. Either 0 or 1. */ @Override protected int doDelete(final K key, final OnProgressUpdateListener onProgressUpdateListener) throws DeleteException { int numUpdates = 0; if (mMap.containsKey(key)) { mMap.remove(key); numUpdates = 1; } return numUpdates; } @Override public String toString() { return mMap.toString(); } /** * Returns an unmodifiable collection of values from the backing map. * @return All the values in the backing map. */ protected Collection<V> getValues() { return Collections.unmodifiableCollection(mMap.values()); } @Override public void close() throws IOException { mMap.clear(); } /** * A factory class for producing keys from values. */ public static interface KeyFactory<K, V> { K createKey(V value); public static class DefaultKeyFactory<K, V> implements KeyFactory<K, V> { private final Function<V, K> mFunction; DefaultKeyFactory(final Function<V, K> function) { mFunction = function; } @Override public K createKey(final V value) { return mFunction.apply(value); } } /** * An implementation of KeyFactory that returns an auto-incrementing Integer as a key. */ public static class AutoIncrementingIntegerKeyFactory<V> extends DefaultKeyFactory<Integer, V> { AutoIncrementingIntegerKeyFactory() { super(new AutoIncrementFunction<V>()); } } /** * An implementation of KeyFactory that returns an auto-incrementing String as a key. */ public static class AutoIncrementingStringKeyFactory<V> extends DefaultKeyFactory<String, V> { public AutoIncrementingStringKeyFactory() { super(Functions.compose(Functions.toStringFunction(), new AutoIncrementFunction<V>())); } } static class AutoIncrementFunction<V> implements Function<V, Integer> { private int mCounter = 0; @Override public Integer apply(final V input) { return mCounter++; } } } }