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
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<String, Double> = ConcurrentHashMap()
private val configRates: ConcurrentMap<String, Double> = ConcurrentHashMap(CONFIG_INITIAL_SIZE)
// 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
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<String, Any?> {
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

View File

@ -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"
}
}

View File

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

View File

@ -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)
}
}