package org.infinispan.atomic.sharded.collections;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.infinispan.atomic.AtomicObjectFactory;
import org.infinispan.atomic.Updatable;
import org.infinispan.atomic.Update;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
*
* A sorted map abstraction implemented via an ordered forest of trees.
* The ordered forest of trees is stored in variable <i>forest</i>.
* Each trees is a shared object implemented with the atomic object factory.
* It contains at most <i>threshold</i> objects.
*
* @author Pierre Sutra
* @since 7.2
*
*/
public class ShardedTreeMap<K extends Comparable<K>,V> extends Updatable implements SortedMap<K, V>
{
private static Log log = LogFactory.getLog(ShardedTreeMap.class);
private final static int DEFAULT_THRESHOLD = 1000;
private SortedMap<K,TreeMap<K,V>> forest; // the ordered forest
private int threshold; // how many entries are stored before creating a new tree in the forest.
public ShardedTreeMap(){
forest = new TreeMap<K, TreeMap<K,V>>();
threshold = DEFAULT_THRESHOLD;
}
public ShardedTreeMap(Integer threshhold){
assert threshhold>=1;
forest = new TreeMap<K, TreeMap<K,V>>();
this.threshold = threshhold;
}
@Override
public SortedMap<K, V> subMap(K k, K k2) {
SortedMap<K,V> result = new TreeMap<K, V>();
for(K key : forest.keySet()){
if (key.compareTo(k2) > 0)
break;
allocateTree(key);
result.putAll(forest.get(key).subMap(k, k2));
}
unallocateTrees();
return result;
}
@Override
public SortedMap<K, V> headMap(K toKey) {
SortedMap<K,V> result = new TreeMap<K, V>();
for(K key : forest.keySet()){
if (key.compareTo(toKey) > 0)
break;
allocateTree(key);
result.putAll(forest.get(key).headMap(toKey));
}
unallocateTrees();
return result;
}
@Override
public SortedMap<K, V> tailMap(K fromKey) {
SortedMap<K,V> result = new TreeMap<K, V>();
for(K key : forest.keySet()){
allocateTree(key);
result.putAll(forest.get(key).tailMap(fromKey));
}
unallocateTrees();
return result;
}
@Override
public K firstKey() {
if(forest.isEmpty())
return null;
allocateTree(forest.firstKey());
assert !forest.get(forest.firstKey()).isEmpty() : forest.toString();
K ret = forest.get(forest.firstKey()).firstKey();
unallocateTrees();
return ret;
}
@Override
public K lastKey() {
if(forest.isEmpty())
return null;
K last = forest.lastKey();
allocateTree(last);
if (!forest.get(last).isEmpty()) {
last = forest.get(last).lastKey();
}
unallocateTrees();
return last;
}
@Override
public int size() {
int ret = 0;
for(K v: forest.keySet()){
allocateTree(v);
ret+= forest.get(v).size();
}
unallocateTrees();
return ret;
}
@Override
public boolean isEmpty() {
return forest.isEmpty();
}
@Override
public V get(Object o) {
if (forest.isEmpty())
return null;
K last = forest.lastKey();
TreeMap<K,V> treeMap = allocateTree(last);
assert !treeMap.isEmpty();
V ret = treeMap.lastEntry().getValue();
unallocateTrees();
return ret;
}
@Update
@Override
public V put(K k, V v) {
V ret = doPut(k,v);
unallocateTrees();
return ret;
}
@Override
public boolean equals(Object o) {
return this == o;
}
@Override
public int hashCode() {
return forest.hashCode();
}
@Update
@Override
public void putAll(Map<? extends K, ? extends V> map) {
if (forest.isEmpty()){
TreeMap<K,V> treeMap = new TreeMap<K, V>(map);
int split = treeMap.size()/this.threshold+1;
K beg = treeMap.firstKey();
for(int i=0; i<split; i++){
TreeMap<K,V> sub = allocateTree(beg);
forest.put(beg,sub);
TreeMap<K,V> toAdd = new TreeMap<K, V>();
for(K k : treeMap.tailMap(beg).keySet()){
if(toAdd.size()==threshold){
beg = k;
break;
}
toAdd.put(k,treeMap.get(k));
}
sub.putAll(toAdd);
}
}else{
for(K k : map.keySet()){
doPut(k, map.get(k));
}
}
unallocateTrees();
}
@Override
public String toString(){
TreeMap<K,V> all = new TreeMap<K, V>();
for(K key : forest.keySet()){
allocateTree(key);
all.putAll(forest.get(key));
}
unallocateTrees();
return all.toString();
}
//
// MARSHALLING
//
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeObject(new ArrayList<K>(forest.keySet()));
}
@Override
public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
forest = new TreeMap<K, TreeMap<K,V>>();
for( K k : (List<K>)objectInput.readObject()){
forest.put(k,null);
}
}
//
// HELPERS
//
private V doPut(K k, V v){
log.debug("adding " + k + "=" + v);
V ret;
K key;
TreeMap<K,V> tree;
SortedMap<K,TreeMap<K,V>> headMap;
// 1 - Find the tree where to add (k,v)
headMap = forest.headMap(k);
if (!headMap.isEmpty() && allocateTree(headMap.lastKey()).size() < threshold)
key = headMap.lastKey();
else
key = k;
// 2 - add (k,v)
tree = allocateTree(key);
ret = tree.put(k,v);
log.debug("in tree "+key+" -> "+tree);
// 3 - update the forest if needed
// 3.1 - change the GLB element in the tree
if (key!=k && tree.firstKey().equals(k)) {
forest.remove(key);
forest.put(k,tree);
}
// 3.2 -split the tree if needed
if (tree.size() > threshold) {
Entry<K,V> entry = tree.lastEntry();
tree.remove(entry.getKey());
put(entry.getKey(),entry.getValue());
}
return ret;
}
private TreeMap<K,V> allocateTree(K k){
log.debug("Allocating "+k);
if(forest.get(k)==null){
TreeMap treeMap = AtomicObjectFactory.forCache(this.getCache()).getInstanceOf(
TreeMap.class, this.getKey().toString()+":"+k.toString(), true, null, false);
forest.put(k, treeMap);
log.debug("... done ");
}
return forest.get(k);
}
private void unallocateTrees(){
List<K> toUnallocate = new ArrayList<K>();
for(K k : forest.keySet()){
if(forest.get(k)!=null){
toUnallocate.add(k);
}
}
for(K k : toUnallocate){
log.debug("Unallocate "+k);
AtomicObjectFactory.forCache(this.getCache()).disposeInstanceOf(
TreeMap.class, this.getKey().toString()+":"+k.toString(), true);
forest.put(k,null);
}
}
@Override
public boolean containsKey(Object o) {
for(K k : forest.keySet()){
allocateTree(k);
if(forest.get(k).containsKey(o))
return true;
}
return false;
}
//
// NOT YET IMPLEMENTED
//
@Override
public V remove(Object o) {
throw new UnsupportedOperationException("to be implemented");
}
@Override
public void clear() {
throw new UnsupportedOperationException("to be implemented");
}
@Override
public Set<K> keySet() {
throw new UnsupportedOperationException("to be implemented");
}
@Override
public Collection<V> values() {
throw new UnsupportedOperationException("to be implemented");
}
@Override
public Set<Entry<K, V>> entrySet() {
throw new UnsupportedOperationException("to be implemented");
}
@Override
public boolean containsValue(Object o) {
throw new UnsupportedOperationException("to be implemented");
}
@Override
public Comparator<? super K> comparator() {
throw new UnsupportedOperationException("to be implemented");
}
}