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:
parent
1a447f1954
commit
8509c578ca
@ -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
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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? {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user