URL Encode / Decode — Java source
Percent-encode or decode URLs and query parameters. Choose component (encodeURIComponent) or full-URI (encodeURI) mode. 100% client-side.
This is the Java implementation — the same logic the interactive tool runs, in a shareable, citable form.
// url-encode — percent encode/decode, component (encodeURIComponent) and full-URI (encodeURI) scope.
// Language: Java (17+, standard library only). Port of src/tools/UrlEncodeTool.tsx — same
// contract as this dir's go.go: each byte outside the safe set becomes %XX (uppercase hex);
// component keeps A-Za-z0-9-_.!~*'(), full URI also keeps ;,/?:@&=+$#; decode is strict (bad %
// pair, invalid UTF-8 -> error) and full-URI scope leaves encoded reserved bytes intact.
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
final class UrlEncode {
record Result(String output, String error) {} // error == null on success
static final String BAD = "malformed URI sequence";
static final String HEX = "0123456789ABCDEF";
static boolean compSafe(byte c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
|| "-_.!~*'()".indexOf(c) >= 0;
}
static boolean uriExtra(byte c) { return ";,/?:@&=+$#".indexOf(c) >= 0; }
static String encode(String s, boolean fullUri) {
StringBuilder b = new StringBuilder(s.length() * 3);
for (byte cb : s.getBytes(StandardCharsets.UTF_8)) {
int c = cb & 0xff;
if (compSafe(cb) || (fullUri && uriExtra(cb))) b.append((char) c);
else b.append('%').append(HEX.charAt(c >> 4)).append(HEX.charAt(c & 0xf));
}
return b.toString();
}
static int unhex(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static Result decode(String s, boolean fullUri) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream(s.length());
for (int i = 0; i < s.length();) {
if (s.charAt(i) != '%') { bytes.write(s.charAt(i++)); continue; }
if (i + 2 >= s.length()) return new Result("", BAD); // truncated tail
int hi = unhex(s.charAt(i + 1)), lo = unhex(s.charAt(i + 2));
if (hi < 0 || lo < 0) return new Result("", BAD); // bad hex digit
int c = hi << 4 | lo;
if (fullUri && c < 0x80 && uriExtra((byte) c)) { // decodeURI keeps encoded reserved
bytes.write('%');
bytes.write(s.charAt(i + 1));
bytes.write(s.charAt(i + 2));
} else {
bytes.write(c);
}
i += 3;
}
try {
// REPORT -> strict decoder: overlong/surrogate/truncated UTF-8 throws
return new Result(StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(bytes.toByteArray())).toString(), null);
} catch (CharacterCodingException e) {
return new Result("", BAD);
}
}
static void show(String label, Result r) { System.out.println(label + (r.error() != null ? r.error() : r.output())); }
public static void main(String[] args) {
System.out.println("enc: " + encode("hello world & café", false));
System.out.println("uri: " + encode("hello world & café", true));
show("dec: ", decode("hello%20world%20%26%20caf%C3%A9", false));
show("duri: ", decode("a%2Fb%3Fc%2Cd", true)); // reserved stay escaped in decodeURI
show("dcmp: ", decode("a%2Fb%3Fc%2Cd", false)); // ...but decode in component scope
show("bad: ", decode("100%", false));
}
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →