Compare commits

..

No commits in common. "5b293d9ca8ad3255b018bfe5e10ad75e54e55e5a" and "783a044cab6515bb05c049fe95fb41c98abb9770" have entirely different histories.

4 changed files with 71 additions and 101 deletions

View file

@ -37,7 +37,6 @@ compared to other solutions like the standard `URLEncoder` in the JDK or
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", "?="); // -> ?test=a%20test
UrlEncoder.encode("foo bar", true); // -> foo+bar (encode space to +)
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!😁"

View file

@ -75,7 +75,7 @@ tasks {
if (project.properties["testsBadgeApiKey"] != null) {
val apiKey = project.properties["testsBadgeApiKey"]
val response: HttpResponse<String> = HttpClient.newHttpClient()
val response: java.net.http.HttpResponse<String> = HttpClient.newHttpClient()
.send(
HttpRequest.newBuilder()
.uri(

View file

@ -5,9 +5,7 @@
package com.uwyn.urlencoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.*;
/**
* Most defensive approach to URL encoding and decoding.
@ -45,10 +43,6 @@ public final class UrlEncoder {
UNRESERVED_URI_CHARS = unreserved;
}
private UrlEncoder() {
// no-op
}
private static void appendUrlEncodedByte(StringBuilder out, int ch) {
out.append("%");
appendUrlEncodedDigit(out, ch >> 4);
@ -59,6 +53,10 @@ public final class UrlEncoder {
out.append(HEX_DIGITS[digit & 0x0F]);
}
private UrlEncoder() {
// no-op
}
/**
* Transforms a provided <code>String</code> URL into a new string,
* containing decoded URL characters in the UTF-8 encoding.
@ -69,7 +67,7 @@ public final class UrlEncoder {
* @since 1.0
*/
public static String decode(String source) {
if (source == null || source.isEmpty()) {
if (source == null || source.isBlank()) {
return source;
}
@ -109,7 +107,7 @@ public final class UrlEncoder {
i += 2;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Illegal characters in escape sequence: " + e.getMessage(), e);
throw new IllegalArgumentException("Illegal characters in escape sequence: " + e.getMessage());
}
} else {
if (bytes_buffer != null) {
@ -149,7 +147,22 @@ public final class UrlEncoder {
* @since 1.0
*/
public static String encode(String source) {
return encode(source, null, false);
return encode(source, (String) null);
}
/**
* Transforms a provided <code>String</code> object into a new string,
* containing only valid URL characters in the UTF-8 encoding.
*
* @param source The string that has to be transformed into a valid URL
* string.
* @param allow Additional characters to allow.
* @return The encoded <code>String</code> object.
* @see #decode(String)
* @since 1.0
*/
public static String encode(String source, char... allow) {
return encode(source, new String(allow));
}
/**
@ -164,37 +177,6 @@ public final class UrlEncoder {
* @since 1.0
*/
public static String encode(String source, String allow) {
return encode(source, allow, false);
}
/**
* Transforms a provided <code>String</code> object into a new string,
* containing only valid URL characters in the UTF-8 encoding.
*
* @param source The string that has to be transformed into a valid URL
* string.
* @param spaceToPlus Convert any space to {@code +}.
* @return The encoded <code>String</code> object.
* @see #decode(String)
* @since 1.0
*/
public static String encode(String source, boolean spaceToPlus) {
return encode(source, null, spaceToPlus);
}
/**
* Transforms a provided <code>String</code> object into a new string,
* containing only valid URL characters in the UTF-8 encoding.
*
* @param source The string that has to be transformed into a valid URL
* string.
* @param allow Additional characters to allow.
* @param spaceToPlus Convert any space to {@code +}.
* @return The encoded <code>String</code> object.
* @see #decode(String)
* @since 1.0
*/
public static String encode(String source, String allow, boolean spaceToPlus) {
if (source == null || source.isEmpty()) {
return source;
}
@ -214,27 +196,23 @@ public final class UrlEncoder {
out = new StringBuilder(source.length());
out.append(source, 0, i);
}
if (spaceToPlus && ch == ' ') {
out.append('+');
var cp = source.codePointAt(i);
if (cp < 0x80) {
appendUrlEncodedByte(out, cp);
i += 1;
} else {
var cp = source.codePointAt(i);
if (cp < 0x80) {
appendUrlEncodedByte(out, cp);
i += 1;
} else if (Character.isBmpCodePoint(cp)) {
for (var b : Character.toString(ch).getBytes(StandardCharsets.UTF_8)) {
appendUrlEncodedByte(out, b);
}
i += 1;
} else if (Character.isSupplementaryCodePoint(cp)) {
var high = Character.highSurrogate(cp);
var low = Character.lowSurrogate(cp);
for (var b : new String(new char[]{high, low}).getBytes(StandardCharsets.UTF_8)) {
appendUrlEncodedByte(out, b);
}
i += 2;
} else if (Character.isBmpCodePoint(cp)) {
for (var b : Character.toString(ch).getBytes(StandardCharsets.UTF_8)) {
appendUrlEncodedByte(out, b);
}
i += 1;
} else if (Character.isSupplementaryCodePoint(cp)) {
var high = Character.highSurrogate(cp);
var low = Character.lowSurrogate(cp);
for (var b : new String(new char[]{high, low}).getBytes(StandardCharsets.UTF_8)) {
appendUrlEncodedByte(out, b);
}
i += 2;
}
}
}
@ -252,37 +230,26 @@ public final class UrlEncoder {
return ch <= 'z' && UNRESERVED_URI_CHARS.get(ch);
}
/**
* Main method to encode/decode URLs on the command line
*
* @param arguments the command line arguments
* @since 1.1
*/
public static void main(String[] arguments) {
try {
var result = processMain(arguments);
if (result.status == 0) {
System.out.println(result.output);
} else {
System.err.println(result.output);
}
System.exit(result.status);
} catch (IllegalArgumentException e) {
System.err.println(UrlEncoder.class.getSimpleName() + ": " + e.getMessage());
System.exit(1);
static class MainResult {
final String output;
final int status;
public MainResult(String output, int status) {
this.output = output;
this.status = status;
}
}
static MainResult processMain(String... arguments) {
static MainResult processMain(String[] arguments) {
var valid_arguments = false;
var perform_decode = false;
var args = new ArrayList<>(List.of(arguments));
if (!args.isEmpty() && args.get(0).startsWith("-")) {
var option = args.remove(0);
if (("-d").equals(option)) {
if (option.equals("-d")) {
perform_decode = true;
valid_arguments = (args.size() == 1);
} else if (("-e").equals(option)) {
} else if (option.equals("-e")) {
valid_arguments = (args.size() == 1);
} else {
args.clear();
@ -308,13 +275,23 @@ public final class UrlEncoder {
}
}
static class MainResult {
final String output;
final int status;
public MainResult(String output, int status) {
this.output = output;
this.status = status;
/**
* Main method to encode/decode URLs on the command line
* @param arguments the command line arguments
* @since 1.1
*/
public static void main(String[] arguments) {
try {
var result = processMain(arguments);
if (result.status == 0) {
System.out.println(result.output);
} else {
System.err.println(result.output);
}
System.exit(result.status);
} catch(IllegalArgumentException e) {
System.err.println(UrlEncoder.class.getSimpleName() + ": " + e.getMessage());
System.exit(1);
}
}
}

View file

@ -75,9 +75,10 @@ class UrlEncoderTest {
@Test
void testEncodeWithAllowArg() {
assertEquals("?test=a%20test", UrlEncoder.encode("?test=a test", '=', '?'), "encode(x, =, ?)");
assertEquals("?test=a%20test", UrlEncoder.encode("?test=a test", "=?"), "encode(x, =?)");
assertEquals("aaa", UrlEncoder.encode("aaa", "a"), "encode(aaa, a)");
assertEquals(" ", UrlEncoder.encode(" ", " "), "encode(' ', ' ')");
assertEquals("aaa", UrlEncoder.encode("aaa", 'a'), "encode(aaa, a)");
assertEquals(" ", UrlEncoder.encode(" ", ' '), "encode(' ', ' ')");
}
@Test
@ -90,15 +91,8 @@ class UrlEncoderTest {
@Test
void testEncodeWithNulls() {
assertNull(UrlEncoder.encode(null), "encode(null)");
assertNull(UrlEncoder.encode(null, null), "encode(null, null)");
assertEquals("foo", UrlEncoder.encode("foo", null), "encode(foo, null");
}
@Test
void testEncodeSpaceToPlus() {
assertEquals("foo+bar", UrlEncoder.encode("foo bar", true));
assertEquals("foo+bar++foo", UrlEncoder.encode("foo bar foo", true));
assertEquals("foo bar", UrlEncoder.encode("foo bar", " ", true));
assertNull(UrlEncoder.encode(null, (String) null), "encode(null, null)");
assertEquals("foo", UrlEncoder.encode("foo", (String) null), "encode(foo, null");
}
@ParameterizedTest(name = "processMain(-d {1}) should be {0}")