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
This commit is contained in:
Sambo Chea 2021-02-09 13:54:59 +07:00
parent 1a447f1954
commit 8509c578ca
4 changed files with 150 additions and 20 deletions

View File

@ -1,5 +1,6 @@
package com.cubetiqs.money package com.cubetiqs.money
import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentMap
@ -11,27 +12,58 @@ import java.util.concurrent.ConcurrentMap
* @since 1.0 * @since 1.0
*/ */
object MoneyConfig { 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. * All money currencies and its rate are stored in memory state for exchange or compute the value.
* *
* Key is the currency * Key is the currency
* Value is the rate * Value is the rate
*/ */
private val config: ConcurrentMap<String, Double> = ConcurrentHashMap() private val configRates: ConcurrentMap<String, Double> = ConcurrentHashMap(CONFIG_INITIAL_SIZE)
// use to format the money for each value, if have // use to format the money for each value, if have
private val configFormatter: ConcurrentMap<String, MoneyFormatProvider> = ConcurrentHashMap() private val configFormatter: ConcurrentMap<String, MoneyFormatProvider> =
ConcurrentHashMap(CONFIG_FORMATTER_INITIAL_SIZE)
// use to custom locales and allow to detect auto formatter, if enable auto locale format
private val configLocales: ConcurrentMap<String, Locale> = ConcurrentHashMap(LOCALES_INITIAL_SIZE)
// use to custom currencies and allow to detect auto, when use auto detect for currencies translate
private val configCurrencies: ConcurrentMap<String, Currency> = 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 // use to identified for config dataset with prefix mode
private var configPrefix: String = "" private var configPrefix: String = ""
private fun isConfigPrefixValid() = configPrefix.isNotEmpty() && configPrefix.isNotBlank()
// use to fallback, if the currency not found // use to fallback, if the currency not found
// if the fallback greater than ZERO, then called it // if the fallback greater than ZERO, then called it
// else throws // else throws
private var fallbackRate: Double = 0.0 private var fallbackRate: Double = 0.0
// validate the config, if have it's valid fun getConfigs(): Map<String, Any?> {
fun isValid(): Boolean { return mapOf(
return config.isNotEmpty() "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 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 // get custom config key within currency generally
// example: myOwned_usd // example: myOwned_usd
private fun getConfigKey(key: String): String { private fun getConfigKey(key: String): String {
@ -78,12 +121,12 @@ object MoneyConfig {
fun parse(config: String, clearAllStates: Boolean = true) { fun parse(config: String, clearAllStates: Boolean = true) {
// remove all states, if needed // remove all states, if needed
if (clearAllStates) { if (clearAllStates) {
if (configPrefix.isEmpty() || config.isBlank()) { if (!isConfigPrefixValid()) {
MoneyConfig.config.clear() configRates.clear()
} else { } 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 -> keys.forEach { key ->
MoneyConfig.config.remove(key) configRates.remove(key)
} }
} }
} }
@ -102,10 +145,10 @@ object MoneyConfig {
val value = temp[1].toDouble() val value = temp[1].toDouble()
// set the value into dataset // set the value into dataset
if (MoneyConfig.config.containsKey(key)) { if (configRates.containsKey(key)) {
MoneyConfig.config.replace(key, value) configRates.replace(key, value)
} else { } else {
MoneyConfig.config.put(key, value) configRates.put(key, value)
} }
} else { } else {
throw MoneyCurrencyStateException("money config format $temp is not valid!") throw MoneyCurrencyStateException("money config format $temp is not valid!")
@ -118,10 +161,10 @@ object MoneyConfig {
fun appendRate(currency: String, rate: Double) = apply { fun appendRate(currency: String, rate: Double) = apply {
val currencyKey = currency.toUpperCase().trim() val currencyKey = currency.toUpperCase().trim()
val key = getConfigKey(currencyKey) val key = getConfigKey(currencyKey)
if (config.containsKey(key)) { if (configRates.containsKey(key)) {
config.replace(key, rate) configRates.replace(key, rate)
} else { } else {
config[key] = rate configRates[key] = rate
} }
} }
@ -151,14 +194,81 @@ object MoneyConfig {
} }
// all currencies with its rate // all currencies with its rate
fun getConfig() = config fun getConfigRates() = configRates
@Throws(MoneyCurrencyStateException::class) @Throws(MoneyCurrencyStateException::class)
fun getRate(currency: StdMoney.Currency): Double { 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!") ?: 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 // apply default formatter for all not exists
fun applyDefaultFormatter( fun applyDefaultFormatter(
provider: MoneyFormatProvider? = null provider: MoneyFormatProvider? = null

View File

@ -2,6 +2,8 @@ package com.cubetiqs.money
import java.io.Serializable import java.io.Serializable
import java.math.RoundingMode import java.math.RoundingMode
import java.text.NumberFormat
import javax.swing.text.NumberFormatter
/** /**
* Money Formatter (Final class) * Money Formatter (Final class)
@ -40,6 +42,20 @@ class MoneyFormatter(
override fun format(): String { override fun format(): String {
value?.getValue() ?: return "" 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) { if (getPattern() == null && getPrecision() < 0 && getRoundingMode() == null) {
return value?.getValue().toString() return value?.getValue().toString()
} }
@ -57,5 +73,7 @@ class MoneyFormatter(
companion object { companion object {
const val DEFAULT_FORMATTER = "defaultFormatter" const val DEFAULT_FORMATTER = "defaultFormatter"
const val DEFAULT_LOCALE = "defaultLocale"
const val DEFAULT_CURRENCY = "defaultCurrency"
} }
} }

View File

@ -13,7 +13,7 @@ open class MoneyView(
} }
fun getValue(): Double { fun getValue(): Double {
return value?.toDouble() ?: 0.0 return (value?.toDouble() ?: 0.0)
} }
fun getCurrency(): String? { fun getCurrency(): String? {

View File

@ -1,6 +1,7 @@
import com.cubetiqs.money.* import com.cubetiqs.money.*
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.util.*
class MoneyTests { class MoneyTests {
private fun initMoneyConfig() { private fun initMoneyConfig() {
@ -38,7 +39,7 @@ class MoneyTests {
// } // }
// Is valid for money config? // Is valid for money config?
Assert.assertTrue(MoneyConfig.isValid()) Assert.assertTrue(MoneyConfig.isConfigRatesValid())
// arithmetic operators calculation // arithmetic operators calculation
val moneyUsd = val moneyUsd =
@ -138,6 +139,7 @@ class MoneyTests {
@Test @Test
fun moneyFormatterTest() { fun moneyFormatterTest() {
val systemCurrency = Currency.getInstance("USD").symbol
println(systemCurrency)
} }
} }