public class Test {
public static void main(final String... args) {
final CodecFactory c = new CodecFactory();
final String s = "dsfröefr32ür4++4ä323ä-vx-";
final String encoded = c.encode(s);
final String decoded = c.decode(encoded);
System.out.println("string: " + s);
System.out.println("encoded: " + encoded);
System.out.println("decoded: " + decoded);
System.out.println("string equals decoded: " + s.equals(decoded));
}
}
class CodecFactory {
public String encode(final String s) {
return encoder.operate(s);
}
public String decode(final String s) {
return decoder.operate(s);
}
private abstract class Codec {
abstract String operate(final String s);
abstract int[] mkGroups(final int a);
abstract int concatenate(final int[] a);
abstract int[] toIntArray(final String s);
abstract String mkString(final int[] ints);
}
private final Codec encoder = new Codec() {
@Override
String operate(final String s) {
final int[][] subs = subs(3, toIntArray(s));
final int len = subs.length;
final int[] concats = new int[len];
for (int i = 0; i < len; ++i) {
concats[i] = concatenate(subs[i]);
}
final int[][] groups = new int[len][];
for (int i = 0; i < len; ++i) {
groups[i] = mkGroups(concats[i]);
}
final String[] strs = new String[len];
for (int i = 0; i < len; ++i) {
strs[i] = mkString(groups[i]);
}
final StringBuilder sb = new StringBuilder(len * 4);
for (final String string : strs) {
sb.append(string);
}
return sb.toString();
}
@Override
int[] mkGroups(final int a) {
return new int[] {
(a >> 18 & 0x3f) + 32,
(a >> 12 & 0x3f) + 32,
(a >> 6 & 0x3f) + 32,
(a & 0x3f) + 32
};
}
@Override
int concatenate(final int[] a) {
return a[2] + (a[1] << 8) + (a[0] << 16);
}
@Override
int[] toIntArray(final String s) {
final char[] c = s.toCharArray();
final int[] ret = new int[c.length];
for (int i = 0; i < c.length; ++i) {
ret[i] = c[i];
}
return ret;
}
@Override
String mkString(final int[] ints) {
final StringBuilder sb = new StringBuilder(ints.length * 2);
for (final int i : ints) {
sb.append(i);
}
return sb.toString();
}
int[][] subs(final int n, final int[] ints) {
final int rest = ints.length % n == 0 ? 0 : 1;
final int[][] ret = new int[ints.length / n + rest][n];
for (int i = n, j = 0; i < ints.length; ++i) {
if (i % n == 0) {
System.arraycopy(ints, i - n, ret[j++], 0, n);
}
}
if (rest != 0) {
final int last = ints.length % n;
System.arraycopy(ints, ints.length - last, ret[ret.length - 1], 0, last);
} else {
System.arraycopy(ints, ints.length - n, ret[ret.length - 1], 0, n);
}
return ret;
}
};
private final Codec decoder = new Codec() {
@Override
String operate(final String s) {
final int[][] ints = toInts(s);
final int len = ints.length;
final int[] concats = new int[len];
for (int i = 0; i < len; ++i) {
concats[i] = concatenate(ints[i]);
}
final int[][] groups = new int[len][];
for (int i = 0; i < len; ++i) {
groups[i] = mkGroups(concats[i]);
}
final String[] strs = new String[len];
for (int i = 0; i < len; ++i) {
strs[i] = mkString(groups[i]);
}
final StringBuilder sb = new StringBuilder(len * 4);
for (final String string : strs) {
sb.append(string);
}
return sb.toString();
}
@Override
int[] mkGroups(final int a) {
return new int[] {
a >> 16 & 0xff,
a >> 8 & 0xff,
a & 0xff
};
}
@Override
int concatenate(final int[] a) {
return a[3] - 32 + (a[2] - 32 << 6) + (a[1] - 32 << 12) + (a[0] - 32 << 18);
}
@Override
int[] toIntArray(final String s) {
final int[] ret = new int[s.length() / 2];
for (int i = 2, j = 0; i < s.length(); ++i) {
if (i % 2 == 0) {
ret[j++] = Integer.valueOf(s.substring(i - 2, i));
}
}
ret[s.length() / 2 - 1] = Integer.valueOf(s.substring(s.length() - 2, s.length()));
return ret;
}
@Override
String mkString(final int[] ints) {
final StringBuilder sb = new StringBuilder(ints.length);
for (final int i : ints) {
sb.append((char) i);
}
return sb.toString().trim();
}
int[][] toInts(final String s) {
final int[][] ret = new int[s.length() / 8][];
for (int i = 8, j = 0; i < s.length(); ++i) {
if (i % 8 == 0) {
ret[j++] = toIntArray(s.substring(i - 8, i));
}
}
ret[s.length() / 8 - 1] = toIntArray(s.substring(s.length() - 8, s.length()));
return ret;
}
};
}