From 8509c578ca0b01e798e8c0b2e304f4b8a6979ed7 Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Tue, 9 Feb 2021 13:54:59 +0700 Subject: [PATCH] Add money config for locale and currency from system and set default and configuratoion auto with apply default locale and currency But for auto translate from currency to view not have yet We need to create a new file or use exist model to generate the locale format with custom currency from system or use defined --- .../kotlin/com/cubetiqs/money/MoneyConfig.kt | 144 +++++++++++++++--- .../com/cubetiqs/money/MoneyFormatter.kt | 18 +++ .../kotlin/com/cubetiqs/money/MoneyView.kt | 2 +- src/test/kotlin/MoneyTests.kt | 6 +- 4 files changed, 150 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt b/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt index bf885ec..b8525ae 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyConfig.kt @@ -1,5 +1,6 @@ package com.cubetiqs.money +import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentMap @@ -11,27 +12,58 @@ import java.util.concurrent.ConcurrentMap * @since 1.0 */ object MoneyConfig { + private const val CONFIG_INITIAL_SIZE = 10 + private const val CONFIG_FORMATTER_INITIAL_SIZE = 10 + private const val LOCALES_INITIAL_SIZE = 5 + private const val CURRENCIES_INITIAL_SIZE = 5 + /** * All money currencies and its rate are stored in memory state for exchange or compute the value. * * Key is the currency * Value is the rate */ - private val config: ConcurrentMap = ConcurrentHashMap() + private val configRates: ConcurrentMap = ConcurrentHashMap(CONFIG_INITIAL_SIZE) // use to format the money for each value, if have - private val configFormatter: ConcurrentMap = ConcurrentHashMap() + private val configFormatter: ConcurrentMap = + ConcurrentHashMap(CONFIG_FORMATTER_INITIAL_SIZE) + + // use to custom locales and allow to detect auto formatter, if enable auto locale format + private val configLocales: ConcurrentMap = ConcurrentHashMap(LOCALES_INITIAL_SIZE) + + // use to custom currencies and allow to detect auto, when use auto detect for currencies translate + private val configCurrencies: ConcurrentMap = ConcurrentHashMap(CURRENCIES_INITIAL_SIZE) + + // use to auto format, when this true + // and formatter for money value, with current locale or custom set locale + private var autoLocaleFormatter: Boolean = false + + // use to auto format, when this true + // and formatter for money value, with current currency or custom set currency + private var autoCurrencyFormatter: Boolean = false // use to identified for config dataset with prefix mode private var configPrefix: String = "" + private fun isConfigPrefixValid() = configPrefix.isNotEmpty() && configPrefix.isNotBlank() + // use to fallback, if the currency not found // if the fallback greater than ZERO, then called it // else throws private var fallbackRate: Double = 0.0 - // validate the config, if have it's valid - fun isValid(): Boolean { - return config.isNotEmpty() + fun getConfigs(): Map { + return mapOf( + "configRates" to configRates, + "configFormatter" to configFormatter, + "configLocales" to configLocales, + "configCurrencies" to configCurrencies, + ) + } + + // validate the config rates, if have it's valid + fun isConfigRatesValid(): Boolean { + return configRates.isNotEmpty() } /** @@ -59,6 +91,17 @@ object MoneyConfig { this.fallbackRate = fallbackRate } + fun setAutoLocaleFormatter(enabled: Boolean) = apply { + this.autoLocaleFormatter = enabled + } + + fun setAutoCurrencyFormatter(enabled: Boolean) = apply { + this.autoCurrencyFormatter = enabled + } + + fun isAutoLocaleFormatterEnabled() = this.autoLocaleFormatter + fun isAutoCurrencyFormatterEnabled() = this.autoCurrencyFormatter + // get custom config key within currency generally // example: myOwned_usd private fun getConfigKey(key: String): String { @@ -78,12 +121,12 @@ object MoneyConfig { fun parse(config: String, clearAllStates: Boolean = true) { // remove all states, if needed if (clearAllStates) { - if (configPrefix.isEmpty() || config.isBlank()) { - MoneyConfig.config.clear() + if (!isConfigPrefixValid()) { + configRates.clear() } else { - val keys = MoneyConfig.config.filter { it.key.startsWith(prefix = configPrefix) }.keys + val keys = configRates.filter { it.key.startsWith(prefix = configPrefix) }.keys keys.forEach { key -> - MoneyConfig.config.remove(key) + configRates.remove(key) } } } @@ -102,10 +145,10 @@ object MoneyConfig { val value = temp[1].toDouble() // set the value into dataset - if (MoneyConfig.config.containsKey(key)) { - MoneyConfig.config.replace(key, value) + if (configRates.containsKey(key)) { + configRates.replace(key, value) } else { - MoneyConfig.config.put(key, value) + configRates.put(key, value) } } else { throw MoneyCurrencyStateException("money config format $temp is not valid!") @@ -118,10 +161,10 @@ object MoneyConfig { fun appendRate(currency: String, rate: Double) = apply { val currencyKey = currency.toUpperCase().trim() val key = getConfigKey(currencyKey) - if (config.containsKey(key)) { - config.replace(key, rate) + if (configRates.containsKey(key)) { + configRates.replace(key, rate) } else { - config[key] = rate + configRates[key] = rate } } @@ -151,14 +194,81 @@ object MoneyConfig { } // all currencies with its rate - fun getConfig() = config + fun getConfigRates() = configRates @Throws(MoneyCurrencyStateException::class) fun getRate(currency: StdMoney.Currency): Double { - return getConfig()[getConfigKey(currency.getCurrency().toUpperCase().trim())] + return getConfigRates()[getConfigKey(currency.getCurrency().toUpperCase().trim())] ?: if (fallbackRate > 0) fallbackRate else throw MoneyCurrencyStateException("money currency ${currency.getCurrency()} is not valid!") } + // apply custom locale below + fun applyDefaultLocale(locale: Locale = Locale.getDefault()) = apply { + configLocales[MoneyFormatter.DEFAULT_LOCALE] = locale + } + + // get default locale from system or user defined + private fun getDefaultLocale(): Locale { + return configLocales[MoneyFormatter.DEFAULT_LOCALE] ?: Locale.getDefault() + } + + // add custom locale + fun applyLocale(locale: Locale) = apply { + if (isConfigPrefixValid()) { + if (configLocales.containsKey(configPrefix)) { + configLocales.replace(configPrefix, locale) + } else { + configLocales[configPrefix] = locale + } + } + } + + // get locale by config prefix or default if not found + fun getLocale(): Locale { + if (isConfigPrefixValid() && configLocales.containsKey(configPrefix)) { + return configLocales[configPrefix] ?: getDefaultLocale() + } + + return getDefaultLocale() + } + + // get default currency from system or user defined + private fun getDefaultCurrency(): Currency { + return configCurrencies[MoneyFormatter.DEFAULT_CURRENCY] ?: Currency.getInstance(getDefaultLocale()) + } + + // apply the default currency for system at runtime + fun applyDefaultCurrency(currency: StdMoney.Currency) = apply { + configCurrencies[MoneyFormatter.DEFAULT_CURRENCY] = currency.findCurrency() + } + + // apply the currency into the config for system currency at runtime + fun applyCurrency(currency: StdMoney.Currency) = apply { + if (isConfigPrefixValid()) { + val systemCurrency = currency.findCurrency() + if (systemCurrency != null) { + if (configCurrencies.containsKey(configPrefix)) { + configCurrencies.replace(configPrefix, systemCurrency) + } else { + configCurrencies[configPrefix] = systemCurrency + } + } + } + } + + // get currency by config prefix or default if not found + fun getCurrency(): Currency { + if (isConfigPrefixValid() && configCurrencies.containsKey(configPrefix)) { + return configCurrencies[configPrefix] ?: getDefaultCurrency() + } + + return getDefaultCurrency() + } + + fun getCurrencySymbol(): String { + return getCurrency().getSymbol(getDefaultLocale()) + } + // apply default formatter for all not exists fun applyDefaultFormatter( provider: MoneyFormatProvider? = null diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyFormatter.kt b/src/main/kotlin/com/cubetiqs/money/MoneyFormatter.kt index e5354c0..7ca58b3 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyFormatter.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyFormatter.kt @@ -2,6 +2,8 @@ package com.cubetiqs.money import java.io.Serializable import java.math.RoundingMode +import java.text.NumberFormat +import javax.swing.text.NumberFormatter /** * Money Formatter (Final class) @@ -40,6 +42,20 @@ class MoneyFormatter( override fun format(): String { value?.getValue() ?: return "" + if (MoneyConfig.isAutoLocaleFormatterEnabled()) { + val systemCurrency = if (MoneyConfig.isAutoCurrencyFormatterEnabled()) { + MoneyConfig.getCurrency() + } else { + value?.getCurrency()?.findCurrency() + } + + if (systemCurrency != null) { + val numberFormatter = NumberFormat.getNumberInstance(MoneyConfig.getLocale()) + numberFormatter.currency = systemCurrency + return numberFormatter.format(value?.getValue()) + } + } + if (getPattern() == null && getPrecision() < 0 && getRoundingMode() == null) { return value?.getValue().toString() } @@ -57,5 +73,7 @@ class MoneyFormatter( companion object { const val DEFAULT_FORMATTER = "defaultFormatter" + const val DEFAULT_LOCALE = "defaultLocale" + const val DEFAULT_CURRENCY = "defaultCurrency" } } \ No newline at end of file diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyView.kt b/src/main/kotlin/com/cubetiqs/money/MoneyView.kt index 0e8b554..e2fcdfc 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyView.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyView.kt @@ -13,7 +13,7 @@ open class MoneyView( } fun getValue(): Double { - return value?.toDouble() ?: 0.0 + return (value?.toDouble() ?: 0.0) } fun getCurrency(): String? { diff --git a/src/test/kotlin/MoneyTests.kt b/src/test/kotlin/MoneyTests.kt index d882efe..2eaee50 100644 --- a/src/test/kotlin/MoneyTests.kt +++ b/src/test/kotlin/MoneyTests.kt @@ -1,6 +1,7 @@ import com.cubetiqs.money.* import org.junit.Assert import org.junit.Test +import java.util.* class MoneyTests { private fun initMoneyConfig() { @@ -38,7 +39,7 @@ class MoneyTests { // } // Is valid for money config? - Assert.assertTrue(MoneyConfig.isValid()) + Assert.assertTrue(MoneyConfig.isConfigRatesValid()) // arithmetic operators calculation val moneyUsd = @@ -138,6 +139,7 @@ class MoneyTests { @Test fun moneyFormatterTest() { - + val systemCurrency = Currency.getInstance("USD").symbol + println(systemCurrency) } } \ No newline at end of file