Fixed the batch computation on money value and money object with string values and builder values and fixed generator for first + next computation concepts with operators

This commit is contained in:
Sambo Chea 2021-02-08 21:25:16 +07:00
parent 980b741ee9
commit f3f41e9c67
2 changed files with 136 additions and 51 deletions

View File

@ -9,19 +9,11 @@ import java.util.*
* @author sombochea * @author sombochea
*/ */
class MoneyObject( class MoneyObject(
val value: Double, var value: Double,
val currency: String, var currency: String,
var operator: MoneyOperator? = null, var operator: MoneyOperator? = null,
var with: MoneyObject? = null, var with: MoneyObject? = null,
) : StdMoney { ) : StdMoney {
fun appendWith(with: MoneyObject?) {
if (this.with == null) {
this.with = with
} else {
this.with!!.appendWith(with)
}
}
override fun getMoneyCurrency(): StdMoney.Currency { override fun getMoneyCurrency(): StdMoney.Currency {
return StdMoney.initCurrency(currency) return StdMoney.initCurrency(currency)
} }
@ -30,20 +22,51 @@ class MoneyObject(
return value return value
} }
private fun generate(): StdMoney { fun appendWithNext(with: MoneyObject?) {
if (this.with != null) { if (with == null) {
val withGenerated = this.with!!.generate() return
return when (operator) { }
MoneyOperator.PLUS -> this plusWith withGenerated
MoneyOperator.MINUS -> this minusWith withGenerated if (this.with == null) {
MoneyOperator.DIVIDE -> this divideWith withGenerated this.with = with
MoneyOperator.MULTIPLY -> this multiplyWith withGenerated } else {
// if operator is null or empty with default Plus operator this.with!!.appendWithNext(with)
else -> this plusWith withGenerated
} }
} }
return StdMoney.initMoney(value, currency = StdMoney.initCurrency(currency)) /**
* Internal Generate the value by next compute (First and next)
*
* Example: we have 3 items => [10, 20, 50]
* We want to sum of them => [10 + 20] => [30 + 50] => 80
*/
private fun generate(): StdMoney {
// get next record for compute with
var next = this.with
// get first operator
var operatorFirst = this.operator
while (next != null) {
val temp = when (operatorFirst) {
MoneyOperator.PLUS -> this plusWith next
MoneyOperator.MINUS -> this minusWith next
MoneyOperator.DIVIDE -> this divideWith next
MoneyOperator.MULTIPLY -> this multiplyWith next
// if operator is null or empty with default Plus operator
else -> this plusWith next
}
// move the temp value that computed
this.value = temp.getMoneyValue()
// move the currency into parent, if changed
this.currency = temp.getMoneyCurrency().getCurrency()
// move the first operator to previous next operator
// example: first ops = +, then next ops = -, then inside next ops = *, n
// return next: first ops = -, then next ops = *, then n
operatorFirst = next.operator
// move next element
next = next.with
}
return this
} }
fun compute(): StdMoney { fun compute(): StdMoney {
@ -68,32 +91,46 @@ class MoneyObject(
MULTIPLY; MULTIPLY;
companion object { companion object {
fun operator(value: Char?): MoneyOperator { fun operator(value: Any?, defaultOperator: MoneyOperator? = null): MoneyOperator {
return try { return try {
when (value!!) { when (val temp = value!!) {
is String -> {
if (temp.length == 1) {
operator(temp[0])
} else {
valueOf(temp.toUpperCase().trim())
}
}
is Char -> {
when (temp) {
'+' -> PLUS '+' -> PLUS
'-' -> MINUS '-' -> MINUS
'/' -> DIVIDE '/' -> DIVIDE
'*' -> MULTIPLY '*' -> MULTIPLY
else -> throw IllegalArgumentException("operator not found with value: $value!") else -> throw IllegalArgumentException("operator not found with value: $temp!")
}
}
else -> throw IllegalArgumentException("operator not found by value: $value!")
} }
} catch (ex: Exception) { } catch (ex: Exception) {
throw IllegalArgumentException("operator not found!") defaultOperator ?: throw IllegalArgumentException("operator not found!")
} }
} }
} }
} }
class MoneyGeneratorBuilder { class MoneyObjectBuilder {
private val defaultCurrency = MoneyCurrency.USD.getCurrency()
private var currency: String? = null private var currency: String? = null
private val withs: MutableCollection<MoneyObject> = LinkedList() private val withs: Deque<MoneyObject> = ArrayDeque()
fun withCurrency(currency: String) = apply { fun withCurrency(currency: String) = apply {
this.currency = currency this.currency = currency
} }
fun with(`object`: MoneyObject) = apply { fun with(value: MoneyObject) = apply {
this.withs.add(`object`) this.withs.add(value)
} }
fun with(value: Double, currency: String, operator: MoneyOperator = MoneyOperator.PLUS) = apply { fun with(value: Double, currency: String, operator: MoneyOperator = MoneyOperator.PLUS) = apply {
@ -112,21 +149,36 @@ class MoneyObject(
) )
} }
/**
* Example: String format => usd:1:+,khr:4000.0:-,usd:1 => 1 + (4000 eh) - 1 = 1USD
*/
fun parseFromString(valuesString: String) = apply {
val valueGroups = valuesString.split(",")
valueGroups.forEach { value ->
val tempValue = value.trim().split(":")
if (tempValue.isNotEmpty() && !tempValue.firstOrNull().isNullOrEmpty()) {
this.with(
tempValue.getOrNull(1)?.toDoubleOrNull() ?: 0.0,
tempValue.firstOrNull() ?: defaultCurrency,
MoneyOperator.operator(tempValue.getOrNull(2), MoneyOperator.PLUS),
)
}
}
}
fun build(): MoneyObject { fun build(): MoneyObject {
val first: MoneyObject val first: MoneyObject = if (this.currency.isNullOrEmpty() && withs.isNotEmpty()) {
if (this.currency.isNullOrEmpty() && withs.isNotEmpty()) { withs.removeFirst()
first = withs.first()
withs.remove(first)
} else { } else {
first = MoneyObject( MoneyObject(
value = 0.0, value = 0.0,
currency = this.currency ?: defaultCurrency,
operator = MoneyOperator.PLUS, operator = MoneyOperator.PLUS,
currency = this.currency ?: MoneyCurrency.USD.getCurrency()
) )
} }
withs.forEach { with -> while (withs.isNotEmpty()) {
first.appendWith(with) first.appendWithNext(withs.pop())
} }
return first return first
@ -138,6 +190,6 @@ class MoneyObject(
} }
companion object { companion object {
fun builder() = MoneyGeneratorBuilder() fun builder() = MoneyObjectBuilder()
} }
} }

View File

@ -14,6 +14,14 @@ class MoneyTests {
} }
} }
object MyBatchRates {
fun getJsonRates(): String {
return """
{"USD": 1.0,"KHR": 4000.0, "eur": 0.5}
""".trimIndent()
}
}
@Test @Test
fun exchange_2usd_to_khr_test() { fun exchange_2usd_to_khr_test() {
initMoneyConfig() initMoneyConfig()
@ -47,14 +55,6 @@ class MoneyTests {
Assert.assertEquals(156000.0, sum.getMoneyValue(), 0.0) Assert.assertEquals(156000.0, sum.getMoneyValue(), 0.0)
} }
object MyBatchRates {
fun getJsonRates(): String {
return """
{"USD": 1.0,"KHR": 4000.0, "eur": 0.5}
""".trimIndent()
}
}
@Test @Test
fun moneyGenerator() { fun moneyGenerator() {
initMoneyConfig() initMoneyConfig()
@ -102,4 +102,37 @@ class MoneyTests {
Assert.assertEquals(expected, result.getMoneyValue(), 0.0) Assert.assertEquals(expected, result.getMoneyValue(), 0.0)
} }
@Test
fun moneyGeneratorBuilderWithStringValues() {
initMoneyConfig()
val values = "usd:1:+,khr:4000:-,usd:1:+,eur:1:+" // result = 3
val expected1 = 3.0
val expected2 = 3.5
val builder = MoneyObject.builder()
.withCurrency("usd")
.with(1.0, "usd", '-')
.with(4000.0, "khr")
.with(1.0, "usd")
.with(1.0, "usd")
.with(1.0, "usd")
.with(1.0, "usd")
.with(1.0, "eur", '-') // 2 usd
.with(1.0, "usd")
.with(2.0, "usd", '/')
.with(2.0, "usd")
.build() // 3.5
val result1 = MoneyObject.builder()
.parseFromString(values)
.withCurrency("usd")
.build()
.compute()
val result2 = builder.compute()
Assert.assertEquals(expected1, result1.getMoneyValue(), 0.0)
Assert.assertEquals(expected2, result2.getMoneyValue(), 0.0)
}
} }