Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/generatecharacter/UnicodeSpec.java
32287 views
/*1* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package build.tools.generatecharacter;2627import java.io.BufferedReader;28import java.io.FileReader;29import java.io.FileNotFoundException;30import java.io.IOException;31import java.util.StringTokenizer;32import java.io.File;33import java.util.regex.Pattern;34import java.util.ArrayList;3536/**37* The UnicodeSpec class provides a way to read in Unicode character38* properties from a Unicode data file. One instance of class UnicodeSpec39* holds a decoded version of one line of the data file. The file may40* be obtained from www.unicode.org. The method readSpecFile returns an array41* of UnicodeSpec objects.42* @author Guy Steele43* @author John O'Conner44*/4546public class UnicodeSpec {4748private static final int MAP_UNDEFINED = 0xFFFFFFFF;4950/**51* Construct a default UnicodeSpec object, with a default52* code point value 0xFFFF.53*54*/55public UnicodeSpec() {56this(0xffff);57}5859/**60* Construct a UnicodeSpec object for the given <code>codePoint<code>61* argument. Provide default properties.62* @param codePoint a Unicode code point between 0x0000 and 0x10FFFF63*/64public UnicodeSpec(int codePoint) {65this.codePoint = codePoint;66generalCategory = UNASSIGNED;67bidiCategory = DIRECTIONALITY_UNDEFINED;68mirrored = false;69titleMap = MAP_UNDEFINED;70upperMap = MAP_UNDEFINED;71lowerMap = MAP_UNDEFINED;72decimalValue = -1;73digitValue = -1;74numericValue = "";75oldName = null;76comment = null;77name = null;78}7980/**81* Create a String representation of this UnicodeSpec object.82* The string will contain the code point and all its case mappings83* if available.84*/85public String toString() {86StringBuffer result = new StringBuffer(hex6(codePoint));87if (getUpperMap() != MAP_UNDEFINED) {88result.append(", upper=").append(hex6(upperMap));89}90if (getLowerMap() != MAP_UNDEFINED) {91result.append(", lower=").append(hex6(lowerMap));92}93if (getTitleMap() != MAP_UNDEFINED) {94result.append(", title=").append(hex6(titleMap));95}96return result.toString();97}9899static String hex4(int n) {100String q = Integer.toHexString(n & 0xFFFF).toUpperCase();101return "0000".substring(Math.min(4, q.length())) + q;102}103104static String hex6(int n) {105String str = Integer.toHexString(n & 0xFFFFFF).toUpperCase();106return "000000".substring(Math.min(6, str.length())) + str;107108}109110111/**112* Given one line of a Unicode data file as a String, parse the line113* and return a UnicodeSpec object that contains the same character information.114*115* @param s a line of the Unicode data file to be parsed116* @return a UnicodeSpec object, or null if the parsing process failed for some reason117*/118public static UnicodeSpec parse(String s) {119UnicodeSpec spec = null;120String[] tokens = null;121122try {123tokens = tokenSeparator.split(s, REQUIRED_FIELDS);124spec = new UnicodeSpec();125spec.setCodePoint(parseCodePoint(tokens[FIELD_VALUE]));126spec.setName(parseName(tokens[FIELD_NAME]));127spec.setGeneralCategory(parseGeneralCategory(tokens[FIELD_CATEGORY]));128spec.setBidiCategory(parseBidiCategory(tokens[FIELD_BIDI]));129spec.setCombiningClass(parseCombiningClass(tokens[FIELD_CLASS]));130spec.setDecomposition(parseDecomposition(tokens[FIELD_DECOMPOSITION]));131spec.setDecimalValue(parseDecimalValue(tokens[FIELD_DECIMAL]));132spec.setDigitValue(parseDigitValue(tokens[FIELD_DIGIT]));133spec.setNumericValue(parseNumericValue(tokens[FIELD_NUMERIC]));134spec.setMirrored(parseMirrored(tokens[FIELD_MIRRORED]));135spec.setOldName(parseOldName(tokens[FIELD_OLDNAME]));136spec.setComment(parseComment(tokens[FIELD_COMMENT]));137spec.setUpperMap(parseUpperMap(tokens[FIELD_UPPERCASE]));138spec.setLowerMap(parseLowerMap(tokens[FIELD_LOWERCASE]));139spec.setTitleMap(parseTitleMap(tokens[FIELD_TITLECASE]));140}141142catch(Exception e) {143spec = null;144System.out.println("Error parsing spec line.");145}146return spec;147}148149/**150* Parse the codePoint attribute for a Unicode character. If the parse succeeds,151* the codePoint field of this UnicodeSpec object is updated and false is returned.152*153* The codePoint attribute should be a four to six digit hexadecimal integer.154*155* @param s the codePoint attribute extracted from a line of the Unicode data file156* @return code point if successful157* @exception NumberFormatException if unable to parse argument158*/159public static int parseCodePoint(String s) throws NumberFormatException {160return Integer.parseInt(s, 16);161}162163public static String parseName(String s) throws Exception {164if (s==null) throw new Exception("Cannot parse name.");165return s;166}167168public static byte parseGeneralCategory(String s) throws Exception {169byte category = GENERAL_CATEGORY_COUNT;170171for (byte x=0; x<generalCategoryList.length; x++) {172if (s.equals(generalCategoryList[x][SHORT])) {173category = x;174break;175}176}177if (category >= GENERAL_CATEGORY_COUNT) {178throw new Exception("Could not parse general category.");179}180return category;181}182183public static byte parseBidiCategory(String s) throws Exception {184byte category = DIRECTIONALITY_CATEGORY_COUNT;185186for (byte x=0; x<bidiCategoryList.length; x++) {187if (s.equals(bidiCategoryList[x][SHORT])) {188category = x;189break;190}191}192if (category >= DIRECTIONALITY_CATEGORY_COUNT) {193throw new Exception("Could not parse bidi category.");194}195return category;196}197198199/**200* Parse the combining attribute for a Unicode character. If there is a combining201* attribute and the parse succeeds, then the hasCombining field is set to true,202* the combining field of this UnicodeSpec object is updated, and false is returned.203* If the combining attribute is an empty string, the parse succeeds but the204* hasCombining field is set to false. (and false is returned).205*206* The combining attribute, if any, should be a nonnegative decimal integer.207*208* @param s the combining attribute extracted from a line of the Unicode data file209* @return the combining class value if any, -1 if property not defined210* @exception Exception if can't parse the combining class211*/212213public static int parseCombiningClass(String s) throws Exception {214int combining = -1;215if (s.length()>0) {216combining = Integer.parseInt(s, 10);217}218return combining;219}220221/**222* Parse the decomposition attribute for a Unicode character. If the parse succeeds,223* the decomposition field of this UnicodeSpec object is updated and false is returned.224*225* The decomposition attribute is complicated; for now, it is treated as a string.226*227* @param s the decomposition attribute extracted from a line of the Unicode data file228* @return true if the parse failed; otherwise false229*/230231public static String parseDecomposition(String s) throws Exception {232if (s==null) throw new Exception("Cannot parse decomposition.");233return s;234}235236237/**238* Parse the decimal value attribute for a Unicode character. If there is a decimal value239* attribute and the parse succeeds, then the hasDecimalValue field is set to true,240* the decimalValue field of this UnicodeSpec object is updated, and false is returned.241* If the decimal value attribute is an empty string, the parse succeeds but the242* hasDecimalValue field is set to false. (and false is returned).243*244* The decimal value attribute, if any, should be a nonnegative decimal integer.245*246* @param s the decimal value attribute extracted from a line of the Unicode data file247* @return the decimal value as an int, -1 if no decimal value defined248* @exception NumberFormatException if the parse fails249*/250public static int parseDecimalValue(String s) throws NumberFormatException {251int value = -1;252253if (s.length() > 0) {254value = Integer.parseInt(s, 10);255}256return value;257}258259/**260* Parse the digit value attribute for a Unicode character. If there is a digit value261* attribute and the parse succeeds, then the hasDigitValue field is set to true,262* the digitValue field of this UnicodeSpec object is updated, and false is returned.263* If the digit value attribute is an empty string, the parse succeeds but the264* hasDigitValue field is set to false. (and false is returned).265*266* The digit value attribute, if any, should be a nonnegative decimal integer.267*268* @param s the digit value attribute extracted from a line of the Unicode data file269* @return the digit value as an non-negative int, or -1 if no digit property defined270* @exception NumberFormatException if the parse fails271*/272public static int parseDigitValue(String s) throws NumberFormatException {273int value = -1;274275if (s.length() > 0) {276value = Integer.parseInt(s, 10);277}278return value;279}280281public static String parseNumericValue(String s) throws Exception {282if (s == null) throw new Exception("Cannot parse numeric value.");283return s;284}285286public static String parseComment(String s) throws Exception {287if (s == null) throw new Exception("Cannot parse comment.");288return s;289}290291public static boolean parseMirrored(String s) throws Exception {292boolean mirrored;293if (s.length() == 1) {294if (s.charAt(0) == 'Y') {mirrored = true;}295else if (s.charAt(0) == 'N') {mirrored = false;}296else {throw new Exception("Cannot parse mirrored property.");}297}298else { throw new Exception("Cannot parse mirrored property.");}299return mirrored;300}301302public static String parseOldName(String s) throws Exception {303if (s == null) throw new Exception("Cannot parse old name");304return s;305}306307/**308* Parse the uppercase mapping attribute for a Unicode character. If there is a uppercase309* mapping attribute and the parse succeeds, then the hasUpperMap field is set to true,310* the upperMap field of this UnicodeSpec object is updated, and false is returned.311* If the uppercase mapping attribute is an empty string, the parse succeeds but the312* hasUpperMap field is set to false. (and false is returned).313*314* The uppercase mapping attribute should be a four to six digit hexadecimal integer.315*316* @param s the uppercase mapping attribute extracted from a line of the Unicode data file317* @return simple uppercase character mapping if defined, MAP_UNDEFINED otherwise318* @exception NumberFormatException if parse fails319*/320public static int parseUpperMap(String s) throws NumberFormatException {321int upperCase = MAP_UNDEFINED;322323int length = s.length();324if (length >= 4 && length <=6) {325upperCase = Integer.parseInt(s, 16);326}327else if (s.length() != 0) {328throw new NumberFormatException();329}330return upperCase;331}332333/**334* Parse the lowercase mapping attribute for a Unicode character. If there is a lowercase335* mapping attribute and the parse succeeds, then the hasLowerMap field is set to true,336* the lowerMap field of this UnicodeSpec object is updated, and false is returned.337* If the lowercase mapping attribute is an empty string, the parse succeeds but the338* hasLowerMap field is set to false. (and false is returned).339*340* The lowercase mapping attribute should be a four to six digit hexadecimal integer.341*342* @param s the lowercase mapping attribute extracted from a line of the Unicode data file343* @return simple lowercase character mapping if defined, MAP_UNDEFINED otherwise344* @exception NumberFormatException if parse fails345*/346public static int parseLowerMap(String s) throws NumberFormatException {347int lowerCase = MAP_UNDEFINED;348int length = s.length();349if (length >= 4 && length <= 6) {350lowerCase = Integer.parseInt(s, 16);351}352else if (s.length() != 0) {353throw new NumberFormatException();354}355return lowerCase;356}357358/**359* Parse the titlecase mapping attribute for a Unicode character. If there is a titlecase360* mapping attribute and the parse succeeds, then the hasTitleMap field is set to true,361* the titleMap field of this UnicodeSpec object is updated, and false is returned.362* If the titlecase mapping attribute is an empty string, the parse succeeds but the363* hasTitleMap field is set to false. (and false is returned).364*365* The titlecase mapping attribute should be a four to six digit hexadecimal integer.366*367* @param s the titlecase mapping attribute extracted from a line of the Unicode data file368* @return simple title case char mapping if defined, MAP_UNDEFINED otherwise369* @exception NumberFormatException if parse fails370*/371public static int parseTitleMap(String s) throws NumberFormatException {372int titleCase = MAP_UNDEFINED;373int length = s.length();374if (length >= 4 && length <= 6) {375titleCase = Integer.parseInt(s, 16);376}377else if (s.length() != 0) {378throw new NumberFormatException();379}380return titleCase;381}382383/**384* Read and parse a Unicode data file.385*386* @param file a file specifying the Unicode data file to be read387* @return an array of UnicodeSpec objects, one for each line of the388* Unicode data file that could be successfully parsed as389* specifying Unicode character attributes390*/391392public static UnicodeSpec[] readSpecFile(File file, int plane) throws FileNotFoundException {393ArrayList<UnicodeSpec> list = new ArrayList<>(3000);394UnicodeSpec[] result = null;395int count = 0;396BufferedReader f = new BufferedReader(new FileReader(file));397String line = null;398loop:399while(true) {400try {401line = f.readLine();402}403catch (IOException e) {404break loop;405}406if (line == null) break loop;407UnicodeSpec item = parse(line.trim());408int specPlane = item.getCodePoint() >>> 16;409if (specPlane < plane) continue;410if (specPlane > plane) break;411412if (item != null) {413list.add(item);414}415}416result = new UnicodeSpec[list.size()];417list.toArray(result);418return result;419}420421void setCodePoint(int value) {422codePoint = value;423}424425/**426* Return the code point in this Unicode specification427* @return the char code point representing by the specification428*/429public int getCodePoint() {430return codePoint;431}432433void setName(String name) {434this.name = name;435}436437public String getName() {438return name;439}440441void setGeneralCategory(byte category) {442generalCategory = category;443}444445public byte getGeneralCategory() {446return generalCategory;447}448449void setBidiCategory(byte category) {450bidiCategory = category;451}452453public byte getBidiCategory() {454return bidiCategory;455}456457void setCombiningClass(int combiningClass) {458this.combiningClass = combiningClass;459}460461public int getCombiningClass() {462return combiningClass;463}464465void setDecomposition(String decomposition) {466this.decomposition = decomposition;467}468469public String getDecomposition() {470return decomposition;471}472473void setDecimalValue(int value) {474decimalValue = value;475}476477public int getDecimalValue() {478return decimalValue;479}480481public boolean isDecimalValue() {482return decimalValue != -1;483}484485void setDigitValue(int value) {486digitValue = value;487}488489public int getDigitValue() {490return digitValue;491}492493public boolean isDigitValue() {494return digitValue != -1;495}496497void setNumericValue(String value) {498numericValue = value;499}500501public String getNumericValue() {502return numericValue;503}504505public boolean isNumericValue() {506return numericValue.length() > 0;507}508509void setMirrored(boolean value) {510mirrored = value;511}512513public boolean isMirrored() {514return mirrored;515}516517void setOldName(String name) {518oldName = name;519}520521public String getOldName() {522return oldName;523}524525void setComment(String comment) {526this.comment = comment;527}528529public String getComment() {530return comment;531}532533void setUpperMap(int ch) {534upperMap = ch;535};536537public int getUpperMap() {538return upperMap;539}540541public boolean hasUpperMap() {542return upperMap != MAP_UNDEFINED;543}544545void setLowerMap(int ch) {546lowerMap = ch;547}548549public int getLowerMap() {550return lowerMap;551}552553public boolean hasLowerMap() {554return lowerMap != MAP_UNDEFINED;555}556557void setTitleMap(int ch) {558titleMap = ch;559}560561public int getTitleMap() {562return titleMap;563}564565public boolean hasTitleMap() {566return titleMap != MAP_UNDEFINED;567}568569int codePoint; // the characters UTF-32 code value570String name; // the ASCII name571byte generalCategory; // general category, available via Characte.getType()572byte bidiCategory; // available via Character.getBidiType()573int combiningClass; // not used in Character574String decomposition; // not used in Character575int decimalValue; // decimal digit value576int digitValue; // not all digits are decimal577String numericValue; // numeric value if digit or non-digit578boolean mirrored; //579String oldName;580String comment;581int upperMap;582int lowerMap;583int titleMap;584585// this is the number of fields in one line of the UnicodeData.txt file586// each field is separated by a semicolon (a token)587static final int REQUIRED_FIELDS = 15;588589/**590* General category types591* To preserve compatibility, these values cannot be changed592*/593public static final byte594UNASSIGNED = 0, // Cn normative595UPPERCASE_LETTER = 1, // Lu normative596LOWERCASE_LETTER = 2, // Ll normative597TITLECASE_LETTER = 3, // Lt normative598MODIFIER_LETTER = 4, // Lm normative599OTHER_LETTER = 5, // Lo normative600NON_SPACING_MARK = 6, // Mn informative601ENCLOSING_MARK = 7, // Me informative602COMBINING_SPACING_MARK = 8, // Mc normative603DECIMAL_DIGIT_NUMBER = 9, // Nd normative604LETTER_NUMBER = 10, // Nl normative605OTHER_NUMBER = 11, // No normative606SPACE_SEPARATOR = 12, // Zs normative607LINE_SEPARATOR = 13, // Zl normative608PARAGRAPH_SEPARATOR = 14, // Zp normative609CONTROL = 15, // Cc normative610FORMAT = 16, // Cf normative611// 17 is unused for no apparent reason,612// but must preserve forward compatibility613PRIVATE_USE = 18, // Co normative614SURROGATE = 19, // Cs normative615DASH_PUNCTUATION = 20, // Pd informative616START_PUNCTUATION = 21, // Ps informative617END_PUNCTUATION = 22, // Pe informative618CONNECTOR_PUNCTUATION = 23, // Pc informative619OTHER_PUNCTUATION = 24, // Po informative620MATH_SYMBOL = 25, // Sm informative621CURRENCY_SYMBOL = 26, // Sc informative622MODIFIER_SYMBOL = 27, // Sk informative623OTHER_SYMBOL = 28, // So informative624INITIAL_QUOTE_PUNCTUATION = 29, // Pi informative625FINAL_QUOTE_PUNCTUATION = 30, // Pf informative626627// this value is only used in the character generation tool628// it can change to accommodate the addition of new categories.629GENERAL_CATEGORY_COUNT = 31; // sentinel value630631static final byte SHORT = 0, LONG = 1;632// general category type strings633// NOTE: The order of this category array is dependent on the assignment of634// category constants above. We want to access this array using constants above.635// [][SHORT] is the SHORT name, [][LONG] is the LONG name636static final String[][] generalCategoryList = {637{"Cn", "UNASSIGNED"},638{"Lu", "UPPERCASE_LETTER"},639{"Ll", "LOWERCASE_LETTER"},640{"Lt", "TITLECASE_LETTER"},641{"Lm", "MODIFIER_LETTER"},642{"Lo", "OTHER_LETTER"},643{"Mn", "NON_SPACING_MARK"},644{"Me", "ENCLOSING_MARK"},645{"Mc", "COMBINING_SPACING_MARK"},646{"Nd", "DECIMAL_DIGIT_NUMBER"},647{"Nl", "LETTER_NUMBER"},648{"No", "OTHER_NUMBER"},649{"Zs", "SPACE_SEPARATOR"},650{"Zl", "LINE_SEPARATOR"},651{"Zp", "PARAGRAPH_SEPARATOR"},652{"Cc", "CONTROL"},653{"Cf", "FORMAT"},654{"xx", "unused"},655{"Co", "PRIVATE_USE"},656{"Cs", "SURROGATE"},657{"Pd", "DASH_PUNCTUATION"},658{"Ps", "START_PUNCTUATION"},659{"Pe", "END_PUNCTUATION"},660{"Pc", "CONNECTOR_PUNCTUATION"},661{"Po", "OTHER_PUNCTUATION"},662{"Sm", "MATH_SYMBOL"},663{"Sc", "CURRENCY_SYMBOL"},664{"Sk", "MODIFIER_SYMBOL"},665{"So", "OTHER_SYMBOL"},666{"Pi", "INITIAL_QUOTE_PUNCTUATION"},667{"Pf", "FINAL_QUOTE_PUNCTUATION"}668};669670/**671* Bidirectional categories672*/673public static final byte674DIRECTIONALITY_UNDEFINED = -1,675// Strong category676DIRECTIONALITY_LEFT_TO_RIGHT = 0, // L677DIRECTIONALITY_RIGHT_TO_LEFT = 1, // R678DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2, // AL679// Weak category680DIRECTIONALITY_EUROPEAN_NUMBER = 3, // EN681DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4, // ES682DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5, // ET683DIRECTIONALITY_ARABIC_NUMBER = 6, // AN684DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7, // CS685DIRECTIONALITY_NONSPACING_MARK = 8, // NSM686DIRECTIONALITY_BOUNDARY_NEUTRAL = 9, // BN687// Neutral category688DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10, // B689DIRECTIONALITY_SEGMENT_SEPARATOR = 11, // S690DIRECTIONALITY_WHITESPACE = 12, // WS691DIRECTIONALITY_OTHER_NEUTRALS = 13, // ON692693DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14, // LRE694DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15, // LRO695DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16, // RLE696DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17, // RLO697DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18, // PDF698699DIRECTIONALITY_CATEGORY_COUNT = 19; // sentinel value700701// If changes are made to the above bidi category assignments, this702// list of bidi category names must be changed to keep their order in synch.703// Access this list using the bidi category constants above.704static final String[][] bidiCategoryList = {705{"L", "DIRECTIONALITY_LEFT_TO_RIGHT"},706{"R", "DIRECTIONALITY_RIGHT_TO_LEFT"},707{"AL", "DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC"},708{"EN", "DIRECTIONALITY_EUROPEAN_NUMBER"},709{"ES", "DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR"},710{"ET", "DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR"},711{"AN", "DIRECTIONALITY_ARABIC_NUMBER"},712{"CS", "DIRECTIONALITY_COMMON_NUMBER_SEPARATOR"},713{"NSM", "DIRECTIONALITY_NONSPACING_MARK"},714{"BN", "DIRECTIONALITY_BOUNDARY_NEUTRAL"},715{"B", "DIRECTIONALITY_PARAGRAPH_SEPARATOR"},716{"S", "DIRECTIONALITY_SEGMENT_SEPARATOR"},717{"WS", "DIRECTIONALITY_WHITESPACE"},718{"ON", "DIRECTIONALITY_OTHER_NEUTRALS"},719{"LRE", "DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING"},720{"LRO", "DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE"},721{"RLE", "DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING"},722{"RLO", "DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE"},723{"PDF", "DIRECTIONALITY_POP_DIRECTIONAL_FORMAT"},724725};726727// Unicode specification lines have fields in this order.728static final byte729FIELD_VALUE = 0,730FIELD_NAME = 1,731FIELD_CATEGORY = 2,732FIELD_CLASS = 3,733FIELD_BIDI = 4,734FIELD_DECOMPOSITION = 5,735FIELD_DECIMAL = 6,736FIELD_DIGIT = 7,737FIELD_NUMERIC = 8,738FIELD_MIRRORED = 9,739FIELD_OLDNAME = 10,740FIELD_COMMENT = 11,741FIELD_UPPERCASE = 12,742FIELD_LOWERCASE = 13,743FIELD_TITLECASE = 14;744745static final Pattern tokenSeparator = Pattern.compile(";");746747public static void main(String[] args) {748UnicodeSpec[] spec = null;749if (args.length == 2 ) {750try {751File file = new File(args[0]);752int plane = Integer.parseInt(args[1]);753spec = UnicodeSpec.readSpecFile(file, plane);754System.out.println("UnicodeSpec[" + spec.length + "]:");755for (int x=0; x<spec.length; x++) {756System.out.println(spec[x].toString());757}758}759catch(Exception e) {760e.printStackTrace();761}762}763764}765766}767768769