package core.framework.api.util;
import java.util.Base64;
import java.util.BitSet;
/**
* @author neo
*/
public final class Encodings {
private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
// refer to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent,
// refer to http://tools.ietf.org/html/rfc3986
private static final BitSet URI_UNESCAPED = new BitSet(128);
static {
for (int i = 'a'; i <= 'z'; i++) {
URI_UNESCAPED.set(i);
}
for (int i = 'A'; i <= 'Z'; i++) {
URI_UNESCAPED.set(i);
}
for (int i = '0'; i <= '9'; i++) {
URI_UNESCAPED.set(i);
}
URI_UNESCAPED.set('-');
URI_UNESCAPED.set('_');
URI_UNESCAPED.set('.');
URI_UNESCAPED.set('!');
URI_UNESCAPED.set('~');
URI_UNESCAPED.set('*');
URI_UNESCAPED.set('\'');
URI_UNESCAPED.set('(');
URI_UNESCAPED.set(')');
}
// refer to impl from org.springframework.web.util.HierarchicalUriComponents#encodeUriComponent
public static String uriComponent(String value) {
byte[] bytes = Strings.bytes(value);
int length = bytes.length;
for (int i = 0; i < length; i++) {
byte b1 = bytes[i];
if (b1 < 0 || !URI_UNESCAPED.get(b1)) { // the bytes java returned is signed, but we only need to check ascii (0-127)
StringBuilder builder = new StringBuilder(length * 2);
if (i > 0) builder.append(value, 0, i); // unescaped bytes must be single byte char
for (int j = i; j < length; j++) {
byte b2 = bytes[j];
if (b2 >= 0 && URI_UNESCAPED.get(b2)) {
builder.append((char) b2);
} else {
builder.append('%');
builder.append(HEX_CHARS[(b2 >> 4) & 0xF]);
builder.append(HEX_CHARS[b2 & 0xF]);
}
}
return builder.toString();
}
}
return value;
}
// refer to impl from org.springframework.web.util.UriUtils#decode
public static String decodeURIComponent(String value) {
int length = value.length();
int index = 0;
for (; index < length; index++) {
int ch = value.charAt(index);
if (ch == '%') break;
}
if (index == length) return value;
byte[] buffer = new byte[length];
for (int i = 0; i < index; i++) buffer[i] = (byte) value.charAt(i);
int position = index;
for (; index < length; index++) {
char ch = value.charAt(index);
if (ch == '%') {
if ((index + 2) >= length) throw new IllegalArgumentException("invalid uri encoding, value=" + value.substring(index));
char hex1 = value.charAt(index + 1);
char hex2 = value.charAt(index + 2);
int u = Character.digit(hex1, 16);
int l = Character.digit(hex2, 16);
if (u == -1 || l == -1) throw new IllegalArgumentException("invalid uri encoding, value=" + value.substring(index));
buffer[position] = (byte) ((u << 4) + l);
index += 2;
} else {
buffer[position] = (byte) ch; // here assume value is valid uri encoded string, if it contains multi-bytes (which should be encoded), here will not give right result
}
position++;
}
return new String(buffer, 0, position, Charsets.UTF_8);
}
public static String base64(String value) {
return base64(Strings.bytes(value));
}
public static String base64(byte[] value) {
return Base64.getEncoder().encodeToString(value);
}
public static byte[] decodeBase64(String value) {
return Base64.getDecoder().decode(value);
}
public static String base64URLSafe(byte[] value) {
return Base64.getUrlEncoder().encodeToString(value);
}
public static byte[] decodeBase64URLSafe(String value) {
return Base64.getUrlDecoder().decode(value);
}
}