From 153529029092fc398262b7a0eeef85a2e5c70fed Mon Sep 17 00:00:00 2001 From: "Erik C. Thauvin" Date: Fri, 30 Jul 2021 01:19:31 -0700 Subject: [PATCH] Implemented appendIfMissing and replaceEach extension functions to remove dependencies on StringUtils. --- .../net/thauvin/erik/mobibot/Mobibot.kt | 9 +- .../kotlin/net/thauvin/erik/mobibot/Utils.kt | 79 ++++++------- .../erik/mobibot/commands/tell/Tell.kt | 2 +- .../thauvin/erik/mobibot/entries/EntryLink.kt | 9 +- .../erik/mobibot/modules/CurrencyConverter.kt | 3 +- .../erik/mobibot/modules/ModuleException.kt | 8 +- .../net/thauvin/erik/mobibot/UtilsTest.kt | 107 ++++++++++++++---- 7 files changed, 143 insertions(+), 74 deletions(-) diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt index 0cb7100..c532b4b 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Mobibot.kt @@ -34,11 +34,11 @@ package net.thauvin.erik.mobibot import net.thauvin.erik.mobibot.PinboardUtils.addPin import net.thauvin.erik.mobibot.PinboardUtils.deletePin import net.thauvin.erik.mobibot.PinboardUtils.updatePin +import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.helpFormat -import net.thauvin.erik.mobibot.Utils.toDir import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.commands.AddLog @@ -95,6 +95,7 @@ import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger import org.jibble.pircbot.PircBot import java.io.BufferedOutputStream +import java.io.File import java.io.FileNotFoundException import java.io.FileOutputStream import java.io.IOException @@ -586,7 +587,7 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert } val nickname = p.getProperty("nick", Mobibot::class.java.name.lowercase()) val channel = p.getProperty("channel") - val logsDir = p.getProperty("logs", ".").toDir() + val logsDir = p.getProperty("logs", ".").appendIfMissing(File.separatorChar) // Redirect stdout and stderr if (!commandLine.hasOption(Constants.DEBUG_ARG[0])) { @@ -656,14 +657,14 @@ class Mobibot(nickname: String, channel: String, logsDirPath: String, p: Propert // Set the URLs weblogUrl = p.getProperty("weblog", "") - backlogsUrl = p.getProperty("backlogs", weblogUrl).toDir(true) + backlogsUrl = p.getProperty("backlogs", weblogUrl).appendIfMissing('/') // Load the current entries and backlogs, if any try { LinksMgr.startup(logsDir + EntriesMgr.CURRENT_XML, logsDir + EntriesMgr.NAV_XML, this.channel) if (logger.isDebugEnabled) logger.debug("Last feed: ${LinksMgr.startDate}") } catch (e: Exception) { - logger.error("An error occurred while loading the logs.", e) + if (logger.isErrorEnabled) logger.error("An error occurred while loading the logs.", e) } // Set the pinboard authentication diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt index 60895b3..a1545b1 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/Utils.kt @@ -31,7 +31,6 @@ */ package net.thauvin.erik.mobibot -import org.apache.commons.lang3.StringUtils import org.jibble.pircbot.Colors import org.jsoup.Jsoup import java.io.BufferedReader @@ -56,6 +55,18 @@ import java.util.stream.Collectors object Utils { private val searchFlags = arrayOf("%c", "%n") + /** + * Appends a suffix to the end of the String if not present. + */ + @JvmStatic + fun String.appendIfMissing(suffix: Char) : String { + return if (this.last() != suffix) { + "$this${suffix}" + } else { + this + } + } + /** * Makes the given int bold. */ @@ -81,7 +92,7 @@ object Utils { @JvmStatic fun buildCmdSyntax(text: String, botNick: String, isPrivate: Boolean): String { val replace = arrayOf(if (isPrivate) "/msg $botNick" else "$botNick:", botNick) - return StringUtils.replaceEach(text, searchFlags, replace) + return text.replaceEach(searchFlags, replace) } /** @@ -95,7 +106,7 @@ object Utils { */ @JvmStatic fun colorize(s: String?, color: String): String { - return if (s.isNullOrBlank()) { + return if (s.isNullOrEmpty()) { Colors.NORMAL } else if (Colors.BOLD == color || Colors.REVERSE == color) { color + s + color @@ -136,7 +147,8 @@ object Utils { @JvmStatic @JvmOverloads fun helpFormat(help: String, isBold: Boolean = true, isIndent: Boolean = true): String { - return (if (isIndent) " " else "").plus(if (isBold) bold(help) else help) + val s = if (isBold) bold(help) else help + return if (isIndent) s.prependIndent() else s } /** @@ -145,21 +157,16 @@ object Utils { @JvmStatic fun String.obfuscate(): String { return if (this.isNotBlank()) { - StringUtils.repeat('x', this.length) + "x".repeat(this.length) } else this } - /** - * Returns the plural form of a word, if count > 1. - */ - fun String.plural(count: Int, plural: String): String = this.plural(count.toLong(), plural) - /** * Returns the plural form of a word, if count > 1. */ @JvmStatic - fun String.plural(count: Long, plural: String): String { - return if (count > 1) plural else this + fun String.plural(count: Long): String { + return if (count > 1) "${this}s" else this } /** @@ -168,6 +175,20 @@ object Utils { @JvmStatic fun red(s: String?): String = colorize(s, Colors.RED) + /** + * Replaces all occurrences of Strings within another String. + */ + @JvmStatic + fun String.replaceEach(search: Array, replace: Array): String { + var result = this + if (search.size == replace.size) { + search.forEachIndexed { i, s -> + result = result.replace(s, replace[i]) + } + } + return result + } + /** * Makes the given string reverse color. */ @@ -180,26 +201,6 @@ object Utils { @JvmStatic fun today(): String = LocalDateTime.now().toIsoLocalDate() - /** - * Ensures that the given location (File/URL) has a trailing slash (`/`) to indicate a directory. - */ - @JvmStatic - fun String.toDir(isUrl: Boolean = false): String { - return if (isUrl) { - if (this.last() == '/') { - this - } else { - "$this/" - } - } else { - if (this.last() == File.separatorChar) { - this - } else { - this + File.separatorChar - } - } - } - /** * Converts a string to an int. */ @@ -266,21 +267,23 @@ object Utils { with(info) { if (years > 0) { - append(years).append(" year ".plural(years, " years ")) + append(years).append(" year".plural(years)).append(' ') } if (months > 0) { - append(weeks).append(" month ".plural(months, " months ")) + append(weeks).append(" month".plural(months)).append(' ') } if (weeks > 0) { - append(weeks).append(" week ".plural(weeks, " weeks ")) + append(weeks).append(" week".plural(weeks)).append(' ') } if (days > 0) { - append(days).append(" day ".plural(days, " days ")) + append(days).append(" day".plural(days)).append(' ') } if (hours > 0) { - append(hours).append(" hour ".plural(hours, " hours ")) + append(hours).append(" hour".plural(hours)).append(' ') } - append(minutes).append(" minute ".plural(minutes, " minutes")) + + append(minutes).append(" minute".plural(minutes)) + return toString() } } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt index 0792eaa..ddacd50 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/commands/tell/Tell.kt @@ -122,7 +122,7 @@ class Tell(bot: Mobibot) : AbstractCommand(bot) { helpFormat("%c $name "), "To view queued and sent messages:", helpFormat("%c $name ${View.VIEW_CMD}"), - "Messages are kept for ${bold(maxDays)}" + " day.".plural(maxDays, " days.") + "Messages are kept for ${bold(maxDays)}" + " day".plural(maxDays.toLong()) + '.' ) override val isOp: Boolean = false override val isPublic: Boolean = isEnabled() diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt index b87f777..1a8468f 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/entries/EntryLink.kt @@ -34,7 +34,6 @@ package net.thauvin.erik.mobibot.entries import com.rometools.rome.feed.synd.SyndCategory import com.rometools.rome.feed.synd.SyndCategoryImpl import net.thauvin.erik.mobibot.commands.links.LinksMgr -import org.apache.commons.lang3.StringUtils import java.io.Serializable import java.util.Calendar import java.util.Date @@ -144,9 +143,11 @@ class EntryLink : Serializable { * Returns true if a string is contained in the link, title, or nick. */ fun matches(match: String?): Boolean { - return (StringUtils.containsIgnoreCase(link, match) - || StringUtils.containsIgnoreCase(title, match) - || StringUtils.containsIgnoreCase(nick, match)) + return if (match.isNullOrEmpty()) { + false + } else { + link.contains(match, true) || title.contains(match, true) || nick.contains(match, true) + } } /** diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt index b714e9a..54a3dc2 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/CurrencyConverter.kt @@ -40,7 +40,6 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.msg.ErrorMessage import net.thauvin.erik.mobibot.msg.Message import net.thauvin.erik.mobibot.msg.PublicMessage -import org.apache.commons.lang3.StringUtils import org.jdom2.JDOMException import org.jdom2.input.SAXBuilder import java.io.IOException @@ -193,7 +192,7 @@ class CurrencyConverter(bot: Mobibot) : ThreadedModule(bot) { val rates = mutableListOf() for ((key, value) in EXCHANGE_RATES) { @Suppress("MagicNumber") - rates.add(" $key: ${StringUtils.leftPad(value, 8)}") + rates.add(" $key: ${value.padStart(8)}") } return rates } diff --git a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt index 48a0462..5318787 100644 --- a/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt +++ b/src/main/kotlin/net/thauvin/erik/mobibot/modules/ModuleException.kt @@ -32,7 +32,7 @@ package net.thauvin.erik.mobibot.modules import net.thauvin.erik.mobibot.Utils.obfuscate -import org.apache.commons.lang3.StringUtils +import net.thauvin.erik.mobibot.Utils.replaceEach /** * The `ModuleException` class. @@ -70,11 +70,11 @@ class ModuleException : Exception { fun getSanitizedMessage(vararg sanitize: String): String { val obfuscate = sanitize.map { it.obfuscate() }.toTypedArray() return when { - cause != null -> { - cause.javaClass.name + ": " + StringUtils.replaceEach(cause.message, sanitize, obfuscate) + cause?.message != null -> { + cause.javaClass.name + ": " + cause.message!!.replaceEach(sanitize, obfuscate) } message != null -> { - message.javaClass.name + ": " + StringUtils.replaceEach(message, sanitize, obfuscate) + message.javaClass.name + ": " + message.replaceEach(sanitize, obfuscate) } else -> "" } diff --git a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt index 4978cbf..15a19e4 100644 --- a/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt +++ b/src/test/kotlin/net/thauvin/erik/mobibot/UtilsTest.kt @@ -31,16 +31,21 @@ */ package net.thauvin.erik.mobibot +import net.thauvin.erik.mobibot.Utils.appendIfMissing import net.thauvin.erik.mobibot.Utils.bold +import net.thauvin.erik.mobibot.Utils.buildCmdSyntax import net.thauvin.erik.mobibot.Utils.capitalise import net.thauvin.erik.mobibot.Utils.colorize import net.thauvin.erik.mobibot.Utils.cyan +import net.thauvin.erik.mobibot.Utils.encodeUrl import net.thauvin.erik.mobibot.Utils.getIntProperty import net.thauvin.erik.mobibot.Utils.green +import net.thauvin.erik.mobibot.Utils.helpFormat import net.thauvin.erik.mobibot.Utils.obfuscate import net.thauvin.erik.mobibot.Utils.plural +import net.thauvin.erik.mobibot.Utils.red +import net.thauvin.erik.mobibot.Utils.replaceEach import net.thauvin.erik.mobibot.Utils.reverseColor -import net.thauvin.erik.mobibot.Utils.toDir import net.thauvin.erik.mobibot.Utils.toIntOrDefault import net.thauvin.erik.mobibot.Utils.toIsoLocalDate import net.thauvin.erik.mobibot.Utils.toUtcDateTime @@ -48,7 +53,6 @@ import net.thauvin.erik.mobibot.Utils.today import net.thauvin.erik.mobibot.Utils.unescapeXml import net.thauvin.erik.mobibot.Utils.uptime import net.thauvin.erik.mobibot.Utils.urlReader -import org.apache.commons.lang3.StringUtils import org.assertj.core.api.Assertions.assertThat import org.jibble.pircbot.Colors import org.testng.annotations.BeforeClass @@ -68,24 +72,47 @@ class UtilsTest { " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" private val cal = Calendar.getInstance() private val localDateTime = LocalDateTime.of(1952, 2, 17, 12, 30, 0) + private val test = "This is a test." @BeforeClass fun setUp() { cal[1952, Calendar.FEBRUARY, 17, 12, 30] = 0 } + @Test + fun testAppendIfMissing() { + val dir = "dir" + val sep = '/' + val url = "https://erik.thauvin.net" + assertThat(dir.appendIfMissing(File.separatorChar)).describedAs("appendIfMissing(dir)") + .isEqualTo(dir + File.separatorChar) + assertThat(url.appendIfMissing(sep)).describedAs("appendIfMissing(url)").isEqualTo("$url$sep") + assertThat("$url$sep".appendIfMissing(sep)).describedAs("appendIfMissing($url$sep)").isEqualTo("$url$sep") + } + @Test fun testBold() { assertThat(bold(1)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "1" + Colors.BOLD) + assertThat(bold(2L)).describedAs("bold(1)").isEqualTo(Colors.BOLD + "2" + Colors.BOLD) assertThat(bold(ascii)).describedAs("bold(ascii)").isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(bold("test")).describedAs("bold(test)").isEqualTo(Colors.BOLD + "test" + Colors.BOLD) - } + @Test + fun testBuildCmdSyntax() { + val bot = "mobibot" + assertThat(buildCmdSyntax("%c $test %n $test", bot, false)).describedAs("public") + .isEqualTo("$bot: $test $bot $test") + assertThat(buildCmdSyntax("%c %n $test %c $test %n", bot, true)).describedAs("public") + .isEqualTo("/msg $bot $bot $test /msg $bot $test $bot") + } + + @Test fun testCapitalise() { assertThat("test".capitalise()).describedAs("capitalize(test)").isEqualTo("Test") assertThat("Test".capitalise()).describedAs("capitalize(Test)").isEqualTo("Test") + assertThat(test.capitalise()).describedAs("capitalize($test)").isEqualTo(test) assertThat("".capitalise()).describedAs("capitalize()").isEqualTo("") } @@ -99,6 +126,8 @@ class UtilsTest { assertThat(colorize(ascii, Colors.BOLD)).describedAs("colorized(bold)") .isEqualTo(Colors.BOLD + ascii + Colors.BOLD) assertThat(colorize(null, Colors.RED)).describedAs("colorize(null)").isEqualTo(Colors.NORMAL) + assertThat(colorize("", Colors.RED)).describedAs("colorize()").isEqualTo(Colors.NORMAL) + } @Test @@ -106,13 +135,19 @@ class UtilsTest { assertThat(cyan(ascii)).isEqualTo(Colors.CYAN + ascii + Colors.NORMAL) } + @Test + fun testEncodeUrl() { + assertThat(encodeUrl("Hello Günter")).isEqualTo("Hello+G%C3%BCnter") + } + @Test fun testGetIntProperty() { val p = Properties() p["one"] = "1" - p["two"] = "foo" - assertThat(p.getIntProperty("one", 1)).describedAs("getIntProperty(one)").isEqualTo(1) + p["two"] = "two" + assertThat(p.getIntProperty("one", 9)).describedAs("getIntProperty(one)").isEqualTo(1) assertThat(p.getIntProperty("two", 2)).describedAs("getIntProperty(two)").isEqualTo(2) + assertThat(p.getIntProperty("foo", 3)).describedAs("getIntProperty(foo)").isEqualTo(3) } @Test @@ -120,6 +155,17 @@ class UtilsTest { assertThat(green(ascii)).isEqualTo(Colors.DARK_GREEN + ascii + Colors.NORMAL) } + @Test + fun testHelpFormat() { + assertThat(helpFormat(test, isBold = true, isIndent = false)).describedAs("bold") + .isEqualTo("${Colors.BOLD}$test${Colors.BOLD}") + assertThat(helpFormat(test, isBold = false, isIndent = true)).describedAs("indent") + .isEqualTo(test.prependIndent()) + assertThat(helpFormat(test, isBold = true, isIndent = true)).describedAs("bold-indent") + .isEqualTo(colorize(test, Colors.BOLD).prependIndent()) + + } + @Test fun testIsoLocalDate() { assertThat(cal.time.toIsoLocalDate()).describedAs("isoLocalDate(date)").isEqualTo("1952-02-17") @@ -129,7 +175,7 @@ class UtilsTest { @Test fun testObfuscate() { assertThat(ascii.obfuscate().length).describedAs("obfuscate is right length").isEqualTo(ascii.length) - assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo(StringUtils.repeat("x", ascii.length)) + assertThat(ascii.obfuscate()).describedAs("obfuscate()").isEqualTo("x".repeat(ascii.length)) assertThat(" ".obfuscate()).describedAs("obfuscate(blank)").isEqualTo(" ") } @@ -137,10 +183,31 @@ class UtilsTest { fun testPlural() { val week = "week" val weeks = "weeks" - assertThat(week.plural(-1, weeks)).describedAs("plural(-1)").isEqualTo(week) - assertThat(week.plural(0, weeks)).describedAs("plural(0)").isEqualTo(week) - assertThat(week.plural(1, weeks)).describedAs("plural(1)").isEqualTo(week) - assertThat(week.plural(2, weeks)).describedAs("plural(2)").isEqualTo(weeks) + + for (i in -1..3) { + assertThat(week.plural(i.toLong())).describedAs("plural($i)").isEqualTo(if (i > 1) weeks else week) + } + } + + @Test + fun testReplaceEach() { + val search = arrayOf("one", "two", "three") + val replace = arrayOf("1", "2", "3") + assertThat(search.joinToString(",").replaceEach(search, replace)).describedAs("replaceEach(1,2,3") + .isEqualTo(replace.joinToString(",")) + + assertThat(test.replaceEach(search, replace)).describedAs("replaceEach(nothing)").isEqualTo(test) + + assertThat(test.replaceEach(arrayOf("t", "e"), arrayOf("", "E"))).describedAs("replaceEach($test)") + .isEqualTo(test.replace("t", "").replace("e", "E")) + + assertThat(test.replaceEach(search, emptyArray())).describedAs("replaceEach(search, empty)") + .isEqualTo(test) + } + + @Test + fun testRed() { + assertThat(red(ascii)).isEqualTo(colorize(ascii, Colors.RED)) } @Test @@ -153,13 +220,6 @@ class UtilsTest { assertThat(today()).isEqualTo(LocalDateTime.now().toIsoLocalDate()) } - @Test - fun testToDir() { - assertThat("dir".toDir(false)).describedAs("toDir(dir, false)").isEqualTo("dir" + File.separatorChar) - assertThat("https://erik.thauvin.net".toDir(true)).describedAs("toDir(erik.thauvin.net, true)") - .isEqualTo("https://erik.thauvin.net/") - } - @Test fun testToIntOrDefault() { assertThat("10".toIntOrDefault(1)).describedAs("toIntOrDefault(10, 1)").isEqualTo(10) @@ -175,16 +235,21 @@ class UtilsTest { @Test fun testUptime() { - assertThat("17 years 2 months 2 weeks 1 day 6 hours 45 minutes").isEqualTo(uptime(547800300076L)) + assertThat(uptime(547800300076L)).describedAs("full") + .isEqualTo("17 years 2 months 2 weeks 1 day 6 hours 45 minutes") + assertThat(uptime(2700000L)).describedAs("minutes").isEqualTo("45 minutes") + assertThat(uptime(24300000L)).describedAs("hours minutes").isEqualTo("6 hours 45 minutes") + assertThat(uptime(110700000L)).describedAs("days hours minutes").isEqualTo("1 day 6 hours 45 minutes") + assertThat(uptime(1320300000L)).describedAs("weeks days hours minutes") + .isEqualTo("2 weeks 1 day 6 hours 45 minutes") + assertThat(uptime(0L)).describedAs("0 minutes").isEqualTo("0 minute") } @Test @Throws(IOException::class) fun testUrlReader() { assertThat(urlReader(URL("https://postman-echo.com/status/200"))).describedAs("urlReader()") - .isEqualTo( - "{\"status\":200}" - ) + .isEqualTo("{\"status\":200}") } @Test