io.bitsquare.gui.util.BSFormatter.java Source code

Java tutorial

Introduction

Here is the source code for io.bitsquare.gui.util.BSFormatter.java

Source

/*
 * This file is part of Bitsquare.
 *
 * Bitsquare 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.
 *
 * Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>.
 */

package io.bitsquare.gui.util;

import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.common.util.MathUtils;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.Fiat;
import org.bitcoinj.utils.MonetaryFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

public class BSFormatter {
    private static final Logger log = LoggerFactory.getLogger(BSFormatter.class);

    private Locale locale = Preferences.getDefaultLocale();
    private boolean useMilliBit;
    private int scale = 3;

    // We don't support localized formatting. Format is always using "." as decimal mark and no grouping separator.
    // Input of "," as decimal mark (like in german locale) will be replaced with ".".
    // Input of a group separator (1,123,45) lead to an validation error.
    // Note: BtcFormat was intended to be used, but it lead to many problems (automatic format to mBit,
    // no way to remove grouping separator). It seems to be not optimal for user input formatting.
    private MonetaryFormat coinFormat = MonetaryFormat.BTC;

    //  private String currencyCode = CurrencyUtil.getDefaultFiatCurrencyAsCode();

    // format is like: 1,00  never more then 2 decimals
    private final MonetaryFormat fiatFormat = MonetaryFormat.FIAT.repeatOptionalDecimals(0, 0);
    private DecimalFormat decimalFormat = new DecimalFormat("#.#");

    @Inject
    public BSFormatter() {
        /*  if (user.tradeCurrencyProperty().get() == null)
        setFiatCurrencyCode(CurrencyUtil.getDefaultFiatCurrencyAsCode());
          else if (user.tradeCurrencyProperty().get() != null)
        setFiatCurrencyCode(user.tradeCurrencyProperty().get().getCode());
            
          user.tradeCurrencyProperty().addListener((ov, oldValue, newValue) -> {
        if (newValue != null)
            setFiatCurrencyCode(newValue.getCode());
          });*/
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Config
    ///////////////////////////////////////////////////////////////////////////////////////////

    public void useMilliBitFormat(boolean useMilliBit) {
        this.useMilliBit = useMilliBit;
        coinFormat = getMonetaryFormat();
        scale = useMilliBit ? 0 : 3;
    }

    /**
     * Note that setting the locale does not set the currency as it might be independent.
     */
    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    private MonetaryFormat getMonetaryFormat() {
        if (useMilliBit)
            return MonetaryFormat.MBTC;
        else
            return MonetaryFormat.BTC.minDecimals(2).repeatOptionalDecimals(1, 6);
    }

    /*  public void setFiatCurrencyCode(String currencyCode) {
    this.currencyCode = currencyCode;
    fiatFormat.code(0, currencyCode);
      }*/

    ///////////////////////////////////////////////////////////////////////////////////////////
    // BTC
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatCoin(Coin coin) {
        if (coin != null) {
            try {
                return coinFormat.noCode().format(coin).toString();
            } catch (Throwable t) {
                log.warn("Exception at formatBtc: " + t.toString());
                return "";
            }
        } else {
            return "";
        }
    }

    public String formatCoinWithCode(Coin coin) {
        if (coin != null) {
            try {
                // we don't use the code feature from coinFormat as it does automatic switching between mBTC and BTC and
                // pre and post fixing
                return coinFormat.postfixCode().format(coin).toString();
            } catch (Throwable t) {
                log.warn("Exception at formatBtcWithCode: " + t.toString());
                return "";
            }
        } else {
            return "";
        }
    }

    public Coin parseToCoin(String input) {
        if (input != null && input.length() > 0) {
            try {
                return coinFormat.parse(cleanInput(input));
            } catch (Throwable t) {
                log.warn("Exception at parseToBtc: " + t.toString());
                return Coin.ZERO;
            }
        } else {
            return Coin.ZERO;
        }
    }

    /**
     * Converts to a coin with max. 4 decimal places. Last place gets rounded.
     * 0.01234 -> 0.0123
     * 0.01235 -> 0.0124
     *
     * @param input
     * @return
     */
    public Coin parseToCoinWith4Decimals(String input) {
        try {
            return Coin.valueOf(new BigDecimal(parseToCoin(cleanInput(input)).value)
                    .setScale(-scale - 1, BigDecimal.ROUND_HALF_UP).setScale(scale + 1).toBigInteger().longValue());
        } catch (Throwable t) {
            if (input != null && input.length() > 0)
                log.warn("Exception at parseToCoinWith4Decimals: " + t.toString());
            return Coin.ZERO;
        }
    }

    public boolean hasBtcValidDecimals(String input) {
        return parseToCoin(input).equals(parseToCoinWith4Decimals(input));
    }

    /**
     * Transform a coin with the properties defined in the format (used to reduce decimal places)
     *
     * @param coin The coin which should be transformed
     * @return The transformed coin
     */
    public Coin reduceTo4Decimals(Coin coin) {
        return parseToCoin(formatCoin(coin));
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // FIAT
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatFiat(Fiat fiat) {
        if (fiat != null) {
            try {
                return fiatFormat.noCode().format(fiat).toString();
            } catch (Throwable t) {
                log.warn("Exception at formatFiat: " + t.toString());
                return "N/A " + fiat.getCurrencyCode();
            }
        } else {
            return "N/A";
        }
    }

    private String formatFiatWithCode(Fiat fiat) {
        if (fiat != null) {
            try {
                return fiatFormat.noCode().format(fiat).toString() + " " + fiat.getCurrencyCode();
            } catch (Throwable t) {
                log.warn("Exception at formatFiatWithCode: " + t.toString());
                return "N/A " + fiat.getCurrencyCode();
            }
        } else {
            return "N/A";
        }
    }

    private Fiat parseToFiat(String input, String currencyCode) {
        if (input != null && input.length() > 0) {
            try {
                return Fiat.parseFiat(currencyCode, cleanInput(input));
            } catch (Exception e) {
                log.warn("Exception at parseToFiat: " + e.toString());
                return Fiat.valueOf(currencyCode, 0);
            }

        } else {
            return Fiat.valueOf(currencyCode, 0);
        }
    }

    /**
     * Converts to a fiat with max. 2 decimal places. Last place gets rounded.
     * 0.234 -> 0.23
     * 0.235 -> 0.24
     *
     * @param input
     * @return
     */

    public Fiat parseToFiatWithPrecision(String input, String currencyCode) {
        if (input != null && input.length() > 0) {
            try {
                return parseToFiat(
                        new BigDecimal(cleanInput(input)).setScale(2, BigDecimal.ROUND_HALF_UP).toString(),
                        currencyCode);
            } catch (Throwable t) {
                log.warn("Exception at parseToFiatWithPrecision: " + t.toString());
                return Fiat.valueOf(currencyCode, 0);
            }

        }
        return Fiat.valueOf(currencyCode, 0);
    }

    public boolean isFiatAlteredWhenPrecisionApplied(String input, String currencyCode) {
        return parseToFiat(input, currencyCode).equals(parseToFiatWithPrecision(input, currencyCode));
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Volume
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatVolume(Fiat fiat) {
        return formatFiat(fiat);
    }

    public String formatVolumeWithCode(Fiat fiat) {
        return formatFiatWithCode(fiat);
    }

    public String formatVolumeLabel(String currencyCode) {
        return formatVolumeLabel(currencyCode, "");
    }

    public String formatVolumeLabel(String currencyCode, String postFix) {
        return CurrencyUtil.getNameByCode(currencyCode) + " amount" + postFix;
    }

    public String formatMinVolumeAndVolume(Offer offer) {
        return formatVolume(offer.getMinOfferVolume()) + " - " + formatVolume(offer.getOfferVolume());
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Amount
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatAmount(Offer offer) {
        return formatCoin(offer.getAmount());
    }

    public String formatAmountWithMinAmount(Offer offer) {
        return formatCoin(offer.getMinAmount()) + " - " + formatCoin(offer.getAmount());
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Price
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatPrice(Fiat fiat) {
        if (fiat != null) {
            final String currencyCode = fiat.getCurrencyCode();
            if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
                decimalFormat.setMinimumFractionDigits(8);
                decimalFormat.setMaximumFractionDigits(8);
                final double value = fiat.value != 0 ? 10000D / fiat.value : 0;
                return decimalFormat.format(MathUtils.roundDouble(value, 8)).replace(",", ".");
            } else
                return formatFiat(fiat);
        } else {
            return "N/A";
        }
    }

    public String formatPriceWithCode(Fiat fiat) {
        return formatPrice(fiat) + " " + getCurrencyPair(fiat.getCurrencyCode());
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Market price
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatMarketPrice(double price, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return formatMarketPrice(price, 2);
        else
            return formatMarketPrice(price, 8);
    }

    public String formatMarketPrice(double price, int precision) {
        return formatRoundedDoubleWithPrecision(price, precision);
    }

    ///////////////////////////////////////////////////////////////////////////////////////////
    // Other
    ///////////////////////////////////////////////////////////////////////////////////////////

    public String formatRoundedDoubleWithPrecision(double value, int precision) {
        decimalFormat.setMinimumFractionDigits(precision);
        decimalFormat.setMaximumFractionDigits(precision);
        return decimalFormat.format(MathUtils.roundDouble(value, precision)).replace(",", ".");
    }

    public String getDirectionWithCode(Offer.Direction direction, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return (direction == Offer.Direction.BUY) ? "Buy BTC" : "Sell BTC";
        else
            return (direction == Offer.Direction.SELL) ? "Buy " + currencyCode : "Sell " + currencyCode;
    }

    public String getDirectionWithCodeDetailed(Offer.Direction direction, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return (direction == Offer.Direction.BUY) ? "buying BTC with " + currencyCode
                    : "selling BTC for " + currencyCode;
        else
            return (direction == Offer.Direction.SELL) ? "buying " + currencyCode + " (selling BTC)"
                    : "selling " + currencyCode + " (buying BTC)";
    }

    public String arbitratorAddressesToString(List<NodeAddress> nodeAddresses) {
        return nodeAddresses.stream().map(NodeAddress::getFullAddress).collect(Collectors.joining(", "));
    }

    public String languageCodesToString(List<String> languageLocales) {
        return languageLocales.stream().map(LanguageUtil::getDisplayName).collect(Collectors.joining(", "));
    }

    public String formatDateTime(Date date) {
        if (date != null) {
            DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
            DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
            return dateFormatter.format(date) + " " + timeFormatter.format(date);
        } else {
            return "";
        }
    }

    public String formatDateTimeSpan(Date dateFrom, Date dateTo) {
        if (dateFrom != null && dateTo != null) {
            DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
            DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
            return dateFormatter.format(dateFrom) + " " + timeFormatter.format(dateFrom) + " - "
                    + timeFormatter.format(dateTo);
        } else {
            return "";
        }
    }

    public String formatTime(Date date) {
        if (date != null) {
            DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.DEFAULT, locale);
            return timeFormatter.format(date);
        } else {
            return "";
        }
    }

    public String formatDate(Date date) {
        if (date != null) {
            DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
            return dateFormatter.format(date);
        } else {
            return "";
        }
    }

    public String formatToPercentWithSymbol(double value) {
        return formatToPercent(value) + "%";
    }

    public String formatPercentagePrice(double value) {
        return formatToPercentWithSymbol(value);
    }

    public String formatToPercent(double value) {
        DecimalFormat decimalFormat = new DecimalFormat("#.##");
        decimalFormat.setMinimumFractionDigits(2);
        decimalFormat.setMaximumFractionDigits(2);
        return decimalFormat.format(MathUtils.roundDouble(value * 100.0, 2)).replace(",", ".");
    }

    public double parseNumberStringToDouble(String percentString) throws NumberFormatException {
        try {
            String input = percentString.replace(",", ".");
            input = input.replace(" ", "");
            return Double.parseDouble(input);
        } catch (NumberFormatException e) {
            throw e;
        }
    }

    public double parsePercentStringToDouble(String percentString) throws NumberFormatException {
        try {
            String input = percentString.replace("%", "");
            input = input.replace(",", ".");
            input = input.replace(" ", "");
            double value = Double.parseDouble(input);
            return value / 100d;
        } catch (NumberFormatException e) {
            throw e;
        }
    }

    private String cleanInput(String input) {
        input = input.replace(",", ".");
        // don't use String.valueOf(Double.parseDouble(input)) as return value as it gives scientific
        // notation (1.0E-6) which screw up coinFormat.parse
        //noinspection ResultOfMethodCallIgnored
        Double.parseDouble(input);
        return input;
    }

    public String formatDurationAsWords(long durationMillis) {
        return formatDurationAsWords(durationMillis, false);
    }

    public static String formatDurationAsWords(long durationMillis, boolean showSeconds) {
        String format;
        if (showSeconds)
            format = "d\' days, \'H\' hours, \'m\' minutes, \'s\' seconds\'";
        else
            format = "d\' days, \'H\' hours, \'m\' minutes\'";
        String duration = DurationFormatUtils.formatDuration(durationMillis, format);
        String tmp;
        duration = " " + duration;
        tmp = StringUtils.replaceOnce(duration, " 0 days", "");
        if (tmp.length() != duration.length()) {
            duration = tmp;
            tmp = StringUtils.replaceOnce(tmp, " 0 hours", "");
            if (tmp.length() != duration.length()) {
                tmp = StringUtils.replaceOnce(tmp, " 0 minutes", "");
                duration = tmp;
                if (tmp.length() != tmp.length()) {
                    duration = StringUtils.replaceOnce(tmp, " 0 seconds", "");
                }
            }
        }

        if (duration.length() != 0) {
            duration = duration.substring(1);
        }

        tmp = StringUtils.replaceOnce(duration, " 0 seconds", "");

        if (tmp.length() != duration.length()) {
            duration = tmp;
            tmp = StringUtils.replaceOnce(tmp, " 0 minutes", "");
            if (tmp.length() != duration.length()) {
                duration = tmp;
                tmp = StringUtils.replaceOnce(tmp, " 0 hours", "");
                if (tmp.length() != duration.length()) {
                    duration = StringUtils.replaceOnce(tmp, " 0 days", "");
                }
            }
        }

        duration = " " + duration;
        duration = StringUtils.replaceOnce(duration, " 1 seconds", " 1 second");
        duration = StringUtils.replaceOnce(duration, " 1 minutes", " 1 minute");
        duration = StringUtils.replaceOnce(duration, " 1 hours", " 1 hour");
        duration = StringUtils.replaceOnce(duration, " 1 days", " 1 day");
        if (duration.startsWith(" ,"))
            duration = duration.replace(" ,", "");
        else if (duration.startsWith(", "))
            duration = duration.replace(", ", "");
        if (duration.equals(""))
            duration = "Trade period is over";
        return duration.trim();
    }

    public String booleanToYesNo(boolean value) {
        return value ? "Yes" : "No";
    }

    public String formatBitcoinNetwork(BitcoinNetwork bitcoinNetwork) {
        switch (bitcoinNetwork) {
        case MAINNET:
            return "Mainnet";
        case TESTNET:
            return "Testnet";
        case REGTEST:
            return "Regtest";
        default:
            return "";
        }
    }

    public String getDirectionBothSides(Offer.Direction direction, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return direction == Offer.Direction.BUY ? "Offerer as BTC buyer / Taker as BTC seller"
                    : "Offerer as BTC seller / Taker as BTC buyer";
        else
            return direction == Offer.Direction.SELL
                    ? "Offerer as " + currencyCode + " buyer / Taker as " + currencyCode + " seller"
                    : "Offerer as " + currencyCode + " seller / Taker as " + currencyCode + " buyer";
    }

    public String getDirectionForBuyer(boolean isMyOffer, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return isMyOffer ? "You are buying BTC as offerer / Taker is selling BTC"
                    : "You are buying BTC as taker / Offerer is selling BTC";
        else
            return isMyOffer
                    ? "You are selling " + currencyCode + " as offerer / Taker is buying " + currencyCode + ""
                    : "You are selling " + currencyCode + " as taker / Offerer is buying " + currencyCode + "";
    }

    public String getDirectionForSeller(boolean isMyOffer, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return isMyOffer ? "You are selling BTC as offerer / Taker is buying BTC"
                    : "You are selling BTC as taker / Offerer is buying BTC";
        else
            return isMyOffer
                    ? "You are buying " + currencyCode + " as offerer / Taker is selling " + currencyCode + ""
                    : "You are buying " + currencyCode + " as taker / Offerer is selling " + currencyCode + "";
    }

    public String getDirectionForTakeOffer(Offer.Direction direction, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return direction == Offer.Direction.BUY ? "You are selling BTC (buying " + currencyCode + ")"
                    : "You are buying BTC (selling " + currencyCode + ")";
        else
            return direction == Offer.Direction.SELL ? "You are selling " + currencyCode + " (buying BTC)"
                    : "You are buying " + currencyCode + " (selling BTC)";
    }

    public String getOfferDirectionForCreateOffer(Offer.Direction direction, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return direction == Offer.Direction.BUY ? "You are creating an offer to buy BTC"
                    : "You are creating an offer to sell BTC";
        else
            return direction == Offer.Direction.SELL
                    ? "You are creating an offer to buy " + currencyCode + " (selling BTC)"
                    : "You are creating an offer to sell " + currencyCode + " (buying BTC)";
    }

    public String getRole(boolean isBuyerOffererAndSellerTaker, boolean isOfferer, String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode)) {
            if (isBuyerOffererAndSellerTaker)
                return isOfferer ? "BTC buyer as offerer" : "BTC seller as taker";
            else
                return isOfferer ? "BTC seller as offerer" : "BTC buyer as taker";
        } else {
            if (isBuyerOffererAndSellerTaker)
                return isOfferer ? currencyCode + " seller as offerer" : currencyCode + " buyer as taker";
            else
                return isOfferer ? currencyCode + " buyer as offerer" : currencyCode + " seller as taker";
        }

    }

    public String formatBytes(long bytes) {
        double kb = 1024;
        double mb = kb * kb;
        DecimalFormat decimalFormat = new DecimalFormat("#.##");
        if (bytes < kb)
            return bytes + " bytes";
        else if (bytes < mb)
            return decimalFormat.format(bytes / kb) + " KB";
        else
            return decimalFormat.format(bytes / mb) + " MB";
    }

    public String getCurrencyPair(String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return "BTC/" + currencyCode;
        else
            return currencyCode + "/BTC";
    }

    public String getCounterCurrency(String currencyCode) {
        if (CurrencyUtil.isFiatCurrency(currencyCode))
            return currencyCode;
        else
            return "BTC";
    }

    public String getBaseCurrency(String currencyCode) {
        if (CurrencyUtil.isCryptoCurrency(currencyCode))
            return currencyCode;
        else
            return "BTC";
    }

    public String getCounterCurrencyAndCurrencyPair(String currencyCode) {
        return getCounterCurrency(currencyCode) + " (" + getCurrencyPair(currencyCode) + ")";
    }

    public String getCurrencyNameAndCurrencyPair(String currencyCode) {
        return CurrencyUtil.getNameByCode(currencyCode) + " (" + getCurrencyPair(currencyCode) + ")";
    }

    public String getPriceWithCurrencyCode(String currencyCode) {
        if (CurrencyUtil.isCryptoCurrency(currencyCode))
            return "Price in BTC for 1 " + currencyCode;
        else
            return "Price in " + currencyCode + " for 1 BTC";
    }
}