blob: 61196167fdeafef12061296d675b75327928c846 [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 1996-2001, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/unicodetools/com/ibm/text/UCD/UCD.java,v $
* $Date: 2006/11/27 23:15:21 $
* $Revision: 1.42 $
*
*******************************************************************************
*/
package com.ibm.text.UCD;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.BitSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedReader;
import com.ibm.text.utility.*;
import com.ibm.icu.dev.test.util.BagFormatter;
import com.ibm.icu.dev.test.util.UnicodeMap;
import com.ibm.icu.dev.test.util.UnicodeProperty;
import com.ibm.icu.text.Transliterator;
import com.ibm.icu.text.UTF16;
import com.ibm.icu.text.UnicodeSet;
public final class UCD implements UCD_Types {
private static int SPOT_CHECK = 0x20AC;
static final boolean DEBUG = false;
/**
* Used for the default version.
*/
public static final String latestVersion = "5.0.0";
public static final String lastVersion = "4.1.0";
/**
* Create singleton instance for default (latest) version
*/
public static UCD make() {
return make("");
}
/**
* Create singleton instance for the specific version
*/
public static UCD make(String version) {
if (version == null || version.length() == 0) version = latestVersion;
if (version.indexOf('.') < 0) throw new IllegalArgumentException("Version must be of form 3.1.1");
UCD result = (UCD)versionCache.get(version);
if (result == null) {
//System.out.println(Utility.getStack());
result = new UCD();
result.fillFromFile(version);
versionCache.put(version, result);
}
return result;
}
/**
* Get the version of the UCD
*/
public String getVersion() {
return version;
}
/**
* Get the date that the data was parsed
*/
public long getDate() {
return date;
}
/**
* Is the code point allocated?
*/
public boolean isAllocated(int codePoint) {
if (getCategory(codePoint) != Cn) return true;
if (isNoncharacter(codePoint)) return true;
return false;
}
public boolean isNoncharacter(int codePoint) {
if ((codePoint & 0xFFFE) == 0xFFFE) {
if (compositeVersion < 0x20000 && codePoint > 0xFFFF) return false;
return true;
}
if (codePoint >= 0xFDD0 && codePoint <= 0xFDEF && compositeVersion >= 0x30100) return true;
return false;
}
/**
* Is the code point assigned to a character (or surrogate)
*/
public boolean isAssigned(int codePoint) {
return getCategory(codePoint) != Cn;
}
/**
* Is the code point a PUA character (fast check)
*/
public boolean isPUA(int codePoint) {
if (codePoint >= 0xE000 && codePoint < 0xF900) return true;
if (compositeVersion < 0x20000) return false;
return (codePoint >= 0xF0000 && codePoint < 0xFFFFE
|| codePoint >= 0x100000 && codePoint < 0x10FFFE);
}
/**
* Many ranges are elided in the UCD. All but the first are not actually
* represented in the data internally. This detects such cases.
*/
public boolean isRepresented(int codePoint) {
return getRaw(codePoint) != null;
}
/**
* Return XML version of the data associated with the code point.
*/
public String toString(int codePoint) {
return get(codePoint, true).toString(this,FULL);
}
/**
* Get the character name.
*/
public String getName(int codePoint) {
return getName(codePoint, NORMAL);
}
/**
* Get the character name.
*/
public String getName(String s) {
return getName(s, NORMAL);
}
/**
* Get the character name.
*/
public String getName(int codePoint, byte style) {
if (style == SHORT) return get(codePoint, true).shortName;
return get(codePoint, true).name;
}
/**
* Get the character names for the code points in a string, separated by ", "
*/
public String getName(String s, byte style) {
return getName(s, style, ", ");
}
public String getName(String s, byte style, String separator) {
if (s.length() == 1) return getName(s.charAt(0), style); // optimize BMP
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF16.getCharCount(cp)) {
cp = UTF16.charAt(s, i);
if (i > 0) result.append(separator);
result.append(getName(cp, style));
}
return result.toString();
}
/**
* Get the code in U+ notation
*/
public static String getCode(int codePoint) {
return "U+" + Utility.hex(codePoint);
}
/**
* Get the code in U+ notation
*/
public static String getCode(String s) {
if (s.length() == 1) return getCode(s.charAt(0)); // fast path
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i > 0) result.append(", ");
result.append(getCode(cp));
}
return result.toString();
}
/**
* Get the name and number (U+xxxx NAME) for a code point
*/
public String getCodeAndName(int codePoint, byte type) {
return getCodeAndName(codePoint, type, null);
}
public String getCodeAndName(int codePoint, byte type, Transliterator charTrans) {
return getCode(codePoint)
+ (charTrans == null ? " " : " ( " + charTrans.transliterate(UTF16.valueOf(codePoint)) + " ) ")
+ getName(codePoint, type);
}
/**
* Get the name and number (U+xxxx NAME) for the code points in a string,
* separated by ", "
*/
public String getCodeAndName(String s, byte type) {
return getCodeAndName(s,type,null);
}
public String getCodeAndName(String s, byte type, Transliterator charTrans) {
if (s == null || s.length() == 0) return "NULL";
if (s.length() == 1) return getCodeAndName(s.charAt(0), type, charTrans); // fast path
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i > 0) result.append(", ");
result.append(getCodeAndName(cp, type, charTrans));
}
return result.toString();
}
/**
* Get the name and number (U+xxxx NAME) for a code point
*/
public String getCodeAndName(int codePoint) {
return getCodeAndName(codePoint, NORMAL);
}
/**
* Get the name and number (U+xxxx NAME) for a code point
*/
public String getCodeAndName(String s) {
return getCodeAndName(s, NORMAL);
}
/**
* Get the general category
*/
public byte getCategory(int codePoint) {
return get(codePoint, false).generalCategory;
}
private static final byte FAKE_SYMBOL = 57; // fake category for comparison
private static final byte FAKE_PUNCTUATION = 58; // fake category for comparison
private static final byte FAKE_SEPERATOR = 59; // fake category for comparison
private static final byte FAKE_NUMBER = 60; // fake category for comparison
private static final byte FAKE_MARK = 61; // fake category for comparison
private static final byte FAKE_LETTER = 62; // fake category for comparison
private static final byte FAKE_OTHER = 63; // fake category for comparison
private static final byte FAKENC = 31; // fake category for comparison
public byte getModCat(int cp, int collapseBits) {
byte cat = getCategory(cp);
if (cat == UNASSIGNED && isNoncharacter(cp)) {
cat = FAKENC;
} else if (((1<<cat) & collapseBits) != 0) {
switch (cat) {
case UNASSIGNED: cat = FAKE_OTHER; break;
case FAKENC: cat = FAKE_OTHER; break;
case UPPERCASE_LETTER: cat = FAKE_LETTER; break;
case LOWERCASE_LETTER: cat = FAKE_LETTER; break;
case TITLECASE_LETTER: cat = FAKE_LETTER; break;
case MODIFIER_LETTER: cat = FAKE_LETTER; break;
case OTHER_LETTER: cat = FAKE_LETTER; break;
case NON_SPACING_MARK: cat = FAKE_MARK; break;
case ENCLOSING_MARK: cat = FAKE_MARK; break;
case COMBINING_SPACING_MARK: cat = FAKE_MARK; break;
case DECIMAL_DIGIT_NUMBER: cat = FAKE_NUMBER; break;
case LETTER_NUMBER: cat = FAKE_NUMBER; break;
case OTHER_NUMBER: cat = FAKE_NUMBER; break;
case SPACE_SEPARATOR: cat = FAKE_SEPERATOR; break;
case LINE_SEPARATOR: cat = FAKE_SEPERATOR; break;
case PARAGRAPH_SEPARATOR: cat = FAKE_SEPERATOR; break;
case CONTROL: cat = FAKE_OTHER; break;
case FORMAT: cat = FAKE_OTHER; break;
case UNUSED_CATEGORY: cat = FAKE_OTHER; break;
case PRIVATE_USE: cat = FAKE_OTHER; break;
case SURROGATE: cat = FAKE_OTHER; break;
case DASH_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case START_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case END_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case CONNECTOR_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case OTHER_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case INITIAL_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case FINAL_PUNCTUATION: cat = FAKE_PUNCTUATION; break;
case MATH_SYMBOL: cat = FAKE_SYMBOL; break;
case CURRENCY_SYMBOL: cat = FAKE_SYMBOL; break;
case MODIFIER_SYMBOL: cat = FAKE_SYMBOL; break;
case OTHER_SYMBOL: cat = FAKE_SYMBOL; break;
}
if (collapseBits == -1) {
switch (cat) {
case FAKE_MARK:
case FAKE_NUMBER:
case FAKE_SEPERATOR:
case FAKE_PUNCTUATION:
case FAKE_SYMBOL:
cat = FAKE_LETTER;
break;
}
}
}
return cat;
}
public String getModCatID_fromIndex(byte cat) {
switch (cat) {
case FAKE_SYMBOL: return "S&";
case FAKE_PUNCTUATION: return "P&";
case FAKE_SEPERATOR: return "Z&";
case FAKE_NUMBER: return "N&";
case FAKE_MARK: return "M&";
case FAKE_LETTER: return "L&";
case FAKE_OTHER: return "C&";
case FAKENC: return "NC";
}
return getCategoryID_fromIndex(cat);
}
/**
* Get the main category, as a mask
*/
public static int mainCategoryMask(byte cat) {
switch (cat) {
case Lu: case Ll: case Lt: case Lm: case Lo: return LETTER_MASK;
case Mn: case Me: case Mc: return MARK_MASK;
case Nd: case Nl: case No: return NUMBER_MASK;
case Zs: case Zl: case Zp: return SEPARATOR_MASK;
case Cc: case Cf: case Cs: case Co: return CONTROL_MASK;
case Pc: case Pd: case Ps: case Pe: case Po: case Pi: case Pf: return PUNCTUATION_MASK;
case Sm: case Sc: case Sk: case So: return SYMBOL_MASK;
case Cn: return UNASSIGNED_MASK;
}
throw new IllegalArgumentException ("Illegal General Category " + cat);
}
/**
* Get the combining class, a number between zero and 255. Returned
* as a short to avoid the signed-byte problem in Java
*/
public short getCombiningClass(int codePoint) {
return (short)(get(codePoint, false).combiningClass & 0xFF);
}
/**
* Does this combining class actually occur in this version of the data.
*/
public boolean isCombiningClassUsed(byte value) {
return combiningClassSet.get(0xFF & value);
}
static UnicodeSet BIDI_R_SET, BIDI_AL_SET, BIDI_BN_SET;
/**
* Get the bidi class
*/
public byte getBidiClass(int codePoint) {
if (getCategory(codePoint) != Cn) return get(codePoint, false).bidiClass;
if (BIDI_R_SET == null) { // build it
BIDI_R_SET = new UnicodeSet();
BIDI_AL_SET = new UnicodeSet();
blockData.getSet("Hebrew",BIDI_R_SET);
blockData.getSet("Cypriot_Syllabary",BIDI_R_SET);
blockData.getSet("Arabic",BIDI_AL_SET);
blockData.getSet("Syriac",BIDI_AL_SET);
blockData.getSet("Thaana",BIDI_AL_SET);
blockData.getSet("Arabic_Presentation_Forms-A",BIDI_AL_SET);
blockData.getSet("Arabic_Presentation_Forms-B",BIDI_AL_SET);
/*
int blockId = 0;
BlockData blockData = new BlockData();
UnicodeSet s = blockData.get
while (getBlockData(blockId++, blockData)) {
if (blockData.name.equals("Hebrew")
|| blockData.name.equals("Cypriot_Syllabary")
) {
System.out.println("R: Adding " + blockData.name + ": "
+ Utility.hex(blockData.start)
+ ".." + Utility.hex(blockData.end));
BIDI_R_SET.add(blockData.start, blockData.end);
} else if (blockData.name.equals("Arabic")
|| blockData.name.equals("Syriac")
|| blockData.name.equals("Thaana")
|| blockData.name.equals("Arabic_Presentation_Forms-A")
|| blockData.name.equals("Arabic_Presentation_Forms-B")
) {
System.out.println("AL: Adding " + blockData.name + ": "
+ Utility.hex(blockData.start)
+ ".." + Utility.hex(blockData.end));
BIDI_AL_SET.add(blockData.start, blockData.end);
} else {
if (false) System.out.println("SKIPPING: " + blockData.name + ": "
+ Utility.hex(blockData.start)
+ ".." + Utility.hex(blockData.end));
}
}
*/
System.out.println("BIDI_R_SET: " + BIDI_R_SET);
System.out.println("BIDI_AL_SET: " + BIDI_AL_SET);
UnicodeSet BIDI_R_Delta = new UnicodeSet(0xFB1D, 0xFB4F).add(0x10800, 0x10FFF).add(0x07C0,0x8FF);
BIDI_R_Delta.removeAll(BIDI_R_SET);
System.out.println("R: Adding " + BIDI_R_Delta);
BIDI_R_SET.addAll(BIDI_R_Delta);
UnicodeSet BIDI_AL_Delta = new UnicodeSet(0x0750, 0x077F);
BIDI_AL_Delta.removeAll(BIDI_AL_SET);
System.out.println("AL: Adding " + BIDI_AL_Delta);
BIDI_AL_SET.addAll(BIDI_AL_Delta);
UnicodeSet noncharacters = UnifiedBinaryProperty.make(BINARY_PROPERTIES + Noncharacter_Code_Point, this).getSet();
noncharacters.remove(Utility.BOM);
System.out.println("Removing Noncharacters/BOM " + noncharacters);
BIDI_R_SET.removeAll(noncharacters);
BIDI_AL_SET.removeAll(noncharacters);
BIDI_BN_SET = new UnicodeSet();
if (compositeVersion >= 0x40001) {
BIDI_BN_SET.addAll(noncharacters);
UnicodeSet DefaultIg = DerivedProperty.make(DefaultIgnorable, this).getSet();
System.out.println("DefaultIg: " + DefaultIg);
BIDI_BN_SET.addAll(DefaultIg);
}
System.out.println("BIDI_R_SET: " + BIDI_R_SET);
System.out.println("BIDI_AL_SET: " + BIDI_AL_SET);
System.out.println("BIDI_BN_SET: " + BIDI_BN_SET);
if (BIDI_R_SET.containsSome(BIDI_AL_SET)) {
throw new ChainException("BIDI values for Cf characters overlap!!", null);
}
}
if (BIDI_BN_SET.contains(codePoint)) {
return BIDI_BN;
}
if (BIDI_R_SET.contains(codePoint)) {
return BIDI_R;
}
if (BIDI_AL_SET.contains(codePoint)) {
return BIDI_AL;
}
return BIDI_L;
}
/**
* Get the RAW decomposition mapping. Must be used recursively for the full mapping!
*/
public String getDecompositionMapping(int codePoint) {
return get(codePoint, true).decompositionMapping;
}
/**
* Get BIDI mirroring character, if there is one.
*/
public String getBidiMirror(int codePoint) {
return get(codePoint, true).bidiMirror;
}
/**
* Get the RAW decomposition type: the <...> field in the UCD data.
*/
public byte getDecompositionType(int codePoint) {
return get(codePoint, false).decompositionType;
}
IntMap hanExceptions = null;
static class HanException {
double numericValue;
byte numericType;
}
public UnicodeMap getHanValue(String propertyName) {
UnicodeMap result = new UnicodeMap();
try {
BufferedReader in = Utility.openUnicodeFile("Unihan", version, true, Utility.UTF8);
int lineCounter = 0;
while (true) {
Utility.dot(++lineCounter);
String line = in.readLine();
if (line == null) break;
if (line.length() < 6) continue;
if (line.charAt(0) == '#') continue;
line = line.trim();
int tabPos = line.indexOf('\t');
int tabPos2 = line.indexOf('\t', tabPos+1);
String property = line.substring(tabPos+1, tabPos2).trim();
if (!property.equalsIgnoreCase(propertyName)) continue;
String scode = line.substring(2, tabPos).trim();
int code = Integer.parseInt(scode, 16);
String propertyValue = line.substring(tabPos2+1).trim();
result.put(code, propertyValue);
}
in.close();
} catch (Exception e) {
throw new ChainException("Han File Processing Exception", null, e);
} finally {
Utility.fixDot();
}
return result;
}
void populateHanExceptions() {
hanExceptions = new IntMap();
BufferedReader in = null;
try {
in = Utility.openUnicodeFile("Unihan", version, true, Utility.UTF8);
int lineCounter = 0;
while (true) {
Utility.dot(++lineCounter);
String line = in.readLine();
if (line == null) break;
if (line.length() < 6) continue;
if (line.charAt(0) == '#') continue;
line = line.trim();
int tabPos = line.indexOf('\t');
int tabPos2 = line.indexOf('\t', tabPos+1);
String property = line.substring(tabPos+1, tabPos2).trim();
if (!property.endsWith("Numeric")) continue;
String propertyValue = line.substring(tabPos2+1).trim();
propertyValue = Utility.replace(propertyValue, ",", "");
int hack = propertyValue.indexOf(' ');
if (hack >= 0) {
Utility.fixDot();
System.out.println("BAD NUMBER: " + line);
propertyValue = propertyValue.substring(0,hack);
}
String scode = line.substring(2, tabPos).trim();
int code = Integer.parseInt(scode, 16);
if (code == 0x5793 || code == 0x4EAC) continue; // two exceptions!!
//kAccountingNumeric
//kOtherNumeric
//kPrimaryNumeric
HanException except = (HanException) hanExceptions.get(code);
if (except != null) throw new Exception("Duplicate Numeric Value for " + line);
except = new HanException();
hanExceptions.put(code, except);
except.numericValue = Double.parseDouble(propertyValue);
except.numericType = property.equals("kAccountingNumeric") ? NUMERIC
: property.equals("kOtherNumeric") ? NUMERIC
: property.equals("kPrimaryNumeric") ? NUMERIC
: NONE;
if (except.numericType == NONE) throw new Exception("Unknown Numeric Type for " + line);
if (false) {
Utility.fixDot();
System.out.println(line);
System.out.println(getNumericValue(code));
System.out.println(getNumericTypeID(code));
}
}
in.close();
} catch (Exception e) {
throw new ChainException("Han File Processing Exception", null, e);
} finally {
Utility.fixDot();
System.out.println("****Size: " + hanExceptions.size());
}
}
public double getNumericValue(int codePoint) {
if (hanExceptions == null) populateHanExceptions();
Object except = hanExceptions.get(codePoint);
if (except != null) {
return ((HanException)except).numericValue;
}
return get(codePoint, false).numericValue;
}
public byte getNumericType(int codePoint) {
if (hanExceptions == null) populateHanExceptions();
Object except = hanExceptions.get(codePoint);
if (except != null) {
return ((HanException)except).numericType;
}
return get(codePoint, false).numericType;
}
public String getCase(int codePoint, byte simpleVsFull, byte caseType) {
return getCase(codePoint, simpleVsFull, caseType, "");
}
public String getCase(String s, byte simpleVsFull, byte caseType) {
return getCase(s, simpleVsFull, caseType, "");
}
public String getCase(int codePoint, byte simpleVsFull, byte caseType, String condition) {
UData udata = get(codePoint, true);
if (caseType < LOWER || caseType > FOLD
|| (simpleVsFull != SIMPLE && simpleVsFull != FULL)) {
throw new IllegalArgumentException("simpleVsFull or caseType out of bounds");
}
if (caseType < FOLD) {
if (simpleVsFull == FULL && udata.specialCasing.length() != 0) {
if (condition.length() == 0
|| udata.specialCasing.indexOf(condition) < 0) {
simpleVsFull = SIMPLE;
}
}
} else {
// special case. For these characters alone, use "I" as option meaning collapse to "i"
//if (codePoint == 0x0131 || codePoint == 0x0130) { // special case turkish i
if (getBinaryProperty(codePoint, CaseFoldTurkishI)) {
if (!udata.specialCasing.equals("I")) simpleVsFull = SIMPLE;
else simpleVsFull = FULL;
}
}
switch (caseType + simpleVsFull) {
case SIMPLE + UPPER: return udata.simpleUppercase;
case SIMPLE + LOWER: return udata.simpleLowercase;
case SIMPLE + TITLE: return udata.simpleTitlecase;
case SIMPLE + FOLD: return udata.simpleCaseFolding;
case FULL + UPPER: return udata.fullUppercase;
case FULL + LOWER: return udata.fullLowercase;
case FULL + TITLE: return udata.fullTitlecase;
case FULL + FOLD: return udata.fullCaseFolding;
}
throw new IllegalArgumentException("getCase: " + caseType + ", " + simpleVsFull);
}
static final char SHY = '\u00AD';
static final char APOSTROPHE = '\u2019';
public String getCase(String s, byte simpleVsFull, byte caseType, String condition) {
if (UTF32.length32(s) == 1) return getCase(UTF32.char32At(s, 0), simpleVsFull, caseType);
StringBuffer result = new StringBuffer();
int cp;
byte currentCaseType = caseType;
UCDProperty defaultIgnorable = DerivedProperty.make(DerivedProperty.DefaultIgnorable, this);
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
String mappedVersion = getCase(cp, simpleVsFull, currentCaseType, condition);
result.append(mappedVersion);
if (caseType == TITLE) { // set the case type for the next character
// certain characters are ignored
if (cp == SHY || cp == '\'' || cp == APOSTROPHE) continue;
byte cat = getCategory(cp);
if (cat == Mn || cat == Me || cat == Cf || cat == Lm) continue;
if (defaultIgnorable.hasValue(cp)) continue;
// if DefaultIgnorable is not supported, then
// check for (Cf + Cc + Cs) - White_Space
// if (cat == Cs && cp != 0x85 && (cp < 9 || cp > 0xD)) continue;
// if letter is cased, change next to lowercase, otherwise revert to TITLE
if (cat == Lu || cat == Ll || cat == Lt
|| getBinaryProperty(cp, Other_Lowercase) // skip if not supported
|| getBinaryProperty(cp, Other_Uppercase) // skip if not supported
) {
currentCaseType = LOWER;
} else {
currentCaseType = TITLE;
}
}
}
return result.toString();
}
/*
public String getSimpleLowercase(int codePoint) {
return get(codePoint, true).simpleLowercase;
}
public String getSimpleUppercase(int codePoint) {
return get(codePoint, true).simpleUppercase;
}
public String getSimpleTitlecase(int codePoint) {
return get(codePoint, true).simpleTitlecase;
}
public String getSimpleCaseFolding(int codePoint) {
return get(codePoint, true).simpleCaseFolding;
}
public String getFullLowercase(int codePoint) {
return get(codePoint, true).fullLowercase;
}
public String getFullUppercase(int codePoint) {
return get(codePoint, true).fullUppercase;
}
public String getFullTitlecase(int codePoint) {
return get(codePoint, true).fullTitlecase;
}
public String getFullCaseFolding(int codePoint) {
return get(codePoint, true).simpleCaseFolding;
}
public String getLowercase(int codePoint, boolean full) {
if (full) return getFullLowercase(codePoint);
return getSimpleLowercase(codePoint);
}
public String getUppercase(int codePoint, boolean full) {
if (full) return getFullUppercase(codePoint);
return getSimpleLowercase(codePoint);
}
public String getTitlecase(int codePoint, boolean full) {
if (full) return getFullTitlecase(codePoint);
return getSimpleTitlecase(codePoint);
}
public String getCaseFolding(int codePoint, boolean full) {
if (full) return getFullCaseFolding(codePoint);
return getSimpleCaseFolding(codePoint);
}
public String getLowercase(String s, boolean full) {
if (s.length() == 1) return getLowercase(s.charAt(0), true);
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i > 0) result.append(", ");
result.append(getLowercase(cp, true));
}
return result.toString();
}
public String getUppercase(String s, boolean full) {
if (s.length() == 1) return getUppercase(s.charAt(0), true);
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i > 0) result.append(", ");
result.append(getUppercase(cp, true));
}
return result.toString();
}
public String getTitlecase(String s, boolean full) {
if (s.length() == 1) return getTitlecase(s.charAt(0), true);
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i > 0) result.append(", ");
result.append(getTitlecase(cp, true));
}
return result.toString();
}
public String getCaseFolding(String s, boolean full) {
if (s.length() == 1) return getCaseFolding(s.charAt(0), true);
StringBuffer result = new StringBuffer();
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i > 0) result.append(", ");
result.append(getCaseFolding(cp, true));
}
return result.toString();
}
*/
public String getSpecialCase(int codePoint) {
return get(codePoint, true).specialCasing;
}
public byte getEastAsianWidth(int codePoint) {
// if (0x30000 <= codepoint && codepoint <= 0x3FFFD) return EAW;
return get(codePoint, false).eastAsianWidth;
}
public byte getLineBreak(int codePoint) {
return get(codePoint, false).lineBreak;
}
public byte getScript(int codePoint) {
if (codePoint == 0xE000) {
codePoint += 0;
}
return get(codePoint, false).script;
}
public byte getScript(String s) {
byte result = COMMON_SCRIPT;
if (s == null || s.length() == 0) return result;
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
byte script = getScript(cp);
if (script == INHERITED_SCRIPT) continue;
result = script;
}
return result;
}
public byte getAge(int codePoint) {
return get(codePoint, false).age;
}
public byte getJoiningType(int codePoint) {
return get(codePoint, false).joiningType;
}
public byte getJoiningGroup(int codePoint) {
return get(codePoint, false).joiningGroup;
}
public long getBinaryProperties(int codePoint) {
return get(codePoint, false).binaryProperties;
}
public boolean getBinaryProperty(int codePoint, int bit) {
return (get(codePoint, false).binaryProperties & (1L<<bit)) != 0;
}
// ENUM Mask Utilties
public int getCategoryMask(int codePoint) {
return 1<<get(codePoint, false).generalCategory;
}
public int getBidiClassMask(int codePoint) {
return 1<<get(codePoint, false).bidiClass;
}
public int getNumericTypeMask(int codePoint) {
return 1<<getNumericType(codePoint);
}
public int getDecompositionTypeMask(int codePoint) {
return 1<<get(codePoint, false).decompositionType;
}
public int getEastAsianWidthMask(int codePoint) {
return 1<<get(codePoint, false).eastAsianWidth;
}
public int getLineBreakMask(int codePoint) {
return 1<<get(codePoint, false).lineBreak;
}
public int getScriptMask(int codePoint) {
return 1<<get(codePoint, false).script;
}
public int getAgeMask(int codePoint) {
return 1<<get(codePoint, false).age;
}
public int getJoiningTypeMask(int codePoint) {
return 1<<get(codePoint, false).joiningType;
}
public int getJoiningGroupMask(int codePoint) {
return 1<<get(codePoint, false).joiningGroup;
}
// VERSIONS WITH NAMES
public String getCategoryID(int codePoint) {
return getCategoryID_fromIndex(getCategory(codePoint));
}
public static String getCategoryID_fromIndex(byte prop) {
return getCategoryID_fromIndex(prop, NORMAL);
}
public static String getCategoryID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.GENERAL_CATEGORY.length ? null
: (style == EXTRA_ALIAS && prop == DECIMAL_DIGIT_NUMBER) ? "digit"
: (style != LONG) ? UCD_Names.GENERAL_CATEGORY[prop]
: UCD_Names.LONG_GENERAL_CATEGORY[prop];
}
public String getCombiningClassID(int codePoint) {
return getCombiningClassID(codePoint, NORMAL);
}
public String getCombiningClassID(int codePoint, byte style) {
return getCombiningClassID_fromIndex(getCombiningClass(codePoint), style);
}
public static String getCombiningClassID_fromIndex(short cc) {
return getCombiningClassID_fromIndex(cc, NORMAL);
}
static String getCombiningClassID_fromIndex (short index, byte style) {
return index < 0
|| index >= UCD_Names.COMBINING_CLASS.length
? null
: style == SHORT
? UCD_Names.COMBINING_CLASS[index]
: UCD_Names.LONG_COMBINING_CLASS[index];
/*
if (index > 255) return null;
index &= 0xFF;
if (style == NORMAL || style == NUMBER) return String.valueOf(index);
String s = "";
switch (index) {
case 0: s = style < LONG ? "NR" : "NotReordered"; break;
case 1: s = style < LONG ? "OV" : "Overlay"; break;
case 7: s = style < LONG ? "NK" : "Nukta"; break;
case 8: s = style < LONG ? "KV" : "KanaVoicing"; break;
case 9: s = style < LONG ? "VR" : "Virama"; break;
case 200: s = style < LONG ? "ATBL" : "AttachedBelowLeft"; break;
case 202: s = style < LONG ? "ATB" : "AttachedBelow"; break;
case 204: s = style < LONG ? "ATBR" : "AttachedBelowRight"; break;
case 208: s = style < LONG ? "ATL" : "AttachedLeft"; break;
case 210: s = style < LONG ? "ATR" : "AttachedRight"; break;
case 212: s = style < LONG ? "ATAL" : "AttachedAboveLeft"; break;
case 214: s = style < LONG ? "ATA" : "AttachedAbove"; break;
case 216: s = style < LONG ? "ATAR" : "AttachedAboveRight"; break;
case 218: s = style < LONG ? "BL" : "BelowLeft"; break;
case 220: s = style < LONG ? "B" : "Below"; break;
case 222: s = style < LONG ? "BR" : "BelowRight"; break;
case 224: s = style < LONG ? "L" : "Left"; break;
case 226: s = style < LONG ? "R" : "Right"; break;
case 228: s = style < LONG ? "AL" : "AboveLeft"; break;
case 230: s = style < LONG ? "A" : "Above"; break;
case 232: s = style < LONG ? "AR" : "AboveRight"; break;
case 233: s = style < LONG ? "DB" : "DoubleBelow"; break;
case 234: s = style < LONG ? "DA" : "DoubleAbove"; break;
case 240: s = style < LONG ? "IS" : "IotaSubscript"; break;
default: s += "" + index;
}
return s;
*/
}
public String getBidiClassID(int codePoint) {
return getBidiClassID_fromIndex(getBidiClass(codePoint));
}
public static String getBidiClassID_fromIndex(byte prop) {
return getBidiClassID_fromIndex(prop, NORMAL);
}
public static String getBidiClassID_fromIndex(byte prop, byte style) {
return prop < 0
|| prop >= UCD_Names.BIDI_CLASS.length
? null
: style == SHORT
? UCD_Names.BIDI_CLASS[prop]
: UCD_Names.LONG_BIDI_CLASS[prop];
}
public String getDecompositionTypeID(int codePoint) {
return getDecompositionTypeID_fromIndex(getDecompositionType(codePoint));
}
public static String getDecompositionTypeID_fromIndex(byte prop) {
return getDecompositionTypeID_fromIndex(prop, NORMAL);
}
public static String getDecompositionTypeID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.LONG_DECOMPOSITION_TYPE.length ? null
: style == SHORT ? UCD_Names.DECOMPOSITION_TYPE[prop] : UCD_Names.LONG_DECOMPOSITION_TYPE[prop];
}
public String getNumericTypeID(int codePoint) {
return getNumericTypeID_fromIndex(getNumericType(codePoint));
}
public static String getNumericTypeID_fromIndex(byte prop) {
return getNumericTypeID_fromIndex(prop, NORMAL);
}
public static String getNumericTypeID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.LONG_NUMERIC_TYPE.length ? null
: style == SHORT ? UCD_Names.NUMERIC_TYPE[prop] : UCD_Names.LONG_NUMERIC_TYPE[prop];
}
public String getEastAsianWidthID(int codePoint) {
return getEastAsianWidthID_fromIndex(getEastAsianWidth(codePoint));
}
public static String getEastAsianWidthID_fromIndex(byte prop) {
return getEastAsianWidthID_fromIndex(prop, NORMAL);
}
public static String getEastAsianWidthID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.LONG_EAST_ASIAN_WIDTH.length ? null
: style != LONG ? UCD_Names.EAST_ASIAN_WIDTH[prop] : UCD_Names.LONG_EAST_ASIAN_WIDTH[prop];
}
public String getLineBreakID(int codePoint) {
return getLineBreakID_fromIndex(getLineBreak(codePoint));
}
public static String getLineBreakID_fromIndex(byte prop) {
return getLineBreakID_fromIndex(prop, NORMAL);
}
public static String getLineBreakID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.LINE_BREAK.length ? null
: style != LONG ? UCD_Names.LINE_BREAK[prop] : UCD_Names.LONG_LINE_BREAK[prop];
}
public String getJoiningTypeID(int codePoint) {
return getJoiningTypeID_fromIndex(getJoiningType(codePoint));
}
public static String getJoiningTypeID_fromIndex(byte prop) {
return getJoiningTypeID_fromIndex(prop, NORMAL);
}
public static String getJoiningTypeID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.JOINING_TYPE.length ? null
: style != LONG ? UCD_Names.JOINING_TYPE[prop] : UCD_Names.LONG_JOINING_TYPE[prop];
}
public String getJoiningGroupID(int codePoint) {
return getJoiningGroupID_fromIndex(getJoiningGroup(codePoint));
}
public static String getJoiningGroupID_fromIndex(byte prop) {
return getJoiningGroupID_fromIndex(prop, NORMAL);
}
public static String getJoiningGroupID_fromIndex(byte prop, byte style) {
// no short version
return prop < 0 || prop >= UCD_Names.JOINING_GROUP.length ? null
: UCD_Names.JOINING_GROUP[prop];
}
public String getScriptID(int codePoint) {
return getScriptID_fromIndex(getScript(codePoint));
}
public static String getScriptID_fromIndex(byte prop) {
return getScriptID_fromIndex(prop, NORMAL);
}
public static String getScriptID_fromIndex(byte prop, byte length) {
return prop < 0 || prop >= UCD_Names.SCRIPT.length ? null
: (length == EXTRA_ALIAS && prop == COPTIC) ? "Qaac"
: (length == SHORT) ? UCD_Names.SCRIPT[prop]
: UCD_Names.LONG_SCRIPT[prop];
}
public String getAgeID(int codePoint) {
return getAgeID_fromIndex(getAge(codePoint));
}
public static String getAgeID_fromIndex(byte prop) {
return getAgeID_fromIndex(prop, NORMAL);
}
public static String getAgeID_fromIndex(byte prop, byte style) {
// no short for
return prop < 0 || prop >= UCD_Names.AGE.length ? null
: UCD_Names.AGE[prop];
}
public String getBinaryPropertiesID(int codePoint, byte bit) {
return getBinaryProperty(codePoint, bit) ? UCD_Names.YN_TABLE[1] : UCD_Names.YN_TABLE[0];
}
public static String getBinaryPropertiesID_fromIndex(byte bit) {
return getBinaryPropertiesID_fromIndex(bit, NORMAL);
}
public static String getBinaryPropertiesID_fromIndex(byte bit, byte style) {
return bit < 0 || bit >= UCD_Names.BP.length ? null
: style == SHORT ? UCD_Names.SHORT_BP[bit] : UCD_Names.BP[bit];
}
public static int mapToRepresentative(int ch, int rCompositeVersion) {
if (ch <= 0xFFFD) {
//if (ch <= 0x2800) return ch;
//if (ch <= 0x28FF) return 0x2800; // braille
if (ch <= 0x3400) return ch; // CJK Ideograph Extension A
if (ch <= 0x4DB5) return 0x3400;
if (ch <= 0x4E00) return ch; // CJK Ideograph
if (ch <= 0x9FA5) return 0x4E00;
if (ch <= 0x9FBB && rCompositeVersion >= 0x40100) return 0x4E00;
if (ch <= 0xAC00) return ch; // Hangul Syllable
if (ch <= 0xD7A3) return 0xAC00;
if (ch <= 0xD800) return ch; // Non Private Use High Surrogate
if (ch <= 0xDB7F) return 0xD800;
if (ch <= 0xDB80) return ch; // Private Use High Surrogate
if (ch <= 0xDBFF) return 0xDB80;
if (ch <= 0xDC00) return ch; // Low Surrogate
if (ch <= 0xDFFF) return 0xDC00;
if (ch <= 0xE000) return ch; // Private Use
if (ch <= 0xF8FF) return 0xE000;
if (rCompositeVersion < 0x20105) {
if (ch <= 0xF900) return ch; // CJK Compatibility Ideograp
if (ch <= 0xFA2D) return 0xF900;
}
if (ch < 0xFDD0) return ch; // Noncharacter
if (ch <= 0xFDEF) return 0xFFFF;
} else {
if ((ch & 0xFFFE) == 0xFFFE) return 0xFFFF; // Noncharacter
if (ch <= 0x20000) return ch; // Extension B
if (ch <= 0x2A6D6) return 0x20000;
//if (ch <= 0x2F800) return ch;
//if (ch <= 0x2FA1D) return 0x2F800; // compat ideographs
if (ch < 0xF0000) return ch; // Plane 15 Private Use
if (rCompositeVersion >= 0x20000) {
return 0xE000;
}
/*
if (ch <= 0xFFFFD) return 0xF0000; // Plane 16 Private Use
if (ch <= 0x100000) return ch; // Plane 15 Private Use
if (ch <= 0x10FFFD) return 0x100000; // Plane 16 Private Use
*/
}
return ch;
}
public boolean isIdentifierStart(int cp) {
/*
if (extended) {
if (cp == 0x0E33 || cp == 0x0EB3 || cp == 0xFF9E || cp == 0xFF9F) return false;
if (cp == 0x037A || cp >= 0xFC5E && cp <= 0xFC63 || cp == 0xFDFA || cp == 0xFDFB) return false;
if (cp >= 0xFE70 && cp <= 0xFE7E && (cp & 1) == 0) return false;
}
*/
byte cat = getCategory(cp);
if (cat == Lu || cat == Ll || cat == Lt || cat == Lm || cat == Lo || cat == Nl) return true;
if (getBinaryProperty(cp, Other_ID_Start)) return true;
return false;
}
public boolean isIdentifierContinue_NO_Cf(int cp) {
if (isIdentifierStart(cp)) return true;
/*
if (extended) {
if (cp == 0x00B7) return true;
if (cp == 0x0E33 || cp == 0x0EB3 || cp == 0xFF9E || cp == 0xFF9F) return true;
}
*/
byte cat = getCategory(cp);
if (cat == Mn || cat == Mc || cat == Nd || cat == Pc) return true;
if (getBinaryProperty(cp, Other_ID_Start)) return true;
if (getBinaryProperty(cp, Other_ID_Continue)) return true;
return false;
}
public boolean isIdentifier(String s) {
if (s.length() == 0) return false; // at least one!
int cp;
for (int i = 0; i < s.length(); i += UTF32.count16(cp)) {
cp = UTF32.char32At(s, i);
if (i == 0) {
if (!isIdentifierStart(cp)) return false;
} else {
if (!isIdentifierContinue_NO_Cf(cp)) return false;
}
}
return true;
}
/*
Middle Dot. Because most Catalan legacy data will be encoded in Latin-1, U+00B7 MIDDLE DOT needs to be
allowed in <identifier_extend>.
In particular, the following four characters should be in <identifier_extend> and not <identifier_start>:
0E33 THAI CHARACTER SARA AM
0EB3 LAO VOWEL SIGN AM
FF9E HALFWIDTH KATAKANA VOICED SOUND MARK
FF9F HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
Irregularly decomposing characters. U+037A GREEK YPOGEGRAMMENI and certain Arabic presentation
forms have irregular compatibility decompositions, and need to be excluded from both <identifier_start>
and <identifier_extend>. It is recommended that all Arabic presentation forms be excluded from identifiers
in any event, although only a few of them are required to be excluded for normalization
to guarantee identifier closure.
*/
// *******************
// PRIVATES
// *******************
// cache of singletons
private static Map versionCache = new HashMap();
private static final int LIMIT_CODE_POINT = 0x110000;
private static final UData[] ALL_NULLS = new UData[1024];
// main data
private UData[][] data = new UData[LIMIT_CODE_POINT>>10][];
// extras
private BitSet combiningClassSet = new BitSet(256);
private String version;
private String file;
private long date = -1;
private byte format = -1;
//private byte major = -1;
//private byte minor = -1;
//private byte update = -1;
private int compositeVersion = -1;
private int size = -1;
// cache last UData
private int lastCode = Integer.MIN_VALUE;
private UData lastResult = UData.UNASSIGNED;
private boolean lastCodeFixed = false;
// hide constructor
private UCD() {
for (int i = 0; i < data.length; ++i) {
data[i] = ALL_NULLS;
}
}
private void add(UData uData) {
int high = uData.codePoint>>10;
if (data[high] == ALL_NULLS) {
UData[] temp = new UData[1024];
data[high] = temp;
}
data[high][uData.codePoint & 0x3FF] = uData;
}
public boolean hasComputableName(int codePoint) {
if (codePoint >= 0xF900 && codePoint <= 0xFA2D) return true;
if (codePoint >= 0x2800 && codePoint <= 0x28FF) return true;
if (codePoint >= 0x2F800 && codePoint <= 0x2FA1D) return true;
int rangeStart = mapToRepresentative(codePoint, compositeVersion);
switch (rangeStart) {
default:
return getRaw(codePoint) == null;
case 0x2800: // braille
case 0xF900: // compat ideos
case 0x2F800: // compat ideos
case 0x3400: // CJK Ideograph Extension A
case 0x4E00: // CJK Ideograph
case 0x20000: // Extension B
case 0xAC00: // Hangul Syllable
case 0xE000: // Private Use
case 0xF0000: // Private Use
case 0x100000: // Private Use
case 0xD800: // Surrogate
case 0xDB80: // Private Use
case 0xDC00: // Private Use
case 0xFFFF: // Noncharacter
return true;
}
}
private UData getRaw(int codePoint) {
return data[codePoint>>10][codePoint & 0x3FF];
}
// access data for codepoint
UData get(int codePoint, boolean fixStrings) {
/*if (codePoint == 0xF901) {
System.out.println(version + ", " + Integer.toString(compositeVersion, 16));
System.out.println("debug: ");
}
*/
if (codePoint < 0 || codePoint > 0x10FFFF) {
throw new IllegalArgumentException("Illegal Code Point: " + Utility.hex(codePoint));
}
//if (codePoint == lastCode && fixStrings <= lastCodeFixed) return lastResult;
/*
// we play some funny tricks for performance
// if cp is not represented, it is either in a elided block or missing.
// elided blocks are either CONTINUE or FFFF
byte cat;
if (!ucdData.isRepresented(cp)) {
int rep = UCD.mapToRepresentative(cp);
if (rep == 0xFFFF) cat = Cn;
else if (rep != cp) return CONTINUE;
else if (!ucdData.isRepresented(rep)) cat = Cn;
else cat = ucdData.getCategory(rep);
} else {
cat = ucdData.getCategory(cp);
}
*/
UData result = null;
// do range stuff
String constructedName = null;
int rangeStart = mapToRepresentative(codePoint, compositeVersion);
boolean isHangul = false;
boolean isRemapped = false;
switch (rangeStart) {
case 0xF900:
if (compositeVersion < 0x020105) {
if (fixStrings) constructedName = "CJK COMPATIBILITY IDEOGRAPH-" + Utility.hex(codePoint, 4);
break;
}
//isRemapped = true;
break;
// FALL THROUGH!!!!
//default:
/*
result = getRaw(codePoint);
if (result == null) {
result = UData.UNASSIGNED;
result.name = null; // clean this up, since we reuse UNASSIGNED
result.shortName = null;
if (fixStrings) {
result.name = "<unassigned-" + Utility.hex(codePoint, 4) + ">";
}
}
if (fixStrings) {
if (result.name == null) {
result.name = "<unassigned-" + Utility.hex(codePoint, 4) + ">";
// System.out.println("Warning: fixing name for " + result.name);
}
if (result.shortName == null) {
result.shortName = Utility.replace(result.name, UCD_Names.NAME_ABBREVIATIONS);
}
}
*/
//break;
case 0x3400: // CJK Ideograph Extension A
case 0x4E00: // CJK Ideograph
case 0x20000: // Extension B
if (fixStrings) constructedName = "CJK UNIFIED IDEOGRAPH-" + Utility.hex(codePoint, 4);
isRemapped = true;
break;
case 0xAC00: // Hangul Syllable
isHangul = true;
if (fixStrings) {
constructedName = "HANGUL SYLLABLE " + getHangulName(codePoint);
}
isRemapped = true;
break;
case 0xE000: // Private Use
case 0xF0000: // Private Use
case 0x100000: // Private Use
if (fixStrings) constructedName = "<private-use-" + Utility.hex(codePoint, 4) + ">";
isRemapped = true;
break;
case 0xD800: // Surrogate
case 0xDB80: // Private Use
case 0xDC00: // Private Use
if (fixStrings) constructedName = "<surrogate-" + Utility.hex(codePoint, 4) + ">";
isRemapped = true;
break;
case 0xFFFF: // Noncharacter
if (fixStrings) constructedName = "<noncharacter-" + Utility.hex(codePoint, 4) + ">";
isRemapped = true;
break;
}
result = getRaw(rangeStart);
if (result == null) {
result = UData.UNASSIGNED;
isRemapped = true;
result.name = null; // clean this up, since we reuse UNASSIGNED
result.shortName = null;
result.decompositionType = NONE;
if (fixStrings) {
constructedName = "<reserved-" + Utility.hex(codePoint, 4) + ">";
//result.shortName = Utility.replace(result.name, UCD_Names.NAME_ABBREVIATIONS);
}
//return result;
}
result.codePoint = codePoint;
if (fixStrings) {
if (result.name == null || isRemapped) result.name = constructedName;
if (result.shortName == null) result.shortName = Utility.replace(result.name, UCD_Names.NAME_ABBREVIATIONS);
if (isRemapped) {
result.decompositionMapping = result.bidiMirror
= result.simpleLowercase = result.simpleUppercase = result.simpleTitlecase = result.simpleCaseFolding
= result.fullLowercase = result.fullUppercase = result.fullTitlecase = result.fullCaseFolding
= UTF32.valueOf32(codePoint);
}
}
if (isHangul) {
if (fixStrings) result.decompositionMapping = getHangulDecompositionPair(codePoint);
if (isLV(codePoint)) result.lineBreak = LB_H2; else result.lineBreak = LB_H3;
result.decompositionType = CANONICAL;
}
return result;
}
// Neither Mapped nor Composite CJK: [\u3400-\u4DB5\u4E00-\u9FA5\U00020000-\U0002A6D6]
public static final boolean isCJK_AB(int bigChar) {
return (CJK_A_BASE <= bigChar && bigChar < CJK_A_LIMIT
|| CJK_B_BASE <= bigChar && bigChar < CJK_B_LIMIT);
}
public static boolean isCJK_BASE(int cp) {
return (CJK_BASE <= cp && cp < CJK_LIMIT
|| cp == 0xFA0E // compat characters that don't decompose.
|| cp == 0xFA0F
|| cp == 0xFA11
|| cp == 0xFA13
|| cp == 0xFA14
|| cp == 0xFA1F
|| cp == 0xFA21
|| cp == 0xFA23
|| cp == 0xFA24
|| cp == 0xFA27
|| cp == 0xFA28
|| cp == 0xFA29
|| cp == 0xFA2E
|| cp == 0xFA2F
);
}
// Hangul constants
public static final int
SBase = 0xAC00, LBase = 0x1100, VBase = 0x1161, TBase = 0x11A7,
LCount = 19, VCount = 21, TCount = 28,
NCount = VCount * TCount, // 588
SCount = LCount * NCount, // 11172
LLimit = LBase + LCount, // 1113
VLimit = VBase + VCount, // 1176
TLimit = TBase + TCount, // 11C3
LLimitFull = 0x1160,
VLimitFull = TBase,
TLimitFull = 0x11FF,
SLimit = SBase + SCount; // D7A4
private static String getHangulName(int s) {
int SIndex = s - SBase;
if (0 > SIndex || SIndex >= SCount) {
throw new IllegalArgumentException("Not a Hangul Syllable: " + s);
}
int LIndex = SIndex / NCount;
int VIndex = (SIndex % NCount) / TCount;
int TIndex = SIndex % TCount;
// if (true) return "?";
return UCD_Names.JAMO_L_TABLE[LIndex] + UCD_Names.JAMO_V_TABLE[VIndex] + UCD_Names.JAMO_T_TABLE[TIndex];
}
private static final char[] pair = new char[2];
static boolean isDoubleHangul(int s) {
int SIndex = s - SBase;
if (0 > SIndex || SIndex >= SCount) {
throw new IllegalArgumentException("Not a Hangul Syllable: " + s);
}
return (SIndex % TCount) == 0;
}
static String getHangulDecompositionPair(int ch) {
int SIndex = ch - SBase;
if (0 > SIndex || SIndex >= SCount) {
return "";
}
int TIndex = SIndex % TCount;
if (TIndex != 0) { // triple
pair[0] = (char)(SBase + SIndex - TIndex);
pair[1] = (char)(TBase + TIndex);
} else {
pair[0] = (char)(LBase + SIndex / NCount);
pair[1] = (char)(VBase + (SIndex % NCount) / TCount);
}
return String.valueOf(pair);
}
static int composeHangul(int char1, int char2) {
if (LBase <= char1 && char1 < LLimit && VBase <= char2 && char2 < VLimit) {
return (SBase + ((char1 - LBase) * VCount + (char2 - VBase)) * TCount);
}
if (SBase <= char1 && char1 < SLimit && TBase <= char2 && char2 < TLimit
&& ((char1 - SBase) % TCount) == 0) {
return char1 + (char2 - TBase);
}
return 0xFFFF; // no composition
}
static public boolean isHangulSyllable(int char1) {
return SBase <= char1 && char1 < SLimit;
}
static boolean isLeadingJamoComposition(int char1) {
return isLeadingJamo(char1) || isLV(char1);
}
static boolean isLV(int char1) {
return (SBase <= char1 && char1 < SLimit && ((char1 - SBase) % TCount) == 0);
}
static boolean isVowelJamo(int cp) {
return (VBase <= cp && cp < VLimit);
}
static boolean isTrailingJamo(int cp) {
return (TBase <= cp && cp < TLimit);
}
static boolean isLeadingJamo(int cp) {
return (LBase <= cp && cp < LLimit);
}
static boolean isNonLeadJamo(int cp) {
return (VBase <= cp && cp < VLimit) || (TBase <= cp && cp < TLimit);
}
byte getHangulSyllableType(int cp) {
if (!isAssigned(cp)) return NA;
if (LBase <= cp && cp < LLimitFull) return L;
if (LLimitFull <= cp && cp < VLimitFull) return V;
if (VLimitFull <= cp && cp < TLimitFull) return T;
if (isLV(cp)) return LV;
if (isHangulSyllable(cp)) return LVT;
return NA;
}
static String getHangulSyllableTypeID_fromIndex(byte prop, byte style) {
return prop < 0 || prop >= UCD_Names.HANGUL_SYLLABLE_TYPE.length ? null
: (style == LONG) ? UCD_Names.LONG_HANGUL_SYLLABLE_TYPE[prop]
: UCD_Names.HANGUL_SYLLABLE_TYPE[prop];
}
String getHangulSyllableTypeID(int char1, byte style) {
return getHangulSyllableTypeID_fromIndex(getHangulSyllableType(char1),style);
}
private void fillFromFile(String version) {
try {
fillFromFile2(version);
} catch (ChainException e) {
try {
ConvertUCD.main(new String[]{version});
} catch (Exception e2) {
throw new ChainException("Can't build data file for {0}", new Object[]{version}, e2);
}
fillFromFile2(version);
}
}
private void fillFromFile2(String version) {
DataInputStream dataIn = null;
String fileName = BIN_DIR + "UCD_Data" + version + ".bin";
int uDataFileCount = 0;
try {
dataIn = new DataInputStream(
new BufferedInputStream(
new FileInputStream(fileName),
128*1024));
// header
format = dataIn.readByte();
byte major = dataIn.readByte();
byte minor = dataIn.readByte();
byte update = dataIn.readByte();
compositeVersion = (major << 16) | (minor << 8) | update;
String foundVersion = major + "." + minor + "." + update;
if (format != BINARY_FORMAT || !version.equals(foundVersion)) {
throw new ChainException("Illegal data file format for {0}: {1}, {2}",
new Object[]{version, new Byte(format), foundVersion});
}
date = dataIn.readLong();
size = uDataFileCount = dataIn.readInt();
boolean didJoiningHack = false;
System.out.println("Loading UCD " + foundVersion);
// records
for (int i = 0; i < uDataFileCount; ++i) {
UData uData = new UData();
uData.readBytes(dataIn);
//T = Mc + (Cf - ZWNJ - ZWJ)
int cp = uData.codePoint;
byte old = uData.joiningType;
byte cat = uData.generalCategory;
if (cat == Me) {
if (compositeVersion >= 0x40100) {
uData.joiningType = JT_T;
}
}
//if (cp == 0x200D) {
// uData.joiningType = JT_C;
//} else
/*
if (cp != 0x200D && cp != 0x200C && (cat == Mn || cat == Cf)) {
uData.joiningType = JT_T;
}
*/
if (!didJoiningHack && uData.joiningType != old) {
System.out.println("HACK " + foundVersion + ": Setting "
+ UCD_Names.LONG_JOINING_TYPE[uData.joiningType]
+ ": " + Utility.hex(cp) + " " + uData.name);
didJoiningHack = true;
}
combiningClassSet.set(uData.combiningClass & 0xFF);
if (cp == 0xE000) {
System.out.println("Check: " + uData.script);
}
add(uData);
}
/*
if (update == -1) {
throw new ChainException("Data File truncated for ",
new Object[]{version}, e);
}
if (size != fileSize) {
throw new ChainException("Counts do not match: file {0}, records {1}",
new Object[]{new Integer(fileSize), new Integer(size)});
}
*/
// everything is ok!
this.version = version;
this.file = fileName;
//+ " " + new File(fileName).lastModified();
} catch (IOException e) {
throw new ChainException("Can't read data file for {0}", new Object[]{version}, e);
} finally {
if (dataIn != null) {
try {
dataIn.close();
} catch (IOException e) {}
}
}
}
UnicodeMap blockData;
public String getBlock(int codePoint) {
if (blockData == null) loadBlocks();
return (String)blockData.getValue(codePoint);
}
public List getBlockNames() {
return getBlockNames(null);
}
public List getBlockNames(List result) {
if (result == null) result = new ArrayList();
if (blockData == null) loadBlocks();
return (List)blockData.getAvailableValues(result);
}
public UnicodeSet getBlockSet(String value, UnicodeSet result) {
if (result == null) result = new UnicodeSet();
if (blockData == null) loadBlocks();
return blockData.getSet(value, result);
}
static final Matcher blockPattern = Pattern.compile("([0-9A-F]+)\\s*(?:[.][.]|[;])\\s*([0-9A-F]+)\\s*[;](.*)").matcher("");
private void loadBlocks() {
blockData = new UnicodeMap();
try {
BufferedReader in = Utility.openUnicodeFile("Blocks", version, true, Utility.LATIN1);
try {
for (int i = 1; ; ++i) {
// 0000..007F; Basic Latin
String line = Utility.readDataLine(in);
if (line == null) break;
if (line.length() == 0) continue;
if (!blockPattern.reset(line).matches()) {
throw new IllegalArgumentException("Bad line: " + line);
}
// int pos1 = line.indexOf(';');
// int pos2 = line.indexOf(';', pos1+1);
//lastBlock = new BlockData();
try {
int start = Integer.parseInt(blockPattern.group(1), 16);
int end = Integer.parseInt(blockPattern.group(2), 16);
String name = blockPattern.group(3).trim().replace(' ', '_');
blockData.putAll(start,end, name);
} catch (RuntimeException e) {
System.err.println("Failed on line " + i + "\t" + line);
throw e;
}
}
blockData.setMissing("No_Block");
} finally {
in.close();
}
} catch (IOException e) {
throw new IllegalArgumentException("Can't read block file");
}
}
/*
public static class BlockData {
public int start;
public int end;
public String name;
}
public String NOBLOCK = Utility.getUnskeleton("no block", true);
private BlockData lastBlock;
public String getBlock(int codePoint) {
if (blocks == null) loadBlocks();
if (codePoint >= lastBlock.start && codePoint <= lastBlock.end) return lastBlock.name;
Iterator it = blocks.iterator();
while (it.hasNext()) {
lastBlock = (BlockData) it.next();
if (codePoint < lastBlock.start) continue;
if (codePoint > lastBlock.end) break;
return lastBlock.name;
}
return NOBLOCK;
}
public Collection getBlockNames(Collection result) {
if (result == null) result = new ArrayList();
if (blocks == null) loadBlocks();
Iterator it = blocks.iterator();
while (it.hasNext()) {
BlockData data = (BlockData) it.next();
UnicodeProperty.addUnique(data.name, result);
}
UnicodeProperty.addUnique(NOBLOCK, result);
return result;
}
public boolean getBlockData(int blockId, BlockData output) {
if (blocks == null) loadBlocks();
BlockData temp;
try {
temp = (BlockData) blocks.get(blockId);
} catch (IndexOutOfBoundsException e) {
return false;
}
output.name = temp.name;
output.start = temp.start;
output.end = temp.end;
return true;
}
private List blocks = null;
private void loadBlocks() {
blocks = new ArrayList();
try {
BufferedReader in = Utility.openUnicodeFile("Blocks", version, true, Utility.LATIN1);
try {
while (true) {
// 0000..007F; Basic Latin
String line = Utility.readDataLine(in);
if (line == null) break;
if (line.length() == 0) continue;
int pos1 = line.indexOf('.');
int pos2 = line.indexOf(';', pos1);
lastBlock = new BlockData();
lastBlock.start = Integer.parseInt(line.substring(0, pos1), 16);
lastBlock.end = Integer.parseInt(line.substring(pos1+2, pos2), 16);
lastBlock.name = line.substring(pos2+1).trim().replace(' ', '_');
blocks.add(lastBlock);
}
} finally {
in.close();
}
} catch (IOException e) {
throw new IllegalArgumentException("Can't read block file");
}
}
*/
/**
* @return
*/
public int getCompositeVersion() {
return compositeVersion;
}
/**
* @param i
*/
public void setCompositeVersion(int i) {
compositeVersion = i;
}
}