]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/tests/core/src/com/ibm/icu/dev/test/normalizer/NormalizerBuilder.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / tests / core / src / com / ibm / icu / dev / test / normalizer / NormalizerBuilder.java
1 /**\r
2  * Builds the normalization tables. This is a separate class so that it\r
3  * can be unloaded once not needed.<br>\r
4  * Copyright (C) 1998-2007 International Business Machines Corporation and\r
5  * Unicode, Inc. All Rights Reserved.<br>\r
6  * The Unicode Consortium makes no expressed or implied warranty of any\r
7  * kind, and assumes no liability for errors or omissions.\r
8  * No liability is assumed for incidental and consequential damages\r
9  * in connection with or arising out of the use of the information here.\r
10  * @author Mark Davis\r
11  * Updates for supplementary code points:\r
12  * Vladimir Weinstein & Markus Scherer\r
13  */\r
14 \r
15 package com.ibm.icu.dev.test.normalizer;\r
16 \r
17 import java.io.BufferedReader;\r
18 import java.util.BitSet;\r
19 \r
20 import com.ibm.icu.dev.test.TestUtil;\r
21 import com.ibm.icu.dev.test.UTF16Util;\r
22 \r
23 class NormalizerBuilder {\r
24     //private static final String copyright = "Copyright (C) 1998-2003 International Business Machines Corporation and Unicode, Inc.";\r
25 \r
26     /**\r
27      * Testing flags\r
28      */\r
29 \r
30     private static final boolean DEBUG = false;\r
31     //private static final boolean GENERATING = false;\r
32 \r
33     /**\r
34      * Constants for the data file version to use.\r
35      */\r
36     /*static final boolean NEW_VERSION = true;\r
37     private static final String DIR = "D:\\UnicodeData\\" + (NEW_VERSION ? "WorkingGroups\\" : "");\r
38 \r
39     static final String UNIDATA_VERSION = NEW_VERSION ? "3.0.0d12" : "2.1.9";\r
40     static final String EXCLUSIONS_VERSION = NEW_VERSION ? "1d4" : "1";\r
41 \r
42     public static final String UNICODE_DATA = DIR + "UnicodeData-" + UNIDATA_VERSION + ".txt";\r
43     public static final String COMPOSITION_EXCLUSIONS = DIR + "CompositionExclusions-" + EXCLUSIONS_VERSION +".txt";\r
44     */\r
45 \r
46     /**\r
47      * Called exactly once by NormalizerData to build the static data\r
48      */\r
49 \r
50     static NormalizerData build(boolean fullData) {\r
51         try {\r
52             IntHashtable canonicalClass = new IntHashtable(0);\r
53             IntStringHashtable decompose = new IntStringHashtable(null);\r
54             LongHashtable compose = new LongHashtable(NormalizerData.NOT_COMPOSITE);\r
55             BitSet isCompatibility = new BitSet();\r
56             BitSet isExcluded = new BitSet();\r
57             if (fullData) {\r
58                 //System.out.println("Building Normalizer Data from file.");\r
59                 readExclusionList(isExcluded);\r
60                 //System.out.println(isExcluded.get(0x00C0));\r
61                 buildDecompositionTables(canonicalClass, decompose, compose,\r
62                   isCompatibility, isExcluded);\r
63             } else {    // for use in Applets\r
64                 //System.out.println("Building abridged data.");\r
65                 setMinimalDecomp(canonicalClass, decompose, compose,\r
66                   isCompatibility, isExcluded);\r
67             }\r
68             return new NormalizerData(canonicalClass, decompose, compose,\r
69                   isCompatibility, isExcluded);\r
70         } catch (java.io.IOException e) {\r
71             System.err.println("Can't load data file." + e + ", " + e.getMessage());\r
72             return null;\r
73         }\r
74     }\r
75 \r
76 // =============================================================\r
77 // Building Decomposition Tables\r
78 // =============================================================\r
79 \r
80     /**\r
81      * Reads exclusion list and stores the data\r
82      */\r
83     private static void readExclusionList(BitSet isExcluded) throws java.io.IOException {\r
84         if (DEBUG) System.out.println("Reading Exclusions");\r
85 \r
86         BufferedReader in = TestUtil.getDataReader("unicode/CompositionExclusions.txt");\r
87 \r
88         while (true) {\r
89             // read a line, discarding comments and blank lines\r
90 \r
91             String line = in.readLine();\r
92             if (line == null) break;\r
93             int comment = line.indexOf('#');                    // strip comments\r
94             if (comment != -1) line = line.substring(0,comment);\r
95             if (line.length() == 0) continue;                   // ignore blanks\r
96             if(line.indexOf(' ') != -1) {\r
97                 line = line.substring(0, line.indexOf(' '));\r
98             }\r
99             // store -1 in the excluded table for each character hit\r
100 \r
101             int value = Integer.parseInt(line,16);\r
102             isExcluded.set(value);\r
103             //System.out.println("Excluding " + hex(value));\r
104         }\r
105         in.close();\r
106         if (DEBUG) System.out.println("Done reading Exclusions");\r
107     }\r
108 \r
109     /**\r
110      * Builds a decomposition table from a UnicodeData file\r
111      */\r
112     private static void buildDecompositionTables(\r
113       IntHashtable canonicalClass, IntStringHashtable decompose,\r
114       LongHashtable compose, BitSet isCompatibility, BitSet isExcluded)\r
115       throws java.io.IOException {\r
116         if (DEBUG) System.out.println("Reading Unicode Character Database");\r
117         //BufferedReader in = new BufferedReader(new FileReader(UNICODE_DATA), 64*1024);\r
118         BufferedReader in = null;\r
119         try {\r
120             in = TestUtil.getDataReader("unicode/UnicodeData.txt");\r
121         } catch (Exception e) {\r
122             System.err.println("Failed to read UnicodeData.txt");\r
123             System.exit(1);\r
124         }\r
125 \r
126         int value;\r
127         long pair;\r
128         int counter = 0;\r
129         while (true) {\r
130 \r
131             // read a line, discarding comments and blank lines\r
132 \r
133             String line = in.readLine();\r
134             if (line == null) break;\r
135             int comment = line.indexOf('#');                    // strip comments\r
136             if (comment != -1) line = line.substring(0,comment);\r
137             if (line.length() == 0) continue;\r
138             if (DEBUG) {\r
139                 counter++;\r
140                 if ((counter & 0xFF) == 0) System.out.println("At: " + line);\r
141             }\r
142 \r
143             // find the values of the particular fields that we need\r
144             // Sample line: 00C0;LATIN ...A GRAVE;Lu;0;L;0041 0300;;;;N;LATIN ... GRAVE;;;00E0;\r
145 \r
146             int start = 0;\r
147             int end = line.indexOf(';'); // code\r
148             value = Integer.parseInt(line.substring(start,end),16);\r
149             if (true && value == '\u00c0') {\r
150                 //System.out.println("debug: " + line);\r
151             }\r
152             end = line.indexOf(';',start=end+1); // name\r
153             /*String name = line.substring(start,end);*/\r
154             end = line.indexOf(';',start=end+1); // general category\r
155             end = line.indexOf(';',start=end+1); // canonical class\r
156 \r
157             // check consistency: canonical classes must be from 0 to 255\r
158 \r
159             int cc = Integer.parseInt(line.substring(start,end));\r
160             if (cc != (cc & 0xFF)) System.err.println("Bad canonical class at: " + line);\r
161             canonicalClass.put(value,cc);\r
162             end = line.indexOf(';',start=end+1); // BIDI\r
163             end = line.indexOf(';',start=end+1); // decomp\r
164 \r
165             // decomp requires more processing.\r
166             // store whether it is canonical or compatibility.\r
167             // store the decomp in one table, and the reverse mapping (from pairs) in another\r
168 \r
169             if (start != end) {\r
170                 String segment = line.substring(start, end);\r
171                 boolean compat = segment.charAt(0) == '<';\r
172                 if (compat) isCompatibility.set(value);\r
173                 String decomp = fromHex(segment);\r
174 \r
175                 // a small snippet of code to generate the Applet data\r
176 \r
177                 /*if (GENERATING) {\r
178                     if (value < 0xFF) {\r
179                         System.out.println(\r
180                             "\"\\u" + hex((char)value) + "\", "\r
181                             + "\"\\u" + hex(decomp, "\\u") + "\", "\r
182                             + (compat ? "\"K\"," : "\"\",")\r
183                             + "// " + name);\r
184                     }\r
185                 }*/\r
186 \r
187                 // check consistency: all canon decomps must be singles or pairs!\r
188                 int decompLen = UTF16Util.countCodePoint(decomp);\r
189                 if (decompLen < 1 || decompLen > 2 && !compat) {\r
190                     System.err.println("Bad decomp at: " + line);\r
191                 }\r
192                 decompose.put(value, decomp);\r
193 \r
194                 // only compositions are canonical pairs\r
195                 // skip if script exclusion\r
196 \r
197                 if (!compat && !isExcluded.get(value)) {\r
198                     int first = '\u0000';\r
199                     int second = UTF16Util.nextCodePoint(decomp, 0);\r
200                     if (decompLen > 1) {\r
201                         first = second;\r
202                         second = UTF16Util.nextCodePoint(decomp,\r
203                             UTF16Util.codePointLength(first));\r
204                     }\r
205 \r
206                     // store composition pair in single integer\r
207 \r
208                     pair = ((long)first << 32) | second;\r
209                     if (DEBUG && value == '\u00C0') {\r
210                         System.out.println("debug2: " + line);\r
211                     }\r
212                     compose.put(pair, value);\r
213                 } else if (DEBUG) {\r
214                     System.out.println("Excluding: " + decomp);\r
215                 }\r
216             }\r
217         }\r
218         in.close();\r
219         if (DEBUG) System.out.println("Done reading Unicode Character Database");\r
220 \r
221         // add algorithmic Hangul decompositions\r
222         // this is more compact if done at runtime, but for simplicity we\r
223         // do it this way.\r
224 \r
225         if (DEBUG) System.out.println("Adding Hangul");\r
226 \r
227         for (int SIndex = 0; SIndex < SCount; ++SIndex) {\r
228             int TIndex = SIndex % TCount;\r
229             char first, second;\r
230             if (TIndex != 0) { // triple\r
231                 first = (char)(SBase + SIndex - TIndex);\r
232                 second = (char)(TBase + TIndex);\r
233             } else {\r
234                 first = (char)(LBase + SIndex / NCount);\r
235                 second = (char)(VBase + (SIndex % NCount) / TCount);\r
236             }\r
237             pair = ((long)first << 32) | second;\r
238             value = SIndex + SBase;\r
239             decompose.put(value, String.valueOf(first) + second);\r
240             compose.put(pair, value);\r
241         }\r
242         if (DEBUG) System.out.println("Done adding Hangul");\r
243     }\r
244 \r
245     /**\r
246      * Hangul composition constants\r
247      */\r
248     static final int\r
249         SBase = 0xAC00, LBase = 0x1100, VBase = 0x1161, TBase = 0x11A7,\r
250         LCount = 19, VCount = 21, TCount = 28,\r
251         NCount = VCount * TCount,   // 588\r
252         SCount = LCount * NCount;   // 11172\r
253 \r
254     /**\r
255      * For use in an applet: just load a minimal set of data.\r
256      */\r
257     private static void setMinimalDecomp(IntHashtable canonicalClass, IntStringHashtable decompose,\r
258       LongHashtable compose, BitSet isCompatibility, BitSet isExcluded) {\r
259         String[] decomposeData = {\r
260             "\u005E", "\u0020\u0302", "K",\r
261             "\u005F", "\u0020\u0332", "K",\r
262             "\u0060", "\u0020\u0300", "K",\r
263             "\u00A0", "\u0020", "K",\r
264             "\u00A8", "\u0020\u0308", "K",\r
265             "\u00AA", "\u0061", "K",\r
266             "\u00AF", "\u0020\u0304", "K",\r
267             "\u00B2", "\u0032", "K",\r
268             "\u00B3", "\u0033", "K",\r
269             "\u00B4", "\u0020\u0301", "K",\r
270             "\u00B5", "\u03BC", "K",\r
271             "\u00B8", "\u0020\u0327", "K",\r
272             "\u00B9", "\u0031", "K",\r
273             "\u00BA", "\u006F", "K",\r
274             "\u00BC", "\u0031\u2044\u0034", "K",\r
275             "\u00BD", "\u0031\u2044\u0032", "K",\r
276             "\u00BE", "\u0033\u2044\u0034", "K",\r
277             "\u00C0", "\u0041\u0300", "",\r
278             "\u00C1", "\u0041\u0301", "",\r
279             "\u00C2", "\u0041\u0302", "",\r
280             "\u00C3", "\u0041\u0303", "",\r
281             "\u00C4", "\u0041\u0308", "",\r
282             "\u00C5", "\u0041\u030A", "",\r
283             "\u00C7", "\u0043\u0327", "",\r
284             "\u00C8", "\u0045\u0300", "",\r
285             "\u00C9", "\u0045\u0301", "",\r
286             "\u00CA", "\u0045\u0302", "",\r
287             "\u00CB", "\u0045\u0308", "",\r
288             "\u00CC", "\u0049\u0300", "",\r
289             "\u00CD", "\u0049\u0301", "",\r
290             "\u00CE", "\u0049\u0302", "",\r
291             "\u00CF", "\u0049\u0308", "",\r
292             "\u00D1", "\u004E\u0303", "",\r
293             "\u00D2", "\u004F\u0300", "",\r
294             "\u00D3", "\u004F\u0301", "",\r
295             "\u00D4", "\u004F\u0302", "",\r
296             "\u00D5", "\u004F\u0303", "",\r
297             "\u00D6", "\u004F\u0308", "",\r
298             "\u00D9", "\u0055\u0300", "",\r
299             "\u00DA", "\u0055\u0301", "",\r
300             "\u00DB", "\u0055\u0302", "",\r
301             "\u00DC", "\u0055\u0308", "",\r
302             "\u00DD", "\u0059\u0301", "",\r
303             "\u00E0", "\u0061\u0300", "",\r
304             "\u00E1", "\u0061\u0301", "",\r
305             "\u00E2", "\u0061\u0302", "",\r
306             "\u00E3", "\u0061\u0303", "",\r
307             "\u00E4", "\u0061\u0308", "",\r
308             "\u00E5", "\u0061\u030A", "",\r
309             "\u00E7", "\u0063\u0327", "",\r
310             "\u00E8", "\u0065\u0300", "",\r
311             "\u00E9", "\u0065\u0301", "",\r
312             "\u00EA", "\u0065\u0302", "",\r
313             "\u00EB", "\u0065\u0308", "",\r
314             "\u00EC", "\u0069\u0300", "",\r
315             "\u00ED", "\u0069\u0301", "",\r
316             "\u00EE", "\u0069\u0302", "",\r
317             "\u00EF", "\u0069\u0308", "",\r
318             "\u00F1", "\u006E\u0303", "",\r
319             "\u00F2", "\u006F\u0300", "",\r
320             "\u00F3", "\u006F\u0301", "",\r
321             "\u00F4", "\u006F\u0302", "",\r
322             "\u00F5", "\u006F\u0303", "",\r
323             "\u00F6", "\u006F\u0308", "",\r
324             "\u00F9", "\u0075\u0300", "",\r
325             "\u00FA", "\u0075\u0301", "",\r
326             "\u00FB", "\u0075\u0302", "",\r
327             "\u00FC", "\u0075\u0308", "",\r
328             "\u00FD", "\u0079\u0301", "",\r
329 // EXTRAS, outside of Latin 1\r
330             "\u1EA4", "\u00C2\u0301", "",\r
331             "\u1EA5", "\u00E2\u0301", "",\r
332             "\u1EA6", "\u00C2\u0300", "",\r
333             "\u1EA7", "\u00E2\u0300", "",\r
334         };\r
335 \r
336         int[] classData = {\r
337             0x0300, 230,\r
338             0x0301, 230,\r
339             0x0302, 230,\r
340             0x0303, 230,\r
341             0x0304, 230,\r
342             0x0305, 230,\r
343             0x0306, 230,\r
344             0x0307, 230,\r
345             0x0308, 230,\r
346             0x0309, 230,\r
347             0x030A, 230,\r
348             0x030B, 230,\r
349             0x030C, 230,\r
350             0x030D, 230,\r
351             0x030E, 230,\r
352             0x030F, 230,\r
353             0x0310, 230,\r
354             0x0311, 230,\r
355             0x0312, 230,\r
356             0x0313, 230,\r
357             0x0314, 230,\r
358             0x0315, 232,\r
359             0x0316, 220,\r
360             0x0317, 220,\r
361             0x0318, 220,\r
362             0x0319, 220,\r
363             0x031A, 232,\r
364             0x031B, 216,\r
365             0x031C, 220,\r
366             0x031D, 220,\r
367             0x031E, 220,\r
368             0x031F, 220,\r
369             0x0320, 220,\r
370             0x0321, 202,\r
371             0x0322, 202,\r
372             0x0323, 220,\r
373             0x0324, 220,\r
374             0x0325, 220,\r
375             0x0326, 220,\r
376             0x0327, 202,\r
377             0x0328, 202,\r
378             0x0329, 220,\r
379             0x032A, 220,\r
380             0x032B, 220,\r
381             0x032C, 220,\r
382             0x032D, 220,\r
383             0x032E, 220,\r
384             0x032F, 220,\r
385             0x0330, 220,\r
386             0x0331, 220,\r
387             0x0332, 220,\r
388             0x0333, 220,\r
389             0x0334, 1,\r
390             0x0335, 1,\r
391             0x0336, 1,\r
392             0x0337, 1,\r
393             0x0338, 1,\r
394             0x0339, 220,\r
395             0x033A, 220,\r
396             0x033B, 220,\r
397             0x033C, 220,\r
398             0x033D, 230,\r
399             0x033E, 230,\r
400             0x033F, 230,\r
401             0x0340, 230,\r
402             0x0341, 230,\r
403             0x0342, 230,\r
404             0x0343, 230,\r
405             0x0344, 230,\r
406             0x0345, 240,\r
407             0x0360, 234,\r
408             0x0361, 234\r
409         };\r
410 \r
411         // build the same tables we would otherwise get from the\r
412         // Unicode Character Database, just with limited data\r
413 \r
414         for (int i = 0; i < decomposeData.length; i+=3) {\r
415             char value = decomposeData[i].charAt(0);\r
416             String decomp = decomposeData[i+1];\r
417             boolean compat = decomposeData[i+2].equals("K");\r
418             if (compat) isCompatibility.set(value);\r
419             decompose.put(value, decomp);\r
420             if (!compat) {\r
421                 int first = '\u0000';\r
422                 int second = UTF16Util.nextCodePoint(decomp, 0);\r
423                 if (decomp.length() > 1) {\r
424                     first = second;\r
425                     second = UTF16Util.nextCodePoint(decomp,\r
426                         UTF16Util.codePointLength(first));\r
427                 }\r
428                 long pair = (first << 16) | second;\r
429                 compose.put(pair, value);\r
430             }\r
431         }\r
432 \r
433         for (int i = 0; i < classData.length;) {\r
434             canonicalClass.put(classData[i++], classData[i++]);\r
435         }\r
436     }\r
437 \r
438     /**\r
439      * Utility: Parses a sequence of hex Unicode characters separated by spaces\r
440      */\r
441     static public String fromHex(String source) {\r
442         StringBuffer result = new StringBuffer();\r
443         for (int i = 0; i < source.length(); ++i) {\r
444             char c = source.charAt(i);\r
445             switch (c) {\r
446               case ' ': break; // ignore\r
447               case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':\r
448               case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':\r
449               case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':\r
450                 int end = 0;\r
451                 int value = 0;\r
452                 try {\r
453                     //System.out.println(source.substring(i, i + 4) + "************" + source);\r
454                     end = source.indexOf(' ',i);\r
455                     if (end < 0) {\r
456                         end = source.length();\r
457                     }\r
458                     value = Integer.parseInt(source.substring(i, end),16);\r
459                     UTF16Util.appendCodePoint(result, value);\r
460                 } catch (Exception e) {\r
461                     System.out.println("i: " + i + ";end:" + end + "source:" + source);\r
462                     //System.out.println(source.substring(i, i + 4) + "************" + source);\r
463                     System.exit(1);\r
464                 }\r
465                 //i+= 3; // skip rest of number\r
466                 i = end;\r
467                 break;\r
468               case '<': int j = source.indexOf('>',i); // skip <...>\r
469                 if (j > 0) {\r
470                     i = j;\r
471                     break;\r
472                 } // else fall through--error\r
473               default:\r
474                 throw new IllegalArgumentException("Bad hex value in " + source);\r
475             }\r
476         }\r
477         return result.toString();\r
478     }\r
479 \r
480     /**\r
481      * Utility: Supplies a zero-padded hex representation of an integer (without 0x)\r
482      */\r
483     static public String hex(int i) {\r
484         String result = Long.toString(i & 0xFFFFFFFFL, 16).toUpperCase();\r
485         return "00000000".substring(result.length(),8) + result;\r
486     }\r
487 \r
488     /**\r
489      * Utility: Supplies a zero-padded hex representation of a Unicode character (without 0x, \\u)\r
490      */\r
491     static public String hex(char i) {\r
492         String result = Integer.toString(i, 16).toUpperCase();\r
493         return "0000".substring(result.length(),4) + result;\r
494     }\r
495 \r
496     /**\r
497      * Utility: Supplies a zero-padded hex representation of a Unicode character (without 0x, \\u)\r
498      */\r
499     public static String hex(String s, String sep) {\r
500         StringBuffer result = new StringBuffer();\r
501         for (int i = 0; i < s.length(); ++i) {\r
502             if (i != 0) result.append(sep);\r
503             result.append(hex(s.charAt(i)));\r
504         }\r
505         return result.toString();\r
506     }\r
507 }\r