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