/******************************************************************************
* Copyright © 2013-2016 The Nxt Core Developers. *
* *
* See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* Nxt software, including this file, may be copied, modified, propagated, *
* or distributed except according to the terms contained in the LICENSE.txt *
* file. *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
package nxt;
import nxt.db.DbClause;
import nxt.db.DbIterator;
import nxt.db.DbKey;
import nxt.db.DbUtils;
import nxt.db.EntityDbTable;
import nxt.util.Listener;
import nxt.util.Listeners;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public final class Exchange {
public enum Event {
EXCHANGE
}
private static final Listeners<Exchange,Event> listeners = new Listeners<>();
private static final DbKey.LinkKeyFactory<Exchange> exchangeDbKeyFactory = new DbKey.LinkKeyFactory<Exchange>("transaction_id", "offer_id") {
@Override
public DbKey newKey(Exchange exchange) {
return exchange.dbKey;
}
};
private static final EntityDbTable<Exchange> exchangeTable = new EntityDbTable<Exchange>("exchange", exchangeDbKeyFactory) {
@Override
protected Exchange load(Connection con, ResultSet rs) throws SQLException {
return new Exchange(rs);
}
@Override
protected void save(Connection con, Exchange exchange) throws SQLException {
exchange.save(con);
}
};
public static DbIterator<Exchange> getAllExchanges(int from, int to) {
return exchangeTable.getAll(from, to);
}
public static int getCount() {
return exchangeTable.getCount();
}
public static boolean addListener(Listener<Exchange> listener, Event eventType) {
return listeners.addListener(listener, eventType);
}
public static boolean removeListener(Listener<Exchange> listener, Event eventType) {
return listeners.removeListener(listener, eventType);
}
public static DbIterator<Exchange> getCurrencyExchanges(long currencyId, int from, int to) {
return exchangeTable.getManyBy(new DbClause.LongClause("currency_id", currencyId), from, to);
}
public static List<Exchange> getLastExchanges(long[] currencyIds) {
try (Connection con = Db.db.getConnection();
PreparedStatement pstmt = con.prepareStatement("SELECT * FROM exchange WHERE currency_id = ? ORDER BY height DESC, db_id DESC LIMIT 1")) {
List<Exchange> result = new ArrayList<>();
for (long currencyId : currencyIds) {
pstmt.setLong(1, currencyId);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
result.add(new Exchange(rs));
}
}
}
return result;
} catch (SQLException e) {
throw new RuntimeException(e.toString(), e);
}
}
public static DbIterator<Exchange> getAccountExchanges(long accountId, int from, int to) {
Connection con = null;
try {
con = Db.db.getConnection();
PreparedStatement pstmt = con.prepareStatement("SELECT * FROM exchange WHERE seller_id = ?"
+ " UNION ALL SELECT * FROM exchange WHERE buyer_id = ? AND seller_id <> ? ORDER BY height DESC, db_id DESC"
+ DbUtils.limitsClause(from, to));
int i = 0;
pstmt.setLong(++i, accountId);
pstmt.setLong(++i, accountId);
pstmt.setLong(++i, accountId);
DbUtils.setLimits(++i, pstmt, from, to);
return exchangeTable.getManyBy(con, pstmt, false);
} catch (SQLException e) {
DbUtils.close(con);
throw new RuntimeException(e.toString(), e);
}
}
public static DbIterator<Exchange> getAccountCurrencyExchanges(long accountId, long currencyId, int from, int to) {
Connection con = null;
try {
con = Db.db.getConnection();
PreparedStatement pstmt = con.prepareStatement("SELECT * FROM exchange WHERE seller_id = ? AND currency_id = ?"
+ " UNION ALL SELECT * FROM exchange WHERE buyer_id = ? AND seller_id <> ? AND currency_id = ? ORDER BY height DESC, db_id DESC"
+ DbUtils.limitsClause(from, to));
int i = 0;
pstmt.setLong(++i, accountId);
pstmt.setLong(++i, currencyId);
pstmt.setLong(++i, accountId);
pstmt.setLong(++i, accountId);
pstmt.setLong(++i, currencyId);
DbUtils.setLimits(++i, pstmt, from, to);
return exchangeTable.getManyBy(con, pstmt, false);
} catch (SQLException e) {
DbUtils.close(con);
throw new RuntimeException(e.toString(), e);
}
}
public static DbIterator<Exchange> getExchanges(long transactionId) {
return exchangeTable.getManyBy(new DbClause.LongClause("transaction_id", transactionId), 0, -1);
}
public static DbIterator<Exchange> getOfferExchanges(long offerId, int from, int to) {
return exchangeTable.getManyBy(new DbClause.LongClause("offer_id", offerId), from, to);
}
public static int getExchangeCount(long currencyId) {
return exchangeTable.getCount(new DbClause.LongClause("currency_id", currencyId));
}
static Exchange addExchange(Transaction transaction, long currencyId, CurrencyExchangeOffer offer, long sellerId, long buyerId, long units) {
Exchange exchange = new Exchange(transaction.getId(), currencyId, offer, sellerId, buyerId, units);
exchangeTable.insert(exchange);
listeners.notify(exchange, Event.EXCHANGE);
return exchange;
}
static void init() {}
private final long transactionId;
private final int timestamp;
private final long currencyId;
private final long blockId;
private final int height;
private final long offerId;
private final long sellerId;
private final long buyerId;
private final DbKey dbKey;
private final long units;
private final long rate;
private Exchange(long transactionId, long currencyId, CurrencyExchangeOffer offer, long sellerId, long buyerId, long units) {
Block block = Nxt.getBlockchain().getLastBlock();
this.transactionId = transactionId;
this.blockId = block.getId();
this.height = block.getHeight();
this.currencyId = currencyId;
this.timestamp = block.getTimestamp();
this.offerId = offer.getId();
this.sellerId = sellerId;
this.buyerId = buyerId;
this.dbKey = exchangeDbKeyFactory.newKey(this.transactionId, this.offerId);
this.units = units;
this.rate = offer.getRateNQT();
}
private Exchange(ResultSet rs) throws SQLException {
this.transactionId = rs.getLong("transaction_id");
this.currencyId = rs.getLong("currency_id");
this.blockId = rs.getLong("block_id");
this.offerId = rs.getLong("offer_id");
this.sellerId = rs.getLong("seller_id");
this.buyerId = rs.getLong("buyer_id");
this.dbKey = exchangeDbKeyFactory.newKey(this.transactionId, this.offerId);
this.units = rs.getLong("units");
this.rate = rs.getLong("rate");
this.timestamp = rs.getInt("timestamp");
this.height = rs.getInt("height");
}
private void save(Connection con) throws SQLException {
try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO exchange (transaction_id, currency_id, block_id, "
+ "offer_id, seller_id, buyer_id, units, rate, timestamp, height) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")) {
int i = 0;
pstmt.setLong(++i, this.transactionId);
pstmt.setLong(++i, this.currencyId);
pstmt.setLong(++i, this.blockId);
pstmt.setLong(++i, this.offerId);
pstmt.setLong(++i, this.sellerId);
pstmt.setLong(++i, this.buyerId);
pstmt.setLong(++i, this.units);
pstmt.setLong(++i, this.rate);
pstmt.setInt(++i, this.timestamp);
pstmt.setInt(++i, this.height);
pstmt.executeUpdate();
}
}
public long getTransactionId() {
return transactionId;
}
public long getBlockId() {
return blockId;
}
public long getOfferId() {
return offerId;
}
public long getSellerId() {
return sellerId;
}
public long getBuyerId() {
return buyerId;
}
public long getUnits() {
return units;
}
public long getRate() {
return rate;
}
public long getCurrencyId() {
return currencyId;
}
public int getTimestamp() {
return timestamp;
}
public int getHeight() {
return height;
}
@Override
public String toString() {
return "Exchange currency: " + Long.toUnsignedString(currencyId) + " offer: " + Long.toUnsignedString(offerId)
+ " rate: " + rate + " units: " + units + " height: " + height + " transaction: " + Long.toUnsignedString(transactionId);
}
}