1// © 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html#License 3/* 4******************************************************************************* 5* Copyright (C) 2008-2010, International Business Machines 6* Corporation and others. All Rights Reserved. 7******************************************************************************* 8*/ 9 10package com.ibm.icu.dev.test.bidi; 11 12import java.util.Arrays; 13 14import org.junit.Test; 15 16import com.ibm.icu.impl.Utility; 17import com.ibm.icu.text.Bidi; 18import com.ibm.icu.text.BidiRun; 19 20/** 21 * Regression test for Bidi multiple paragraphs 22 * 23 * @author Lina Kemmel, Matitiahu Allouche 24 */ 25 26public class TestMultipleParagraphs extends BidiFmwk { 27 28 private static final String text = 29 "__ABC\u001c" /* Para #0 offset 0 */ 30 + "__\u05d0DE\u001c" /* 1 6 */ 31 + "__123\u001c" /* 2 12 */ 32 + "\r\n" /* 3 18 */ 33 + "FG\r" /* 4 20 */ 34 + "\r" /* 5 23 */ 35 + "HI\r\n" /* 6 24 */ 36 + "\r\n" /* 7 28 */ 37 + "\n" /* 8 30 */ 38 + "\n" /* 9 31 */ 39 + "JK\u001c"; /* 10 32 */ 40 private static final int paraCount = 11; 41 private static final int[] paraBounds = { 42 0, 6, 12, 18, 20, 23, 24, 28, 30, 31, 32, 35 43 }; 44 private static final byte[] paraLevels = { 45 Bidi.LTR, Bidi.RTL, Bidi.LEVEL_DEFAULT_LTR, Bidi.LEVEL_DEFAULT_RTL, 22, 23 46 }; 47 private static final byte[][] multiLevels = { 48 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 49 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, 50 {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 51 {0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0}, 52 {22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22}, 53 {23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23} 54 }; 55 private static final String text2 = "\u05d0 1-2\u001c\u0630 1-2\u001c1-2"; 56 private static final byte[] levels2 = { 57 1, 1, 2, 2, 2, 0, 1, 1, 2, 1, 2, 0, 2, 2, 2 58 }; 59 private static final char[] multiparaTestString = { 60 0x5de, 0x5e0, 0x5e1, 0x5d4, 0x20, 0x5e1, 0x5e4, 0x5da, 61 0x20, 0xa, 0xa, 0x41, 0x72, 0x74, 0x69, 0x73, 62 0x74, 0x3a, 0x20, 0x5de, 0x5e0, 0x5e1, 0x5d4, 0x20, 63 0x5e1, 0x5e4, 0x5da, 0x20, 0xa, 0xa, 0x41, 0x6c, 64 0x62, 0x75, 0x6d, 0x3a, 0x20, 0x5de, 0x5e0, 0x5e1, 65 0x5d4, 0x20, 0x5e1, 0x5e4, 0x5da, 0x20, 0xa, 0xa, 66 0x54, 0x69, 0x6d, 0x65, 0x3a, 0x20, 0x32, 0x3a, 67 0x32, 0x37, 0xa, 0xa 68 }; 69 private static final byte[] multiparaTestLevels = { 70 1, 1, 1, 1, 1, 1, 1, 1, 71 1, 1, 0, 0, 0, 0, 0, 0, 72 0, 0, 0, 1, 1, 1, 1, 1, 73 1, 1, 1, 0, 0, 0, 0, 0, 74 0, 0, 0, 0, 0, 1, 1, 1, 75 1, 1, 1, 1, 1, 0, 0, 0, 76 0, 0, 0, 0, 0, 0, 0, 0, 77 0, 0, 0, 0 78 }; 79 80 @Test 81 public void testMultipleParagraphs() 82 { 83 byte gotLevel; 84 byte[] gotLevels; 85 boolean orderParagraphsLTR; 86 String src; 87 Bidi bidi = new Bidi(); 88 Bidi bidiLine; 89 int count, paraStart, paraLimit, paraIndex, length; 90 int i, j, k; 91 92 logln("\nEntering TestMultipleParagraphs\n"); 93 try { 94 bidi.setPara(text, Bidi.LTR, null); 95 } catch (IllegalArgumentException e) { 96 errln("1st Bidi.setPara failed, paraLevel = " + Bidi.LTR); 97 } 98 99 /* check paragraph count and boundaries */ 100 if (paraCount != (count = bidi.countParagraphs())) { 101 errln("1st Bidi.countParagraphs returned " + count + ", should be " + 102 paraCount); 103 } 104 BidiRun run; 105 for (i = 0; i < paraCount; i++) { 106 run = bidi.getParagraphByIndex(i); 107 paraStart = run.getStart(); 108 paraLimit = run.getLimit(); 109 if ((paraStart != paraBounds[i]) || 110 (paraLimit != paraBounds[i + 1])) { 111 errln("Found boundaries of paragraph " + i + ": " + 112 paraStart + "-" + paraLimit + "; expected: " + 113 paraBounds[i] + "-" + paraBounds[i + 1]); 114 } 115 } 116 117 /* check with last paragraph not terminated by B */ 118 char[] chars = text.toCharArray(); 119 chars[chars.length - 1] = 'L'; 120 src = new String(chars); 121 try { 122 bidi.setPara(src, Bidi.LTR, null); 123 } catch (IllegalArgumentException e) { 124 errln("2nd Bidi.setPara failed, paraLevel = " + Bidi.LTR); 125 } 126 if (paraCount != (count = bidi.countParagraphs())) { 127 errln("2nd Bidi.countParagraphs returned " + count + 128 ", should be " + paraCount); 129 } 130 i = paraCount - 1; 131 run = bidi.getParagraphByIndex(i); 132 paraStart = run.getStart(); 133 paraLimit = run.getLimit(); 134 if ((paraStart != paraBounds[i]) || 135 (paraLimit != paraBounds[i + 1])) { 136 errln("2nd Found boundaries of paragraph " + i + ": " + 137 paraStart + "-" + paraLimit + "; expected: " + 138 paraBounds[i] + "-" + paraBounds[i + 1]); 139 } 140 141 /* check paraLevel for all paragraphs under various paraLevel specs */ 142 for (k = 0; k < 6; k++) { 143 try { 144 bidi.setPara(src, paraLevels[k], null); 145 } catch (IllegalArgumentException e) { 146 errln("3nd Bidi.setPara failed, paraLevel = " + paraLevels[k]); 147 } 148 for (i = 0; i < paraCount; i++) { 149 paraIndex = bidi.getParagraphIndex(paraBounds[i]); 150 run = bidi.getParagraph(paraBounds[i]); 151 if (paraIndex != i) { 152 errln("#1 For paraLevel = " + paraLevels[k] + 153 " paragraph = " + i + ", found paragraph" + 154 " index = " + paraIndex + " expected = " + i); 155 } 156 gotLevel = run.getEmbeddingLevel(); 157 if (gotLevel != multiLevels[k][i]) { 158 errln("#2 For paraLevel = " + paraLevels[k] + 159 " paragraph = " + i + ", found level = " + gotLevel + 160 ", expected = " + multiLevels[k][i]); 161 } 162 } 163 gotLevel = bidi.getParaLevel(); 164 if (gotLevel != multiLevels[k][0]) { 165 errln("#3 For paraLevel = " + paraLevels[k] + 166 " getParaLevel = " + gotLevel + ", expected " + 167 multiLevels[k][0]); 168 } 169 } 170 171 /* check that the result of Bidi.getParaLevel changes if the first 172 * paragraph has a different level 173 */ 174 chars[0] = '\u05d2'; /* Hebrew letter Gimel */ 175 src = new String(chars); 176 try { 177 bidi.setPara(src, Bidi.LEVEL_DEFAULT_LTR, null); 178 } catch (IllegalArgumentException e) { 179 errln("Bidi.setPara failed, paraLevel = " + Bidi.LEVEL_DEFAULT_LTR); 180 } 181 gotLevel = bidi.getParaLevel(); 182 if (gotLevel != Bidi.RTL) { 183 errln("#4 For paraLevel = Bidi.LEVEL_DEFAULT_LTR getParaLevel = " + 184 gotLevel + ", expected = " + Bidi.RTL); 185 } 186 187 /* check that line cannot overlap paragraph boundaries */ 188 bidiLine = new Bidi(); 189 i = paraBounds[1]; 190 k = paraBounds[2] + 1; 191 try { 192 bidiLine = bidi.setLine(i, k); 193 errln("For line limits " + i + "-" + k 194 + " got success, while expected failure"); 195 } catch (Exception e) {} 196 197 i = paraBounds[1]; 198 k = paraBounds[2]; 199 try { 200 bidiLine = bidi.setLine(i, k); 201 } catch (Exception e) { 202 errln("For line limits " + i + "-" + k + " got failure"); 203 } 204 205 /* check level of block separator at end of paragraph when orderParagraphsLTR==FALSE */ 206 try { 207 bidi.setPara(src, Bidi.RTL, null); 208 } catch (IllegalArgumentException e) { 209 errln("Bidi.setPara failed, paraLevel = " + Bidi.RTL); 210 } 211 /* get levels through para Bidi block */ 212 try { 213 gotLevels = bidi.getLevels(); 214 } catch (Exception e) { 215 errln("Error on Bidi.getLevels"); 216 gotLevels = new byte[bidi.getLength()]; 217 Arrays.fill(gotLevels, (byte)-1); 218 } 219 for (i = 26; i < 32; i++) { 220 if (gotLevels[i] != Bidi.RTL) { 221 errln("For char " + i + "(0x" + Utility.hex(chars[i]) + 222 "), level = " + gotLevels[i] + ", expected = " + Bidi.RTL); 223 } 224 } 225 /* get levels through para Line block */ 226 i = paraBounds[1]; 227 k = paraBounds[2]; 228 try { 229 bidiLine = bidi.setLine(i, k); 230 } catch (Exception e) { 231 errln("For line limits " + i + "-" + k + " got failure"); 232 return; 233 } 234 paraIndex = bidiLine.getParagraphIndex(i); 235 run = bidiLine.getParagraph(i); 236 try { 237 gotLevels = bidiLine.getLevels(); 238 } catch (Exception e) { 239 errln("Error on bidiLine.getLevels"); 240 gotLevels = new byte[bidiLine.getLength()]; 241 Arrays.fill(gotLevels, (byte)-1); 242 } 243 length = bidiLine.getLength(); 244 gotLevel = run.getEmbeddingLevel(); 245 if ((gotLevel != Bidi.RTL) || (gotLevels[length - 1] != Bidi.RTL)) { 246 errln("For paragraph " + paraIndex + " with limits " + 247 run.getStart() + "-" + run.getLimit() + 248 ", paraLevel = " + gotLevel + 249 "expected = " + Bidi.RTL + 250 ", level of separator = " + gotLevels[length - 1] + 251 " expected = " + Bidi.RTL); 252 } 253 orderParagraphsLTR = bidi.isOrderParagraphsLTR(); 254 assertFalse("orderParagraphsLTR is true", orderParagraphsLTR); 255 bidi.orderParagraphsLTR(true); 256 orderParagraphsLTR = bidi.isOrderParagraphsLTR(); 257 assertTrue("orderParagraphsLTR is false", orderParagraphsLTR); 258 259 /* check level of block separator at end of paragraph when orderParagraphsLTR==TRUE */ 260 try { 261 bidi.setPara(src, Bidi.RTL, null); 262 } catch (IllegalArgumentException e) { 263 errln("Bidi.setPara failed, paraLevel = " + Bidi.RTL); 264 } 265 /* get levels through para Bidi block */ 266 try { 267 gotLevels = bidi.getLevels(); 268 } catch (Exception e) { 269 errln("Error on Bidi.getLevels"); 270 gotLevels = new byte[bidi.getLength()]; 271 Arrays.fill(gotLevels, (byte)-1); 272 } 273 for (i = 26; i < 32; i++) { 274 if (gotLevels[i] != 0) { 275 errln("For char " + i + "(0x" + Utility.hex(chars[i]) + 276 "), level = "+ gotLevels[i] + ", expected = 0"); 277 } 278 } 279 /* get levels through para Line block */ 280 i = paraBounds[1]; 281 k = paraBounds[2]; 282 paraStart = run.getStart(); 283 paraLimit = run.getLimit(); 284 try { 285 bidiLine = bidi.setLine(paraStart, paraLimit); 286 } catch (Exception e) { 287 errln("For line limits " + paraStart + "-" + paraLimit + 288 " got failure"); 289 } 290 paraIndex = bidiLine.getParagraphIndex(i); 291 run = bidiLine.getParagraph(i); 292 try { 293 gotLevels = bidiLine.getLevels(); 294 } catch (Exception e) { 295 errln("Error on bidiLine.getLevels"); 296 gotLevels = new byte[bidiLine.getLength()]; 297 Arrays.fill(gotLevels, (byte)-1); 298 } 299 length = bidiLine.getLength(); 300 gotLevel = run.getEmbeddingLevel(); 301 if ((gotLevel != Bidi.RTL) || (gotLevels[length - 1] != 0)) { 302 err("\nFor paragraph " + paraIndex + " with limits " + 303 run.getStart() + "-" + run.getLimit() + 304 ", paraLevel = " + gotLevel + "expected = " + Bidi.RTL + 305 ", level of separator = " + gotLevels[length - 1] + 306 " expected = 0\nlevels = "); 307 for (count = 0; count < length; count++) { 308 errcont(gotLevels[count] + " "); 309 } 310 errcont("\n"); 311 } 312 313 /* test that the concatenation of separate invocations of the bidi code 314 * on each individual paragraph in order matches the levels array that 315 * results from invoking bidi once over the entire multiparagraph tests 316 * (with orderParagraphsLTR false, of course) 317 */ 318 src = text; /* restore original content */ 319 bidi.orderParagraphsLTR(false); 320 try { 321 bidi.setPara(src, Bidi.LEVEL_DEFAULT_RTL, null); 322 } catch (IllegalArgumentException e) { 323 errln("Bidi.setPara failed, paraLevel = " + Bidi.LEVEL_DEFAULT_RTL); 324 } 325 try { 326 gotLevels = bidi.getLevels(); 327 } catch (Exception e) { 328 errln("Error on bidiLine.getLevels"); 329 gotLevels = new byte[bidi.getLength()]; 330 Arrays.fill(gotLevels, (byte)-1); 331 } 332 for (i = 0; i < paraCount; i++) { 333 /* use pLine for individual paragraphs */ 334 paraStart = paraBounds[i]; 335 length = paraBounds[i + 1] - paraStart; 336 try { 337 bidiLine.setPara(src.substring(paraStart, paraStart + length), 338 Bidi.LEVEL_DEFAULT_RTL, null); 339 } catch (IllegalArgumentException e) { 340 errln("Bidi.setPara failed, paraLevel = " + Bidi.LEVEL_DEFAULT_RTL); 341 } 342 for (j = 0; j < length; j++) { 343 if ((k = bidiLine.getLevelAt(j)) != 344 (gotLevel = gotLevels[paraStart + j])) { 345 errln("Checking paragraph concatenation: for paragraph[" + 346 i + "], char[" + j + "] = 0x" + 347 Utility.hex(src.charAt(paraStart + j)) + 348 ", level = " + k + ", expected = " + gotLevel); 349 } 350 } 351 } 352 353 /* ensure that leading numerics in a paragraph are not treated as arabic 354 numerals because of arabic text in a preceding paragraph 355 */ 356 src = text2; 357 bidi.orderParagraphsLTR(true); 358 try { 359 bidi.setPara(src, Bidi.RTL, null); 360 } catch (IllegalArgumentException e) { 361 errln("Bidi.setPara failed, paraLevel = " + Bidi.RTL); 362 } 363 try { 364 gotLevels = bidi.getLevels(); 365 } catch (Exception e) { 366 errln("Error on Bidi.getLevels"); 367 gotLevels = new byte[bidi.getLength()]; 368 Arrays.fill(gotLevels, (byte)-1); 369 } 370 for (i = 0, length = src.length(); i < length; i++) { 371 if (gotLevels[i] != levels2[i]) { 372 errln("Checking leading numerics: for char " + i + "(0x" + 373 Utility.hex(src.charAt(i)) + "), level = " + 374 gotLevels[i] + ", expected = " + levels2[i]); 375 } 376 } 377 378 /* check handling of whitespace before end of paragraph separator when 379 * orderParagraphsLTR==TRUE, when last paragraph has, and lacks, a terminating B 380 */ 381 chars = src.toCharArray(); 382 Arrays.fill(chars, '\u0020'); 383 bidi.orderParagraphsLTR(true); 384 for (i = 0x001c; i <= 0x0020; i += (0x0020-0x001c)) { 385 chars[4] = (char)i; /* with and without terminating B */ 386 for (j = 0x0041; j <= 0x05d0; j += (0x05d0-0x0041)) { 387 chars[0] = (char)j; /* leading 'A' or Alef */ 388 src = new String(chars); 389 for (gotLevel = 4; gotLevel <= 5; gotLevel++) { 390 /* test even and odd paraLevel */ 391 try { 392 bidi.setPara(src, gotLevel, null); 393 } catch (IllegalArgumentException e) { 394 errln("Bidi.setPara failed, paraLevel = " + gotLevel); 395 } 396 try { 397 gotLevels = bidi.getLevels(); 398 } catch (Exception e) { 399 errln("Error on Bidi.getLevels"); 400 gotLevels = new byte[bidi.getLength()]; 401 Arrays.fill(gotLevels, (byte)-1); 402 } 403 for (k = 1; k <= 3; k++) { 404 if (gotLevels[k] != gotLevel) { 405 errln("Checking trailing spaces for leading char 0x" + 406 Utility.hex(chars[0]) + ", last_char = " + 407 Utility.hex(chars[4]) + ", index = " + k + 408 "level = " + gotLevels[k] + 409 ", expected = " + gotLevel); 410 } 411 } 412 } 413 } 414 } 415 416 /* check default orientation when inverse bidi and paragraph starts 417 * with LTR strong char and ends with RTL strong char, with and without 418 * a terminating B 419 */ 420 bidi.setReorderingMode(Bidi.REORDER_INVERSE_LIKE_DIRECT); 421 bidi.setPara("abc \u05d2\u05d1\n", Bidi.LEVEL_DEFAULT_LTR, null); 422 String out = bidi.writeReordered(0); 423 assertEquals("\nInvalid output", "\u05d1\u05d2 abc\n", out); 424 bidi.setPara("abc \u05d2\u05d1", Bidi.LEVEL_DEFAULT_LTR, null); 425 out = bidi.writeReordered(0); 426 assertEquals("\nInvalid output #1", "\u05d1\u05d2 abc", out); 427 428 /* check multiple paragraphs together with explicit levels 429 */ 430 bidi.setReorderingMode(Bidi.REORDER_DEFAULT); 431 gotLevels = new byte[] {0,0,0,0,0,0,0,0,0,0}; 432 bidi.setPara("ab\u05d1\u05d2\n\u05d3\u05d4123", Bidi.LTR, gotLevels); 433 out = bidi.writeReordered(0); 434 assertEquals("\nInvalid output #2", "ab\u05d2\u05d1\n123\u05d4\u05d3", out); 435 assertEquals("\nInvalid number of paras", 2, bidi.countParagraphs()); 436 437 logln("\nExiting TestMultipleParagraphs\n"); 438 439 /* check levels in multiple paragraphs with default para level 440 */ 441 bidi = new Bidi(); 442 bidi.setPara(multiparaTestString, Bidi.LEVEL_DEFAULT_LTR, null); 443 try { 444 gotLevels = bidi.getLevels(); 445 } catch (Exception e) { 446 errln("Error on Bidi.getLevels for multiparaTestString"); 447 return; 448 } 449 for (i = 0; i < multiparaTestString.length; i++) { 450 if (gotLevels[i] != multiparaTestLevels[i]) { 451 errln("Error on level for multiparaTestString at index " + i + 452 ", expected=" + multiparaTestLevels[i] + 453 ", actual=" + gotLevels[i]); 454 } 455 } 456 } 457} 458