mirror of
https://github.com/bisq-network/bisq-api-reference.git
synced 2026-03-16 01:30:15 +00:00
Copy bisq.cli's console output fmt api, grpc utils
Use the existing Bisq API CLI utils for making calls to the daemon, and formatting responses. When/if the CLI is released on jitpack, this code can be removed from this repo, and loaded from a gradle dependency.
This commit is contained in:
parent
e7b33e9e71
commit
6422ac0cab
120
java-examples/src/main/java/bisq/bots/CurrencyFormat.java
Normal file
120
java-examples/src/main/java/bisq/bots/CurrencyFormat.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots;
|
||||
|
||||
import bisq.proto.grpc.TxFeeRateInfo;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.math.RoundingMode.HALF_UP;
|
||||
import static java.math.RoundingMode.UNNECESSARY;
|
||||
|
||||
/**
|
||||
* Utility for formatting amounts, volumes and fees; there is no i18n support in the CLI.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public class CurrencyFormat {
|
||||
|
||||
// Use the US locale as a base for all DecimalFormats, but commas should be omitted from number strings.
|
||||
private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = DecimalFormatSymbols.getInstance(Locale.US);
|
||||
|
||||
// Use the US locale as a base for all NumberFormats, but commas should be omitted from number strings.
|
||||
private static final NumberFormat US_LOCALE_NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||
|
||||
// Formats numbers for internal use, i.e., grpc request parameters.
|
||||
private static final DecimalFormat INTERNAL_FIAT_DECIMAL_FORMAT = new DecimalFormat("##############0.0000");
|
||||
|
||||
static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100_000_000);
|
||||
static final DecimalFormat SATOSHI_FORMAT = new DecimalFormat("###,##0.00000000", DECIMAL_FORMAT_SYMBOLS);
|
||||
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.########", DECIMAL_FORMAT_SYMBOLS);
|
||||
static final DecimalFormat BTC_TX_FEE_FORMAT = new DecimalFormat("###,###,##0", DECIMAL_FORMAT_SYMBOLS);
|
||||
|
||||
static final BigDecimal BSQ_SATOSHI_DIVISOR = new BigDecimal(100);
|
||||
static final DecimalFormat BSQ_FORMAT = new DecimalFormat("###,###,###,##0.00", DECIMAL_FORMAT_SYMBOLS);
|
||||
|
||||
public static String formatSatoshis(String sats) {
|
||||
//noinspection BigDecimalMethodWithoutRoundingCalled
|
||||
return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatSatoshis(long sats) {
|
||||
return SATOSHI_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatBtc(long sats) {
|
||||
return BTC_FORMAT.format(new BigDecimal(sats).divide(SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
|
||||
public static String formatBsq(long sats) {
|
||||
return BSQ_FORMAT.format(new BigDecimal(sats).divide(BSQ_SATOSHI_DIVISOR));
|
||||
}
|
||||
|
||||
public static String formatTxFeeRateInfo(TxFeeRateInfo txFeeRateInfo) {
|
||||
if (txFeeRateInfo.getUseCustomTxFeeRate())
|
||||
return format("custom tx fee rate: %s sats/byte, network rate: %s sats/byte, min network rate: %s sats/byte",
|
||||
formatFeeSatoshis(txFeeRateInfo.getCustomTxFeeRate()),
|
||||
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
|
||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
||||
else
|
||||
return format("tx fee rate: %s sats/byte, min tx fee rate: %s sats/byte",
|
||||
formatFeeSatoshis(txFeeRateInfo.getFeeServiceRate()),
|
||||
formatFeeSatoshis(txFeeRateInfo.getMinFeeServiceRate()));
|
||||
}
|
||||
|
||||
public static String formatPrice(long price) {
|
||||
US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(4);
|
||||
US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(4);
|
||||
US_LOCALE_NUMBER_FORMAT.setRoundingMode(UNNECESSARY);
|
||||
return US_LOCALE_NUMBER_FORMAT.format((double) price / 10_000);
|
||||
}
|
||||
|
||||
public static String formatFiatVolume(long volume) {
|
||||
US_LOCALE_NUMBER_FORMAT.setMinimumFractionDigits(0);
|
||||
US_LOCALE_NUMBER_FORMAT.setMaximumFractionDigits(0);
|
||||
US_LOCALE_NUMBER_FORMAT.setRoundingMode(HALF_UP);
|
||||
return US_LOCALE_NUMBER_FORMAT.format((double) volume / 10_000);
|
||||
}
|
||||
|
||||
public static long toSatoshis(BigDecimal btc) {
|
||||
return btc.multiply(SATOSHI_DIVISOR).longValue();
|
||||
}
|
||||
|
||||
public static long toSatoshis(String btc) {
|
||||
if (btc.startsWith("-"))
|
||||
throw new IllegalArgumentException(format("'%s' is not a positive number", btc));
|
||||
|
||||
try {
|
||||
return new BigDecimal(btc).multiply(SATOSHI_DIVISOR).longValue();
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException(format("'%s' is not a number", btc));
|
||||
}
|
||||
}
|
||||
|
||||
public static String formatFeeSatoshis(long sats) {
|
||||
return BTC_TX_FEE_FORMAT.format(BigDecimal.valueOf(sats));
|
||||
}
|
||||
}
|
||||
67
java-examples/src/main/java/bisq/bots/GrpcStubs.java
Normal file
67
java-examples/src/main/java/bisq/bots/GrpcStubs.java
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots;
|
||||
|
||||
import bisq.proto.grpc.*;
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
/**
|
||||
* gRPC Service Stubs -- all blocking.
|
||||
*/
|
||||
@Slf4j
|
||||
final class GrpcStubs {
|
||||
|
||||
public final DisputeAgentsGrpc.DisputeAgentsBlockingStub disputeAgentsService;
|
||||
public final HelpGrpc.HelpBlockingStub helpService;
|
||||
public final GetVersionGrpc.GetVersionBlockingStub versionService;
|
||||
public final OffersGrpc.OffersBlockingStub offersService;
|
||||
public final PaymentAccountsGrpc.PaymentAccountsBlockingStub paymentAccountsService;
|
||||
public final PriceGrpc.PriceBlockingStub priceService;
|
||||
public final ShutdownServerGrpc.ShutdownServerBlockingStub shutdownService;
|
||||
public final TradesGrpc.TradesBlockingStub tradesService;
|
||||
public final WalletsGrpc.WalletsBlockingStub walletsService;
|
||||
|
||||
public GrpcStubs(String apiHost, int apiPort, String apiPassword) {
|
||||
CallCredentials credentials = new PasswordCallCredentials(apiPassword);
|
||||
|
||||
var channel = ManagedChannelBuilder.forAddress(apiHost, apiPort).usePlaintext().build();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
try {
|
||||
log.info("Shutting down bot's grpc channel.");
|
||||
channel.shutdown().awaitTermination(1, SECONDS);
|
||||
log.info("Bot channel shutdown complete.");
|
||||
} catch (InterruptedException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}));
|
||||
|
||||
this.disputeAgentsService = DisputeAgentsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.helpService = HelpGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.priceService = PriceGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.shutdownService = ShutdownServerGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.tradesService = TradesGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
this.walletsService = WalletsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots;
|
||||
|
||||
import io.grpc.CallCredentials;
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.Metadata.Key;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Sets the {@value PASSWORD_KEY} rpc call header to a given value.
|
||||
*/
|
||||
class PasswordCallCredentials extends CallCredentials {
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
|
||||
private final String passwordValue;
|
||||
|
||||
public PasswordCallCredentials(String passwordValue) {
|
||||
if (passwordValue == null)
|
||||
throw new IllegalArgumentException(format("'%s' value must not be null", PASSWORD_KEY));
|
||||
this.passwordValue = passwordValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier metadataApplier) {
|
||||
appExecutor.execute(() -> {
|
||||
try {
|
||||
var headers = new Metadata();
|
||||
var passwordKey = Key.of(PASSWORD_KEY, ASCII_STRING_MARSHALLER);
|
||||
headers.put(passwordKey, passwordValue);
|
||||
metadataApplier.apply(headers);
|
||||
} catch (Throwable ex) {
|
||||
metadataApplier.fail(UNAUTHENTICATED.withCause(ex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void thisUsesUnstableApi() {
|
||||
}
|
||||
}
|
||||
153
java-examples/src/main/java/bisq/bots/table/Table.java
Normal file
153
java-examples/src/main/java/bisq/bots/table/Table.java
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table;
|
||||
|
||||
import bisq.bots.table.column.Column;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* A simple table of formatted data for the CLI's output console. A table must be
|
||||
* created with at least one populated column, and each column passed to the constructor
|
||||
* must contain the same number of rows. Null checking is omitted because tables are
|
||||
* populated by protobuf message fields which cannot be null.
|
||||
* <p>
|
||||
* All data in a column has the same type: long, string, etc., but a table
|
||||
* may contain an arbitrary number of columns of any type. For output formatting
|
||||
* purposes, numeric and date columns should be transformed to a StringColumn type with
|
||||
* formatted and justified string values before being passed to the constructor.
|
||||
* <p>
|
||||
* This is not a relational, rdbms table.
|
||||
*/
|
||||
public class Table {
|
||||
|
||||
public final Column<?>[] columns;
|
||||
public final int rowCount;
|
||||
|
||||
// Each printed column is delimited by two spaces.
|
||||
private final int columnDelimiterLength = 2;
|
||||
|
||||
/**
|
||||
* Default constructor. Takes populated Columns.
|
||||
*
|
||||
* @param columns containing the same number of rows
|
||||
*/
|
||||
public Table(Column<?>... columns) {
|
||||
this.columns = columns;
|
||||
this.rowCount = columns.length > 0 ? columns[0].rowCount() : 0;
|
||||
validateStructure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print table data to a PrintStream.
|
||||
*
|
||||
* @param printStream the target output stream
|
||||
*/
|
||||
public void print(PrintStream printStream) {
|
||||
printColumnNames(printStream);
|
||||
for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
|
||||
printRow(printStream, rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print table column names to a PrintStream.
|
||||
*
|
||||
* @param printStream the target output stream
|
||||
*/
|
||||
private void printColumnNames(PrintStream printStream) {
|
||||
IntStream.range(0, columns.length).forEachOrdered(colIndex -> {
|
||||
var c = columns[colIndex];
|
||||
var justifiedName = c.getJustification().equals(RIGHT)
|
||||
? padStart(c.getName(), c.getWidth(), ' ')
|
||||
: c.getName();
|
||||
var paddedWidth = colIndex == columns.length - 1
|
||||
? c.getName().length()
|
||||
: c.getWidth() + columnDelimiterLength;
|
||||
printStream.printf("%-" + paddedWidth + "s", justifiedName);
|
||||
});
|
||||
printStream.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a table row to a PrintStream.
|
||||
*
|
||||
* @param printStream the target output stream
|
||||
*/
|
||||
private void printRow(PrintStream printStream, int rowIndex) {
|
||||
IntStream.range(0, columns.length).forEachOrdered(colIndex -> {
|
||||
var c = columns[colIndex];
|
||||
var paddedWidth = colIndex == columns.length - 1
|
||||
? c.getWidth()
|
||||
: c.getWidth() + columnDelimiterLength;
|
||||
printStream.printf("%-" + paddedWidth + "s", c.getRow(rowIndex));
|
||||
if (colIndex == columns.length - 1)
|
||||
printStream.println();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table's formatted output as a String.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (PrintStream ps = new PrintStream(baos, true, UTF_8)) {
|
||||
print(ps);
|
||||
}
|
||||
return baos.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the table has columns, and each column has the same number of rows.
|
||||
*/
|
||||
private void validateStructure() {
|
||||
if (columns.length == 0)
|
||||
throw new IllegalArgumentException("Table has no columns.");
|
||||
|
||||
if (columns[0].isEmpty())
|
||||
throw new IllegalArgumentException(
|
||||
format("Table's 1st column (%s) has no data.",
|
||||
columns[0].getName()));
|
||||
|
||||
IntStream.range(1, columns.length).forEachOrdered(colIndex -> {
|
||||
var c = columns[colIndex];
|
||||
|
||||
if (c.isEmpty())
|
||||
throw new IllegalStateException(
|
||||
format("Table column # %d (%s) does not have any data.",
|
||||
colIndex + 1,
|
||||
c.getName()));
|
||||
|
||||
if (this.rowCount != c.rowCount())
|
||||
throw new IllegalStateException(
|
||||
format("Table column # %d (%s) does not have same number of rows as 1st column.",
|
||||
colIndex + 1,
|
||||
c.getName()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Abstract superclass for TableBuilder implementations.
|
||||
*/
|
||||
abstract class AbstractTableBuilder {
|
||||
|
||||
protected final Predicate<OfferInfo> isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC");
|
||||
|
||||
protected final TableType tableType;
|
||||
protected final List<?> protos;
|
||||
|
||||
AbstractTableBuilder(TableType tableType, List<?> protos) {
|
||||
this.tableType = tableType;
|
||||
this.protos = protos;
|
||||
if (protos.isEmpty())
|
||||
throw new IllegalArgumentException("cannot build a table without rows");
|
||||
}
|
||||
|
||||
public abstract Table build();
|
||||
}
|
||||
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.column.Column;
|
||||
import bisq.bots.table.column.MixedTradeFeeColumn;
|
||||
import bisq.proto.grpc.ContractInfo;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.bots.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.COL_HEADER_BUYER_DEPOSIT;
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.COL_HEADER_SELLER_DEPOSIT;
|
||||
import static bisq.bots.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static java.lang.String.format;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
abstract class AbstractTradeListBuilder extends AbstractTableBuilder {
|
||||
|
||||
protected final List<TradeInfo> trades;
|
||||
|
||||
protected final TradeTableColumnSupplier colSupplier;
|
||||
|
||||
protected final Column<String> colTradeId;
|
||||
@Nullable
|
||||
protected final Column<Long> colCreateDate;
|
||||
@Nullable
|
||||
protected final Column<String> colMarket;
|
||||
protected final Column<String> colPrice;
|
||||
@Nullable
|
||||
protected final Column<String> colPriceDeviation;
|
||||
@Nullable
|
||||
protected final Column<String> colCurrency;
|
||||
@Nullable
|
||||
protected final Column<Long> colAmount;
|
||||
@Nullable
|
||||
protected final Column<String> colMixedAmount;
|
||||
@Nullable
|
||||
protected final Column<Long> colMinerTxFee;
|
||||
@Nullable
|
||||
protected final MixedTradeFeeColumn colMixedTradeFee;
|
||||
@Nullable
|
||||
protected final Column<Long> colBuyerDeposit;
|
||||
@Nullable
|
||||
protected final Column<Long> colSellerDeposit;
|
||||
@Nullable
|
||||
protected final Column<String> colPaymentMethod;
|
||||
@Nullable
|
||||
protected final Column<String> colRole;
|
||||
@Nullable
|
||||
protected final Column<String> colOfferType;
|
||||
@Nullable
|
||||
protected final Column<String> colClosingStatus;
|
||||
|
||||
// Trade detail tbl specific columns
|
||||
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsDepositPublished;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsDepositConfirmed;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsPayoutPublished;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsCompleted;
|
||||
@Nullable
|
||||
protected final Column<Long> colBisqTradeFee;
|
||||
@Nullable
|
||||
protected final Column<String> colTradeCost;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsPaymentStartedMessageSent;
|
||||
@Nullable
|
||||
protected final Column<Boolean> colIsPaymentReceivedMessageSent;
|
||||
@Nullable
|
||||
protected final Column<String> colAltcoinReceiveAddressColumn;
|
||||
|
||||
// BSQ swap trade detail specific columns
|
||||
|
||||
@Nullable
|
||||
protected final Column<String> status;
|
||||
@Nullable
|
||||
protected final Column<String> colTxId;
|
||||
@Nullable
|
||||
protected final Column<Long> colNumConfirmations;
|
||||
|
||||
AbstractTradeListBuilder(TableType tableType, List<?> protos) {
|
||||
super(tableType, protos);
|
||||
validate();
|
||||
|
||||
this.trades = protos.stream().map(p -> (TradeInfo) p).collect(Collectors.toList());
|
||||
this.colSupplier = new TradeTableColumnSupplier(tableType, trades);
|
||||
|
||||
this.colTradeId = colSupplier.tradeIdColumn.get();
|
||||
this.colCreateDate = colSupplier.createDateColumn.get();
|
||||
this.colMarket = colSupplier.marketColumn.get();
|
||||
this.colPrice = colSupplier.priceColumn.get();
|
||||
this.colPriceDeviation = colSupplier.priceDeviationColumn.get();
|
||||
this.colCurrency = colSupplier.currencyColumn.get();
|
||||
this.colAmount = colSupplier.amountColumn.get();
|
||||
this.colMixedAmount = colSupplier.mixedAmountColumn.get();
|
||||
this.colMinerTxFee = colSupplier.minerTxFeeColumn.get();
|
||||
this.colMixedTradeFee = colSupplier.mixedTradeFeeColumn.get();
|
||||
this.colBuyerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_BUYER_DEPOSIT);
|
||||
this.colSellerDeposit = colSupplier.toSecurityDepositColumn.apply(COL_HEADER_SELLER_DEPOSIT);
|
||||
this.colPaymentMethod = colSupplier.paymentMethodColumn.get();
|
||||
this.colRole = colSupplier.roleColumn.get();
|
||||
this.colOfferType = colSupplier.offerTypeColumn.get();
|
||||
this.colClosingStatus = colSupplier.statusDescriptionColumn.get();
|
||||
|
||||
// Trade detail specific columns, some in common with BSQ swap trades detail.
|
||||
|
||||
this.colIsDepositPublished = colSupplier.depositPublishedColumn.get();
|
||||
this.colIsDepositConfirmed = colSupplier.depositConfirmedColumn.get();
|
||||
this.colIsPayoutPublished = colSupplier.payoutPublishedColumn.get();
|
||||
this.colIsCompleted = colSupplier.fundsWithdrawnColumn.get();
|
||||
this.colBisqTradeFee = colSupplier.bisqTradeDetailFeeColumn.get();
|
||||
this.colTradeCost = colSupplier.tradeCostColumn.get();
|
||||
this.colIsPaymentStartedMessageSent = colSupplier.paymentStartedMessageSentColumn.get();
|
||||
this.colIsPaymentReceivedMessageSent = colSupplier.paymentReceivedMessageSentColumn.get();
|
||||
//noinspection ConstantConditions
|
||||
this.colAltcoinReceiveAddressColumn = colSupplier.altcoinReceiveAddressColumn.get();
|
||||
|
||||
// BSQ swap trade detail specific columns
|
||||
|
||||
this.status = colSupplier.bsqSwapStatusColumn.get();
|
||||
this.colTxId = colSupplier.bsqSwapTxIdColumn.get();
|
||||
this.colNumConfirmations = colSupplier.numConfirmationsColumn.get();
|
||||
}
|
||||
|
||||
protected void validate() {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
if (protos.size() != 1)
|
||||
throw new IllegalArgumentException("trade detail tbl can have only one row");
|
||||
} else if (protos.isEmpty()) {
|
||||
throw new IllegalArgumentException("trade tbl has no rows");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper Functions
|
||||
|
||||
private final Supplier<Boolean> isTradeDetailTblBuilder = () -> tableType.equals(TRADE_DETAIL_TBL);
|
||||
protected final Predicate<TradeInfo> isFiatTrade = (t) -> isFiatOffer.test(t.getOffer());
|
||||
protected final Predicate<TradeInfo> isBsqTrade = (t) -> !isFiatOffer.test(t.getOffer()) && t.getOffer().getBaseCurrencyCode().equals("BSQ");
|
||||
protected final Predicate<TradeInfo> isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer();
|
||||
protected final Predicate<TradeInfo> isMyOffer = (t) -> t.getOffer().getIsMyOffer();
|
||||
protected final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
|
||||
protected final Predicate<TradeInfo> isSellOffer = (t) -> t.getOffer().getDirection().equals(SELL.name());
|
||||
protected final Predicate<TradeInfo> isBtcSeller = (t) -> (isMyOffer.test(t) && isSellOffer.test(t))
|
||||
|| (!isMyOffer.test(t) && !isSellOffer.test(t));
|
||||
protected final Predicate<TradeInfo> isTradeFeeBtc = (t) -> isMyOffer.test(t)
|
||||
? t.getOffer().getIsCurrencyForMakerFeeBtc()
|
||||
: t.getIsCurrencyForTakerFeeBtc();
|
||||
|
||||
|
||||
// Column Value Functions
|
||||
|
||||
// Altcoin volumes from server are string representations of decimals.
|
||||
// Converting them to longs ("sats") requires shifting the decimal points
|
||||
// to left: 2 for BSQ, 8 for other altcoins.
|
||||
protected final Function<TradeInfo, Long> toAltcoinTradeVolumeAsLong = (t) ->
|
||||
isBsqTrade.test(t)
|
||||
? new BigDecimal(t.getTradeVolume()).movePointRight(2).longValue()
|
||||
: new BigDecimal(t.getTradeVolume()).movePointRight(8).longValue();
|
||||
|
||||
protected final Function<TradeInfo, String> toTradeVolumeAsString = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getTradeVolume()
|
||||
: formatSatoshis(t.getTradeAmountAsLong());
|
||||
|
||||
protected final Function<TradeInfo, Long> toTradeVolumeAsLong = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? Long.parseLong(t.getTradeVolume())
|
||||
: toAltcoinTradeVolumeAsLong.apply(t);
|
||||
|
||||
protected final Function<TradeInfo, Long> toTradeAmount = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getTradeAmountAsLong()
|
||||
: toTradeVolumeAsLong.apply(t);
|
||||
|
||||
protected final Function<TradeInfo, String> toMarket = (t) ->
|
||||
t.getOffer().getBaseCurrencyCode() + "/"
|
||||
+ t.getOffer().getCounterCurrencyCode();
|
||||
|
||||
protected final Function<TradeInfo, String> toPaymentCurrencyCode = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
protected final Function<TradeInfo, String> toPriceDeviation = (t) ->
|
||||
t.getOffer().getUseMarketBasedPrice()
|
||||
? format("%.2f%s", t.getOffer().getMarketPriceMarginPct(), "%")
|
||||
: "N/A";
|
||||
|
||||
protected final Function<TradeInfo, Long> toMyMinerTxFee = (t) -> {
|
||||
if (isBsqSwapTrade.test(t)) {
|
||||
// The BTC seller pays the miner fee for both sides.
|
||||
return isBtcSeller.test(t) ? t.getTxFeeAsLong() : 0L;
|
||||
} else {
|
||||
return isTaker.test(t)
|
||||
? t.getTxFeeAsLong()
|
||||
: t.getOffer().getTxFee();
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, Long> toTradeFeeBsq = (t) -> {
|
||||
var isMyOffer = t.getOffer().getIsMyOffer();
|
||||
if (isMyOffer) {
|
||||
return t.getOffer().getIsCurrencyForMakerFeeBtc()
|
||||
? 0L // Maker paid BTC fee, return 0.
|
||||
: t.getOffer().getMakerFee();
|
||||
} else {
|
||||
return t.getIsCurrencyForTakerFeeBtc()
|
||||
? 0L // Taker paid BTC fee, return 0.
|
||||
: t.getTakerFeeAsLong();
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, Long> toTradeFeeBtc = (t) -> {
|
||||
var isMyOffer = t.getOffer().getIsMyOffer();
|
||||
if (isMyOffer) {
|
||||
return t.getOffer().getIsCurrencyForMakerFeeBtc()
|
||||
? t.getOffer().getMakerFee()
|
||||
: 0L; // Maker paid BSQ fee, return 0.
|
||||
} else {
|
||||
return t.getIsCurrencyForTakerFeeBtc()
|
||||
? t.getTakerFeeAsLong()
|
||||
: 0L; // Taker paid BSQ fee, return 0.
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, Long> toMyMakerOrTakerFee = (t) -> {
|
||||
if (isBsqSwapTrade.test(t)) {
|
||||
return isTaker.test(t)
|
||||
? t.getBsqSwapTradeInfo().getBsqTakerTradeFee()
|
||||
: t.getBsqSwapTradeInfo().getBsqMakerTradeFee();
|
||||
} else {
|
||||
return isTaker.test(t)
|
||||
? t.getTakerFeeAsLong()
|
||||
: t.getOffer().getMakerFee();
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, String> toOfferType = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return t.getOffer().getDirection() + " " + t.getOffer().getBaseCurrencyCode();
|
||||
} else {
|
||||
if (t.getOffer().getDirection().equals("BUY")) {
|
||||
return "SELL " + t.getOffer().getBaseCurrencyCode();
|
||||
} else {
|
||||
return "BUY " + t.getOffer().getBaseCurrencyCode();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected final Predicate<TradeInfo> showAltCoinBuyerAddress = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return false;
|
||||
} else {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
if (isTaker.test(t)) {
|
||||
return !isBuyerMakerAndSellerTaker;
|
||||
} else {
|
||||
return isBuyerMakerAndSellerTaker;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected final Function<TradeInfo, String> toAltcoinReceiveAddress = (t) -> {
|
||||
if (showAltCoinBuyerAddress.test(t)) {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
return isBuyerMakerAndSellerTaker // (is BTC buyer / maker)
|
||||
? contract.getTakerPaymentAccountPayload().getAddress()
|
||||
: contract.getMakerPaymentAccountPayload().getAddress();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.*;
|
||||
import bisq.proto.grpc.AddressBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.ADDRESS_BALANCE_TBL;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a List of
|
||||
* {@code bisq.proto.grpc.AddressBalanceInfo} objects.
|
||||
*/
|
||||
class AddressBalanceTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with address info.
|
||||
private final Column<String> colAddress;
|
||||
private final Column<Long> colAvailableBalance;
|
||||
private final Column<Long> colConfirmations;
|
||||
private final Column<Boolean> colIsUsed;
|
||||
|
||||
AddressBalanceTableBuilder(List<?> protos) {
|
||||
super(ADDRESS_BALANCE_TBL, protos);
|
||||
colAddress = new StringColumn(format(COL_HEADER_ADDRESS, "BTC"));
|
||||
this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE);
|
||||
this.colConfirmations = new LongColumn(COL_HEADER_CONFIRMATIONS);
|
||||
this.colIsUsed = new BooleanColumn(COL_HEADER_IS_USED_ADDRESS);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
List<AddressBalanceInfo> addresses = protos.stream()
|
||||
.map(a -> (AddressBalanceInfo) a)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Populate columns with address info.
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
addresses.stream().forEachOrdered(a -> {
|
||||
colAddress.addRow(a.getAddress());
|
||||
colAvailableBalance.addRow(a.getBalance());
|
||||
colConfirmations.addRow(a.getNumConfirmations());
|
||||
colIsUsed.addRow(!a.getIsAddressUnused());
|
||||
});
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
return new Table(colAddress,
|
||||
colAvailableBalance.asStringColumn(),
|
||||
colConfirmations.asStringColumn(),
|
||||
colIsUsed.asStringColumn());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.Column;
|
||||
import bisq.bots.table.column.SatoshiColumn;
|
||||
import bisq.proto.grpc.BsqBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.BSQ_BALANCE_TBL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a
|
||||
* {@code bisq.proto.grpc.BsqBalanceInfo} object.
|
||||
*/
|
||||
class BsqBalanceTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with bsq balance info.
|
||||
private final Column<Long> colAvailableConfirmedBalance;
|
||||
private final Column<Long> colUnverifiedBalance;
|
||||
private final Column<Long> colUnconfirmedChangeBalance;
|
||||
private final Column<Long> colLockedForVotingBalance;
|
||||
private final Column<Long> colLockupBondsBalance;
|
||||
private final Column<Long> colUnlockingBondsBalance;
|
||||
|
||||
BsqBalanceTableBuilder(List<?> protos) {
|
||||
super(BSQ_BALANCE_TBL, protos);
|
||||
this.colAvailableConfirmedBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_CONFIRMED_BALANCE, true);
|
||||
this.colUnverifiedBalance = new SatoshiColumn(COL_HEADER_UNVERIFIED_BALANCE, true);
|
||||
this.colUnconfirmedChangeBalance = new SatoshiColumn(COL_HEADER_UNCONFIRMED_CHANGE_BALANCE, true);
|
||||
this.colLockedForVotingBalance = new SatoshiColumn(COL_HEADER_LOCKED_FOR_VOTING_BALANCE, true);
|
||||
this.colLockupBondsBalance = new SatoshiColumn(COL_HEADER_LOCKUP_BONDS_BALANCE, true);
|
||||
this.colUnlockingBondsBalance = new SatoshiColumn(COL_HEADER_UNLOCKING_BONDS_BALANCE, true);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
BsqBalanceInfo balance = (BsqBalanceInfo) protos.get(0);
|
||||
|
||||
// Populate columns with bsq balance info.
|
||||
|
||||
colAvailableConfirmedBalance.addRow(balance.getAvailableConfirmedBalance());
|
||||
colUnverifiedBalance.addRow(balance.getUnverifiedBalance());
|
||||
colUnconfirmedChangeBalance.addRow(balance.getUnconfirmedChangeBalance());
|
||||
colLockedForVotingBalance.addRow(balance.getLockedForVotingBalance());
|
||||
colLockupBondsBalance.addRow(balance.getLockupBondsBalance());
|
||||
colUnlockingBondsBalance.addRow(balance.getUnlockingBondsBalance());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
return new Table(colAvailableConfirmedBalance.asStringColumn(),
|
||||
colUnverifiedBalance.asStringColumn(),
|
||||
colUnconfirmedChangeBalance.asStringColumn(),
|
||||
colLockedForVotingBalance.asStringColumn(),
|
||||
colLockupBondsBalance.asStringColumn(),
|
||||
colUnlockingBondsBalance.asStringColumn());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.Column;
|
||||
import bisq.bots.table.column.SatoshiColumn;
|
||||
import bisq.proto.grpc.BtcBalanceInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.BTC_BALANCE_TBL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a
|
||||
* {@code bisq.proto.grpc.BtcBalanceInfo} object.
|
||||
*/
|
||||
class BtcBalanceTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with btc balance info.
|
||||
private final Column<Long> colAvailableBalance;
|
||||
private final Column<Long> colReservedBalance;
|
||||
private final Column<Long> colTotalAvailableBalance;
|
||||
private final Column<Long> colLockedBalance;
|
||||
|
||||
BtcBalanceTableBuilder(List<?> protos) {
|
||||
super(BTC_BALANCE_TBL, protos);
|
||||
this.colAvailableBalance = new SatoshiColumn(COL_HEADER_AVAILABLE_BALANCE);
|
||||
this.colReservedBalance = new SatoshiColumn(COL_HEADER_RESERVED_BALANCE);
|
||||
this.colTotalAvailableBalance = new SatoshiColumn(COL_HEADER_TOTAL_AVAILABLE_BALANCE);
|
||||
this.colLockedBalance = new SatoshiColumn(COL_HEADER_LOCKED_BALANCE);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
BtcBalanceInfo balance = (BtcBalanceInfo) protos.get(0);
|
||||
|
||||
// Populate columns with btc balance info.
|
||||
|
||||
colAvailableBalance.addRow(balance.getAvailableBalance());
|
||||
colReservedBalance.addRow(balance.getReservedBalance());
|
||||
colTotalAvailableBalance.addRow(balance.getTotalAvailableBalance());
|
||||
colLockedBalance.addRow(balance.getLockedBalance());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
return new Table(colAvailableBalance.asStringColumn(),
|
||||
colReservedBalance.asStringColumn(),
|
||||
colTotalAvailableBalance.asStringColumn(),
|
||||
colLockedBalance.asStringColumn());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.bots.table.builder.TableType.CLOSED_TRADES_TBL;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
class ClosedTradeTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
ClosedTradeTableBuilder(List<?> protos) {
|
||||
super(CLOSED_TRADES_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
populateColumns();
|
||||
return new Table(colTradeId,
|
||||
colCreateDate.asStringColumn(),
|
||||
colMarket,
|
||||
colPrice.justify(),
|
||||
colPriceDeviation.justify(),
|
||||
colAmount.asStringColumn(),
|
||||
colMixedAmount.justify(),
|
||||
colCurrency,
|
||||
colMinerTxFee.asStringColumn(),
|
||||
colMixedTradeFee.asStringColumn(),
|
||||
colBuyerDeposit.asStringColumn(),
|
||||
colSellerDeposit.asStringColumn(),
|
||||
colOfferType,
|
||||
colClosingStatus);
|
||||
}
|
||||
|
||||
private void populateColumns() {
|
||||
trades.forEach(t -> {
|
||||
colTradeId.addRow(t.getTradeId());
|
||||
colCreateDate.addRow(t.getDate());
|
||||
colMarket.addRow(toMarket.apply(t));
|
||||
colPrice.addRow(t.getTradePrice());
|
||||
colPriceDeviation.addRow(toPriceDeviation.apply(t));
|
||||
colAmount.addRow(t.getTradeAmountAsLong());
|
||||
colMixedAmount.addRow(t.getTradeVolume());
|
||||
colCurrency.addRow(toPaymentCurrencyCode.apply(t));
|
||||
colMinerTxFee.addRow(toMyMinerTxFee.apply(t));
|
||||
|
||||
if (t.getOffer().getIsBsqSwapOffer()) {
|
||||
// For BSQ Swaps, BTC buyer pays the BSQ trade fee for both sides (BTC seller pays no fee).
|
||||
var optionalTradeFeeBsq = isBtcSeller.test(t) ? 0L : toTradeFeeBsq.apply(t);
|
||||
colMixedTradeFee.addRow(optionalTradeFeeBsq, true);
|
||||
} else if (isTradeFeeBtc.test(t)) {
|
||||
colMixedTradeFee.addRow(toTradeFeeBtc.apply(t), false);
|
||||
} else {
|
||||
// V1 trade fee paid in BSQ.
|
||||
colMixedTradeFee.addRow(toTradeFeeBsq.apply(t), true);
|
||||
}
|
||||
|
||||
colBuyerDeposit.addRow(t.getOffer().getBuyerSecurityDeposit());
|
||||
colSellerDeposit.addRow(t.getOffer().getSellerSecurityDeposit());
|
||||
colOfferType.addRow(toOfferType.apply(t));
|
||||
colClosingStatus.addRow(t.getClosingStatus());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.bots.table.builder.TableType.FAILED_TRADES_TBL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a list of {@code bisq.proto.grpc.TradeInfo} objects.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
class FailedTradeTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
FailedTradeTableBuilder(List<?> protos) {
|
||||
super(FAILED_TRADES_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
populateColumns();
|
||||
return new Table(colTradeId,
|
||||
colCreateDate.asStringColumn(),
|
||||
colMarket,
|
||||
colPrice.justify(),
|
||||
colAmount.asStringColumn(),
|
||||
colMixedAmount.justify(),
|
||||
colCurrency,
|
||||
colOfferType,
|
||||
colRole,
|
||||
colClosingStatus);
|
||||
}
|
||||
|
||||
private void populateColumns() {
|
||||
trades.forEach(t -> {
|
||||
colTradeId.addRow(t.getTradeId());
|
||||
colCreateDate.addRow(t.getDate());
|
||||
colMarket.addRow(toMarket.apply(t));
|
||||
colPrice.addRow(t.getTradePrice());
|
||||
colAmount.addRow(t.getTradeAmountAsLong());
|
||||
colMixedAmount.addRow(t.getTradeVolume());
|
||||
colCurrency.addRow(toPaymentCurrencyCode.apply(t));
|
||||
colOfferType.addRow(toOfferType.apply(t));
|
||||
colRole.addRow(t.getRole());
|
||||
colClosingStatus.addRow("Failed");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.*;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.OFFER_TBL;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.*;
|
||||
import static bisq.bots.table.column.ZippedStringColumns.DUPLICATION_MODE.EXCLUDE_DUPLICATES;
|
||||
import static java.lang.String.format;
|
||||
import static protobuf.OfferDirection.BUY;
|
||||
import static protobuf.OfferDirection.SELL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a List of
|
||||
* {@code bisq.proto.grpc.OfferInfo} objects.
|
||||
*/
|
||||
class OfferTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Columns common to both fiat and cryptocurrency offers.
|
||||
private final Column<String> colOfferId = new StringColumn(COL_HEADER_UUID, LEFT);
|
||||
private final Column<String> colDirection = new StringColumn(COL_HEADER_DIRECTION, LEFT);
|
||||
private final Column<Long> colAmount = new SatoshiColumn("Temp Amount", NONE);
|
||||
private final Column<Long> colMinAmount = new SatoshiColumn("Temp Min Amount", NONE);
|
||||
private final Column<String> colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT);
|
||||
private final Column<Long> colCreateDate = new Iso8601DateTimeColumn(COL_HEADER_CREATION_DATE);
|
||||
|
||||
OfferTableBuilder(List<?> protos) {
|
||||
super(OFFER_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
List<OfferInfo> offers = protos.stream().map(p -> (OfferInfo) p).collect(Collectors.toList());
|
||||
return isShowingFiatOffers.get()
|
||||
? buildFiatOfferTable(offers)
|
||||
: buildCryptoCurrencyOfferTable(offers);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public Table buildFiatOfferTable(List<OfferInfo> offers) {
|
||||
@Nullable
|
||||
Column<String> colEnabled = enabledColumn.get(); // Not boolean: "YES", "NO", or "PENDING"
|
||||
Column<String> colFiatPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE, fiatTradeCurrency.get()), RIGHT);
|
||||
Column<String> colVolume = new StringColumn(format("Temp Volume (%s)", fiatTradeCurrency.get()), NONE);
|
||||
Column<String> colMinVolume = new StringColumn(format("Temp Min Volume (%s)", fiatTradeCurrency.get()), NONE);
|
||||
@Nullable
|
||||
Column<String> colTriggerPrice = fiatTriggerPriceColumn.get();
|
||||
|
||||
// Populate columns with offer info.
|
||||
|
||||
offers.forEach(o -> {
|
||||
if (colEnabled != null)
|
||||
colEnabled.addRow(toEnabled.apply(o));
|
||||
|
||||
colDirection.addRow(o.getDirection());
|
||||
colFiatPrice.addRow(o.getPrice());
|
||||
colMinAmount.addRow(o.getMinAmount());
|
||||
colAmount.addRow(o.getAmount());
|
||||
colVolume.addRow(o.getVolume());
|
||||
colMinVolume.addRow(o.getMinVolume());
|
||||
|
||||
if (colTriggerPrice != null)
|
||||
colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice()));
|
||||
|
||||
colPaymentMethod.addRow(o.getPaymentMethodShortName());
|
||||
colCreateDate.addRow(o.getDate());
|
||||
colOfferId.addRow(o.getId());
|
||||
});
|
||||
|
||||
ZippedStringColumns amountRange = zippedAmountRangeColumns.get();
|
||||
ZippedStringColumns volumeRange =
|
||||
new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, fiatTradeCurrency.get()),
|
||||
RIGHT,
|
||||
" - ",
|
||||
colMinVolume.asStringColumn(),
|
||||
colVolume.asStringColumn());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
if (isShowingMyOffers.get()) {
|
||||
return new Table(colEnabled.asStringColumn(),
|
||||
colDirection,
|
||||
colFiatPrice.justify(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colTriggerPrice.justify(),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
} else {
|
||||
return new Table(colDirection,
|
||||
colFiatPrice.justify(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public Table buildCryptoCurrencyOfferTable(List<OfferInfo> offers) {
|
||||
@Nullable
|
||||
Column<String> colEnabled = enabledColumn.get(); // Not boolean: YES, NO, or PENDING
|
||||
Column<String> colBtcPrice = new StringColumn(format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, altcoinTradeCurrency.get()), RIGHT);
|
||||
Column<String> colVolume = new StringColumn(format("Temp Volume (%s)", altcoinTradeCurrency.get()), NONE);
|
||||
Column<String> colMinVolume = new StringColumn(format("Temp Min Volume (%s)", altcoinTradeCurrency.get()), NONE);
|
||||
@Nullable
|
||||
Column<String> colTriggerPrice = altcoinTriggerPriceColumn.get();
|
||||
|
||||
// Populate columns with offer info.
|
||||
|
||||
offers.forEach(o -> {
|
||||
if (colEnabled != null)
|
||||
colEnabled.addRow(toEnabled.apply(o));
|
||||
|
||||
colDirection.addRow(directionFormat.apply(o));
|
||||
colBtcPrice.addRow(o.getPrice());
|
||||
colAmount.addRow(o.getAmount());
|
||||
colMinAmount.addRow(o.getMinAmount());
|
||||
colVolume.addRow(o.getVolume());
|
||||
colMinVolume.addRow(o.getMinVolume());
|
||||
|
||||
if (colTriggerPrice != null)
|
||||
colTriggerPrice.addRow(toBlankOrNonZeroValue.apply(o.getTriggerPrice()));
|
||||
|
||||
colPaymentMethod.addRow(o.getPaymentMethodShortName());
|
||||
colCreateDate.addRow(o.getDate());
|
||||
colOfferId.addRow(o.getId());
|
||||
});
|
||||
|
||||
ZippedStringColumns amountRange = zippedAmountRangeColumns.get();
|
||||
ZippedStringColumns volumeRange =
|
||||
new ZippedStringColumns(format(COL_HEADER_VOLUME_RANGE, altcoinTradeCurrency.get()),
|
||||
RIGHT,
|
||||
" - ",
|
||||
colMinVolume.asStringColumn(),
|
||||
colVolume.asStringColumn());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
if (isShowingMyOffers.get()) {
|
||||
if (isShowingBsqOffers.get()) {
|
||||
return new Table(colEnabled.asStringColumn(),
|
||||
colDirection,
|
||||
colBtcPrice.justify(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
} else {
|
||||
return new Table(colEnabled.asStringColumn(),
|
||||
colDirection,
|
||||
colBtcPrice.justify(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colTriggerPrice.justify(),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
}
|
||||
} else {
|
||||
return new Table(colDirection,
|
||||
colBtcPrice.justify(),
|
||||
amountRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
volumeRange.asStringColumn(EXCLUDE_DUPLICATES),
|
||||
colPaymentMethod,
|
||||
colCreateDate.asStringColumn(),
|
||||
colOfferId);
|
||||
}
|
||||
}
|
||||
|
||||
private final Function<String, String> toBlankOrNonZeroValue = (s) -> s.trim().equals("0") ? "" : s;
|
||||
private final Supplier<OfferInfo> firstOfferInList = () -> (OfferInfo) protos.get(0);
|
||||
private final Supplier<Boolean> isShowingMyOffers = () -> firstOfferInList.get().getIsMyOffer();
|
||||
private final Supplier<Boolean> isShowingFiatOffers = () -> isFiatOffer.test(firstOfferInList.get());
|
||||
private final Supplier<String> fiatTradeCurrency = () -> firstOfferInList.get().getCounterCurrencyCode();
|
||||
private final Supplier<String> altcoinTradeCurrency = () -> firstOfferInList.get().getBaseCurrencyCode();
|
||||
private final Supplier<Boolean> isShowingBsqOffers = () ->
|
||||
!isFiatOffer.test(firstOfferInList.get()) && altcoinTradeCurrency.get().equals("BSQ");
|
||||
|
||||
@Nullable // Not a boolean column: YES, NO, or PENDING.
|
||||
private final Supplier<StringColumn> enabledColumn = () ->
|
||||
isShowingMyOffers.get()
|
||||
? new StringColumn(COL_HEADER_ENABLED, LEFT)
|
||||
: null;
|
||||
@Nullable
|
||||
private final Supplier<StringColumn> fiatTriggerPriceColumn = () ->
|
||||
isShowingMyOffers.get()
|
||||
? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, fiatTradeCurrency.get()), RIGHT)
|
||||
: null;
|
||||
@Nullable
|
||||
private final Supplier<StringColumn> altcoinTriggerPriceColumn = () ->
|
||||
isShowingMyOffers.get() && !isShowingBsqOffers.get()
|
||||
? new StringColumn(format(COL_HEADER_TRIGGER_PRICE, altcoinTradeCurrency.get()), RIGHT)
|
||||
: null;
|
||||
|
||||
private final Function<OfferInfo, String> toEnabled = (o) -> {
|
||||
if (o.getIsMyOffer() && o.getIsMyPendingOffer())
|
||||
return "PENDING";
|
||||
else
|
||||
return o.getIsActivated() ? "YES" : "NO";
|
||||
};
|
||||
|
||||
private final Function<String, String> toMirroredDirection = (d) ->
|
||||
d.equalsIgnoreCase(BUY.name()) ? SELL.name() : BUY.name();
|
||||
|
||||
private final Function<OfferInfo, String> directionFormat = (o) -> {
|
||||
if (isFiatOffer.test(o)) {
|
||||
return o.getBaseCurrencyCode();
|
||||
} else {
|
||||
// Return "Sell BSQ (Buy BTC)", or "Buy BSQ (Sell BTC)".
|
||||
String direction = o.getDirection();
|
||||
String mirroredDirection = toMirroredDirection.apply(direction);
|
||||
Function<String, String> mixedCase = (word) -> word.charAt(0) + word.substring(1).toLowerCase();
|
||||
return format("%s %s (%s %s)",
|
||||
mixedCase.apply(mirroredDirection),
|
||||
o.getBaseCurrencyCode(),
|
||||
mixedCase.apply(direction),
|
||||
o.getCounterCurrencyCode());
|
||||
}
|
||||
};
|
||||
|
||||
private final Supplier<ZippedStringColumns> zippedAmountRangeColumns = () -> {
|
||||
if (colMinAmount.isEmpty() || colAmount.isEmpty())
|
||||
throw new IllegalStateException("amount columns must have data");
|
||||
|
||||
return new ZippedStringColumns(COL_HEADER_AMOUNT_RANGE,
|
||||
RIGHT,
|
||||
" - ",
|
||||
colMinAmount.asStringColumn(),
|
||||
colAmount.asStringColumn());
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.bots.table.builder.TableType.OPEN_TRADES_TBL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a list of {@code bisq.proto.grpc.TradeInfo} objects.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
class OpenTradeTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
OpenTradeTableBuilder(List<?> protos) {
|
||||
super(OPEN_TRADES_TBL, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
populateColumns();
|
||||
return new Table(colTradeId,
|
||||
colCreateDate.asStringColumn(),
|
||||
colMarket,
|
||||
colPrice.justify(),
|
||||
colAmount.asStringColumn(),
|
||||
colMixedAmount.justify(),
|
||||
colCurrency,
|
||||
colPaymentMethod,
|
||||
colRole);
|
||||
}
|
||||
|
||||
private void populateColumns() {
|
||||
trades.forEach(t -> {
|
||||
colTradeId.addRow(t.getTradeId());
|
||||
colCreateDate.addRow(t.getDate());
|
||||
colMarket.addRow(toMarket.apply(t));
|
||||
colPrice.addRow(t.getTradePrice());
|
||||
colAmount.addRow(t.getTradeAmountAsLong());
|
||||
colMixedAmount.addRow(t.getTradeVolume());
|
||||
colCurrency.addRow(toPaymentCurrencyCode.apply(t));
|
||||
colPaymentMethod.addRow(t.getOffer().getPaymentMethodShortName());
|
||||
colRole.addRow(t.getRole());
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.Column;
|
||||
import bisq.bots.table.column.StringColumn;
|
||||
import protobuf.PaymentAccount;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.PAYMENT_ACCOUNT_TBL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a List of
|
||||
* {@code protobuf.PaymentAccount} objects.
|
||||
*/
|
||||
class PaymentAccountTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with payment account info.
|
||||
private final Column<String> colName;
|
||||
private final Column<String> colCurrency;
|
||||
private final Column<String> colPaymentMethod;
|
||||
private final Column<String> colId;
|
||||
|
||||
PaymentAccountTableBuilder(List<?> protos) {
|
||||
super(PAYMENT_ACCOUNT_TBL, protos);
|
||||
this.colName = new StringColumn(COL_HEADER_NAME);
|
||||
this.colCurrency = new StringColumn(COL_HEADER_CURRENCY);
|
||||
this.colPaymentMethod = new StringColumn(COL_HEADER_PAYMENT_METHOD);
|
||||
this.colId = new StringColumn(COL_HEADER_UUID);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
List<PaymentAccount> paymentAccounts = protos.stream()
|
||||
.map(a -> (PaymentAccount) a)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Populate columns with payment account info.
|
||||
//noinspection SimplifyStreamApiCallChains
|
||||
paymentAccounts.stream().forEachOrdered(a -> {
|
||||
colName.addRow(a.getAccountName());
|
||||
colCurrency.addRow(a.getSelectedTradeCurrency().getCode());
|
||||
colPaymentMethod.addRow(a.getPaymentMethod().getId());
|
||||
colId.addRow(a.getId());
|
||||
});
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
return new Table(colName, colCurrency, colPaymentMethod, colId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
/**
|
||||
* Table builder factory. It is not conventionally named TableBuilderFactory because
|
||||
* it has no static factory methods. The number of static fields and methods in the
|
||||
* {@code bisq.bots.table} are kept to a minimum in an effort o reduce class load time
|
||||
* in the session-less CLI.
|
||||
*/
|
||||
public class TableBuilder extends AbstractTableBuilder {
|
||||
|
||||
public TableBuilder(TableType tableType, Object proto) {
|
||||
this(tableType, singletonList(proto));
|
||||
}
|
||||
|
||||
public TableBuilder(TableType tableType, List<?> protos) {
|
||||
super(tableType, protos);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
switch (tableType) {
|
||||
case ADDRESS_BALANCE_TBL:
|
||||
return new AddressBalanceTableBuilder(protos).build();
|
||||
case BSQ_BALANCE_TBL:
|
||||
return new BsqBalanceTableBuilder(protos).build();
|
||||
case BTC_BALANCE_TBL:
|
||||
return new BtcBalanceTableBuilder(protos).build();
|
||||
case CLOSED_TRADES_TBL:
|
||||
return new ClosedTradeTableBuilder(protos).build();
|
||||
case FAILED_TRADES_TBL:
|
||||
return new FailedTradeTableBuilder(protos).build();
|
||||
case OFFER_TBL:
|
||||
return new OfferTableBuilder(protos).build();
|
||||
case OPEN_TRADES_TBL:
|
||||
return new OpenTradeTableBuilder(protos).build();
|
||||
case PAYMENT_ACCOUNT_TBL:
|
||||
return new PaymentAccountTableBuilder(protos).build();
|
||||
case TRADE_DETAIL_TBL:
|
||||
return new TradeDetailTableBuilder(protos).build();
|
||||
case TRANSACTION_TBL:
|
||||
return new TransactionTableBuilder(protos).build();
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid cli table type " + tableType.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
/**
|
||||
* Table column name constants.
|
||||
*/
|
||||
class TableBuilderConstants {
|
||||
static final String COL_HEADER_ADDRESS = "%-3s Address";
|
||||
static final String COL_HEADER_AMOUNT = "Amount";
|
||||
static final String COL_HEADER_AMOUNT_IN_BTC = "Amount in BTC";
|
||||
static final String COL_HEADER_AMOUNT_RANGE = "BTC(min - max)";
|
||||
static final String COL_HEADER_AVAILABLE_BALANCE = "Available Balance";
|
||||
static final String COL_HEADER_AVAILABLE_CONFIRMED_BALANCE = "Available Confirmed Balance";
|
||||
static final String COL_HEADER_UNCONFIRMED_CHANGE_BALANCE = "Unconfirmed Change Balance";
|
||||
static final String COL_HEADER_RESERVED_BALANCE = "Reserved Balance";
|
||||
static final String COL_HEADER_TOTAL_AVAILABLE_BALANCE = "Total Available Balance";
|
||||
static final String COL_HEADER_LOCKED_BALANCE = "Locked Balance";
|
||||
static final String COL_HEADER_LOCKED_FOR_VOTING_BALANCE = "Locked For Voting Balance";
|
||||
static final String COL_HEADER_LOCKUP_BONDS_BALANCE = "Lockup Bonds Balance";
|
||||
static final String COL_HEADER_UNLOCKING_BONDS_BALANCE = "Unlocking Bonds Balance";
|
||||
static final String COL_HEADER_UNVERIFIED_BALANCE = "Unverified Balance";
|
||||
static final String COL_HEADER_BSQ_SWAP_TRADE_ROLE = "My BSQ Swap Role";
|
||||
static final String COL_HEADER_BUYER_DEPOSIT = "Buyer Deposit (BTC)";
|
||||
static final String COL_HEADER_SELLER_DEPOSIT = "Seller Deposit (BTC)";
|
||||
static final String COL_HEADER_CONFIRMATIONS = "Confirmations";
|
||||
static final String COL_HEADER_DEVIATION = "Deviation";
|
||||
static final String COL_HEADER_IS_USED_ADDRESS = "Is Used";
|
||||
static final String COL_HEADER_CREATION_DATE = "Creation Date (UTC)";
|
||||
static final String COL_HEADER_CURRENCY = "Currency";
|
||||
static final String COL_HEADER_DATE_TIME = "Date/Time (UTC)";
|
||||
static final String COL_HEADER_DETAILED_AMOUNT = "Amount(%-3s)";
|
||||
static final String COL_HEADER_DETAILED_PRICE = "Price in %-3s for 1 BTC";
|
||||
static final String COL_HEADER_DETAILED_PRICE_OF_ALTCOIN = "Price in BTC for 1 %-3s";
|
||||
static final String COL_HEADER_DIRECTION = "Buy/Sell";
|
||||
static final String COL_HEADER_ENABLED = "Enabled";
|
||||
static final String COL_HEADER_MARKET = "Market";
|
||||
static final String COL_HEADER_NAME = "Name";
|
||||
static final String COL_HEADER_OFFER_TYPE = "Offer Type";
|
||||
static final String COL_HEADER_PAYMENT_METHOD = "Payment Method";
|
||||
static final String COL_HEADER_PRICE = "Price";
|
||||
static final String COL_HEADER_STATUS = "Status";
|
||||
static final String COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS = "%-3s Buyer Address";
|
||||
static final String COL_HEADER_TRADE_BUYER_COST = "Buyer Cost(%-3s)";
|
||||
static final String COL_HEADER_TRADE_DEPOSIT_CONFIRMED = "Deposit Confirmed";
|
||||
static final String COL_HEADER_TRADE_DEPOSIT_PUBLISHED = "Deposit Published";
|
||||
static final String COL_HEADER_TRADE_PAYMENT_SENT = "%-3s Sent";
|
||||
static final String COL_HEADER_TRADE_PAYMENT_RECEIVED = "%-3s Received";
|
||||
static final String COL_HEADER_TRADE_PAYOUT_PUBLISHED = "Payout Published";
|
||||
static final String COL_HEADER_TRADE_WITHDRAWN = "Withdrawn";
|
||||
static final String COL_HEADER_TRADE_ID = "Trade ID";
|
||||
static final String COL_HEADER_TRADE_ROLE = "My Role";
|
||||
static final String COL_HEADER_TRADE_SHORT_ID = "ID";
|
||||
static final String COL_HEADER_TRADE_MAKER_FEE = "Maker Fee(%-3s)";
|
||||
static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)";
|
||||
static final String COL_HEADER_TRADE_FEE = "Trade Fee";
|
||||
static final String COL_HEADER_TRIGGER_PRICE = "Trigger Price(%-3s)";
|
||||
static final String COL_HEADER_TX_ID = "Tx ID";
|
||||
static final String COL_HEADER_TX_INPUT_SUM = "Tx Inputs (BTC)";
|
||||
static final String COL_HEADER_TX_OUTPUT_SUM = "Tx Outputs (BTC)";
|
||||
static final String COL_HEADER_TX_FEE = "Tx Fee (BTC)";
|
||||
static final String COL_HEADER_TX_SIZE = "Tx Size (Bytes)";
|
||||
static final String COL_HEADER_TX_IS_CONFIRMED = "Is Confirmed";
|
||||
static final String COL_HEADER_TX_MEMO = "Memo";
|
||||
static final String COL_HEADER_VOLUME_RANGE = "%-3s(min - max)";
|
||||
static final String COL_HEADER_UUID = "ID";
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
/**
|
||||
* Used as param in TableBuilder constructor instead of inspecting
|
||||
* protos to find out what kind of CLI output table should be built.
|
||||
*/
|
||||
public enum TableType {
|
||||
ADDRESS_BALANCE_TBL,
|
||||
BSQ_BALANCE_TBL,
|
||||
BTC_BALANCE_TBL,
|
||||
CLOSED_TRADES_TBL,
|
||||
FAILED_TRADES_TBL,
|
||||
OFFER_TBL,
|
||||
OPEN_TRADES_TBL,
|
||||
PAYMENT_ACCOUNT_TBL,
|
||||
TRADE_DETAIL_TBL,
|
||||
TRANSACTION_TBL
|
||||
}
|
||||
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.Column;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static bisq.bots.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||
import static java.lang.String.format;
|
||||
import static protobuf.BsqSwapTrade.State.COMPLETED;
|
||||
import static protobuf.BsqSwapTrade.State.PREPARATION;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a {@code bisq.proto.grpc.TradeInfo} object.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
class TradeDetailTableBuilder extends AbstractTradeListBuilder {
|
||||
|
||||
private final Predicate<TradeInfo> isPendingBsqSwap = (t) -> t.getState().equals(PREPARATION.name());
|
||||
private final Predicate<TradeInfo> isCompletedBsqSwap = (t) -> t.getState().equals(COMPLETED.name());
|
||||
|
||||
TradeDetailTableBuilder(List<?> protos) {
|
||||
super(TRADE_DETAIL_TBL, protos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a single row trade detail table.
|
||||
*
|
||||
* @return Table containing one row
|
||||
*/
|
||||
public Table build() {
|
||||
// A trade detail table only has one row.
|
||||
var trade = trades.get(0);
|
||||
populateColumns(trade);
|
||||
List<Column<?>> columns = defineColumnList(trade);
|
||||
return new Table(columns.toArray(new Column<?>[0]));
|
||||
}
|
||||
|
||||
private void populateColumns(TradeInfo trade) {
|
||||
if (isBsqSwapTrade.test(trade)) {
|
||||
var isPending = isPendingBsqSwap.test(trade);
|
||||
var isCompleted = isCompletedBsqSwap.test(trade);
|
||||
if (isPending == isCompleted)
|
||||
throw new IllegalStateException(
|
||||
format("programmer error: trade must be either pending or completed, is pending=%s and completed=%s",
|
||||
isPending,
|
||||
isCompleted));
|
||||
populateBsqSwapTradeColumns(trade);
|
||||
} else {
|
||||
populateBisqV1TradeColumns(trade);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateBisqV1TradeColumns(TradeInfo trade) {
|
||||
colTradeId.addRow(trade.getShortId());
|
||||
colRole.addRow(trade.getRole());
|
||||
colPrice.addRow(trade.getTradePrice());
|
||||
colAmount.addRow(toTradeAmount.apply(trade));
|
||||
colMinerTxFee.addRow(toMyMinerTxFee.apply(trade));
|
||||
colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade));
|
||||
colIsDepositPublished.addRow(trade.getIsDepositPublished());
|
||||
colIsDepositConfirmed.addRow(trade.getIsDepositConfirmed());
|
||||
colTradeCost.addRow(toTradeVolumeAsString.apply(trade));
|
||||
colIsPaymentStartedMessageSent.addRow(trade.getIsPaymentStartedMessageSent());
|
||||
colIsPaymentReceivedMessageSent.addRow(trade.getIsPaymentReceivedMessageSent());
|
||||
colIsPayoutPublished.addRow(trade.getIsPayoutPublished());
|
||||
colIsCompleted.addRow(trade.getIsCompleted());
|
||||
if (colAltcoinReceiveAddressColumn != null)
|
||||
colAltcoinReceiveAddressColumn.addRow(toAltcoinReceiveAddress.apply(trade));
|
||||
}
|
||||
|
||||
private void populateBsqSwapTradeColumns(TradeInfo trade) {
|
||||
colTradeId.addRow(trade.getShortId());
|
||||
colRole.addRow(trade.getRole());
|
||||
colPrice.addRow(trade.getTradePrice());
|
||||
colAmount.addRow(toTradeAmount.apply(trade));
|
||||
colMinerTxFee.addRow(toMyMinerTxFee.apply(trade));
|
||||
colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade));
|
||||
|
||||
colTradeCost.addRow(toTradeVolumeAsString.apply(trade));
|
||||
|
||||
var isCompleted = isCompletedBsqSwap.test(trade);
|
||||
status.addRow(isCompleted ? "COMPLETED" : "PENDING");
|
||||
if (isCompleted) {
|
||||
colTxId.addRow(trade.getBsqSwapTradeInfo().getTxId());
|
||||
colNumConfirmations.addRow(trade.getBsqSwapTradeInfo().getNumConfirmations());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Column<?>> defineColumnList(TradeInfo trade) {
|
||||
return isBsqSwapTrade.test(trade)
|
||||
? getBsqSwapTradeColumnList(isCompletedBsqSwap.test(trade))
|
||||
: getBisqV1TradeColumnList();
|
||||
}
|
||||
|
||||
private List<Column<?>> getBisqV1TradeColumnList() {
|
||||
List<Column<?>> columns = new ArrayList<>() {{
|
||||
add(colTradeId);
|
||||
add(colRole);
|
||||
add(colPrice.justify());
|
||||
add(colAmount.asStringColumn());
|
||||
add(colMinerTxFee.asStringColumn());
|
||||
add(colBisqTradeFee.asStringColumn());
|
||||
add(colIsDepositPublished.asStringColumn());
|
||||
add(colIsDepositConfirmed.asStringColumn());
|
||||
add(colTradeCost.justify());
|
||||
add(colIsPaymentStartedMessageSent.asStringColumn());
|
||||
add(colIsPaymentReceivedMessageSent.asStringColumn());
|
||||
add(colIsPayoutPublished.asStringColumn());
|
||||
add(colIsCompleted.asStringColumn());
|
||||
}};
|
||||
|
||||
if (colAltcoinReceiveAddressColumn != null)
|
||||
columns.add(colAltcoinReceiveAddressColumn);
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
private List<Column<?>> getBsqSwapTradeColumnList(boolean isCompleted) {
|
||||
List<Column<?>> columns = new ArrayList<>() {{
|
||||
add(colTradeId);
|
||||
add(colRole);
|
||||
add(colPrice.justify());
|
||||
add(colAmount.asStringColumn());
|
||||
add(colMinerTxFee.asStringColumn());
|
||||
add(colBisqTradeFee.asStringColumn());
|
||||
add(colTradeCost.justify());
|
||||
add(status);
|
||||
}};
|
||||
|
||||
if (isCompleted)
|
||||
columns.add(colTxId);
|
||||
|
||||
if (!colNumConfirmations.isEmpty())
|
||||
columns.add(colNumConfirmations.asStringColumn());
|
||||
|
||||
return columns;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.column.*;
|
||||
import bisq.proto.grpc.ContractInfo;
|
||||
import bisq.proto.grpc.OfferInfo;
|
||||
import bisq.proto.grpc.TradeInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.*;
|
||||
import static bisq.bots.table.column.AltcoinVolumeColumn.DISPLAY_MODE.ALTCOIN_VOLUME;
|
||||
import static bisq.bots.table.column.AltcoinVolumeColumn.DISPLAY_MODE.BSQ_VOLUME;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static java.lang.String.format;
|
||||
|
||||
/**
|
||||
* Convenience for supplying column definitions to
|
||||
* open/closed/failed/detail trade table builders.
|
||||
*/
|
||||
@Slf4j
|
||||
class TradeTableColumnSupplier {
|
||||
|
||||
@Getter
|
||||
private final TableType tableType;
|
||||
@Getter
|
||||
private final List<TradeInfo> trades;
|
||||
|
||||
public TradeTableColumnSupplier(TableType tableType, List<TradeInfo> trades) {
|
||||
this.tableType = tableType;
|
||||
this.trades = trades;
|
||||
}
|
||||
|
||||
private final Supplier<Boolean> isTradeDetailTblBuilder = () -> getTableType().equals(TRADE_DETAIL_TBL);
|
||||
private final Supplier<Boolean> isOpenTradeTblBuilder = () -> getTableType().equals(OPEN_TRADES_TBL);
|
||||
private final Supplier<Boolean> isClosedTradeTblBuilder = () -> getTableType().equals(CLOSED_TRADES_TBL);
|
||||
private final Supplier<Boolean> isFailedTradeTblBuilder = () -> getTableType().equals(FAILED_TRADES_TBL);
|
||||
private final Supplier<TradeInfo> firstRow = () -> getTrades().get(0);
|
||||
private final Predicate<OfferInfo> isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC");
|
||||
private final Predicate<TradeInfo> isFiatTrade = (t) -> isFiatOffer.test(t.getOffer());
|
||||
private final Predicate<TradeInfo> isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer();
|
||||
private final Predicate<TradeInfo> isTaker = (t) -> t.getRole().toLowerCase().contains("taker");
|
||||
private final Supplier<Boolean> isSwapTradeDetail = () ->
|
||||
isTradeDetailTblBuilder.get() && isBsqSwapTrade.test(firstRow.get());
|
||||
|
||||
final Supplier<StringColumn> tradeIdColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? new StringColumn(COL_HEADER_TRADE_SHORT_ID)
|
||||
: new StringColumn(COL_HEADER_TRADE_ID);
|
||||
|
||||
final Supplier<Iso8601DateTimeColumn> createDateColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new Iso8601DateTimeColumn(COL_HEADER_DATE_TIME);
|
||||
|
||||
final Supplier<StringColumn> marketColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_MARKET);
|
||||
|
||||
private final Function<TradeInfo, Column<String>> toDetailedPriceColumn = (t) -> {
|
||||
String colHeader = isFiatTrade.test(t)
|
||||
? format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode())
|
||||
: format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, t.getOffer().getBaseCurrencyCode());
|
||||
return new StringColumn(colHeader, RIGHT);
|
||||
};
|
||||
|
||||
final Supplier<Column<String>> priceColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toDetailedPriceColumn.apply(firstRow.get())
|
||||
: new StringColumn(COL_HEADER_PRICE, RIGHT);
|
||||
|
||||
final Supplier<Column<String>> priceDeviationColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_DEVIATION, RIGHT);
|
||||
|
||||
final Supplier<StringColumn> currencyColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_CURRENCY);
|
||||
|
||||
private final Function<TradeInfo, Column<Long>> toDetailedAmountColumn = (t) -> {
|
||||
String headerCurrencyCode = t.getOffer().getBaseCurrencyCode();
|
||||
String colHeader = format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode);
|
||||
AltcoinVolumeColumn.DISPLAY_MODE displayMode = headerCurrencyCode.equals("BSQ") ? BSQ_VOLUME : ALTCOIN_VOLUME;
|
||||
return isFiatTrade.test(t)
|
||||
? new SatoshiColumn(colHeader)
|
||||
: new AltcoinVolumeColumn(colHeader, displayMode);
|
||||
};
|
||||
|
||||
// Can be fiat, btc or altcoin amount represented as longs. Placing the decimal
|
||||
// in the displayed string representation is done in the Column implementation.
|
||||
final Supplier<Column<Long>> amountColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? toDetailedAmountColumn.apply(firstRow.get())
|
||||
: new BtcColumn(COL_HEADER_AMOUNT_IN_BTC);
|
||||
|
||||
final Supplier<StringColumn> mixedAmountColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_AMOUNT, RIGHT);
|
||||
|
||||
final Supplier<Column<Long>> minerTxFeeColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get()
|
||||
? new SatoshiColumn(COL_HEADER_TX_FEE)
|
||||
: null;
|
||||
|
||||
final Supplier<MixedTradeFeeColumn> mixedTradeFeeColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new MixedTradeFeeColumn(COL_HEADER_TRADE_FEE);
|
||||
|
||||
final Supplier<StringColumn> paymentMethodColumn = () -> isTradeDetailTblBuilder.get() || isClosedTradeTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT);
|
||||
|
||||
final Supplier<StringColumn> roleColumn = () -> {
|
||||
if (isSwapTradeDetail.get())
|
||||
return new StringColumn(COL_HEADER_BSQ_SWAP_TRADE_ROLE);
|
||||
else
|
||||
return isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get()
|
||||
? new StringColumn(COL_HEADER_TRADE_ROLE)
|
||||
: null;
|
||||
};
|
||||
|
||||
final Function<String, Column<Long>> toSecurityDepositColumn = (name) -> isClosedTradeTblBuilder.get()
|
||||
? new SatoshiColumn(name)
|
||||
: null;
|
||||
|
||||
final Supplier<StringColumn> offerTypeColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_OFFER_TYPE);
|
||||
|
||||
final Supplier<StringColumn> statusDescriptionColumn = () -> isTradeDetailTblBuilder.get()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_STATUS);
|
||||
|
||||
private final Function<String, Column<Boolean>> toBooleanColumn = BooleanColumn::new;
|
||||
|
||||
final Supplier<Column<Boolean>> depositPublishedColumn = () -> {
|
||||
if (isSwapTradeDetail.get())
|
||||
return null;
|
||||
else
|
||||
return isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED)
|
||||
: null;
|
||||
};
|
||||
|
||||
final Supplier<Column<Boolean>> depositConfirmedColumn = () -> {
|
||||
if (isSwapTradeDetail.get())
|
||||
return null;
|
||||
else
|
||||
return isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED)
|
||||
: null;
|
||||
|
||||
};
|
||||
|
||||
final Supplier<Column<Boolean>> payoutPublishedColumn = () -> {
|
||||
if (isSwapTradeDetail.get())
|
||||
return null;
|
||||
else
|
||||
return isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED)
|
||||
: null;
|
||||
};
|
||||
|
||||
final Supplier<Column<Boolean>> fundsWithdrawnColumn = () -> {
|
||||
if (isSwapTradeDetail.get())
|
||||
return null;
|
||||
else
|
||||
return isTradeDetailTblBuilder.get()
|
||||
? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN)
|
||||
: null;
|
||||
};
|
||||
|
||||
final Supplier<Column<Long>> bisqTradeDetailFeeColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
TradeInfo t = firstRow.get();
|
||||
String headerCurrencyCode = isTaker.test(t)
|
||||
? t.getIsCurrencyForTakerFeeBtc() ? "BTC" : "BSQ"
|
||||
: t.getOffer().getIsCurrencyForMakerFeeBtc() ? "BTC" : "BSQ";
|
||||
String colHeader = isTaker.test(t)
|
||||
? format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode)
|
||||
: format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode);
|
||||
boolean isBsqSatoshis = headerCurrencyCode.equals("BSQ");
|
||||
return new SatoshiColumn(colHeader, isBsqSatoshis);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Function<TradeInfo, String> toPaymentCurrencyCode = (t) ->
|
||||
isFiatTrade.test(t)
|
||||
? t.getOffer().getCounterCurrencyCode()
|
||||
: t.getOffer().getBaseCurrencyCode();
|
||||
|
||||
final Supplier<Column<Boolean>> paymentStartedMessageSentColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get());
|
||||
String colHeader = format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode);
|
||||
return new BooleanColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Supplier<Column<Boolean>> paymentReceivedMessageSentColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get());
|
||||
String colHeader = format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode);
|
||||
return new BooleanColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Supplier<Column<String>> tradeCostColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
TradeInfo t = firstRow.get();
|
||||
String headerCurrencyCode = t.getOffer().getCounterCurrencyCode();
|
||||
String colHeader = format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode);
|
||||
return new StringColumn(colHeader, RIGHT);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Supplier<Column<String>> bsqSwapTxIdColumn = () -> isSwapTradeDetail.get()
|
||||
? new StringColumn(COL_HEADER_TX_ID)
|
||||
: null;
|
||||
|
||||
final Supplier<Column<String>> bsqSwapStatusColumn = () -> isSwapTradeDetail.get()
|
||||
? new StringColumn(COL_HEADER_STATUS)
|
||||
: null;
|
||||
|
||||
final Supplier<Column<Long>> numConfirmationsColumn = () -> isSwapTradeDetail.get()
|
||||
? new LongColumn(COL_HEADER_CONFIRMATIONS)
|
||||
: null;
|
||||
|
||||
final Predicate<TradeInfo> showAltCoinBuyerAddress = (t) -> {
|
||||
if (isFiatTrade.test(t)) {
|
||||
return false;
|
||||
} else {
|
||||
ContractInfo contract = t.getContract();
|
||||
boolean isBuyerMakerAndSellerTaker = contract.getIsBuyerMakerAndSellerTaker();
|
||||
if (isTaker.test(t)) {
|
||||
return !isBuyerMakerAndSellerTaker;
|
||||
} else {
|
||||
return isBuyerMakerAndSellerTaker;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
final Supplier<Column<String>> altcoinReceiveAddressColumn = () -> {
|
||||
if (isTradeDetailTblBuilder.get()) {
|
||||
TradeInfo t = firstRow.get();
|
||||
if (showAltCoinBuyerAddress.test(t)) {
|
||||
String headerCurrencyCode = toPaymentCurrencyCode.apply(t);
|
||||
String colHeader = format(COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS, headerCurrencyCode);
|
||||
return new StringColumn(colHeader);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.builder;
|
||||
|
||||
import bisq.bots.table.Table;
|
||||
import bisq.bots.table.column.*;
|
||||
import bisq.proto.grpc.TxInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static bisq.bots.table.builder.TableBuilderConstants.*;
|
||||
import static bisq.bots.table.builder.TableType.TRANSACTION_TBL;
|
||||
|
||||
/**
|
||||
* Builds a {@code bisq.bots.table.Table} from a {@code bisq.proto.grpc.TxInfo} object.
|
||||
*/
|
||||
class TransactionTableBuilder extends AbstractTableBuilder {
|
||||
|
||||
// Default columns not dynamically generated with tx info.
|
||||
private final Column<String> colTxId;
|
||||
private final Column<Boolean> colIsConfirmed;
|
||||
private final Column<Long> colInputSum;
|
||||
private final Column<Long> colOutputSum;
|
||||
private final Column<Long> colTxFee;
|
||||
private final Column<Long> colTxSize;
|
||||
|
||||
TransactionTableBuilder(List<?> protos) {
|
||||
super(TRANSACTION_TBL, protos);
|
||||
this.colTxId = new StringColumn(COL_HEADER_TX_ID);
|
||||
this.colIsConfirmed = new BooleanColumn(COL_HEADER_TX_IS_CONFIRMED);
|
||||
this.colInputSum = new SatoshiColumn(COL_HEADER_TX_INPUT_SUM);
|
||||
this.colOutputSum = new SatoshiColumn(COL_HEADER_TX_OUTPUT_SUM);
|
||||
this.colTxFee = new SatoshiColumn(COL_HEADER_TX_FEE);
|
||||
this.colTxSize = new LongColumn(COL_HEADER_TX_SIZE);
|
||||
}
|
||||
|
||||
public Table build() {
|
||||
// TODO Add 'gettransactions' api method & show multiple tx in the console.
|
||||
// For now, a tx tbl is only one row.
|
||||
TxInfo tx = (TxInfo) protos.get(0);
|
||||
|
||||
// Declare the columns derived from tx info.
|
||||
|
||||
@Nullable
|
||||
Column<String> colMemo = tx.getMemo().isEmpty()
|
||||
? null
|
||||
: new StringColumn(COL_HEADER_TX_MEMO);
|
||||
|
||||
// Populate columns with tx info.
|
||||
|
||||
colTxId.addRow(tx.getTxId());
|
||||
colIsConfirmed.addRow(!tx.getIsPending());
|
||||
colInputSum.addRow(tx.getInputSum());
|
||||
colOutputSum.addRow(tx.getOutputSum());
|
||||
colTxFee.addRow(tx.getFee());
|
||||
colTxSize.addRow((long) tx.getSize());
|
||||
if (colMemo != null)
|
||||
colMemo.addRow(tx.getMemo());
|
||||
|
||||
// Define and return the table instance with populated columns.
|
||||
|
||||
if (colMemo != null) {
|
||||
return new Table(colTxId,
|
||||
colIsConfirmed.asStringColumn(),
|
||||
colInputSum.asStringColumn(),
|
||||
colOutputSum.asStringColumn(),
|
||||
colTxFee.asStringColumn(),
|
||||
colTxSize.asStringColumn(),
|
||||
colMemo);
|
||||
} else {
|
||||
return new Table(colTxId,
|
||||
colIsConfirmed.asStringColumn(),
|
||||
colInputSum.asStringColumn(),
|
||||
colOutputSum.asStringColumn(),
|
||||
colTxFee.asStringColumn(),
|
||||
colTxSize.asStringColumn());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
|
||||
/**
|
||||
* Partial implementation of the {@link Column} interface.
|
||||
*/
|
||||
abstract class AbstractColumn<C extends Column<T>, T> implements Column<T> {
|
||||
|
||||
// We create an encapsulated StringColumn up front to populate with formatted
|
||||
// strings in each this.addRow(Long value) call. But we will not know how
|
||||
// to justify the cached, formatted string until the column is fully populated.
|
||||
protected final StringColumn stringColumn;
|
||||
|
||||
// The name field is not final, so it can be re-set for column alignment.
|
||||
protected String name;
|
||||
protected final JUSTIFICATION justification;
|
||||
// The max width is not known until after column is fully populated.
|
||||
protected int maxWidth;
|
||||
|
||||
public AbstractColumn(String name, JUSTIFICATION justification) {
|
||||
this.name = name;
|
||||
this.justification = justification;
|
||||
this.stringColumn = this instanceof StringColumn ? null : new StringColumn(name, justification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JUSTIFICATION getJustification() {
|
||||
return this.justification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Column<T> justify() {
|
||||
if (this instanceof StringColumn && this.justification.equals(RIGHT))
|
||||
return this.justify();
|
||||
else
|
||||
return this; // no-op
|
||||
}
|
||||
|
||||
protected final String toJustifiedString(String s) {
|
||||
switch (justification) {
|
||||
case LEFT:
|
||||
return padEnd(s, maxWidth, ' ');
|
||||
case RIGHT:
|
||||
return padStart(s, maxWidth, ' ');
|
||||
case NONE:
|
||||
default:
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying altcoin volume with appropriate precision.
|
||||
*/
|
||||
public class AltcoinVolumeColumn extends LongColumn {
|
||||
|
||||
public enum DISPLAY_MODE {
|
||||
ALTCOIN_VOLUME,
|
||||
BSQ_VOLUME,
|
||||
}
|
||||
|
||||
private final DISPLAY_MODE displayMode;
|
||||
|
||||
// The default AltcoinVolumeColumn JUSTIFICATION is RIGHT.
|
||||
public AltcoinVolumeColumn(String name, DISPLAY_MODE displayMode) {
|
||||
this(name, RIGHT, displayMode);
|
||||
}
|
||||
|
||||
public AltcoinVolumeColumn(String name,
|
||||
JUSTIFICATION justification,
|
||||
DISPLAY_MODE displayMode) {
|
||||
super(name, justification);
|
||||
this.displayMode = displayMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = toFormattedString.apply(value, displayMode);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return toFormattedString.apply(getRow(rowIndex), displayMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted altcoin value strings, but we did
|
||||
// not know how much padding each string needed until now.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
return this.stringColumn;
|
||||
}
|
||||
|
||||
private final BiFunction<Long, DISPLAY_MODE, String> toFormattedString = (value, displayMode) -> {
|
||||
switch (displayMode) {
|
||||
case ALTCOIN_VOLUME:
|
||||
return value > 0 ? new BigDecimal(value).movePointLeft(8).toString() : "";
|
||||
case BSQ_VOLUME:
|
||||
return value > 0 ? new BigDecimal(value).movePointLeft(2).toString() : "";
|
||||
default:
|
||||
throw new IllegalStateException("invalid display mode: " + displayMode);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.LEFT;
|
||||
|
||||
/**
|
||||
* For displaying boolean values as YES, NO, or user's choice for 'true' and 'false'.
|
||||
*/
|
||||
public class BooleanColumn extends AbstractColumn<BooleanColumn, Boolean> {
|
||||
|
||||
private static final String DEFAULT_TRUE_AS_STRING = "YES";
|
||||
private static final String DEFAULT_FALSE_AS_STRING = "NO";
|
||||
|
||||
private final List<Boolean> rows = new ArrayList<>();
|
||||
|
||||
private final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
private final String trueAsString;
|
||||
private final String falseAsString;
|
||||
|
||||
// The default BooleanColumn JUSTIFICATION is LEFT.
|
||||
// The default BooleanColumn True AsString value is YES.
|
||||
// The default BooleanColumn False AsString value is NO.
|
||||
public BooleanColumn(String name) {
|
||||
this(name, LEFT, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING);
|
||||
}
|
||||
|
||||
// Use this constructor to override default LEFT justification.
|
||||
@SuppressWarnings("unused")
|
||||
public BooleanColumn(String name, JUSTIFICATION justification) {
|
||||
this(name, justification, DEFAULT_TRUE_AS_STRING, DEFAULT_FALSE_AS_STRING);
|
||||
}
|
||||
|
||||
// Use this constructor to override default true/false as string defaults.
|
||||
public BooleanColumn(String name, String trueAsString, String falseAsString) {
|
||||
this(name, LEFT, trueAsString, falseAsString);
|
||||
}
|
||||
|
||||
// Use this constructor to override default LEFT justification.
|
||||
public BooleanColumn(String name,
|
||||
JUSTIFICATION justification,
|
||||
String trueAsString,
|
||||
String falseAsString) {
|
||||
super(name, justification);
|
||||
this.trueAsString = trueAsString;
|
||||
this.falseAsString = falseAsString;
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Boolean value) {
|
||||
rows.add(value);
|
||||
|
||||
// We do not know how much padding each StringColumn value needs until it has all the values.
|
||||
String s = asString(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Boolean> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Boolean newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex)
|
||||
? trueAsString
|
||||
: falseAsString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted satoshi strings, but we did
|
||||
// not know how much padding each string needed until now.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
return stringColumn;
|
||||
}
|
||||
|
||||
private String asString(boolean value) {
|
||||
return value ? trueAsString : falseAsString;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.CurrencyFormat.formatBtc;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
|
||||
public class BtcColumn extends SatoshiColumn {
|
||||
|
||||
public BtcColumn(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = formatBtc(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return formatBtc(getRow(rowIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted satoshi strings, but we did
|
||||
// not know how much zero padding each string needed until now.
|
||||
int maxColumnValueWidth = stringColumn.getRows().stream()
|
||||
.max(comparingInt(String::length))
|
||||
.get()
|
||||
.length();
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String btcString = stringColumn.getRow(rowIndex);
|
||||
if (btcString.length() < maxColumnValueWidth) {
|
||||
String paddedBtcString = padEnd(btcString, maxColumnValueWidth, '0');
|
||||
stringColumn.updateRow(rowIndex, paddedBtcString);
|
||||
}
|
||||
});
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
||||
122
java-examples/src/main/java/bisq/bots/table/column/Column.java
Normal file
122
java-examples/src/main/java/bisq/bots/table/column/Column.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Column<T> {
|
||||
|
||||
enum JUSTIFICATION {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
NONE
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the column's name.
|
||||
*
|
||||
* @return name as String
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Sets the column name.
|
||||
*
|
||||
* @param name of the column
|
||||
*/
|
||||
void setName(String name);
|
||||
|
||||
/**
|
||||
* Add column value.
|
||||
*
|
||||
* @param value added to column's data (row)
|
||||
*/
|
||||
void addRow(T value);
|
||||
|
||||
/**
|
||||
* Returns the column data.
|
||||
*
|
||||
* @return rows as List<T>
|
||||
*/
|
||||
List<T> getRows();
|
||||
|
||||
/**
|
||||
* Returns the maximum width of the column name, or longest,
|
||||
* formatted string value -- whichever is greater.
|
||||
*
|
||||
* @return width of the populated column as int
|
||||
*/
|
||||
int getWidth();
|
||||
|
||||
/**
|
||||
* Returns the number of rows in the column.
|
||||
*
|
||||
* @return number of rows in the column as int.
|
||||
*/
|
||||
int rowCount();
|
||||
|
||||
/**
|
||||
* Returns true if the column has no data.
|
||||
*
|
||||
* @return true if empty, false if not
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Returns the column value (data) at given row index.
|
||||
*
|
||||
* @return value object
|
||||
*/
|
||||
T getRow(int rowIndex);
|
||||
|
||||
/**
|
||||
* Update an existing value at the given row index to a new value.
|
||||
*
|
||||
* @param rowIndex row index of value to be updated
|
||||
* @param newValue new value
|
||||
*/
|
||||
void updateRow(int rowIndex, T newValue);
|
||||
|
||||
/**
|
||||
* Returns the row value as a formatted String.
|
||||
*
|
||||
* @return a row value as formatted String
|
||||
*/
|
||||
String getRowAsFormattedString(int rowIndex);
|
||||
|
||||
/**
|
||||
* Return the column with all of its data as a StringColumn with all of its
|
||||
* formatted string data.
|
||||
*
|
||||
* @return StringColumn
|
||||
*/
|
||||
StringColumn asStringColumn();
|
||||
|
||||
/**
|
||||
* Convenience for justifying populated StringColumns before being displayed.
|
||||
* Is only useful for StringColumn instances.
|
||||
*/
|
||||
Column<T> justify();
|
||||
|
||||
/**
|
||||
* Returns JUSTIFICATION value (RIGHT|LEFT|NONE) for the column.
|
||||
*
|
||||
* @return column JUSTIFICATION
|
||||
*/
|
||||
JUSTIFICATION getJustification();
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying Double values.
|
||||
*/
|
||||
public class DoubleColumn extends NumberColumn<DoubleColumn, Double> {
|
||||
|
||||
protected final List<Double> rows = new ArrayList<>();
|
||||
|
||||
protected final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default DoubleColumn JUSTIFICATION is RIGHT.
|
||||
public DoubleColumn(String name) {
|
||||
this(name, RIGHT);
|
||||
}
|
||||
|
||||
public DoubleColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Double value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = String.valueOf(value);
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Double> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Double newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
String s = String.valueOf(getRow(rowIndex));
|
||||
return toJustifiedString(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.CurrencyFormat.formatFiatVolume;
|
||||
import static bisq.bots.CurrencyFormat.formatPrice;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying fiat volume or price with appropriate precision.
|
||||
*/
|
||||
public class FiatColumn extends LongColumn {
|
||||
|
||||
public enum DISPLAY_MODE {
|
||||
FIAT_PRICE,
|
||||
FIAT_VOLUME
|
||||
}
|
||||
|
||||
private final DISPLAY_MODE displayMode;
|
||||
|
||||
// The default FiatColumn JUSTIFICATION is RIGHT.
|
||||
// The default FiatColumn DISPLAY_MODE is PRICE.
|
||||
public FiatColumn(String name) {
|
||||
this(name, RIGHT, DISPLAY_MODE.FIAT_PRICE);
|
||||
}
|
||||
|
||||
public FiatColumn(String name, DISPLAY_MODE displayMode) {
|
||||
this(name, RIGHT, displayMode);
|
||||
}
|
||||
|
||||
public FiatColumn(String name,
|
||||
JUSTIFICATION justification,
|
||||
DISPLAY_MODE displayMode) {
|
||||
super(name, justification);
|
||||
this.displayMode = displayMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = displayMode.equals(DISPLAY_MODE.FIAT_PRICE) ? formatPrice(value) : formatFiatVolume(value);
|
||||
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
// We cached the formatted fiat price strings, but we did
|
||||
// not know how much padding each string needed until now.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
return this.stringColumn;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying Integer values.
|
||||
*/
|
||||
public class IntegerColumn extends NumberColumn<IntegerColumn, Integer> {
|
||||
|
||||
protected final List<Integer> rows = new ArrayList<>();
|
||||
|
||||
protected final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default IntegerColumn JUSTIFICATION is RIGHT.
|
||||
public IntegerColumn(String name) {
|
||||
this(name, RIGHT);
|
||||
}
|
||||
|
||||
public IntegerColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Integer value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = String.valueOf(value);
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Integer newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
String s = String.valueOf(getRow(rowIndex));
|
||||
return toJustifiedString(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static com.google.common.base.Strings.padEnd;
|
||||
import static com.google.common.base.Strings.padStart;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.TimeZone.getTimeZone;
|
||||
|
||||
/**
|
||||
* For displaying (long) timestamp values as ISO-8601 dates in UTC time zone.
|
||||
*/
|
||||
public class Iso8601DateTimeColumn extends LongColumn {
|
||||
|
||||
protected final SimpleDateFormat iso8601DateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
// The default Iso8601DateTimeColumn JUSTIFICATION is LEFT.
|
||||
public Iso8601DateTimeColumn(String name) {
|
||||
this(name, LEFT);
|
||||
}
|
||||
|
||||
public Iso8601DateTimeColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
iso8601DateFormat.setTimeZone(getTimeZone("UTC"));
|
||||
this.maxWidth = Math.max(name.length(), String.valueOf(currentTimeMillis()).length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
long time = getRow(rowIndex);
|
||||
return justification.equals(LEFT)
|
||||
? padEnd(iso8601DateFormat.format(new Date(time)), maxWidth, ' ')
|
||||
: padStart(iso8601DateFormat.format(new Date(time)), maxWidth, ' ');
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying Long values.
|
||||
*/
|
||||
public class LongColumn extends NumberColumn<LongColumn, Long> {
|
||||
|
||||
protected final List<Long> rows = new ArrayList<>();
|
||||
|
||||
protected final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default LongColumn JUSTIFICATION is RIGHT.
|
||||
public LongColumn(String name) {
|
||||
this(name, RIGHT);
|
||||
}
|
||||
|
||||
public LongColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
String s = String.valueOf(value);
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, Long newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
String s = String.valueOf(getRow(rowIndex));
|
||||
return toJustifiedString(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
IntStream.range(0, rows.size()).forEachOrdered(rowIndex ->
|
||||
stringColumn.addRow(getRowAsFormattedString(rowIndex)));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import static bisq.bots.CurrencyFormat.formatBsq;
|
||||
import static bisq.bots.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying a mix of BSQ and BTC trade fees with appropriate precision.
|
||||
*/
|
||||
public class MixedTradeFeeColumn extends LongColumn {
|
||||
|
||||
public MixedTradeFeeColumn(String name) {
|
||||
super(name, RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
throw new UnsupportedOperationException("use public void addRow(Long value, boolean isBsq) instead");
|
||||
}
|
||||
|
||||
public void addRow(Long value, boolean isBsq) {
|
||||
rows.add(value);
|
||||
|
||||
String s = isBsq
|
||||
? formatBsq(value) + " BSQ"
|
||||
: formatSatoshis(value) + " BTC";
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
/**
|
||||
* Abstract superclass for numeric Columns.
|
||||
*
|
||||
* @param <C> the subclass column's type (LongColumn, IntegerColumn, ...)
|
||||
* @param <T> the subclass column's numeric Java type (Long, Integer, ...)
|
||||
*/
|
||||
abstract class NumberColumn<C extends NumberColumn<C, T>,
|
||||
T extends Number> extends AbstractColumn<C, T> implements Column<T> {
|
||||
|
||||
public NumberColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import static bisq.bots.CurrencyFormat.formatBsq;
|
||||
import static bisq.bots.CurrencyFormat.formatSatoshis;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying BTC or BSQ satoshi values with appropriate precision.
|
||||
*/
|
||||
public class SatoshiColumn extends LongColumn {
|
||||
|
||||
protected final boolean isBsqSatoshis;
|
||||
|
||||
// The default SatoshiColumn JUSTIFICATION is RIGHT.
|
||||
public SatoshiColumn(String name) {
|
||||
this(name, RIGHT, false);
|
||||
}
|
||||
|
||||
public SatoshiColumn(String name, boolean isBsqSatoshis) {
|
||||
this(name, RIGHT, isBsqSatoshis);
|
||||
}
|
||||
|
||||
public SatoshiColumn(String name, JUSTIFICATION justification) {
|
||||
this(name, justification, false);
|
||||
}
|
||||
|
||||
public SatoshiColumn(String name, JUSTIFICATION justification, boolean isBsqSatoshis) {
|
||||
super(name, justification);
|
||||
this.isBsqSatoshis = isBsqSatoshis;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(Long value) {
|
||||
rows.add(value);
|
||||
|
||||
// We do not know how much padding each StringColumn value needs until it has all the values.
|
||||
String s = isBsqSatoshis ? formatBsq(value) : formatSatoshis(value);
|
||||
stringColumn.addRow(s);
|
||||
|
||||
if (isNewMaxWidth.test(s))
|
||||
maxWidth = s.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return isBsqSatoshis
|
||||
? formatBsq(getRow(rowIndex))
|
||||
: formatSatoshis(getRow(rowIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return stringColumn.justify();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.LEFT;
|
||||
import static bisq.bots.table.column.Column.JUSTIFICATION.RIGHT;
|
||||
|
||||
/**
|
||||
* For displaying justified string values.
|
||||
*/
|
||||
public class StringColumn extends AbstractColumn<StringColumn, String> {
|
||||
|
||||
private final List<String> rows = new ArrayList<>();
|
||||
|
||||
private final Predicate<String> isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth;
|
||||
|
||||
// The default StringColumn JUSTIFICATION is LEFT.
|
||||
public StringColumn(String name) {
|
||||
this(name, LEFT);
|
||||
}
|
||||
|
||||
// Use this constructor to override default LEFT justification.
|
||||
public StringColumn(String name, JUSTIFICATION justification) {
|
||||
super(name, justification);
|
||||
this.maxWidth = name.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRow(String value) {
|
||||
rows.add(value);
|
||||
if (isNewMaxWidth.test(value))
|
||||
maxWidth = value.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int rowCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return rows.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRow(int rowIndex) {
|
||||
return rows.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRow(int rowIndex, String newValue) {
|
||||
rows.set(rowIndex, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRowAsFormattedString(int rowIndex) {
|
||||
return getRow(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn asStringColumn() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringColumn justify() {
|
||||
if (justification.equals(RIGHT)) {
|
||||
IntStream.range(0, getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = getRow(rowIndex);
|
||||
String justified = toJustifiedString(unjustified);
|
||||
updateRow(rowIndex, justified);
|
||||
});
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.bots.table.column;
|
||||
|
||||
import bisq.bots.table.column.Column.JUSTIFICATION;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* For zipping multiple StringColumns into a single StringColumn.
|
||||
* Useful for displaying amount and volume range values.
|
||||
*/
|
||||
public class ZippedStringColumns {
|
||||
|
||||
public enum DUPLICATION_MODE {
|
||||
EXCLUDE_DUPLICATES,
|
||||
INCLUDE_DUPLICATES
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final JUSTIFICATION justification;
|
||||
private final String delimiter;
|
||||
private final StringColumn[] columns;
|
||||
|
||||
public ZippedStringColumns(String name,
|
||||
JUSTIFICATION justification,
|
||||
String delimiter,
|
||||
StringColumn... columns) {
|
||||
this.name = name;
|
||||
this.justification = justification;
|
||||
this.delimiter = delimiter;
|
||||
this.columns = columns;
|
||||
validateColumnData();
|
||||
}
|
||||
|
||||
public StringColumn asStringColumn(DUPLICATION_MODE duplicationMode) {
|
||||
StringColumn stringColumn = new StringColumn(name, justification);
|
||||
|
||||
buildRows(stringColumn, duplicationMode);
|
||||
|
||||
// Re-set the column name field to its justified value, in case any of the column
|
||||
// values are longer than the name passed to this constructor.
|
||||
stringColumn.setName(stringColumn.toJustifiedString(name));
|
||||
|
||||
return stringColumn;
|
||||
}
|
||||
|
||||
private void buildRows(StringColumn stringColumn, DUPLICATION_MODE duplicationMode) {
|
||||
// Populate the StringColumn with unjustified zipped values; we cannot justify
|
||||
// the zipped values until stringColumn knows its final maxWidth.
|
||||
IntStream.range(0, columns[0].getRows().size()).forEach(rowIndex -> {
|
||||
String row = buildRow(rowIndex, duplicationMode);
|
||||
stringColumn.addRow(row);
|
||||
});
|
||||
|
||||
formatRows(stringColumn);
|
||||
}
|
||||
|
||||
private String buildRow(int rowIndex, DUPLICATION_MODE duplicationMode) {
|
||||
StringBuilder rowBuilder = new StringBuilder();
|
||||
@Nullable
|
||||
List<String> processedValues = duplicationMode.equals(DUPLICATION_MODE.EXCLUDE_DUPLICATES)
|
||||
? new ArrayList<>()
|
||||
: null;
|
||||
IntStream.range(0, columns.length).forEachOrdered(colIndex -> {
|
||||
// For each column @ rowIndex ...
|
||||
var value = columns[colIndex].getRows().get(rowIndex);
|
||||
if (duplicationMode.equals(DUPLICATION_MODE.INCLUDE_DUPLICATES)) {
|
||||
if (rowBuilder.length() > 0)
|
||||
rowBuilder.append(delimiter);
|
||||
|
||||
rowBuilder.append(value);
|
||||
} else if (!processedValues.contains(value)) {
|
||||
if (rowBuilder.length() > 0)
|
||||
rowBuilder.append(delimiter);
|
||||
|
||||
rowBuilder.append(value);
|
||||
processedValues.add(value);
|
||||
}
|
||||
});
|
||||
return rowBuilder.toString();
|
||||
}
|
||||
|
||||
private void formatRows(StringColumn stringColumn) {
|
||||
// Now we can justify the zipped string values in the new StringColumn.
|
||||
IntStream.range(0, stringColumn.getRows().size()).forEach(rowIndex -> {
|
||||
String unjustified = stringColumn.getRow(rowIndex);
|
||||
String justified = stringColumn.toJustifiedString(unjustified);
|
||||
stringColumn.updateRow(rowIndex, justified);
|
||||
});
|
||||
}
|
||||
|
||||
private void validateColumnData() {
|
||||
if (columns.length == 0)
|
||||
throw new IllegalStateException("cannot zip columns because they do not have any data");
|
||||
|
||||
StringColumn firstColumn = columns[0];
|
||||
if (firstColumn.getRows().isEmpty())
|
||||
throw new IllegalStateException("1st column has no data");
|
||||
|
||||
IntStream.range(1, columns.length).forEach(colIndex -> {
|
||||
if (columns[colIndex].getRows().size() != firstColumn.getRows().size())
|
||||
throw new IllegalStateException("columns do not have same number of rows");
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user