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