package database;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import org.mapdb.BTreeMap;
import org.mapdb.Bind;
import org.mapdb.DB;
import org.mapdb.Fun.Function2;
import org.mapdb.Fun.Tuple2;
import utils.ObserverMessage;
public abstract class DBMap<T, U> extends Observable {
protected static final int NOTIFY_ADD = 1;
protected static final int NOTIFY_REMOVE = 2;
protected static final int NOTIFY_LIST = 3;
public static final int DEFAULT_INDEX = 0;
protected DBMap<T, U> parent;
protected IDB databaseSet;
protected Map<T, U> map;
protected List<T> deleted;
private Map<Integer, NavigableSet<Tuple2<?, T>>> indexes;
public DBMap(IDB databaseSet, DB database)
{
this.databaseSet = databaseSet;
//OPEN MAP
this.map = this.getMap(database);
//CREATE INDEXES
this.indexes = new HashMap<Integer, NavigableSet<Tuple2<?, T>>>();
this.createIndexes(database);
}
public DBMap(DBMap<T, U> parent)
{
this.parent = parent;
//OPEN MAP
this.map = this.getMemoryMap();
this.deleted = new ArrayList<T>();
}
protected abstract Map<T, U> getMap(DB database);
protected abstract Map<T, U> getMemoryMap();
protected abstract U getDefaultValue();
protected abstract Map<Integer, Integer> getObservableData();
protected abstract void createIndexes(DB database);
@SuppressWarnings("unchecked")
protected <V> void createIndex(int index, NavigableSet<?> indexSet, NavigableSet<?> descendingIndexSet, Function2<V, T, U> function)
{
Bind.secondaryKey((BTreeMap<T, U>) this.map, (NavigableSet<Tuple2<V, T>>) indexSet, function);
this.indexes.put(index, (NavigableSet<Tuple2<?, T>>) indexSet);
Bind.secondaryKey((BTreeMap<T, U>) this.map, (NavigableSet<Tuple2<V, T>>) descendingIndexSet, function);
this.indexes.put(index + 10000, (NavigableSet<Tuple2<?, T>>) descendingIndexSet);
}
@SuppressWarnings("unchecked")
protected <V> void createIndexes(int index, NavigableSet<?> indexSet, NavigableSet<?> descendingIndexSet, Function2<V[], T, U> function)
{
Bind.secondaryKeys((BTreeMap<T, U>) this.map, (NavigableSet<Tuple2<V, T>>) indexSet, function);
this.indexes.put(index, (NavigableSet<Tuple2<?, T>>) indexSet);
Bind.secondaryKeys((BTreeMap<T, U>) this.map, (NavigableSet<Tuple2<V, T>>) descendingIndexSet, function);
this.indexes.put(index + 10000, (NavigableSet<Tuple2<?, T>>) descendingIndexSet);
}
public int size() {
return this.map.size();
}
public U get(T key)
{
try
{
if(this.map.containsKey(key))
{
return this.map.get(key);
}
else
{
if(this.deleted == null || !this.deleted.contains(key))
{
if(this.parent != null)
{
return this.parent.get(key);
}
}
}
return this.getDefaultValue();
}
catch(Exception e)
{
e.printStackTrace();
return this.getDefaultValue();
}
}
public Set<T> getKeys()
{
return this.map.keySet();
}
public Collection<U> getValues()
{
return this.map.values();
}
public boolean set(T key, U value)
{
try
{
U old = this.map.put(key, value);
if(this.deleted != null)
{
this.deleted.remove(key);
}
//COMMIT
if(this.databaseSet != null)
{
this.databaseSet.commit();
}
//NOTIFY ADD
if(this.getObservableData().containsKey(NOTIFY_ADD))
{
this.setChanged();
this.notifyObservers(new ObserverMessage(this.getObservableData().get(NOTIFY_ADD), value));
}
//NOTIFY LIST
if(this.getObservableData().containsKey(NOTIFY_LIST))
{
this.setChanged();
this.notifyObservers(new ObserverMessage(this.getObservableData().get(NOTIFY_LIST), new SortableList<T, U>(this)));
}
return old != null;
}
catch(Exception e)
{
e.printStackTrace();
}
return false;
}
public void delete(T key)
{
try
{
//REMOVE
if(this.map.containsKey(key))
{
U value = this.map.remove(key);
//NOTIFY REMOVE
if(this.getObservableData().containsKey(NOTIFY_REMOVE))
{
this.setChanged();
this.notifyObservers(new ObserverMessage(this.getObservableData().get(NOTIFY_REMOVE), value));
}
//NOTIFY LIST
/*if(this.getObservableData().containsKey(NOTIFY_LIST))
{
this.setChanged();
this.notifyObservers(new ObserverMessage(this.getObservableData().get(NOTIFY_LIST), new SortableList<T, U>(this)));
}*/
}
if(this.deleted != null)
{
this.deleted.add(key);
}
//COMMIT
if(this.databaseSet != null)
{
this.databaseSet.commit();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
public boolean contains(T key)
{
if(this.map.containsKey(key))
{
return true;
}
else
{
if(this.deleted == null || !this.deleted.contains(key))
{
if(this.parent != null)
{
return this.parent.contains(key);
}
}
}
return false;
}
@Override
public void addObserver(Observer o)
{
//ADD OBSERVER
super.addObserver(o);
//NOTIFY LIST
if(this.getObservableData().containsKey(NOTIFY_LIST))
{
//CREATE LIST
SortableList<T, U> list = new SortableList<T, U>(this);
//UPDATE
o.update(null, new ObserverMessage(this.getObservableData().get(NOTIFY_LIST), list));
}
}
public Iterator<T> getIterator(int index, boolean descending)
{
if(index == DEFAULT_INDEX)
{
if(descending)
{
return ((NavigableMap<T, U>) this.map).descendingKeySet().iterator();
}
return ((NavigableMap<T, U>) this.map).keySet().iterator();
}
else
{
if(descending)
{
index += 10000;
}
return new IndexIterator<T>(this.indexes.get(index));
}
}
public void reset()
{
//RESET MAP
this.map.clear();
//RESET INDEXES
for(Set<Tuple2<?, T>> set: this.indexes.values())
{
set.clear();
}
//NOTIFY LIST
if(this.getObservableData().containsKey(NOTIFY_LIST))
{
//CREATE LIST
SortableList<T, U> list = new SortableList<T, U>(this);
//UPDATE
this.setChanged();
this.notifyObservers(new ObserverMessage(this.getObservableData().get(NOTIFY_LIST), list));
}
}
}