/* * $Id$ * * Copyright 2017 Valentyn Kolesnikov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.moneytostr /** * Converts numbers to symbols. * * @author Valentyn Kolesnikov * @version $Revision$ $Date$ */ class MoneyToStr { private val messages = java.util.LinkedHashMap>() private val rubOneUnit: String private val rubTwoUnit: String private val rubFiveUnit: String private val rubSex: String private val kopOneUnit: String private val kopTwoUnit: String private val kopFiveUnit: String private val kopSex: String private val rubShortUnit: String private val currency: Currency private val language: Language private val pennies: Pennies /** Currency. */ enum class Currency { /**. */ RUR, /**. */ UAH, /**. */ USD, /**. */ PER10, /**. */ PER100, /**. */ PER1000, /**. */ PER10000, /**. */ Custom } /** Language. */ enum class Language { /**. */ RUS, /**. */ UKR, /**. */ ENG } /** Pennies. */ enum class Pennies { /**. */ NUMBER, /**. */ TEXT } /** * Inits class with currency. Usage: MoneyToStr moneyToStr = new MoneyToStr( * MoneyToStr.Currency.UAH, MoneyToStr.Language.UKR, MoneyToStr.Pennies.NUMBER); * Definition for currency is placed into currlist.xml * * @param currency the currency (UAH, RUR, USD) * @param language the language (UKR, RUS, ENG) * @param pennies the pennies (NUMBER, TEXT) */ constructor(currency: Currency?, language: Language?, pennies: Pennies?) { if (currency == null) { throw IllegalArgumentException("currency is null") } if (language == null) { throw IllegalArgumentException("language is null") } if (pennies == null) { throw IllegalArgumentException("pennies is null") } this.currency = currency this.language = language this.pennies = pennies val theISOstr = currency.name val languageElement = xmlDoc!!.getElementsByTagName(language.name).item(0) as org.w3c.dom.Element val items = languageElement.getElementsByTagName("item") run { var index = 0 while (index < items.length) { val languageItem = items.item(index) as org.w3c.dom.Element messages[languageItem.getAttribute("value")] = languageItem.getAttribute("text").split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() index += 1 } } val theISOElements = xmlDoc!!.getElementsByTagName(theISOstr) as org.w3c.dom.NodeList var theISOElement: org.w3c.dom.Element? = null var index = 0 while (index < theISOElements.length) { if ((theISOElements.item(index) as org.w3c.dom.Element).getAttribute("language") == language.name) { theISOElement = theISOElements.item(index) as org.w3c.dom.Element break } index += 1 } rubOneUnit = theISOElement!!.getAttribute("RubOneUnit") rubTwoUnit = theISOElement.getAttribute("RubTwoUnit") rubFiveUnit = theISOElement.getAttribute("RubFiveUnit") kopOneUnit = theISOElement.getAttribute("KopOneUnit") kopTwoUnit = theISOElement.getAttribute("KopTwoUnit") kopFiveUnit = theISOElement.getAttribute("KopFiveUnit") rubSex = theISOElement.getAttribute("RubSex") kopSex = theISOElement.getAttribute("KopSex") rubShortUnit = if (theISOElement.hasAttribute("RubShortUnit")) theISOElement.getAttribute("RubShortUnit") else "" } /** * Inits class with currency. Usage: MoneyToStr moneyToStr = new MoneyToStr( * MoneyToStr.Currency.UAH, MoneyToStr.Language.UKR, MoneyToStr.Pennies.NUMBER); * * @param currency the currency (UAH, RUR, USD) * @param language the language (UKR, RUS, ENG) * @param pennies the pennies (NUMBER, TEXT) * @param names the custom names */ constructor(currency: Currency?, language: Language?, pennies: Pennies?, names: Array?) { if (currency == null) { throw IllegalArgumentException("currency is null") } if (language == null) { throw IllegalArgumentException("language is null") } if (pennies == null) { throw IllegalArgumentException("pennies is null") } if (names == null || names.size != 8) { throw IllegalArgumentException("names is null") } this.currency = currency this.language = language this.pennies = pennies val languageElement = xmlDoc!!.getElementsByTagName(language.name).item(0) as org.w3c.dom.Element val items = languageElement.getElementsByTagName("item") var index = 0 while (index < items.length) { val languageItem = items.item(index) as org.w3c.dom.Element messages[languageItem.getAttribute("value")] = languageItem.getAttribute("text").split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() index += 1 } rubOneUnit = names[0] rubTwoUnit = names[1] rubFiveUnit = names[2] rubSex = names[3] kopOneUnit = names[4] kopTwoUnit = names[5] kopFiveUnit = names[6] kopSex = names[7] rubShortUnit = names[0] } /** * Converts double value to the text description. * * @param theMoney * the amount of money in format major.minor * @return the string description of money value */ fun convert(theMoney: Double?): String { if (theMoney == null) { throw IllegalArgumentException("theMoney is null") } val intPart = theMoney.toLong() var fractPart: Long? = Math.round((theMoney - intPart) * NUM100) if (currency == Currency.PER1000) { fractPart = Math.round((theMoney - intPart) * NUM1000) } return convert(intPart, fractPart) } /** * Converts amount to words. Usage: MoneyToStr moneyToStr = * new MoneyToStr(MoneyToStr.Currency.UAH, MoneyToStr.Language.UKR, MoneyToStr.Pennies.NUMBER); * String result = moneyToStr.convert(123D); Expected: result = сто двадцять три гривні 00 копійок * * @param theMoney * the amount of money major currency * @param theKopeiki * the amount of money minor currency * @return the string description of money value */ fun convert(theMoney: Long?, theKopeiki: Long?): String { if (theMoney == null) { throw IllegalArgumentException("theMoney is null") } if (theKopeiki == null) { throw IllegalArgumentException("theKopeiki is null") } val money2str = StringBuilder() var triadNum: Long = 0L var theTriad: Long? var intPart: Long = Math.abs(theMoney) if (intPart == 0L) { money2str.append(messages["0"]?.get(0) + " ") } do { theTriad = intPart!! % NUM1000 money2str.insert(0, triad2Word(theTriad, triadNum, rubSex)) if (triadNum == 0L) { if (theTriad % NUM100 / NUM10 == NUM1.toLong()) { money2str.append(rubFiveUnit) } else { when (java.lang.Long.valueOf(theTriad % NUM10)!!.toInt()) { NUM1 -> money2str.append(rubOneUnit) NUM2, NUM3, NUM4 -> money2str.append(rubTwoUnit) else -> money2str.append(rubFiveUnit) } } } intPart /= NUM1000.toLong() triadNum += 1 } while (intPart > 0) if (theMoney < 0) { money2str.insert(0, messages["minus"]?.get(0) + " ") } if (pennies == Pennies.TEXT) { money2str.append(if (language == Language.ENG) " and " else " ").append( if (theKopeiki == 0L) messages["0"]?.get(0) + " " else triad2Word(Math.abs(theKopeiki), 0L, kopSex)) } else { money2str.append(" " + (if (Math.abs(theKopeiki) < 10) "0" + Math.abs(theKopeiki) else Math.abs(theKopeiki)) + " ") } if (theKopeiki >= NUM11 && theKopeiki <= NUM14) { money2str.append(kopFiveUnit) } else { when ((theKopeiki % NUM10).toInt()) { NUM1 -> money2str.append(kopOneUnit) NUM2, NUM3, NUM4 -> money2str.append(kopTwoUnit) else -> money2str.append(kopFiveUnit) } } return money2str.toString().trim { it <= ' ' } } private fun triad2Word(triad: Long?, triadNum: Long?, sex: String): String { val triadWord = StringBuilder(NUM100) if (triad == 0L) { return "" } triadWord.append(concat(arrayOf(""), messages["100_900"]!!)?.get(java.lang.Long.valueOf(triad!! / NUM100)!!.toInt())) val range10 = triad!! % NUM100 / NUM10 triadWord.append(concat(arrayOf("", ""), messages["20_90"]!!)?.get(range10.toInt())) if (language == Language.ENG && triadWord.length > 0 && triad!! % NUM10 == 0L) { triadWord.deleteCharAt(triadWord.length - 1) triadWord.append(" ") } check2(triadNum?.toInt(), sex, triadWord, triad, range10) when (triadNum!!.toInt()) { NUM0 -> { } NUM1, NUM2, NUM3, NUM4 -> if (range10.toInt() == NUM1) { triadWord.append(messages["1000_10"]?.get(triadNum.toByte() - 1) + " ") } else { val range = triad % NUM10 when (range.toInt()) { NUM1 -> triadWord.append(messages["1000_1"]?.get(triadNum.toByte() - 1) + " ") NUM2, NUM3, NUM4 -> triadWord.append(messages["1000_234"]?.get(triadNum.toByte() - 1) + " ") else -> triadWord.append(messages["1000_5"]?.get(triadNum.toByte() - 1) + " ") } } else -> triadWord.append("??? ") } return triadWord.toString() } /** * @param triadNum the triad num * @param sex the sex * @param triadWord the triad word * @param triad the triad * @param range10 the range 10 */ private fun check2(triadNum: Int?, sex: String, triadWord: StringBuilder, triad: Long?, range10: Long?) { val range = triad!! % NUM10 if (range10 == 1L) { triadWord.append(messages["10_19"]?.get(range.toInt()) + " ") } else { when (range.toInt()) { NUM1 -> if (triadNum == NUM1) { triadWord.append(messages["1"]?.get(INDEX_0) + " ") } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) { triadWord.append(messages["1"]?.get(INDEX_1) + " ") } else if ("M" == sex) { triadWord.append(messages["1"]?.get(INDEX_2) + " ") } else if ("F" == sex) { triadWord.append(messages["1"]?.get(INDEX_3) + " ") } NUM2 -> if (triadNum == NUM1) { triadWord.append(messages["2"]?.get(INDEX_0) + " ") } else if (triadNum == NUM2 || triadNum == NUM3 || triadNum == NUM4) { triadWord.append(messages["2"]?.get(INDEX_1) + " ") } else if ("M" == sex) { triadWord.append(messages["2"]?.get(INDEX_2) + " ") } else if ("F" == sex) { triadWord.append(messages["2"]?.get(INDEX_3) + " ") } NUM3, NUM4, NUM5, NUM6, NUM7, NUM8, NUM9 -> triadWord.append(concat(arrayOf("", "", ""), messages["3_9"]!!)?.get(range.toInt()) + " ") else -> { } } } } private fun concat(first: Array, second: Array): Array { val result = java.util.Arrays.copyOf(first, first.size + second.size) System.arraycopy(second, 0, result, first.size, second.size) return result } fun getMessages(): MutableMap> { return messages } companion object { private val INDEX_3 = 3 private val INDEX_2 = 2 private val INDEX_1 = 1 private val INDEX_0 = 0 private var xmlDoc: org.w3c.dom.Document? = null private val NUM0 = 0 private val NUM1 = 1 private val NUM2 = 2 private val NUM3 = 3 private val NUM4 = 4 private val NUM5 = 5 private val NUM6 = 6 private val NUM7 = 7 private val NUM8 = 8 private val NUM9 = 9 private val NUM10 = 10 private val NUM11 = 11 private val NUM14 = 14 private val NUM100 = 100 private val NUM1000 = 1000 private val NUM10000 = 10000 private val CURRENCY_LIST = ( "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n" + " \n" + " \n" + " \n" + "\n" + " \n" + " \n" + " \n" + "\n" + " \n" + " \n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + " \n" + "\n" + "\n") init { initXmlDoc(CURRENCY_LIST) } fun initXmlDoc(xmlData: String) { val docFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance() try { val xmlDocBuilder = docFactory.newDocumentBuilder() xmlDoc = xmlDocBuilder.parse(java.io.ByteArrayInputStream(xmlData.toByteArray(charset("UTF8")))) } catch (ex: Exception) { throw UnsupportedOperationException(ex) } } /** * Converts percent to string. * @param amount the amount of percent * @param lang the language (RUS, UKR, ENG) * @param pennies the pennies (NUMBER, TEXT) * @return the string of percent */ @JvmOverloads fun percentToStr(amount: Double?, lang: Language?, pennies: Pennies? = Pennies.TEXT): String { if (amount == null) { throw IllegalArgumentException("amount is null") } if (lang == null) { throw IllegalArgumentException("language is null") } if (pennies == null) { throw IllegalArgumentException("pennies is null") } val intPart = amount.toLong() var fractPart: Long? = 0L val result: String if (amount.toFloat() == amount.toInt().toFloat()) { result = MoneyToStr(Currency.PER10, lang, pennies).convert(amount.toLong(), 0L) } else if (java.lang.Double.valueOf(amount * NUM10).toFloat() == java.lang.Double.valueOf(amount * NUM10).toInt().toFloat()) { fractPart = Math.round((amount - intPart) * NUM10) result = MoneyToStr(Currency.PER10, lang, pennies).convert(intPart, fractPart) } else if (java.lang.Double.valueOf(amount * NUM100).toFloat() == java.lang.Double.valueOf(amount * NUM100).toInt().toFloat()) { fractPart = Math.round((amount - intPart) * NUM100) result = MoneyToStr(Currency.PER100, lang, pennies).convert(intPart, fractPart) } else if (java.lang.Double.valueOf(amount * NUM1000).toFloat() == java.lang.Double.valueOf(amount * NUM1000).toInt().toFloat()) { fractPart = Math.round((amount - intPart) * NUM1000) result = MoneyToStr(Currency.PER1000, lang, pennies).convert(intPart, fractPart) } else { fractPart = Math.round((amount - intPart) * NUM10000) result = MoneyToStr(Currency.PER10000, lang, pennies).convert(intPart, fractPart) } return result } @JvmStatic fun main(args: Array) { var amount = "123.25" var language = "ENG" var currency = "USD" var pennies = "TEXT" if (args.size == 0) { println("Usage: java -jar moneytostr.jar --amount=123.25 --language=rus|ukr|eng --currency=rur|uah|usd --pennies=text|number") } else { for (arg in args) { if (arg.startsWith("--amount=")) { amount = arg.substring("--amount=".length).trim { it <= ' ' }.replace(",", ".") } else if (arg.startsWith("--language=")) { language = arg.substring("--language=".length).trim { it <= ' ' }.toUpperCase() } else if (arg.startsWith("--currency=")) { currency = arg.substring("--currency=".length).trim { it <= ' ' }.toUpperCase() } else if (arg.startsWith("--pennies=")) { pennies = arg.substring("--pennies=".length).trim { it <= ' ' }.toUpperCase() } } val result = MoneyToStr(Currency.valueOf(currency), Language.valueOf(language), Pennies.valueOf(pennies)).convert( java.lang.Double.valueOf(amount)) println(result) } } } }