package org.multibit.exchange; import org.joda.money.CurrencyUnit; import org.joda.money.IllegalCurrencyException; import org.joda.money.Money; import org.joda.money.format.MoneyAmountStyle; import org.joda.money.format.MoneyFormatter; import org.joda.money.format.MoneyFormatterBuilder; import org.multibit.controller.Controller; import org.multibit.model.exchange.ExchangeData; import org.multibit.model.exchange.ExchangeModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.*; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.ParseException; import java.util.*; public enum CurrencyConverter { INSTANCE; private static final Logger log = LoggerFactory.getLogger(CurrencyConverter.class); private Controller controller; private Collection<CurrencyConverterListener> listeners; public static final BigInteger NUMBER_OF_SATOSHI_IN_ONE_BITCOIN = BigInteger.valueOf(100000000); // 8 zeros public static final int NUMBER_OF_DECIMAL_POINTS_IN_A_BITCOIN = 8; // Extra digits used in calculation. public static final int ADDITIONAL_CALCULATION_DIGITS = 16; // This is the Bitcoin currency unit, denominated in satoshi with 0 decimal places. public CurrencyUnit BITCOIN_CURRENCY_UNIT; /** * The currency unit for the currency being converted. */ private CurrencyUnit currencyUnit; /** * MoneyFormatter without currency code */ MoneyFormatter moneyFormatter; /** * MoneyFormatter with currency code */ MoneyFormatter moneyFormatterWithCurrencyCode; /** * The exchange rate i.e the value of 1 BTC in the currency. */ private BigDecimal rate; /** * The rate rate in terms of satoshi i.e. value of 1 satoshi in the currency */ private BigDecimal rateDividedByNumberOfSatoshiInOneBitcoin; private String groupingSeparator; /** * Map of currency code to currency info. */ private Map<String, CurrencyInfo> currencyCodeToInfoMap; /** * Map of currency code to currency description (from OpenExchangeRates) */ private Map<String, String> currencyCodeToDescriptionMap; public void initialise(Controller controller) { // Initialise conversion currency. String currencyCode = controller.getModel().getUserPreference(ExchangeModel.TICKER_FIRST_ROW_CURRENCY); String exchange = controller.getModel().getUserPreference(ExchangeModel.TICKER_FIRST_ROW_EXCHANGE); String newCurrencyCode = currencyCode; if (ExchangeData.BITCOIN_CHARTS_EXCHANGE_NAME.equals(exchange)) { // Use only the last three characters - the currency code. if (currencyCode.length() >= 3) { newCurrencyCode = currencyCode.substring(currencyCode.length() - 3); } } initialise(controller, newCurrencyCode); } public void initialise(Controller controller, String currencyCode) { this.controller = controller; try { BITCOIN_CURRENCY_UNIT = CurrencyUnit.of("BTC"); if (currencyCode != null && !"".equals(currencyCode)) { currencyUnit = CurrencyUnit.of(currencyCode); } else { currencyUnit = CurrencyUnit.of("USD"); } } catch (IllegalCurrencyException ice) { ice.printStackTrace(); // Default to USD. currencyUnit = CurrencyUnit.of("USD"); } // Exchange rate is unknown. rate = null; rateDividedByNumberOfSatoshiInOneBitcoin = null; // Setup listeners listeners = new ArrayList<CurrencyConverterListener>(); // Initialise currency info map. currencyCodeToInfoMap = new HashMap<String, CurrencyInfo>(); currencyCodeToInfoMap.put("USD", new CurrencyInfo("USD", "$", true)); currencyCodeToInfoMap.put("AUD", new CurrencyInfo("AUD", "AU$", true)); currencyCodeToInfoMap.put("CAD", new CurrencyInfo("CAD", "CA$", true)); currencyCodeToInfoMap.put("NZD", new CurrencyInfo("NZD", "NZ$", true)); currencyCodeToInfoMap.put("SGD", new CurrencyInfo("SGD", "SG$", true)); currencyCodeToInfoMap.put("HKD", new CurrencyInfo("HKD", "HK$", true)); currencyCodeToInfoMap.put("GBP", new CurrencyInfo("GBP", "\u00A3", true)); currencyCodeToInfoMap.put("EUR", new CurrencyInfo("EUR", "\u20AC", true)); currencyCodeToInfoMap.put("CHF", new CurrencyInfo("CHF", " CHF", false)); currencyCodeToInfoMap.put("JPY", new CurrencyInfo("JPY", "\u00A5", true)); currencyCodeToInfoMap.put("CNY", new CurrencyInfo("CNY", "\u5143", false)); currencyCodeToInfoMap.put("RUB", new CurrencyInfo("RUB", "\u0440\u0443\u0431", false)); currencyCodeToInfoMap.put("SEK", new CurrencyInfo("SEK", "\u006B\u0072", true)); currencyCodeToInfoMap.put("DKK", new CurrencyInfo("DKK", "\u006B\u0072.", true)); currencyCodeToInfoMap.put("THB", new CurrencyInfo("THB", "\u0E3F", true)); currencyCodeToInfoMap.put("PLN", new CurrencyInfo("PLN", "\u007A\u0142", false)); updateFormatters(); // Initialise currency description map. currencyCodeToDescriptionMap = new HashMap<String, String>(); currencyCodeToDescriptionMap.put("AED", "United Arab Emirates Dirham"); currencyCodeToDescriptionMap.put("AFN", "Afghan Afghani"); currencyCodeToDescriptionMap.put("ALL", "Albanian Lek"); currencyCodeToDescriptionMap.put("AMD", "Armenian Dram"); currencyCodeToDescriptionMap.put("ANG", "Netherlands Antillean Guilder"); currencyCodeToDescriptionMap.put("AOA", "Angolan Kwanza"); currencyCodeToDescriptionMap.put("ARS", "Argentine Peso"); currencyCodeToDescriptionMap.put("AUD", "Australian Dollar"); currencyCodeToDescriptionMap.put("AWG", "Aruban Florin"); currencyCodeToDescriptionMap.put("AZN", "Azerbaijani Manat"); currencyCodeToDescriptionMap.put("BAM", "Bosnia-Herzegovina Convertible Mark"); currencyCodeToDescriptionMap.put("BBD", "Barbadian Dollar"); currencyCodeToDescriptionMap.put("BDT", "Bangladeshi Taka"); currencyCodeToDescriptionMap.put("BGN", "Bulgarian Lev"); currencyCodeToDescriptionMap.put("BHD", "Bahraini Dinar"); currencyCodeToDescriptionMap.put("BIF", "Burundian Franc"); currencyCodeToDescriptionMap.put("BMD", "Bermudan Dollar"); currencyCodeToDescriptionMap.put("BND", "Brunei Dollar"); currencyCodeToDescriptionMap.put("BOB", "Bolivian Boliviano"); currencyCodeToDescriptionMap.put("BRL", "Brazilian Real"); currencyCodeToDescriptionMap.put("BSD", "Bahamian Dollar"); currencyCodeToDescriptionMap.put("BTC", "Bitcoin"); currencyCodeToDescriptionMap.put("BTN", "Bhutanese Ngultrum"); currencyCodeToDescriptionMap.put("BWP", "Botswanan Pula"); currencyCodeToDescriptionMap.put("BYR", "Belarusian Ruble"); currencyCodeToDescriptionMap.put("BZD", "Belize Dollar"); currencyCodeToDescriptionMap.put("CAD", "Canadian Dollar"); currencyCodeToDescriptionMap.put("CDF", "Congolese Franc"); currencyCodeToDescriptionMap.put("CHF", "Swiss Franc"); currencyCodeToDescriptionMap.put("CLP", "Chilean Peso"); currencyCodeToDescriptionMap.put("CNY", "Chinese Yuan"); currencyCodeToDescriptionMap.put("COP", "Colombian Peso"); currencyCodeToDescriptionMap.put("CRC", "Costa Rican Col\u00F3n"); currencyCodeToDescriptionMap.put("CUP", "Cuban Peso"); currencyCodeToDescriptionMap.put("CVE", "Cape Verdean Escudo"); currencyCodeToDescriptionMap.put("CZK", "Czech Republic Koruna"); currencyCodeToDescriptionMap.put("DJF", "Djiboutian Franc"); currencyCodeToDescriptionMap.put("DKK", "Danish Krone"); currencyCodeToDescriptionMap.put("DOP", "Dominican Peso"); currencyCodeToDescriptionMap.put("DZD", "Algerian Dinar"); currencyCodeToDescriptionMap.put("EGP", "Egyptian Pound"); currencyCodeToDescriptionMap.put("ETB", "Ethiopian Birr"); currencyCodeToDescriptionMap.put("EUR", "Euro"); currencyCodeToDescriptionMap.put("FJD", "Fijian Dollar"); currencyCodeToDescriptionMap.put("FKP", "Falkland Islands Pound"); currencyCodeToDescriptionMap.put("GBP", "British Pound Sterling"); currencyCodeToDescriptionMap.put("GEL", "Georgian Lari"); currencyCodeToDescriptionMap.put("GHS", "Ghanaian Cedi"); currencyCodeToDescriptionMap.put("GIP", "Gibraltar Pound"); currencyCodeToDescriptionMap.put("GMD", "Gambian Dalasi"); currencyCodeToDescriptionMap.put("GNF", "Guinean Franc"); currencyCodeToDescriptionMap.put("GTQ", "Guatemalan Quetzal"); currencyCodeToDescriptionMap.put("GYD", "Guyanaese Dollar"); currencyCodeToDescriptionMap.put("HKD", "Hong Kong Dollar"); currencyCodeToDescriptionMap.put("HNL", "Honduran Lempira"); currencyCodeToDescriptionMap.put("HRK", "Croatian Kuna"); currencyCodeToDescriptionMap.put("HTG", "Haitian Gourde"); currencyCodeToDescriptionMap.put("HUF", "Hungarian Forint"); currencyCodeToDescriptionMap.put("IDR", "Indonesian Rupiah"); currencyCodeToDescriptionMap.put("ILS", "Israeli New Sheqel"); currencyCodeToDescriptionMap.put("INR", "Indian Rupee"); currencyCodeToDescriptionMap.put("IQD", "Iraqi Dinar"); currencyCodeToDescriptionMap.put("IRR", "Iranian Rial"); currencyCodeToDescriptionMap.put("ISK", "Icelandic Kr\u00F3na"); currencyCodeToDescriptionMap.put("JMD", "Jamaican Dollar"); currencyCodeToDescriptionMap.put("JOD", "Jordanian Dinar"); currencyCodeToDescriptionMap.put("JPY", "Japanese Yen"); currencyCodeToDescriptionMap.put("KES", "Kenyan Shilling"); currencyCodeToDescriptionMap.put("KGS", "Kyrgystani Som"); currencyCodeToDescriptionMap.put("KHR", "Cambodian Riel"); currencyCodeToDescriptionMap.put("KMF", "Comorian Franc"); currencyCodeToDescriptionMap.put("KPW", "North Korean Won"); currencyCodeToDescriptionMap.put("KRW", "South Korean Won"); currencyCodeToDescriptionMap.put("KWD", "Kuwaiti Dinar"); currencyCodeToDescriptionMap.put("KYD", "Cayman Islands Dollar"); currencyCodeToDescriptionMap.put("KZT", "Kazakhstani Tenge"); currencyCodeToDescriptionMap.put("LAK", "Laotian Kip"); currencyCodeToDescriptionMap.put("LBP", "Lebanese Pound"); currencyCodeToDescriptionMap.put("LKR", "Sri Lankan Rupee"); currencyCodeToDescriptionMap.put("LRD", "Liberian Dollar"); currencyCodeToDescriptionMap.put("LSL", "Lesotho Loti"); currencyCodeToDescriptionMap.put("LTL", "Lithuanian Litas"); currencyCodeToDescriptionMap.put("LVL", "Latvian Lats"); currencyCodeToDescriptionMap.put("LYD", "Libyan Dinar"); currencyCodeToDescriptionMap.put("MAD", "Moroccan Dirham"); currencyCodeToDescriptionMap.put("MDL", "Moldovan Leu"); currencyCodeToDescriptionMap.put("MGA", "Malagasy Ariary"); currencyCodeToDescriptionMap.put("MKD", "Macedonian Denar"); currencyCodeToDescriptionMap.put("MMK", "Myanma Kyat"); currencyCodeToDescriptionMap.put("MNT", "Mongolian Tugrik"); currencyCodeToDescriptionMap.put("MOP", "Macanese Pataca"); currencyCodeToDescriptionMap.put("MRO", "Mauritanian Ouguiya"); currencyCodeToDescriptionMap.put("MUR", "Mauritian Rupee"); currencyCodeToDescriptionMap.put("MVR", "Maldivian Rufiyaa"); currencyCodeToDescriptionMap.put("MWK", "Malawian Kwacha"); currencyCodeToDescriptionMap.put("MXN", "Mexican Peso"); currencyCodeToDescriptionMap.put("MYR", "Malaysian Ringgit"); currencyCodeToDescriptionMap.put("MZN", "Mozambican Metical"); currencyCodeToDescriptionMap.put("NAD", "Namibian Dollar"); currencyCodeToDescriptionMap.put("NGN", "Nigerian Naira"); currencyCodeToDescriptionMap.put("NIO", "Nicaraguan C\u00F3rdoba"); currencyCodeToDescriptionMap.put("NOK", "Norwegian Krone"); currencyCodeToDescriptionMap.put("NPR", "Nepalese Rupee"); currencyCodeToDescriptionMap.put("NZD", "New Zealand Dollar"); currencyCodeToDescriptionMap.put("OMR", "Omani Rial"); currencyCodeToDescriptionMap.put("PAB", "Panamanian Balboa"); currencyCodeToDescriptionMap.put("PEN", "Peruvian Nuevo Sol"); currencyCodeToDescriptionMap.put("PGK", "Papua New Guinean Kina"); currencyCodeToDescriptionMap.put("PHP", "Philippine Peso"); currencyCodeToDescriptionMap.put("PKR", "Pakistani Rupee"); currencyCodeToDescriptionMap.put("PLN", "Polish Zloty"); currencyCodeToDescriptionMap.put("PYG", "Paraguayan Guarani"); currencyCodeToDescriptionMap.put("QAR", "Qatari Rial"); currencyCodeToDescriptionMap.put("RON", "Romanian Leu"); currencyCodeToDescriptionMap.put("RSD", "Serbian Dinar"); currencyCodeToDescriptionMap.put("RUB", "Russian Ruble"); currencyCodeToDescriptionMap.put("RWF", "Rwandan Franc"); currencyCodeToDescriptionMap.put("SAR", "Saudi Riyal"); currencyCodeToDescriptionMap.put("SBD", "Solomon Islands Dollar"); currencyCodeToDescriptionMap.put("SCR", "Seychellois Rupee"); currencyCodeToDescriptionMap.put("SDG", "Sudanese Pound"); currencyCodeToDescriptionMap.put("SEK", "Swedish Krona"); currencyCodeToDescriptionMap.put("SGD", "Singapore Dollar"); currencyCodeToDescriptionMap.put("SHP", "Saint Helena Pound"); currencyCodeToDescriptionMap.put("SLL", "Sierra Leonean Leone"); currencyCodeToDescriptionMap.put("SOS", "Somali Shilling"); currencyCodeToDescriptionMap.put("SRD", "Surinamese Dollar"); currencyCodeToDescriptionMap.put("STD", "S\u0101o Tom\u00E9 and Principe Dobra"); currencyCodeToDescriptionMap.put("SYP", "Syrian Pound"); currencyCodeToDescriptionMap.put("SZL", "Swazi Lilangeni"); currencyCodeToDescriptionMap.put("THB", "Thai Baht"); currencyCodeToDescriptionMap.put("TJS", "Tajikistani Somoni"); currencyCodeToDescriptionMap.put("TMT", "Turkmenistani Manat"); currencyCodeToDescriptionMap.put("TND", "Tunisian Dinar"); currencyCodeToDescriptionMap.put("TOP", "Tongan Pa'anga"); currencyCodeToDescriptionMap.put("TRY", "Turkish Lira"); currencyCodeToDescriptionMap.put("TTD", "Trinidad and Tobago Dollar"); currencyCodeToDescriptionMap.put("TWD", "New Taiwan Dollar"); currencyCodeToDescriptionMap.put("TZS", "Tanzanian Shilling"); currencyCodeToDescriptionMap.put("UAH", "Ukrainian Hryvnia"); currencyCodeToDescriptionMap.put("UGX", "Ugandan Shilling"); currencyCodeToDescriptionMap.put("USD", "United States Dollar"); currencyCodeToDescriptionMap.put("UYU", "Uruguayan Peso"); currencyCodeToDescriptionMap.put("UZS", "Uzbekistan Som"); currencyCodeToDescriptionMap.put("VEF", "Venezuelan Bol\u00EDvar"); currencyCodeToDescriptionMap.put("VND", "Vietnamese Dong"); currencyCodeToDescriptionMap.put("VUV", "Vanuatu Vatu"); currencyCodeToDescriptionMap.put("WST", "Samoan Tala"); currencyCodeToDescriptionMap.put("XAF", "CFA Franc BEAC"); currencyCodeToDescriptionMap.put("XCD", "East Caribbean Dollar"); currencyCodeToDescriptionMap.put("XDR", "Special Drawing Rights"); currencyCodeToDescriptionMap.put("XOF", "CFA Franc BCEAO"); currencyCodeToDescriptionMap.put("XPF", "CFP Franc"); currencyCodeToDescriptionMap.put("YER", "Yemeni Rial"); currencyCodeToDescriptionMap.put("ZAR", "South African Rand"); currencyCodeToDescriptionMap.put("ZMK", "Zambian Kwacha"); currencyCodeToDescriptionMap.put("ZWL", "Zimbabwean Dollar"); } public void updateFormatters() { moneyFormatter = getMoneyFormatter(false); moneyFormatterWithCurrencyCode = getMoneyFormatter(true); DecimalFormat fiatFormatter = (DecimalFormat) DecimalFormat.getInstance(controller.getLocaliser().getLocale()); groupingSeparator = String.valueOf(fiatFormatter.getDecimalFormatSymbols().getGroupingSeparator()); } /** * Convert a number of satoshis to fiat * @param bitcoinAmountInSatoshi in satoshis * @return equivalent fiat amount */ public Money convertFromBTCToFiat(BigInteger bitcoinAmountInSatoshi) { if (rate == null) { return null; } else { Money bitcoin = Money.of(BITCOIN_CURRENCY_UNIT, new BigDecimal(bitcoinAmountInSatoshi)); Money fiatAmount = null; if (rateDividedByNumberOfSatoshiInOneBitcoin != null) { fiatAmount = bitcoin.convertedTo(currencyUnit, rateDividedByNumberOfSatoshiInOneBitcoin, RoundingMode.HALF_EVEN); } return fiatAmount; } } public CurrencyConverterResult convertFromFiatToBTC(String fiat) { if (rate == null || rate.equals(BigDecimal.ZERO)) { return new CurrencyConverterResult(); } else { if (fiat == null || fiat.trim().equals("")) { return new CurrencyConverterResult(); } Money btcAmount = null; DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(controller.getLocaliser().getLocale()); formatter.setParseBigDecimal(true); // Convert spaces to non breakable space. fiat = fiat.replaceAll(" ", "\u00A0"); try { BigDecimal parsedFiat = (BigDecimal)formatter.parse(fiat); Money fiatMoney = Money.of(currencyUnit, parsedFiat); btcAmount = fiatMoney.convertedTo(BITCOIN_CURRENCY_UNIT, new BigDecimal(NUMBER_OF_SATOSHI_IN_ONE_BITCOIN).divide(rate, BITCOIN_CURRENCY_UNIT.getDecimalPlaces() + ADDITIONAL_CALCULATION_DIGITS, RoundingMode.HALF_EVEN), RoundingMode.HALF_EVEN); CurrencyConverterResult result = new CurrencyConverterResult(); result.setBtcMoneyValid(true); result.setBtcMoney(btcAmount); result.setFiatMoneyValid(true); result.setFiatMoney(fiatMoney); return result; } catch (ParseException pe) { log.debug("convertFromFiatToBTC: " + pe.getClass().getName() + " " + pe.getMessage()); CurrencyConverterResult result = new CurrencyConverterResult(); result.setBtcMoneyValid(false); result.setFiatMoneyValid(false); result.setFiatMessage(controller.getLocaliser().getString("currencyConverter.couldNotUnderstandAmount", new Object[]{fiat})); return result; } catch (ArithmeticException ae) { log.debug("convertFromFiatToBTC: " + ae.getClass().getName() + " " + ae.getMessage()); String currencyString = currencyUnit.getCurrencyCode(); if (currencyCodeToInfoMap.get(currencyString) != null) { currencyString = currencyCodeToInfoMap.get(currencyString).getCurrencySymbol(); } CurrencyConverterResult result = new CurrencyConverterResult(); result.setBtcMoneyValid(false); result.setFiatMoneyValid(false); result.setFiatMessage(controller.getLocaliser().getString("currencyConverter.fiatCanOnlyHaveSetDecimalPlaces", new Object[]{currencyString, currencyUnit.getDecimalPlaces()})); return result; } } } private MoneyFormatter getMoneyFormatter(boolean addCurrencySymbol) { MoneyFormatter moneyFormatter; // Suffix currency codes. String currencyCode = currencyUnit.getCurrencyCode(); CurrencyInfo currencyInfo = currencyCodeToInfoMap.get(currencyCode); if (currencyInfo == null) { // Create a default currency info with the raw currency code as a suffix, including a separator space currencyInfo = new CurrencyInfo(currencyCode, currencyCode, false); currencyInfo.setHasSeparatingSpace(true); } DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(controller.getLocaliser().getLocale()); char decimalSeparator = formatter.getDecimalFormatSymbols().getDecimalSeparator(); char groupingSeparator = formatter.getDecimalFormatSymbols().getGroupingSeparator(); MoneyAmountStyle moneyAmountStyle; if ('.' == decimalSeparator) { if (',' == groupingSeparator) { moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_POINT_GROUP3_COMMA; } else if (' ' == groupingSeparator || '\u00A0' == groupingSeparator) { moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_POINT_GROUP3_SPACE; } else { moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_POINT_NO_GROUPING; } } else { if (',' == decimalSeparator) { if ('.' == groupingSeparator) { moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_COMMA_GROUP3_DOT; } else if (' ' == groupingSeparator || '\u00A0' == groupingSeparator) { moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_COMMA_GROUP3_SPACE; } else { moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_COMMA_NO_GROUPING; } } else { // Do not really know - keep it simple. moneyAmountStyle = MoneyAmountStyle.ASCII_DECIMAL_POINT_NO_GROUPING; } } String separator; if (currencyInfo.hasSeparatingSpace) { separator = " "; } else { separator = ""; } if (currencyInfo.isPrefix()) { // Prefix currency code. if (addCurrencySymbol) { moneyFormatter = new MoneyFormatterBuilder().appendLiteral(currencyInfo.getCurrencySymbol()).appendLiteral(separator).appendAmount(moneyAmountStyle).toFormatter(controller.getLocaliser().getLocale()); } else { moneyFormatter = new MoneyFormatterBuilder().appendAmount(moneyAmountStyle).toFormatter(controller.getLocaliser().getLocale()); } } else { // Postfix currency code. if (addCurrencySymbol) { moneyFormatter = new MoneyFormatterBuilder().appendAmount(moneyAmountStyle).appendLiteral(separator).appendLiteral(currencyInfo.getCurrencySymbol()).toFormatter(controller.getLocaliser().getLocale()); } else { moneyFormatter = new MoneyFormatterBuilder().appendAmount(moneyAmountStyle).toFormatter(controller.getLocaliser().getLocale()); } } return moneyFormatter; } public String getFiatAsLocalisedString(Money money) { return getFiatAsLocalisedString(money, true, false); } public String getFiatAsLocalisedString(Money money, boolean addCurrencySymbol, boolean addParenthesis) { if (money == null) { return ""; } MoneyFormatter moneyFormatterToUse; if (addCurrencySymbol) { if (moneyFormatterWithCurrencyCode == null) { moneyFormatterWithCurrencyCode = getMoneyFormatter(true); } moneyFormatterToUse = moneyFormatterWithCurrencyCode; } else { if (moneyFormatter == null) { moneyFormatter = getMoneyFormatter(false); } moneyFormatterToUse = moneyFormatter; } if (groupingSeparator == null) { DecimalFormat fiatFormatter = (DecimalFormat) DecimalFormat.getInstance(controller.getLocaliser().getLocale()); groupingSeparator = String.valueOf(fiatFormatter.getDecimalFormatSymbols().getGroupingSeparator()); } String toReturn = moneyFormatterToUse.print(money); // Get rid of negative sign followed by thousand separator if (".".equals(groupingSeparator)) { // Escape regex. groupingSeparator = "\\."; } toReturn = toReturn.replaceAll("-" + groupingSeparator, "-"); if (addParenthesis) { toReturn = " (" + toReturn + ")"; } return toReturn; } public String getBTCAsLocalisedString(Money btcMoney) { DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(controller.getLocaliser().getLocale()); formatter.setMaximumFractionDigits(NUMBER_OF_DECIMAL_POINTS_IN_A_BITCOIN); String btcString = formatter.format(btcMoney.getAmount().divide(new BigDecimal(NUMBER_OF_SATOSHI_IN_ONE_BITCOIN))); return btcString; } /** * Parse a localised string and returns a Money denominated in Satoshi * @param btcString the BTC amount to parse, as a String * @return currencyConverterResult The result of the currency conversion */ public CurrencyConverterResult parseToBTC(String btcString) { return parseToBTC(btcString, controller.getLocaliser().getLocale()); } /** * Parse a non localised string and returns a Money denominated in Satoshi * @param btcString the BTC amount to parse, as a String * @return currencyConverterResult The result of the currency conversion */ public CurrencyConverterResult parseToBTCNotLocalised(String btcString) { return parseToBTC(btcString, Locale.ENGLISH); } private CurrencyConverterResult parseToBTC(String btcString, Locale locale) { if (btcString == null || btcString.equals("")) { return new CurrencyConverterResult(); } // Convert spaces to non breakable space. btcString = btcString.replaceAll(" ", "\u00A0"); Money btcAmount = null; DecimalFormat formatter = (DecimalFormat) DecimalFormat.getInstance(locale); formatter.setParseBigDecimal(true); try { BigDecimal parsedBTC = ((BigDecimal)formatter.parse(btcString)).movePointRight(NUMBER_OF_DECIMAL_POINTS_IN_A_BITCOIN); //log.debug("For locale " + controller.getLocaliser().getLocale().toString() + ", '" + btcString + "' parses to " + parsedBTC.toPlainString()); btcAmount = Money.of(BITCOIN_CURRENCY_UNIT, parsedBTC); CurrencyConverterResult result = new CurrencyConverterResult(); result.setBtcMoneyValid(true); result.setBtcMoney(btcAmount); return result; } catch (ParseException pe) { log.debug("parseToBTC: " + pe.getClass().getName() + " " + pe.getMessage()); CurrencyConverterResult result = new CurrencyConverterResult(); result.setBtcMoneyValid(false); result.setBtcMessage(controller.getLocaliser().getString("currencyConverter.couldNotUnderstandAmount", new Object[]{btcString})); return result; } catch (ArithmeticException ae) { log.debug("parseToBTC: " + ae.getClass().getName() + " " + ae.getMessage()); CurrencyConverterResult result = new CurrencyConverterResult(); result.setBtcMoneyValid(false); result.setBtcMessage(controller.getLocaliser().getString("currencyConverter.btcCanOnlyHaveEightDecimalPlaces")); return result; } } /** * Convert an unlocalised BTC amount e.g. 0.1234 to a localised BTC value with fiat * e.g. 0,1234 ($10,23) * @param btcAsString the amount of bitcoin to pretty print * @return pretty string with format <btc localised> (<fiat localised>) */ public String prettyPrint(String btcAsString) { String prettyPrint; CurrencyConverterResult converterResult = parseToBTCNotLocalised(btcAsString); if (converterResult.isBtcMoneyValid()) { prettyPrint = getBTCAsLocalisedString(converterResult.getBtcMoney()); } else { // BTC did not parse - just use the original text prettyPrint = btcAsString; } prettyPrint = prettyPrint + " " + controller.getLocaliser().getString("sendBitcoinPanel.amountUnitLabel"); if (btcAsString != null && !"".equals(btcAsString)) { if (getRate() != null && isShowingFiat()) { if (converterResult.isBtcMoneyValid()) { Money fiat = convertFromBTCToFiat(converterResult.getBtcMoney().getAmount() .toBigInteger()); prettyPrint = prettyPrint + getFiatAsLocalisedString(fiat, true, true); } } } return prettyPrint; } public boolean isShowingFiat() { return !Boolean.FALSE.toString().equals(controller.getModel().getUserPreference(ExchangeModel.SHOW_BITCOIN_CONVERTED_TO_FIAT)); } public CurrencyUnit getCurrencyUnit() { return currencyUnit; } public void setCurrencyUnit(CurrencyUnit currencyUnit) { // If this is a new currency, blank the rate. // Thus you should set the currency unit first. if (this.currencyUnit != null && !this.currencyUnit.equals(currencyUnit)) { rate = null; rateDividedByNumberOfSatoshiInOneBitcoin = null; } this.currencyUnit = currencyUnit; // Reinitialise currency formatters. moneyFormatter = getMoneyFormatter(false); moneyFormatterWithCurrencyCode = getMoneyFormatter(true); } public BigDecimal getRate() { return rate; } public void setRate(BigDecimal rate) { boolean fireFoundInsteadOfUpdated = (rate== null); this.rate = rate; rateDividedByNumberOfSatoshiInOneBitcoin = rate.divide(new BigDecimal(CurrencyConverter.NUMBER_OF_SATOSHI_IN_ONE_BITCOIN)); if (fireFoundInsteadOfUpdated) { notifyFoundExchangeRate(); } else { notifyUpdatedExchangeRate(); } } public void addCurrencyConverterListener(CurrencyConverterListener listener) { if (listeners == null) { throw new IllegalStateException("You need to initialise the CurrencyConverter first"); } listeners.add(listener); } private void notifyFoundExchangeRate() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (listeners != null) { for (CurrencyConverterListener listener : listeners) { listener.foundExchangeRate(new ExchangeRate(currencyUnit, rate, new Date())); } } } }); } private void notifyUpdatedExchangeRate() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (listeners != null) { for (CurrencyConverterListener listener : listeners) { listener.updatedExchangeRate(new ExchangeRate(currencyUnit, rate, new Date())); } } } }); } public Map<String, CurrencyInfo> getCurrencyCodeToInfoMap() { return currencyCodeToInfoMap; } public Map<String, String> getCurrencyCodeToDescriptionMap() { return currencyCodeToDescriptionMap; } }