From f3f41e9c67a1eae4452ec83b3df53d37c5168dbf Mon Sep 17 00:00:00 2001 From: Sambo Chea Date: Mon, 8 Feb 2021 21:25:16 +0700 Subject: [PATCH] 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 --- .../kotlin/com/cubetiqs/money/MoneyObject.kt | 138 ++++++++++++------ src/test/kotlin/MoneyTests.kt | 49 ++++++- 2 files changed, 136 insertions(+), 51 deletions(-) diff --git a/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt b/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt index 5bff641..777f362 100644 --- a/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt +++ b/src/main/kotlin/com/cubetiqs/money/MoneyObject.kt @@ -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 = LinkedList() + private val withs: Deque = 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() } } \ No newline at end of file diff --git a/src/test/kotlin/MoneyTests.kt b/src/test/kotlin/MoneyTests.kt index 97aec99..7d78ebd 100644 --- a/src/test/kotlin/MoneyTests.kt +++ b/src/test/kotlin/MoneyTests.kt @@ -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) + } } \ No newline at end of file