Amount: add support for basic parsing from text.

This commit is contained in:
Mike Hearn 2017-03-05 22:20:11 +01:00
parent bea256caf1
commit 7cac55ca4a
2 changed files with 60 additions and 0 deletions

View File

@ -48,6 +48,58 @@ data class Amount<T>(val quantity: Long, val token: T) : Comparable<Amount<T>> {
val longQuantity = quantity.movePointRight(currency.defaultFractionDigits).toLong()
return Amount(longQuantity, currency)
}
private val currencySymbols: Map<String, Currency> = mapOf(
"$" to USD,
"£" to GBP,
"" to EUR
)
private val currencyCodes: Map<String, Currency> by lazy { Currency.getAvailableCurrencies().map { it.currencyCode to it }.toMap() }
/**
* Returns an amount that is equal to the given currency amount in text. Examples of what is supported:
*
* - 12 USD
* - 14.50 USD
* - 10 USD
* - 30 CHF
* - $10.24
* - £13
* - 5000
*
* Note this method does NOT respect internationalisation rules: it ignores commas and uses . as the
* decimal point separator, always. It also ignores the users locale: $ is special cased to be USD,
* £ is special cased to GBP and is special cased to Euro. Thus an input of $12 expecting some other
* countries dollar will not work. Do your own parsing if you need correct handling of currency amounts
* with locale-sensitive handling.
*
* @throws IllegalArgumentException if the input string was not understood.
*/
fun parseCurrency(input: String): Amount<Currency> {
val i = input.filter { it != ',' }
try {
// First check the symbols at the front.
for ((symbol, currency) in currencySymbols) {
if (i.startsWith(symbol)) {
val rest = i.substring(symbol.length)
return fromDecimal(BigDecimal(rest), currency)
}
}
// Now check the codes at the end.
val split = i.split(' ')
if (split.size == 2) {
val (rest, code) = split
for ((cc, currency) in currencyCodes) {
if (cc == code) {
return fromDecimal(BigDecimal(rest), currency)
}
}
}
} catch(e: Exception) {
throw IllegalArgumentException("Could not parse $input as a currency", e)
}
throw IllegalArgumentException("Did not recognise the currency in $input or could not parse")
}
}
init {

View File

@ -23,4 +23,12 @@ class AmountTests {
assertEquals(expected, amount.toDecimal())
assertEquals(amount, Amount.fromDecimal(amount.toDecimal(), amount.token))
}
@Test
fun parsing() {
assertEquals(Amount(1234L, GBP), Amount.parseCurrency("£12.34"))
assertEquals(Amount(1200L, GBP), Amount.parseCurrency("£12"))
assertEquals(Amount(1000L, USD), Amount.parseCurrency("$10"))
assertEquals(Amount(1500000000L, CHF), Amount.parseCurrency("15,000,000 CHF"))
}
}