]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/main/tests/core/src/com/ibm/icu/dev/test/bidi/BidiTest.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / main / tests / core / src / com / ibm / icu / dev / test / bidi / BidiTest.java
1 /*
2 *******************************************************************************
3 *   Copyright (C) 2001-2010, International Business Machines
4 *   Corporation and others.  All Rights Reserved.
5 *******************************************************************************
6 */
7
8 package com.ibm.icu.dev.test.bidi;
9
10 import java.util.Arrays;
11
12 import com.ibm.icu.dev.test.TestFmwk;
13 import com.ibm.icu.impl.Utility;
14 import com.ibm.icu.lang.UCharacter;
15 import com.ibm.icu.text.Bidi;
16 import com.ibm.icu.text.BidiRun;
17 import com.ibm.icu.util.VersionInfo;
18
19 /**
20  * A base class for the Bidi test suite.
21  *
22  * @author Lina Kemmel, Matitiahu Allouche
23  */
24
25 public class BidiTest extends TestFmwk {
26
27     protected static final char[] charFromDirProp = {
28          /* L      R    EN    ES    ET     AN    CS    B    S    WS    ON */
29          0x61, 0x5d0, 0x30, 0x2f, 0x25, 0x660, 0x2c, 0xa, 0x9, 0x20, 0x26,
30          /* LRE     LRO     AL     RLE     RLO     PDF    NSM      BN */
31          0x202a, 0x202d, 0x627, 0x202b, 0x202e, 0x202c, 0x308, 0x200c
32     };
33
34     static {
35         initCharFromDirProps();
36     }
37
38     private static void initCharFromDirProps() {
39         final VersionInfo ucd401 =  VersionInfo.getInstance(4, 0, 1, 0);
40         VersionInfo ucdVersion = VersionInfo.getInstance(0, 0, 0, 0);
41
42         /* lazy initialization */
43         if (ucdVersion.getMajor() > 0) {
44             return;
45
46         }
47         ucdVersion = UCharacter.getUnicodeVersion();
48         if (ucdVersion.compareTo(ucd401) >= 0) {
49             /* Unicode 4.0.1 changes bidi classes for +-/ */
50             /* change ES character from / to + */
51             charFromDirProp[TestData.ES] = 0x2b;
52         }
53     }
54
55     protected boolean assertEquals(String message, String expected, String actual,
56                                    String src, String mode, String option,
57                                    String level) {
58         if (expected == null || actual == null) {
59             return super.assertEquals(message, expected, actual);
60         }
61         if (expected.equals(actual)) {
62             return true;
63         }
64         errln("");
65         errcontln(message);
66         if (src != null) {
67             errcontln("source            : \"" + Utility.escape(src) + "\"");
68         }
69         errcontln("expected          : \"" + Utility.escape(expected) + "\"");
70         errcontln("actual            : \"" + Utility.escape(actual) + "\"");
71         if (mode != null) {
72             errcontln("reordering mode   : " + mode);
73         }
74         if (option != null) {
75             errcontln("reordering option : " + option);
76         }
77         if (level != null) {
78             errcontln("paragraph level   : " + level);
79         }
80         return false;
81     }
82
83     protected static String valueOf(int[] array) {
84         StringBuffer result = new StringBuffer(array.length * 4);
85         for (int i = 0; i < array.length; i++) {
86             result.append(' ');
87             result.append(array[i]);
88         }
89         return result.toString();
90     }
91
92     private static final String[] modeDescriptions = {
93         "REORDER_DEFAULT",
94         "REORDER_NUMBERS_SPECIAL",
95         "REORDER_GROUP_NUMBERS_WITH_R",
96         "REORDER_RUNS_ONLY",
97         "REORDER_INVERSE_NUMBERS_AS_L",
98         "REORDER_INVERSE_LIKE_DIRECT",
99         "REORDER_INVERSE_FOR_NUMBERS_SPECIAL"
100     };
101
102     protected static String modeToString(int mode) {
103         if (mode < Bidi.REORDER_DEFAULT ||
104             mode > Bidi.REORDER_INVERSE_FOR_NUMBERS_SPECIAL) {
105             return "INVALID";
106         }
107         return modeDescriptions[mode];
108     }
109
110     private static final short SETPARA_MASK = Bidi.OPTION_INSERT_MARKS |
111         Bidi.OPTION_REMOVE_CONTROLS | Bidi.OPTION_STREAMING;
112
113     private static final String[] setParaDescriptions = {
114         "OPTION_INSERT_MARKS",
115         "OPTION_REMOVE_CONTROLS",
116         "OPTION_STREAMING"
117     };
118
119     protected static String spOptionsToString(int option) {
120         return optionToString(option, SETPARA_MASK, setParaDescriptions);
121     }
122
123     private static final int MAX_WRITE_REORDERED_OPTION = Bidi.OUTPUT_REVERSE;
124     private static final int REORDER_MASK = (MAX_WRITE_REORDERED_OPTION << 1) - 1;
125
126     private static final String[] writeReorderedDescriptions = {
127         "KEEP_BASE_COMBINING",      //  1
128         "DO_MIRRORING",             //  2
129         "INSERT_LRM_FOR_NUMERIC",   //  4
130         "REMOVE_BIDI_CONTROLS",     //  8
131         "OUTPUT_REVERSE"            // 16
132     };
133
134     public static String wrOptionsToString(int option) {
135         return optionToString(option, REORDER_MASK, writeReorderedDescriptions);
136     }
137     public static String optionToString(int option, int mask,
138                                         String[] descriptions) {
139         StringBuffer desc = new StringBuffer(50);
140
141         if ((option &= mask) == 0) {
142             return "0";
143         }
144         desc.setLength(0);
145
146         for (int i = 0; option > 0; i++, option >>= 1) {
147             if ((option & 1) != 0) {
148                 if (desc.length() > 0) {
149                     desc.append(" | ");
150                 }
151                 desc.append(descriptions[i]);
152             }
153         }
154         return desc.toString();
155     }
156
157     static final String columnString =
158         "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
159     static final char[] columns = columnString.toCharArray();
160     private static final int TABLE_SIZE = 256;
161     private static boolean tablesInitialized = false;
162     private static char[] pseudoToUChar;
163     private static char[] UCharToPseudo;    /* used for Unicode chars < 0x0100 */
164     private static char[] UCharToPseud2;    /* used for Unicode chars >=0x0100 */
165
166     static void buildPseudoTables()
167     /*
168         The rules for pseudo-Bidi are as follows:
169         - [ == LRE
170         - ] == RLE
171         - { == LRO
172         - } == RLO
173         - ^ == PDF
174         - @ == LRM
175         - & == RLM
176         - A-F == Arabic Letters 0631-0636
177         - G-V == Hebrew letters 05d7-05ea
178         - W-Z == Unassigned RTL 08d0-08d3
179         - 0-5 == western digits 0030-0035
180         - 6-9 == Arabic-Indic digits 0666-0669
181         - ` == Combining Grave Accent 0300 (NSM)
182         - ~ == Delete 007f (BN)
183         - | == Paragraph Separator 2029 (B)
184         - _ == Info Separator 1 001f (S)
185         All other characters represent themselves as Latin-1, with the corresponding
186         Bidi properties.
187     */
188     {
189         int     i;
190         char    uchar;
191         char    c;
192
193         /* initialize all tables to unknown */
194         pseudoToUChar = new char[TABLE_SIZE];
195         UCharToPseudo = new char[TABLE_SIZE];
196         UCharToPseud2 = new char[TABLE_SIZE];
197         for (i = 0; i < TABLE_SIZE; i++) {
198             pseudoToUChar[i] = 0xFFFD;
199             UCharToPseudo[i] = '?';
200             UCharToPseud2[i] = '?';
201         }
202         /* initialize non letters or digits */
203         pseudoToUChar[ 0 ] = 0x0000;    UCharToPseudo[0x00] =  0 ;
204         pseudoToUChar[' '] = 0x0020;    UCharToPseudo[0x20] = ' ';
205         pseudoToUChar['!'] = 0x0021;    UCharToPseudo[0x21] = '!';
206         pseudoToUChar['"'] = 0x0022;    UCharToPseudo[0x22] = '"';
207         pseudoToUChar['#'] = 0x0023;    UCharToPseudo[0x23] = '#';
208         pseudoToUChar['$'] = 0x0024;    UCharToPseudo[0x24] = '$';
209         pseudoToUChar['%'] = 0x0025;    UCharToPseudo[0x25] = '%';
210         pseudoToUChar['\'']= 0x0027;    UCharToPseudo[0x27] = '\'';
211         pseudoToUChar['('] = 0x0028;    UCharToPseudo[0x28] = '(';
212         pseudoToUChar[')'] = 0x0029;    UCharToPseudo[0x29] = ')';
213         pseudoToUChar['*'] = 0x002A;    UCharToPseudo[0x2A] = '*';
214         pseudoToUChar['+'] = 0x002B;    UCharToPseudo[0x2B] = '+';
215         pseudoToUChar[','] = 0x002C;    UCharToPseudo[0x2C] = ',';
216         pseudoToUChar['-'] = 0x002D;    UCharToPseudo[0x2D] = '-';
217         pseudoToUChar['.'] = 0x002E;    UCharToPseudo[0x2E] = '.';
218         pseudoToUChar['/'] = 0x002F;    UCharToPseudo[0x2F] = '/';
219         pseudoToUChar[':'] = 0x003A;    UCharToPseudo[0x3A] = ':';
220         pseudoToUChar[';'] = 0x003B;    UCharToPseudo[0x3B] = ';';
221         pseudoToUChar['<'] = 0x003C;    UCharToPseudo[0x3C] = '<';
222         pseudoToUChar['='] = 0x003D;    UCharToPseudo[0x3D] = '=';
223         pseudoToUChar['>'] = 0x003E;    UCharToPseudo[0x3E] = '>';
224         pseudoToUChar['?'] = 0x003F;    UCharToPseudo[0x3F] = '?';
225         pseudoToUChar['\\']= 0x005C;    UCharToPseudo[0x5C] = '\\';
226         /* initialize specially used characters */
227         pseudoToUChar['`'] = 0x0300;    UCharToPseud2[0x00] = '`';  /* NSM */
228         pseudoToUChar['@'] = 0x200E;    UCharToPseud2[0x0E] = '@';  /* LRM */
229         pseudoToUChar['&'] = 0x200F;    UCharToPseud2[0x0F] = '&';  /* RLM */
230         pseudoToUChar['_'] = 0x001F;    UCharToPseudo[0x1F] = '_';  /* S   */
231         pseudoToUChar['|'] = 0x2029;    UCharToPseud2[0x29] = '|';  /* B   */
232         pseudoToUChar['['] = 0x202A;    UCharToPseud2[0x2A] = '[';  /* LRE */
233         pseudoToUChar[']'] = 0x202B;    UCharToPseud2[0x2B] = ']';  /* RLE */
234         pseudoToUChar['^'] = 0x202C;    UCharToPseud2[0x2C] = '^';  /* PDF */
235         pseudoToUChar['{'] = 0x202D;    UCharToPseud2[0x2D] = '{';  /* LRO */
236         pseudoToUChar['}'] = 0x202E;    UCharToPseud2[0x2E] = '}';  /* RLO */
237         pseudoToUChar['~'] = 0x007F;    UCharToPseudo[0x7F] = '~';  /* BN  */
238         /* initialize western digits */
239         for (i = 0, uchar = 0x0030; i < 6; i++, uchar++) {
240             c = columns[i];
241             pseudoToUChar[c] = uchar;
242             UCharToPseudo[uchar & 0x00ff] = c;
243         }
244         /* initialize Hindi digits */
245         for (i = 6, uchar = 0x0666; i < 10; i++, uchar++) {
246             c = columns[i];
247             pseudoToUChar[c] = uchar;
248             UCharToPseud2[uchar & 0x00ff] = c;
249         }
250         /* initialize Arabic letters */
251         for (i = 10, uchar = 0x0631; i < 16; i++, uchar++) {
252             c = columns[i];
253             pseudoToUChar[c] = uchar;
254             UCharToPseud2[uchar & 0x00ff] = c;
255         }
256         /* initialize Hebrew letters */
257         for (i = 16, uchar = 0x05D7; i < 32; i++, uchar++) {
258             c = columns[i];
259             pseudoToUChar[c] = uchar;
260             UCharToPseud2[uchar & 0x00ff] = c;
261         }
262         /* initialize Unassigned code points */
263         for (i = 32, uchar = 0x08D0; i < 36; i++, uchar++) {
264             c = columns[i];
265             pseudoToUChar[c] = uchar;
266             UCharToPseud2[uchar & 0x00ff] = c;
267         }
268         /* initialize Latin lower case letters */
269         for (i = 36, uchar = 0x0061; i < 62; i++, uchar++) {
270             c = columns[i];
271             pseudoToUChar[c] = uchar;
272             UCharToPseudo[uchar & 0x00ff] = c;
273         }
274         tablesInitialized = true;
275     }
276
277     /*----------------------------------------------------------------------*/
278
279     static String pseudoToU16(String input)
280     /*  This function converts a pseudo-Bidi string into a char string.
281         It returns the char string.
282     */
283     {
284         int len = input.length();
285         char[] output = new char[len];
286         int i;
287         if (!tablesInitialized) {
288             buildPseudoTables();
289         }
290         for (i = 0; i < len; i++)
291             output[i] = pseudoToUChar[input.charAt(i)];
292         return new String(output);
293     }
294
295     /*----------------------------------------------------------------------*/
296
297     static String u16ToPseudo(String input)
298     /*  This function converts a char string into a pseudo-Bidi string.
299         It returns the pseudo-Bidi string.
300     */
301     {
302         int len = input.length();
303         char[] output = new char[len];
304         int i;
305         char uchar;
306         if (!tablesInitialized) {
307             buildPseudoTables();
308         }
309         for (i = 0; i < len; i++)
310         {
311             uchar = input.charAt(i);
312             output[i] = uchar < 0x0100 ? UCharToPseudo[uchar] :
313                                          UCharToPseud2[uchar & 0x00ff];
314         }
315         return new String(output);
316     }
317
318     void errcont(String message) {
319         msg(message, ERR, false, false);
320     }
321
322     void errcontln(String message) {
323         msg(message, ERR, false, true);
324     }
325
326     void printCaseInfo(Bidi bidi, String src, String dst)
327     {
328         int length = bidi.getProcessedLength();
329         byte[] levels = bidi.getLevels();
330         char[] levelChars  = new char[length];
331         byte lev;
332         int runCount = bidi.countRuns();
333         errcontln("========================================");
334         errcontln("Processed length: " + length);
335         for (int i = 0; i < length; i++) {
336             lev = levels[i];
337             if (lev < 0) {
338                 levelChars[i] = '-';
339             } else if (lev < columns.length) {
340                 levelChars[i] = columns[lev];
341             } else {
342                 levelChars[i] = '+';
343             }
344         }
345         errcontln("Levels: " + new String(levelChars));
346         errcontln("Source: " + src);
347         errcontln("Result: " + dst);
348         errcontln("Direction: " + bidi.getDirection());
349         errcontln("paraLevel: " + Byte.toString(bidi.getParaLevel()));
350         errcontln("reorderingMode: " + modeToString(bidi.getReorderingMode()));
351         errcontln("reorderingOptions: " + spOptionsToString(bidi.getReorderingOptions()));
352         errcont("Runs: " + runCount + " => logicalStart.length/level: ");
353         for (int i = 0; i < runCount; i++) {
354             BidiRun run;
355             run = bidi.getVisualRun(i);
356             errcont(" " + run.getStart() + "." + run.getLength() + "/" +
357                     run.getEmbeddingLevel());
358         }
359         errcont("\n");
360     }
361
362     static final String mates1 = "<>()[]{}";
363     static final String mates2 = "><)(][}{";
364     static final char[] mates1Chars = mates1.toCharArray();
365     static final char[] mates2Chars = mates2.toCharArray();
366
367     boolean matchingPair(Bidi bidi, int i, char c1, char c2)
368     {
369         if (c1 == c2) {
370             return true;
371         }
372         /* For REORDER_RUNS_ONLY, it would not be correct to check levels[i],
373            so we use the appropriate run's level, which is good for all cases.
374          */
375         if (bidi.getLogicalRun(i).getDirection() == 0) {
376             return false;
377         }
378         for (int k = 0; k < mates1Chars.length; k++) {
379             if ((c1 == mates1Chars[k]) && (c2 == mates2Chars[k])) {
380                 return true;
381             }
382         }
383         return false;
384     }
385
386     boolean checkWhatYouCan(Bidi bidi, String src, String dst)
387     {
388         int i, idx, logLimit, visLimit;
389         boolean testOK, errMap, errDst;
390         char[] srcChars = src.toCharArray();
391         char[] dstChars = dst.toCharArray();
392         int[] visMap = bidi.getVisualMap();
393         int[] logMap = bidi.getLogicalMap();
394
395         testOK = true;
396         errMap = errDst = false;
397         logLimit = bidi.getProcessedLength();
398         visLimit = bidi.getResultLength();
399         if (visLimit > dstChars.length) {
400             visLimit = dstChars.length;
401         }
402         char[] accumSrc = new char[logLimit];
403         char[] accumDst = new char[visLimit];
404         Arrays.fill(accumSrc, '?');
405         Arrays.fill(accumDst, '?');
406
407         if (logMap.length != logLimit) {
408             errMap = true;
409         }
410         for (i = 0; i < logLimit; i++) {
411             idx = bidi.getVisualIndex(i);
412             if (idx != logMap[i]) {
413                 errMap = true;
414             }
415             if (idx == Bidi.MAP_NOWHERE) {
416                 continue;
417             }
418             if (idx >= visLimit) {
419                 continue;
420             }
421             accumDst[idx] = srcChars[i];
422             if (!matchingPair(bidi, i, srcChars[i], dstChars[idx])) {
423                 errDst = true;
424             }
425         }
426         if (errMap) {
427             if (testOK) {
428                 printCaseInfo(bidi, src, dst);
429                 testOK = false;
430             }
431             errln("Mismatch between getLogicalMap() and getVisualIndex()");
432             errcont("Map    :" + valueOf(logMap));
433             errcont("\n");
434             errcont("Indexes:");
435             for (i = 0; i < logLimit; i++) {
436                 errcont(" " + bidi.getVisualIndex(i));
437             }
438             errcont("\n");
439         }
440         if (errDst) {
441             if (testOK) {
442                 printCaseInfo(bidi, src, dst);
443                 testOK = false;
444             }
445             errln("Source does not map to Result");
446             errcontln("We got: " + new String(accumDst));
447         }
448
449         errMap = errDst = false;
450         if (visMap.length != visLimit) {
451             errMap = true;
452         }
453         for (i = 0; i < visLimit; i++) {
454             idx = bidi.getLogicalIndex(i);
455             if (idx != visMap[i]) {
456                 errMap = true;
457             }
458             if (idx == Bidi.MAP_NOWHERE) {
459                 continue;
460             }
461             if (idx >= logLimit) {
462                 continue;
463             }
464             accumSrc[idx] = dstChars[i];
465             if (!matchingPair(bidi, idx, srcChars[idx], dstChars[i])) {
466                 errDst = true;
467             }
468         }
469         if (errMap) {
470             if (testOK) {
471                 printCaseInfo(bidi, src, dst);
472                 testOK = false;
473             }
474             errln("Mismatch between getVisualMap() and getLogicalIndex()");
475             errcont("Map    :" + valueOf(visMap));
476             errcont("\n");
477             errcont("Indexes:");
478             for (i = 0; i < visLimit; i++) {
479                 errcont(" " + bidi.getLogicalIndex(i));
480             }
481             errcont("\n");
482         }
483         if (errDst) {
484             if (testOK) {
485                 printCaseInfo(bidi, src, dst);
486                 testOK = false;
487             }
488             errln("Result does not map to Source");
489             errcontln("We got: " + new String(accumSrc));
490         }
491         return testOK;
492     }
493
494 }