diff --git a/README.md b/README.md index 636d1d2..e95eb63 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ compared to other solutions like the standard `URLEncoder` in the JDK. UrlEncoder.encode("a test &") // -> a%20test%20%26 UrlEncoder.encode("%#okékÉȢ smile!😁") // -> %25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81 UrlEncoder.encode("?test=a test", allow = "?=") // -> ?test=a%20test +UrlEncoder.endode("foo bar", spaceToPlus = true) // -> foo+bar UrlEncoder.decode("a%20test%20%26") // -> a test & UrlEncoder.decode("%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81") // -> %#okékÉȢ smile!😁 diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 4787125..19d997f 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -23,7 +23,7 @@ plugins { description = "A simple defensive library to encode/decode URL components" group = "net.thauvin.erik" -version = "1.0.1" +version = "1.2.1-SNAPSHOT" val mavenName = "UrlEncoder" @@ -40,7 +40,7 @@ repositories { dependencies { // testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25") - testImplementation("org.junit.jupiter:junit-jupiter:5.9.0") + testImplementation("org.junit.jupiter:junit-jupiter:5.9.1") } base { diff --git a/lib/detekt-baseline.xml b/lib/detekt-baseline.xml index f4b9fda..571f54a 100644 --- a/lib/detekt-baseline.xml +++ b/lib/detekt-baseline.xml @@ -9,6 +9,6 @@ MagicNumber:UrlEncoder.kt$UrlEncoder$3 MagicNumber:UrlEncoder.kt$UrlEncoder$4 MaxLineLength:UrlEncoder.kt$UrlEncoder$* - NestedBlockDepth:UrlEncoder.kt$UrlEncoder$@JvmStatic fun encode(source: String, allow: String): String + NestedBlockDepth:UrlEncoder.kt$UrlEncoder$@JvmStatic @JvmOverloads fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String diff --git a/lib/pom.xml b/lib/pom.xml index 25f1f01..7a70ae1 100644 --- a/lib/pom.xml +++ b/lib/pom.xml @@ -8,7 +8,7 @@ 4.0.0 net.thauvin.erik urlencoder - 1.0.1 + 1.2.1-SNAPSHOT UrlEncoder for Kotlin A simple defensive library to encode/decode URL components https://github.com/ethauvin/urlencoder diff --git a/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt b/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt index d39b354..37ea9c2 100644 --- a/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt +++ b/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt @@ -40,7 +40,7 @@ import kotlin.system.exitProcess object UrlEncoder { private val hexDigits = "0123456789ABCDEF".toCharArray() internal val usage = - "Usage : java -jar urlencoder-*all.jar [-ed] text" + System.lineSeparator() + + "Usage : java -jar urlencoder-*all.jar [-ed] " + System.lineSeparator() + "Encode and decode URL components defensively." + System.lineSeparator() + " -e encode (default) " + System.lineSeparator() + " -d decode" @@ -85,7 +85,7 @@ object UrlEncoder { */ @JvmStatic fun decode(source: String): String { - if (source.isBlank()) { + if (source.isEmpty()) { return source } @@ -142,7 +142,8 @@ object UrlEncoder { * - Letters, numbers, unreserved (`_-!.'()*`) and allowed characters are left intact. */ @JvmStatic - fun encode(source: String, allow: String): String { + @JvmOverloads + fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String { if (source.isEmpty()) { return source } @@ -159,22 +160,27 @@ object UrlEncoder { out = StringBuilder(source.length) out.append(source, 0, i) } - val cp = source.codePointAt(i) - if (cp < 0x80) { - out.appendEncodedByte(cp) + if (spaceToPlus && ch == ' ') { + out.append('+') i++ - } else if (Character.isBmpCodePoint(cp)) { - for (b in ch.toString().toByteArray(StandardCharsets.UTF_8)) { - out.appendEncodedByte(b.toInt()) + } else { + val cp = source.codePointAt(i) + if (cp < 0x80) { + out.appendEncodedByte(cp) + i++ + } else if (Character.isBmpCodePoint(cp)) { + for (b in ch.toString().toByteArray(StandardCharsets.UTF_8)) { + out.appendEncodedByte(b.toInt()) + } + i++ + } else if (Character.isSupplementaryCodePoint(cp)) { + val high = Character.highSurrogate(cp) + val low = Character.lowSurrogate(cp) + for (b in charArrayOf(high, low).concatToString().toByteArray(StandardCharsets.UTF_8)) { + out.appendEncodedByte(b.toInt()) + } + i += 2 } - i++ - } else if (Character.isSupplementaryCodePoint(cp)) { - val high = Character.highSurrogate(cp) - val low = Character.lowSurrogate(cp) - for (b in charArrayOf(high, low).concatToString().toByteArray(StandardCharsets.UTF_8)) { - out.appendEncodedByte(b.toInt()) - } - i += 2 } } } @@ -182,17 +188,6 @@ object UrlEncoder { return out?.toString() ?: source } - /** - * Transforms a provided [String] object into a new string, containing only valid URL characters in the UTF-8 - * encoding. - * - * - Letters, numbers, unreserved (`_-!.'()*`) and allowed characters are left intact. - */ - @JvmStatic - fun encode(source: String, vararg allow: Char): String { - return encode(source, String(allow)) - } - /** * Encodes and decodes URLs from the command line. * diff --git a/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt index 79658e0..a99a29e 100644 --- a/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt +++ b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt @@ -81,23 +81,29 @@ class UrlEncoderTest { @Test fun `Encode Empty or Blank`() { - assertTrue(encode("", "").isEmpty(), "encode('','')") + assertTrue(encode("", allow = "").isEmpty(), "encode('','')") assertEquals("", encode(""), "encode('')") - assertEquals("%20", encode(" "), "encode('')") + assertEquals("%20", encode(" "), "encode(' ')") } @Test fun `Encode when None needed`() { assertSame(same, encode(same)) - assertSame(same, encode(same, ""), "with empty allow") + assertSame(same, encode(same, allow = ""), "with empty allow") } @Test fun `Encode with Allow Arg`() { - assertEquals("?test=a%20test", encode("?test=a test", '=', '?'), "encode(x, =, ?)") - assertEquals("?test=a%20test", encode("?test=a test", "=?"), "encode(x, =?)") - assertEquals("aaa", encode("aaa", 'a'), "encode(aaa, a)") - assertEquals(" ", encode(" ", ' '), "encode(' ', ' ')") + assertEquals("?test=a%20test", encode("?test=a test", allow = "=?"), "encode(x, =?)") + assertEquals("aaa", encode("aaa", "a"), "encode(aaa, a)") + assertEquals(" ", encode(" ", " "), "encode(' ', ' ')") + } + + @Test + fun `Encode with Space to Plus`() { + assertEquals("foo+bar", encode("foo bar", spaceToPlus = true)) + assertEquals("foo+bar++foo", encode("foo bar foo", spaceToPlus = true)) + assertEquals("foo bar", encode("foo bar", " ", true)) } @ParameterizedTest(name = "processMain(-d {1}) should be {0}")