import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
private static final String ZEROES
= "000"; public static String codePointToHexString
(int codePoint
) { assert codePoint >= 0;
if (str.length() < 4) str = ZEROES.substring(3 - 4 + str.length()) + str;
return str;
}
private static class Block {
public final int startCodePoint;
public final int endCodePoint;
public final String description
;
public Block
(int startCodePoint,
int endCodePoint,
String description
) { this.startCodePoint = startCodePoint;
this.endCodePoint = endCodePoint;
this.description = description;
}
@Override
return codePointToHexString(startCodePoint) + ".." + codePointToHexString(endCodePoint) + "; " + description;
}
}
final URL blocksTxtURL
= new URL("http://w...content-available-to-author-only...e.org/Public/UCD/latest/ucd/Blocks.txt");
final List<Block> blocks = new ArrayList<Block>();
while ((line = lnr.readLine()) != null) {
if (line.isEmpty() || line.charAt(0) == '#') continue;
final int semicolonPos = line.indexOf(';');
if (semicolonPos < 0) continue;
final int dotdotPos = line.lastIndexOf("..", semicolonPos);
if (dotdotPos < 0) continue;
final int startCodePoint
= Integer.
parseInt(line.
substring(0, dotdotPos
),
16); final int endCodePoint
= Integer.
parseInt(line.
substring(dotdotPos
+ 2, semicolonPos
),
16); final String description
= line.
substring(semicolonPos
+ 1).
trim(); blocks.add(new Block(startCodePoint, endCodePoint, description));
}
blocks.add(new Block(0, 0xFFFF, "(BMP)"));
for (final Block b : blocks) {
final Map
<Integer, List
<String
>> m
= new HashMap
<Integer, List
<String
>>(); for (int i = b.startCodePoint; i < b.endCodePoint; ++i) {
final String normStr
= Normalizer.
normalize(str, Normalizer.
Form.
NFC); final int codePointCount = normStr.codePointCount(0, normStr.length());
if (!m.containsKey(codePointCount)) m.put(codePointCount, new ArrayList<String>());
m.get(codePointCount).add(str);
}
for (final Map.
Entry<Integer, List
<String
>> e
: m.
entrySet()) { System.
out.
println(e.
getKey() + ": " + e.
getValue().
size()); if (e.getKey() > 1) {
for (final String str
: e.
getValue()) { System.
out.
print(" " + codePointToHexString
(str.
codePointAt(0))); }
}
}
}
}
}
aW1wb3J0IGphdmEuaW8uSW5wdXRTdHJlYW1SZWFkZXI7CmltcG9ydCBqYXZhLmlvLkxpbmVOdW1iZXJSZWFkZXI7CmltcG9ydCBqYXZhLm5ldC5VUkw7CmltcG9ydCBqYXZhLnRleHQuTm9ybWFsaXplcjsKaW1wb3J0IGphdmEudXRpbC5BcnJheUxpc3Q7CmltcG9ydCBqYXZhLnV0aWwuSGFzaE1hcDsKaW1wb3J0IGphdmEudXRpbC5MaXN0OwppbXBvcnQgamF2YS51dGlsLk1hcDsKCnB1YmxpYyBjbGFzcyBNYWluIHsKICAgIHByaXZhdGUgc3RhdGljIGZpbmFsIFN0cmluZyBaRVJPRVMgPSAiMDAwIjsKICAgIHB1YmxpYyBzdGF0aWMgU3RyaW5nIGNvZGVQb2ludFRvSGV4U3RyaW5nKGludCBjb2RlUG9pbnQpIHsKICAgICAgICBhc3NlcnQgY29kZVBvaW50ID49IDA7CiAgICAgICAgU3RyaW5nIHN0ciA9IEludGVnZXIudG9TdHJpbmcoY29kZVBvaW50LCAxNikudG9VcHBlckNhc2UoKTsKICAgICAgICBpZiAoc3RyLmxlbmd0aCgpIDwgNCkgc3RyID0gWkVST0VTLnN1YnN0cmluZygzIC0gNCArIHN0ci5sZW5ndGgoKSkgKyBzdHI7CiAgICAgICAgcmV0dXJuIHN0cjsKICAgIH0KCiAgICBwcml2YXRlIHN0YXRpYyBjbGFzcyBCbG9jayB7CiAgICAgICAgcHVibGljIGZpbmFsIGludCBzdGFydENvZGVQb2ludDsKICAgICAgICBwdWJsaWMgZmluYWwgaW50IGVuZENvZGVQb2ludDsKICAgICAgICBwdWJsaWMgZmluYWwgU3RyaW5nIGRlc2NyaXB0aW9uOwoKICAgICAgICBwdWJsaWMgQmxvY2soaW50IHN0YXJ0Q29kZVBvaW50LCBpbnQgZW5kQ29kZVBvaW50LCBTdHJpbmcgZGVzY3JpcHRpb24pIHsKICAgICAgICAgICAgdGhpcy5zdGFydENvZGVQb2ludCA9IHN0YXJ0Q29kZVBvaW50OwogICAgICAgICAgICB0aGlzLmVuZENvZGVQb2ludCA9IGVuZENvZGVQb2ludDsKICAgICAgICAgICAgdGhpcy5kZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uOwogICAgICAgIH0KCiAgICAgICAgQE92ZXJyaWRlCiAgICAgICAgcHVibGljIFN0cmluZyB0b1N0cmluZygpIHsKICAgICAgICAgICAgcmV0dXJuIGNvZGVQb2ludFRvSGV4U3RyaW5nKHN0YXJ0Q29kZVBvaW50KSArICIuLiIgKyBjb2RlUG9pbnRUb0hleFN0cmluZyhlbmRDb2RlUG9pbnQpICsgIjsgIiArIGRlc2NyaXB0aW9uOwogICAgICAgIH0KICAgIH0KCiAgICBwdWJsaWMgc3RhdGljIHZvaWQgbWFpbihTdHJpbmdbXSBhcmdzKSB0aHJvd3MgVGhyb3dhYmxlIHsKICAgICAgICBmaW5hbCBVUkwgYmxvY2tzVHh0VVJMID0gbmV3IFVSTCgiaHR0cDovL3cuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmUub3JnL1B1YmxpYy9VQ0QvbGF0ZXN0L3VjZC9CbG9ja3MudHh0Iik7CiAgICAgICAgZmluYWwgTGluZU51bWJlclJlYWRlciBsbnIgPSBuZXcgTGluZU51bWJlclJlYWRlcihuZXcgSW5wdXRTdHJlYW1SZWFkZXIoYmxvY2tzVHh0VVJMLm9wZW5TdHJlYW0oKSkpOwoKICAgICAgICBmaW5hbCBMaXN0PEJsb2NrPiBibG9ja3MgPSBuZXcgQXJyYXlMaXN0PEJsb2NrPigpOwoKICAgICAgICBTdHJpbmcgbGluZTsKICAgICAgICB3aGlsZSAoKGxpbmUgPSBsbnIucmVhZExpbmUoKSkgIT0gbnVsbCkgewogICAgICAgICAgICBpZiAobGluZS5pc0VtcHR5KCkgfHwgbGluZS5jaGFyQXQoMCkgPT0gJyMnKSBjb250aW51ZTsKICAgICAgICAgICAgZmluYWwgaW50IHNlbWljb2xvblBvcyA9IGxpbmUuaW5kZXhPZignOycpOwogICAgICAgICAgICBpZiAoc2VtaWNvbG9uUG9zIDwgMCkgY29udGludWU7CgogICAgICAgICAgICBmaW5hbCBpbnQgZG90ZG90UG9zID0gbGluZS5sYXN0SW5kZXhPZigiLi4iLCBzZW1pY29sb25Qb3MpOwogICAgICAgICAgICBpZiAoZG90ZG90UG9zIDwgMCkgY29udGludWU7CgogICAgICAgICAgICBmaW5hbCBpbnQgc3RhcnRDb2RlUG9pbnQgPSBJbnRlZ2VyLnBhcnNlSW50KGxpbmUuc3Vic3RyaW5nKDAsIGRvdGRvdFBvcyksIDE2KTsKICAgICAgICAgICAgZmluYWwgaW50IGVuZENvZGVQb2ludCA9IEludGVnZXIucGFyc2VJbnQobGluZS5zdWJzdHJpbmcoZG90ZG90UG9zICsgMiwgc2VtaWNvbG9uUG9zKSwgMTYpOwogICAgICAgICAgICBmaW5hbCBTdHJpbmcgZGVzY3JpcHRpb24gPSBsaW5lLnN1YnN0cmluZyhzZW1pY29sb25Qb3MgKyAxKS50cmltKCk7CiAgICAgICAgICAgIGJsb2Nrcy5hZGQobmV3IEJsb2NrKHN0YXJ0Q29kZVBvaW50LCBlbmRDb2RlUG9pbnQsIGRlc2NyaXB0aW9uKSk7CiAgICAgICAgfQoKICAgICAgICBibG9ja3MuYWRkKG5ldyBCbG9jaygwLCAweEZGRkYsICIoQk1QKSIpKTsKCiAgICAgICAgZm9yIChmaW5hbCBCbG9jayBiIDogYmxvY2tzKSB7CiAgICAgICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbihiKTsKCiAgICAgICAgICAgIGZpbmFsIE1hcDxJbnRlZ2VyLCBMaXN0PFN0cmluZz4+IG0gPSBuZXcgSGFzaE1hcDxJbnRlZ2VyLCBMaXN0PFN0cmluZz4+KCk7CiAgICAgICAgICAgIGZvciAoaW50IGkgPSBiLnN0YXJ0Q29kZVBvaW50OyBpIDwgYi5lbmRDb2RlUG9pbnQ7ICsraSkgewogICAgICAgICAgICAgICAgZmluYWwgU3RyaW5nIHN0ciA9IG5ldyBTdHJpbmcobmV3IGludFtdIHsgaSB9LCAwLCAxKTsKICAgICAgICAgICAgICAgIGZpbmFsIFN0cmluZyBub3JtU3RyID0gTm9ybWFsaXplci5ub3JtYWxpemUoc3RyLCBOb3JtYWxpemVyLkZvcm0uTkZDKTsKICAgICAgICAgICAgICAgIGZpbmFsIGludCBjb2RlUG9pbnRDb3VudCA9IG5vcm1TdHIuY29kZVBvaW50Q291bnQoMCwgbm9ybVN0ci5sZW5ndGgoKSk7CiAgICAgICAgICAgICAgICBpZiAoIW0uY29udGFpbnNLZXkoY29kZVBvaW50Q291bnQpKSBtLnB1dChjb2RlUG9pbnRDb3VudCwgbmV3IEFycmF5TGlzdDxTdHJpbmc+KCkpOwogICAgICAgICAgICAgICAgbS5nZXQoY29kZVBvaW50Q291bnQpLmFkZChzdHIpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBmb3IgKGZpbmFsIE1hcC5FbnRyeTxJbnRlZ2VyLCBMaXN0PFN0cmluZz4+IGUgOiBtLmVudHJ5U2V0KCkpIHsKICAgICAgICAgICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbihlLmdldEtleSgpICsgIjogIiArIGUuZ2V0VmFsdWUoKS5zaXplKCkpOwogICAgICAgICAgICAgICAgaWYgKGUuZ2V0S2V5KCkgPiAxKSB7CiAgICAgICAgICAgICAgICAgICAgZm9yIChmaW5hbCBTdHJpbmcgc3RyIDogZS5nZXRWYWx1ZSgpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgIFN5c3RlbS5vdXQucHJpbnQoIiAiICsgY29kZVBvaW50VG9IZXhTdHJpbmcoc3RyLmNvZGVQb2ludEF0KDApKSk7CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CgogICAgICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oKTsKICAgICAgICB9CiAgICB9Cn0=