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
*/
class MoneyObject(
val value: Double,
val currency: String,
var value: Double,
var currency: String,
var operator: MoneyOperator? = null,
var with: MoneyObject? = null,
) : StdMoney {
fun appendWith(with: MoneyObject?) {
if (this.with == null) {
this.with = with
} else {
this.with!!.appendWith(with)
}
}
override fun getMoneyCurrency(): StdMoney.Currency {
return StdMoney.initCurrency(currency)
}
@ -30,20 +22,51 @@ class MoneyObject(
return value
}
private fun generate(): StdMoney {
if (this.with != null) {
val withGenerated = this.with!!.generate()
return when (operator) {
MoneyOperator.PLUS -> this plusWith withGenerated
MoneyOperator.MINUS -> this minusWith withGenerated
MoneyOperator.DIVIDE -> this divideWith withGenerated
MoneyOperator.MULTIPLY -> this multiplyWith withGenerated
// if operator is null or empty with default Plus operator
else -> this plusWith withGenerated
}
fun appendWithNext(with: MoneyObject?) {
if (with == null) {
return
}
return StdMoney.initMoney(value, currency = StdMoney.initCurrency(currency))
if (this.with == null) {
this.with = with
} else {
this.with!!.appendWithNext(with)
}
}
/**
* 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 {
@ -68,32 +91,46 @@ class MoneyObject(
MULTIPLY;
companion object {
fun operator(value: Char?): MoneyOperator {
fun operator(value: Any?, defaultOperator: MoneyOperator? = null): MoneyOperator {
return try {
when (value!!) {
'+' -> PLUS
'-' -> MINUS
'/' -> DIVIDE
'*' -> MULTIPLY
else -> throw IllegalArgumentException("operator not found with value: $value!")
when (val temp = value!!) {
is String -> {
if (temp.length == 1) {
operator(temp[0])
} else {
valueOf(temp.toUpperCase().trim())
}
}
is Char -> {
when (temp) {
'+' -> PLUS
'-' -> MINUS
'/' -> DIVIDE
'*' -> MULTIPLY
else -> throw IllegalArgumentException("operator not found with value: $temp!")
}
}
else -> throw IllegalArgumentException("operator not found by value: $value!")
}
} 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 val withs: MutableCollection<MoneyObject> = LinkedList()
private val withs: Deque<MoneyObject> = ArrayDeque()
fun withCurrency(currency: String) = apply {
this.currency = currency
}
fun with(`object`: MoneyObject) = apply {
this.withs.add(`object`)
fun with(value: MoneyObject) = apply {
this.withs.add(value)
}
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 {
val first: MoneyObject
if (this.currency.isNullOrEmpty() && withs.isNotEmpty()) {
first = withs.first()
withs.remove(first)
val first: MoneyObject = if (this.currency.isNullOrEmpty() && withs.isNotEmpty()) {
withs.removeFirst()
} else {
first = MoneyObject(
MoneyObject(
value = 0.0,
currency = this.currency ?: defaultCurrency,
operator = MoneyOperator.PLUS,
currency = this.currency ?: MoneyCurrency.USD.getCurrency()
)
}
withs.forEach { with ->
first.appendWith(with)
while (withs.isNotEmpty()) {
first.appendWithNext(withs.pop())
}
return first
@ -138,6 +190,6 @@ class MoneyObject(
}
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
fun exchange_2usd_to_khr_test() {
initMoneyConfig()
@ -47,14 +55,6 @@ class MoneyTests {
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
fun moneyGenerator() {
initMoneyConfig()
@ -102,4 +102,37 @@ class MoneyTests {
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)
}
}