package database; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.mapdb.BTreeMap; import org.mapdb.Bind; import org.mapdb.DB; import org.mapdb.DBMaker; import org.mapdb.Fun; import org.mapdb.Fun.Tuple2; import org.mapdb.Fun.Tuple3; import qora.assets.Order; import qora.assets.Trade; import utils.ObserverMessage; import database.DBSet; import database.serializer.TradeSerializer; public class TradeMap extends DBMap<Tuple2<BigInteger, BigInteger>, Trade> { private Map<Integer, Integer> observableData = new HashMap<Integer, Integer>(); @SuppressWarnings("rawtypes") private BTreeMap pairKeyMap; @SuppressWarnings("rawtypes") private BTreeMap reverseKeyMap; public TradeMap(DBSet databaseSet, DB database) { super(databaseSet, database); this.observableData.put(DBMap.NOTIFY_ADD, ObserverMessage.ADD_TRADE_TYPE); this.observableData.put(DBMap.NOTIFY_REMOVE, ObserverMessage.REMOVE_TRADE_TYPE); //this.observableData.put(DBMap.NOTIFY_LIST, ObserverMessage.LIST_ORDER_TYPE); } public TradeMap(TradeMap parent) { super(parent); } protected void createIndexes(DB database){} @Override protected Map<Tuple2<BigInteger, BigInteger>, Trade> getMap(DB database) { //OPEN MAP return this.openMap(database); } @Override protected Map<Tuple2<BigInteger, BigInteger>, Trade> getMemoryMap() { DB database = DBMaker.newMemoryDB().make(); //OPEN MAP return this.openMap(database); } @SuppressWarnings("unchecked") private Map<Tuple2<BigInteger, BigInteger>, Trade> openMap(final DB database) { //OPEN MAP BTreeMap<Tuple2<BigInteger, BigInteger>, Trade> map = database.createTreeMap("trades") .valueSerializer(new TradeSerializer()) .makeOrGet(); //CHECK IF NOT MEMORY DATABASE if(databaseSet != null) { //PAIR KEY this.pairKeyMap = database.createTreeMap("trades_key_pair") .comparator(Fun.COMPARATOR) .makeOrGet(); //BIND PAIR KEY Bind.secondaryKey(map, this.pairKeyMap, new Fun.Function2<Tuple3<String, Long, Tuple2<BigInteger, BigInteger>>, Tuple2<BigInteger, BigInteger>, Trade>() { @Override public Tuple3<String, Long, Tuple2<BigInteger, BigInteger>> run(Tuple2<BigInteger, BigInteger> key, Trade value) { Order order = value.getInitiatorOrder((DBSet) databaseSet); long have = order.getHave(); long want = order.getWant(); String pairKey; if(have > want) { pairKey = have + "/" + want; } else { pairKey = want + "/" + have; } return new Tuple3<String, Long, Tuple2<BigInteger, BigInteger>>(pairKey, Long.MAX_VALUE - value.getTimestamp(), key); } }); //REVERSE KEY this.reverseKeyMap = database.createTreeMap("trades_key_reverse") .comparator(Fun.COMPARATOR) .makeOrGet(); //BIND REVERSE KEY Bind.secondaryKey(map, this.reverseKeyMap, new Fun.Function2<Tuple2<BigInteger, BigInteger>, Tuple2<BigInteger, BigInteger>, Trade>() { @Override public Tuple2<BigInteger, BigInteger> run(Tuple2<BigInteger, BigInteger> key, Trade value) { return new Tuple2<BigInteger, BigInteger>(key.b, key.a); } }); Bind.secondaryKey(map, this.reverseKeyMap, new Fun.Function2<Tuple2<BigInteger, BigInteger>, Tuple2<BigInteger, BigInteger>, Trade>() { @Override public Tuple2<BigInteger, BigInteger> run(Tuple2<BigInteger, BigInteger> key, Trade value) { return new Tuple2<BigInteger, BigInteger>(key.a, key.b); } }); } //RETURN return map; } @Override protected Trade getDefaultValue() { return null; } @Override protected Map<Integer, Integer> getObservableData() { return this.observableData; } public void add(Trade trade) { this.set(new Tuple2<BigInteger, BigInteger>(trade.getInitiator(), trade.getTarget()), trade); } @SuppressWarnings({ "unchecked", "rawtypes" }) private Collection<Tuple2> getKeys(Order order) { Map uncastedMap = this.map; //FILTER ALL KEYS Collection<Tuple2> keys = ((BTreeMap<Tuple2, Order>) uncastedMap).subMap( Fun.t2(order.getId(), null), Fun.t2(order.getId(), Fun.HI())).keySet(); //IF THIS IS A FORK if(this.parent != null) { //GET ALL KEYS FOR FORK Collection<Tuple2> forkKeys = ((TradeMap) this.parent).getKeys(order); //COMBINE LISTS Set<Tuple2> combinedKeys = new TreeSet<Tuple2>(keys); combinedKeys.addAll(forkKeys); //DELETE DELETED for(Tuple2 deleted: this.deleted) { combinedKeys.remove(deleted); } //CONVERT SET BACK TO COLLECTION keys = combinedKeys; } return keys; } @SuppressWarnings({ "unchecked", "rawtypes" }) public List<Trade> getInitiatedTrades(Order order) { //FILTER ALL TRADES Collection<Tuple2> keys = this.getKeys(order); //GET ALL TRADES FOR KEYS List<Trade> trades = new ArrayList<Trade>(); for(Tuple2 key: keys) { trades.add(this.get(key)); } //RETURN return trades; } @SuppressWarnings({ "unchecked", "rawtypes" }) public SortableList<Tuple2<BigInteger, BigInteger>, Trade> getTrades(Order order) { //ADD REVERSE KEYS Collection<Tuple2<BigInteger, BigInteger>> keys = ((BTreeMap<Tuple2, Tuple2<BigInteger, BigInteger>>) this.reverseKeyMap).subMap( Fun.t2(order.getId(), null), Fun.t2(order.getId(), Fun.HI())).values(); //RETURN return new SortableList<Tuple2<BigInteger, BigInteger>, Trade>(this, keys); } @SuppressWarnings({ "unchecked", "rawtypes" }) public SortableList<Tuple2<BigInteger, BigInteger>, Trade> getTradesSortableList(long have, long want) { String pairKey; if(have > want) { pairKey = have + "/" + want; } else { pairKey = want + "/" + have; } //FILTER ALL KEYS Collection<Tuple2<BigInteger, BigInteger>> keys = ((BTreeMap<Tuple3, Tuple2<BigInteger, BigInteger>>) this.pairKeyMap).subMap( Fun.t3(pairKey, null, null), Fun.t3(pairKey, Fun.HI(), Fun.HI())).values(); //RETURN return new SortableList<Tuple2<BigInteger, BigInteger>, Trade>(this, keys); } public void delete(Trade trade) { this.delete(new Tuple2<BigInteger, BigInteger>(trade.getInitiator(), trade.getTarget())); } }