java.net.IDN Maven / Gradle / Ivy
package java.net;
import java.util.Locale;
public class IDN {
/* Punycode parameters */
private static final int TMIN = 1;
private static final int TMAX = 26;
private static final int BASE = 36;
private static final int INITIAL_N = 128;
private static final int INITIAL_BIAS = 72;
private static final int DAMP = 700;
private static final int SKEW = 38;
private static final char DELIMITER = '-';
private static final String ACE_PREFIX = "xn--";
public static String toASCII(final String input) {
if (isOnlyASCII(input)) {
return input;
}
if (input == null) {
throw new IllegalArgumentException("input can not be null!");
}
return encodeDomain(input.toLowerCase(Locale.ENGLISH));
}
/**
* Punycodes a unicode string.
*
* @param input Unicode string.
* @return Punycoded string.
*/
private static String encodeDomain(final String input) {
final StringBuffer resultBuffer = new StringBuffer();
final String[] domainParts = input.split("[\\.\\u3002\\uFF0E\\uFF61]");
for (final String part : domainParts) {
if (resultBuffer.length() > 0) {
resultBuffer.append('.');
}
if (isOnlyASCII(part)) {
resultBuffer.append(part);
} else {
final String encodedPart = encode(part);
resultBuffer.append(ACE_PREFIX);
resultBuffer.append(encodedPart);
}
}
return resultBuffer.toString();
}
/**
* Punycodes a unicode string.
*
* @param input Unicode string.
* @return Punycoded string.
*/
public static String encode(final String input) throws IllegalArgumentException {
int n = INITIAL_N;
int delta = 0;
int bias = INITIAL_BIAS;
final StringBuffer output = new StringBuffer();
// Copy all basic code points to the output
int b = 0;
for (int i = 0; i < input.length(); i++) {
final char c = input.charAt(i);
final int charAsInt = c;
if (charAsInt <= 0x9F && (charAsInt >= 0x7F || charAsInt >>> 5 == 0) || charAsInt > 55296) {
throw new IllegalArgumentException("Bad input");
} else if (isBasic(c)) {
output.append(c);
b++;
}
}
// Append delimiter
if (b > 0) {
output.append(DELIMITER);
}
int h = b;
while (h < input.length()) {
int m = Integer.MAX_VALUE;
// Find the minimum code point >= n
for (int i = 0; i < input.length(); i++) {
final int c = input.charAt(i);
if (c >= n && c < m) {
m = c;
}
}
if (m - n > (Integer.MAX_VALUE - delta) / (h + 1)) {
throw new IllegalArgumentException("overflow");
}
delta = delta + (m - n) * (h + 1);
n = m;
for (int j = 0; j < input.length(); j++) {
final int c = input.charAt(j);
if (c < n) {
delta++;
if (0 == delta) {
throw new IllegalArgumentException("overflow");
}
}
if (c == n) {
int q = delta;
for (int k = BASE;; k += BASE) {
int t;
if (k <= bias) {
t = TMIN;
} else if (k >= bias + TMAX) {
t = TMAX;
} else {
t = k - bias;
}
if (q < t) {
break;
}
output.append((char) digit2codepoint(t + (q - t) % (BASE - t)));
q = (q - t) / (BASE - t);
}
output.append((char) digit2codepoint(q));
bias = adapt(delta, h + 1, h == b);
delta = 0;
h++;
}
}
delta++;
n++;
}
return output.toString();
}
public final static int adapt(int delta, final int numpoints, final boolean first) {
if (first) {
delta = delta / DAMP;
} else {
delta = delta / 2;
}
delta = delta + delta / numpoints;
int k = 0;
while (delta > (BASE - TMIN) * TMAX / 2) {
delta = delta / (BASE - TMIN);
k = k + BASE;
}
return k + (BASE - TMIN + 1) * delta / (delta + SKEW);
}
private final static boolean isBasic(final char c) {
return c < 0x80;
}
private final static int digit2codepoint(final int d) throws IllegalArgumentException {
if (d < 26) {
// 0..25 : 'a'..'z'
return d + 'a';
} else if (d < 36) {
// 26..35 : '0'..'9';
return d - 26 + '0';
} else {
throw new IllegalArgumentException("bad input");
}
}
/*
* Check if input contains only ASCII Treats null as all ASCII
*/
private static boolean isOnlyASCII(final String input) {
if (input == null) {
return true;
}
for (int i = 0; i < input.length(); i++) {
if (input.charAt(i) > 0x7F) {
return false;
}
}
return true;
}
public static String toUnicode(final String line) {
return line;
}
}