|
| 1 | +package com.tschuchort.compiletesting |
| 2 | + |
| 3 | +import java.io.ByteArrayOutputStream |
| 4 | +import java.nio.charset.StandardCharsets |
| 5 | + |
| 6 | +/** |
| 7 | + * Encode the given URI [path] with UTF_8 encoding |
| 8 | + */ |
| 9 | +fun encodePath(path: String): String { |
| 10 | + if (path.isBlank()) { |
| 11 | + return path |
| 12 | + } |
| 13 | + val bytes = path.encodeToByteArray() |
| 14 | + var alreadyEncoded = true |
| 15 | + for (b in bytes) { |
| 16 | + if (!isValidEncodedChar(b.toInt())) { |
| 17 | + alreadyEncoded = false |
| 18 | + break |
| 19 | + } |
| 20 | + } |
| 21 | + if (alreadyEncoded) { |
| 22 | + return path |
| 23 | + } |
| 24 | + |
| 25 | + val baos = ByteArrayOutputStream(bytes.size) |
| 26 | + for (b in bytes) { |
| 27 | + val byte = b.toInt() |
| 28 | + if (isValidEncodedChar(byte)) { |
| 29 | + baos.write(byte) |
| 30 | + } else { |
| 31 | + baos.write('%'.code) |
| 32 | + val hex1 = Character.forDigit(/* digit = */ b.toInt() shr 4 and 0xF, /* radix = */ 16).uppercaseChar() |
| 33 | + val hex2 = Character.forDigit(/* digit = */ b.toInt() and 0xF, /* radix = */ 16).uppercaseChar() |
| 34 | + baos.write(hex1.code) |
| 35 | + baos.write(hex2.code) |
| 36 | + } |
| 37 | + } |
| 38 | + return baos.toString(StandardCharsets.UTF_8.toString()) |
| 39 | +} |
| 40 | + |
| 41 | +/** |
| 42 | + * Checks whether input char [c] is valid and already an encoded character or not. |
| 43 | + */ |
| 44 | +fun isValidEncodedChar(c: Int): Boolean { |
| 45 | + return isPchar(c) || '/'.code == c |
| 46 | +} |
| 47 | + |
| 48 | +/** |
| 49 | + * Indicates whether the given character is in the {@code pchar} set. |
| 50 | + * @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a> |
| 51 | + */ |
| 52 | +fun isPchar(c: Int): Boolean { |
| 53 | + return (isUnreserved(c) || isSubDelimiter(c) || ':'.code == c || '@'.code == c) |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * Indicates whether the given character is in the `sub-delims` set. |
| 58 | + * @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a> |
| 59 | + */ |
| 60 | +fun isSubDelimiter(c: Int): Boolean { |
| 61 | + return '!'.code == c || '$'.code == c || '&'.code == c || '\''.code == c || '('.code == c || ')'.code == c || '*'.code == c || '+'.code == c || ','.code == c || ';'.code == c || '='.code == c |
| 62 | +} |
| 63 | + |
| 64 | +/** |
| 65 | + * Indicates whether the given character is in the `unreserved` set. |
| 66 | + * @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a> |
| 67 | + */ |
| 68 | +fun isUnreserved(c: Int): Boolean { |
| 69 | + return isAlpha(c) || Character.isDigit(c) || '-'.code == c || '.'.code == c || '_'.code == c || '~'.code == c |
| 70 | +} |
| 71 | + |
| 72 | +/** |
| 73 | + * Indicates whether the given character is in the `ALPHA` set. |
| 74 | + * @see <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC 3986, appendix A</a> |
| 75 | + */ |
| 76 | +fun isAlpha(c: Int): Boolean { |
| 77 | + return c >= 'a'.code && c <= 'z'.code || c >= 'A'.code && c <= 'Z'.code |
| 78 | +} |
| 79 | + |
0 commit comments