Added spaceToPlus parameter to encode function

This commit is contained in:
Erik C. Thauvin 2023-01-05 20:42:00 -08:00
parent 50ffe56ba8
commit f8b9376f40
6 changed files with 41 additions and 39 deletions

View file

@ -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("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("%#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.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("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!😁 UrlEncoder.decode("%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81") // -> %#okékÉȢ smile!😁

View file

@ -23,7 +23,7 @@ plugins {
description = "A simple defensive library to encode/decode URL components" description = "A simple defensive library to encode/decode URL components"
group = "net.thauvin.erik" group = "net.thauvin.erik"
version = "1.0.1" version = "1.2.1-SNAPSHOT"
val mavenName = "UrlEncoder" val mavenName = "UrlEncoder"
@ -40,7 +40,7 @@ repositories {
dependencies { dependencies {
// testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25") // 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 { base {

View file

@ -9,6 +9,6 @@
<ID>MagicNumber:UrlEncoder.kt$UrlEncoder$3</ID> <ID>MagicNumber:UrlEncoder.kt$UrlEncoder$3</ID>
<ID>MagicNumber:UrlEncoder.kt$UrlEncoder$4</ID> <ID>MagicNumber:UrlEncoder.kt$UrlEncoder$4</ID>
<ID>MaxLineLength:UrlEncoder.kt$UrlEncoder$*</ID> <ID>MaxLineLength:UrlEncoder.kt$UrlEncoder$*</ID>
<ID>NestedBlockDepth:UrlEncoder.kt$UrlEncoder$@JvmStatic fun encode(source: String, allow: String): String</ID> <ID>NestedBlockDepth:UrlEncoder.kt$UrlEncoder$@JvmStatic @JvmOverloads fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String</ID>
</CurrentIssues> </CurrentIssues>
</SmellBaseline> </SmellBaseline>

View file

@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.thauvin.erik</groupId> <groupId>net.thauvin.erik</groupId>
<artifactId>urlencoder</artifactId> <artifactId>urlencoder</artifactId>
<version>1.0.1</version> <version>1.2.1-SNAPSHOT</version>
<name>UrlEncoder for Kotlin</name> <name>UrlEncoder for Kotlin</name>
<description>A simple defensive library to encode/decode URL components</description> <description>A simple defensive library to encode/decode URL components</description>
<url>https://github.com/ethauvin/urlencoder</url> <url>https://github.com/ethauvin/urlencoder</url>

View file

@ -40,7 +40,7 @@ import kotlin.system.exitProcess
object UrlEncoder { object UrlEncoder {
private val hexDigits = "0123456789ABCDEF".toCharArray() private val hexDigits = "0123456789ABCDEF".toCharArray()
internal val usage = internal val usage =
"Usage : java -jar urlencoder-*all.jar [-ed] text" + System.lineSeparator() + "Usage : java -jar urlencoder-*all.jar [-ed] <text>" + System.lineSeparator() +
"Encode and decode URL components defensively." + System.lineSeparator() + " -e encode (default) " + "Encode and decode URL components defensively." + System.lineSeparator() + " -e encode (default) " +
System.lineSeparator() + " -d decode" System.lineSeparator() + " -d decode"
@ -85,7 +85,7 @@ object UrlEncoder {
*/ */
@JvmStatic @JvmStatic
fun decode(source: String): String { fun decode(source: String): String {
if (source.isBlank()) { if (source.isEmpty()) {
return source return source
} }
@ -142,7 +142,8 @@ object UrlEncoder {
* - Letters, numbers, unreserved (`_-!.'()*`) and allowed characters are left intact. * - Letters, numbers, unreserved (`_-!.'()*`) and allowed characters are left intact.
*/ */
@JvmStatic @JvmStatic
fun encode(source: String, allow: String): String { @JvmOverloads
fun encode(source: String, allow: String = "", spaceToPlus: Boolean = false): String {
if (source.isEmpty()) { if (source.isEmpty()) {
return source return source
} }
@ -159,22 +160,27 @@ object UrlEncoder {
out = StringBuilder(source.length) out = StringBuilder(source.length)
out.append(source, 0, i) out.append(source, 0, i)
} }
val cp = source.codePointAt(i) if (spaceToPlus && ch == ' ') {
if (cp < 0x80) { out.append('+')
out.appendEncodedByte(cp)
i++ i++
} else if (Character.isBmpCodePoint(cp)) { } else {
for (b in ch.toString().toByteArray(StandardCharsets.UTF_8)) { val cp = source.codePointAt(i)
out.appendEncodedByte(b.toInt()) 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 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. * Encodes and decodes URLs from the command line.
* *

View file

@ -81,23 +81,29 @@ class UrlEncoderTest {
@Test @Test
fun `Encode Empty or Blank`() { fun `Encode Empty or Blank`() {
assertTrue(encode("", "").isEmpty(), "encode('','')") assertTrue(encode("", allow = "").isEmpty(), "encode('','')")
assertEquals("", encode(""), "encode('')") assertEquals("", encode(""), "encode('')")
assertEquals("%20", encode(" "), "encode('')") assertEquals("%20", encode(" "), "encode(' ')")
} }
@Test @Test
fun `Encode when None needed`() { fun `Encode when None needed`() {
assertSame(same, encode(same)) assertSame(same, encode(same))
assertSame(same, encode(same, ""), "with empty allow") assertSame(same, encode(same, allow = ""), "with empty allow")
} }
@Test @Test
fun `Encode with Allow Arg`() { fun `Encode with Allow Arg`() {
assertEquals("?test=a%20test", encode("?test=a test", '=', '?'), "encode(x, =, ?)") assertEquals("?test=a%20test", encode("?test=a test", allow = "=?"), "encode(x, =?)")
assertEquals("?test=a%20test", encode("?test=a test", "=?"), "encode(x, =?)") assertEquals("aaa", encode("aaa", "a"), "encode(aaa, a)")
assertEquals("aaa", encode("aaa", 'a'), "encode(aaa, a)") assertEquals(" ", encode(" ", " "), "encode(' ', ' ')")
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}") @ParameterizedTest(name = "processMain(-d {1}) should be {0}")