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