package nl.thanod.cassandra.alpha.map;
import java.util.*;
import javax.imageio.spi.ServiceRegistry;
import nl.thanod.cassandra.CassandraConstants;
import nl.thanod.cassandra.bytes.ByteTranslator;
import org.apache.cassandra.thrift.*;
import org.apache.cassandra.thrift.Cassandra.Client;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
public final class ColumnFamilyMap<K, V> implements Map<K, V> {
protected static final byte[] EMPTY = new byte[0];
public static int FETCH_SIZE = 100;
protected final Client client;
protected final String keyspace;
protected final String key;
protected final String column_family;
protected final ConsistencyLevel consistency_level;
protected final ByteTranslator valueTranslator;
protected final ByteTranslator keyTranslator;
protected final boolean reversed;
public ColumnFamilyMap(final Class<K> keyType, final Class<V> valType, final Cassandra.Client client, final String keyspace, final String column_family, final String key, final ConsistencyLevel consistency_level) {
this(keyType, valType, client, keyspace, column_family, key, consistency_level, false);
}
public ColumnFamilyMap(final Class<K> keyType, final Class<V> valType, final Cassandra.Client client, final String keyspace, final String column_family, final String key, final ConsistencyLevel consistency_level, boolean reversed) {
Iterator<ByteTranslator> list = ServiceRegistry.lookupProviders(ByteTranslator.class);
ByteTranslator trans = null;
ByteTranslator valtrans = null;
ByteTranslator keytrans = null;
while (list.hasNext()) {
trans = list.next();
if (trans.canTranslate(valType))
valtrans = trans;
if (trans.canTranslate(keyType))
keytrans = trans;
}
if (valtrans == null)
throw new RuntimeException("No translator found for " + valType.getCanonicalName());
if (keytrans == null)
throw new RuntimeException("No translator found for " + keyType.getCanonicalName());
this.client = client;
this.key = key;
this.keyspace = keyspace;
this.column_family = column_family;
this.consistency_level = consistency_level;
this.keyTranslator = keytrans;
this.valueTranslator = valtrans;
this.reversed = reversed;
}
@Override
public void clear() {
try {
client.remove(keyspace, key, new ColumnPath(column_family), CassandraConstants.getTime(), consistency_level);
} catch (Throwable ball) {
// InvalidRequestException, UnavailableException, TimedOutException, TException
throw new RuntimeException("Unrecoverable exception " + ball.getClass().getCanonicalName(), ball);
}
}
@Override
public boolean containsKey(Object o) {
try {
ColumnPath path = new ColumnPath(column_family);
path.column = ObjectToByteArray(o);
ColumnOrSuperColumn cosc = client.get(keyspace, key, path, consistency_level);
return cosc.column != null;
} catch (NotFoundException ball) {
return false;
} catch (Throwable ball) {
throw new RuntimeException("Error while looking for the appearance of " + o, ball);
}
}
@Override
public boolean containsValue(Object paramObject) {
throw new UnsupportedOperationException("Unable to performe with a Cassandra Backend");
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new EntrySet<K, V>(this);
}
@Override
public V get(Object o) {
try {
ColumnPath path = new ColumnPath(column_family);
path.column = ObjectToByteArray(o);
ColumnOrSuperColumn cosc = client.get(keyspace, key, path, consistency_level);
return (V) valueTranslator.getObject(cosc.column.value);
} catch (NotFoundException ball) {
return null;
} catch (Throwable ball) {
throw new RuntimeException("Error while fetching object stored under " + o, ball);
}
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public Set<K> keySet() {
throw new NotImplementedException();
}
@Override
public V put(K name, V value) {
try {
ColumnPath column_path = new ColumnPath(column_family);
column_path.column = keyTranslator.getBytes(name);
client.insert(keyspace, key, column_path, valueTranslator.getBytes(value), CassandraConstants.getTime(), consistency_level);
return value;
} catch (Throwable ball) {
throw new RuntimeException("Unable to recover from " + ball.getClass().getCanonicalName(), ball);
}
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
for (Entry<? extends K, ? extends V> e : map.entrySet())
put(e.getKey(), e.getValue());
}
@Override
public V remove(Object o) {
V val = get(o);
if (val == null)
return null;
try {
ColumnPath column_path = new ColumnPath(column_family);
column_path.column = keyTranslator.getBytes(o);
client.remove(keyspace, key, column_path, CassandraConstants.getTime(), consistency_level);
return val;
} catch (Throwable ball) {
throw new RuntimeException("Unable to recover from " + ball.getClass().getCanonicalName(), ball);
}
}
@Override
public int size() {
try {
return client.get_count(keyspace, key, new ColumnParent(column_family), consistency_level);
} catch (Throwable ball) {
throw new RuntimeException("Unable to recover from " + ball.getClass().getCanonicalName(), ball);
}
}
@Override
public Collection<V> values() {
throw new NotImplementedException();
}
public static byte[] ObjectToByteArray(Object o) {
Class<?> type = o.getClass();
Iterator<ByteTranslator> list = ServiceRegistry.lookupProviders(ByteTranslator.class);
while (list.hasNext()) {
ByteTranslator trans = list.next();
if (trans.canTranslate(type))
return trans.getBytes(o);
}
return null;
}
static class EntrySet<K, V> implements Set<Entry<K, V>> {
private final ColumnFamilyMap<K, V> parent;
public EntrySet(ColumnFamilyMap<K, V> parent) {
this.parent = parent;
}
@Override
public boolean add(java.util.Map.Entry<K, V> paramE) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends java.util.Map.Entry<K, V>> paramCollection) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
parent.clear();
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Entry))
return false;
Entry<?, ?> e = (Entry) o;
return parent.containsKey(e.getKey());
}
@Override
public boolean containsAll(Collection<?> paramCollection) {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return parent.isEmpty();
}
@Override
public Iterator<java.util.Map.Entry<K, V>> iterator() {
return new EntryIterator<K, V>(this.parent);
}
@Override
public boolean remove(Object paramObject) {
throw new NotImplementedException();
}
@Override
public boolean removeAll(Collection<?> oo) {
for (Object o : oo)
if (!remove(o))
return false;
return true;
}
@Override
public boolean retainAll(Collection<?> paramCollection) {
throw new NotImplementedException();
}
@Override
public int size() {
return parent.size();
}
@Override
public Object[] toArray() {
throw new NotImplementedException();
}
@Override
public <T> T[] toArray(T[] paramArrayOfT) {
throw new NotImplementedException();
}
}
static class EntryIterator<K, V> implements Iterator<Entry<K, V>> {
private final ColumnFamilyMap<K, V> parent;
Queue<Entry<K, V>> buffer = new LinkedList<Entry<K, V>>();
private Entry<K, V> working = null;
private byte[] last = EMPTY;
private boolean finished = false;
public EntryIterator(ColumnFamilyMap<K, V> parent) {
this.parent = parent;
}
@Override
public boolean hasNext() {
if (buffer.size() == 0)
fillBuffer();
return buffer.size() > 0;
}
private void fillBuffer() {
if (finished)
return;
try {
ColumnParent column_parent = new ColumnParent(parent.column_family);
SlicePredicate predicate = new SlicePredicate();
predicate.slice_range = new SliceRange(last, EMPTY, parent.reversed, ColumnFamilyMap.FETCH_SIZE);
List<ColumnOrSuperColumn> lcosc = parent.client.get_slice(parent.keyspace, parent.key, column_parent, predicate, parent.consistency_level);
if (!Arrays.equals(EMPTY, last)) {
if (lcosc.size() > 0)
lcosc.remove(0);
}
for (ColumnOrSuperColumn cosc : lcosc) {
if (cosc.column == null)
throw new RuntimeException("Expected Columns instead of SuperColumns");
buffer.add(new ColumnEntry<K, V>(parent, (K) parent.keyTranslator.getObject(cosc.column.name), (V) parent.valueTranslator.getObject(cosc.column.value)));
last = cosc.column.name;
}
if (lcosc.size() == 0)
finished = true;
} catch (Throwable ball) {
throw new RuntimeException("Unable to recover from " + ball.getClass().getCanonicalName(), ball);
}
}
@Override
public java.util.Map.Entry<K, V> next() {
if (hasNext()) {
this.working = buffer.poll();
return this.working;
}
return null;
}
@Override
public void remove() {
if (this.working != null)
parent.remove(this.working.getKey());
}
}
static class ColumnEntry<K, V> implements Entry<K, V> {
private final Map<K, V> parent;
private final K key;
private V value;
public ColumnEntry(Map<K, V> parent, K key, V value) {
this.parent = parent;
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
return this.value = parent.put(getKey(), value);
}
}
}