1 /* 2 ******************************************************************************* 3 * Copyright (C) 1996-2014, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7 8 package com.ibm.icu.dev.test.lang; 9 10 import com.ibm.icu.dev.test.TestFmwk; 11 import com.ibm.icu.dev.test.UTF16Util; 12 import com.ibm.icu.impl.Utility; 13 import com.ibm.icu.lang.UCharacter; 14 import com.ibm.icu.text.ReplaceableString; 15 import com.ibm.icu.text.UTF16; 16 import com.ibm.icu.text.UTF16.StringComparator; 17 18 /** 19 * Testing class for UTF16 20 * @author Syn Wee Quek 21 * @since feb 09 2001 22 */ 23 public final class UTF16Test extends TestFmwk 24 { 25 // constructor =================================================== 26 27 /** 28 * Constructor 29 */ UTF16Test()30 public UTF16Test() 31 { 32 } 33 34 // public methods ================================================ 35 36 /** 37 * Testing UTF16 class methods append 38 */ TestAppend()39 public void TestAppend() 40 { 41 StringBuffer strbuff = new StringBuffer("this is a string "); 42 char array[] = new char[UCharacter.MAX_VALUE >> 2]; 43 int strsize = strbuff.length(); 44 int arraysize = strsize; 45 46 if (0 != strsize) { 47 strbuff.getChars(0, strsize, array, 0); 48 } 49 for (int i = 1; i < UCharacter.MAX_VALUE; i += 100) { 50 UTF16.append(strbuff, i); 51 arraysize = UTF16.append(array, arraysize, i); 52 53 String arraystr = new String(array, 0, arraysize); 54 if (!arraystr.equals(strbuff.toString())) { 55 errln("FAIL Comparing char array append and string append " + 56 "with 0x" + Integer.toHexString(i)); 57 } 58 59 // this is to cater for the combination of 0xDBXX 0xDC50 which 60 // forms a supplementary character 61 if (i == 0xDC51) { 62 strsize --; 63 } 64 65 if (UTF16.countCodePoint(strbuff) != strsize + (i / 100) + 1) { 66 errln("FAIL Counting code points in string appended with " + 67 " 0x" + Integer.toHexString(i)); 68 break; 69 } 70 } 71 72 // coverage for new 1.5 - cover only so no real test 73 strbuff = new StringBuffer(); 74 UTF16.appendCodePoint(strbuff, 0x10000); 75 if (strbuff.length() != 2) { 76 errln("fail appendCodePoint"); 77 } 78 } 79 80 /** 81 * Testing UTF16 class methods bounds 82 */ TestBounds()83 public void TestBounds() 84 { 85 StringBuffer strbuff = 86 //0 12345 6 7 8 9 87 new StringBuffer("\udc000123\ud800\udc00\ud801\udc01\ud802"); 88 String str = strbuff.toString(); 89 char array[] = str.toCharArray(); 90 int boundtype[] = {UTF16.SINGLE_CHAR_BOUNDARY, 91 UTF16.SINGLE_CHAR_BOUNDARY, 92 UTF16.SINGLE_CHAR_BOUNDARY, 93 UTF16.SINGLE_CHAR_BOUNDARY, 94 UTF16.SINGLE_CHAR_BOUNDARY, 95 UTF16.LEAD_SURROGATE_BOUNDARY, 96 UTF16.TRAIL_SURROGATE_BOUNDARY, 97 UTF16.LEAD_SURROGATE_BOUNDARY, 98 UTF16.TRAIL_SURROGATE_BOUNDARY, 99 UTF16.SINGLE_CHAR_BOUNDARY}; 100 int length = str.length(); 101 for (int i = 0; i < length; i ++) { 102 if (UTF16.bounds(str, i) != boundtype[i]) { 103 errln("FAIL checking bound type at index " + i); 104 } 105 if (UTF16.bounds(strbuff, i) != boundtype[i]) { 106 errln("FAIL checking bound type at index " + i); 107 } 108 if (UTF16.bounds(array, 0, length, i) != boundtype[i]) { 109 errln("FAIL checking bound type at index " + i); 110 } 111 } 112 // does not straddle between supplementary character 113 int start = 4; 114 int limit = 9; 115 int subboundtype1[] = {UTF16.SINGLE_CHAR_BOUNDARY, 116 UTF16.LEAD_SURROGATE_BOUNDARY, 117 UTF16.TRAIL_SURROGATE_BOUNDARY, 118 UTF16.LEAD_SURROGATE_BOUNDARY, 119 UTF16.TRAIL_SURROGATE_BOUNDARY}; 120 try { 121 UTF16.bounds(array, start, limit, -1); 122 errln("FAIL Out of bounds index in bounds should fail"); 123 } catch (Exception e) { 124 // getting rid of warnings 125 System.out.print(""); 126 } 127 128 for (int i = 0; i < limit - start; i ++) { 129 if (UTF16.bounds(array, start, limit, i) != subboundtype1[i]) { 130 errln("FAILED Subarray bounds in [" + start + ", " + limit + 131 "] expected " + subboundtype1[i] + " at offset " + i); 132 } 133 } 134 135 // starts from the mid of a supplementary character 136 int subboundtype2[] = {UTF16.SINGLE_CHAR_BOUNDARY, 137 UTF16.LEAD_SURROGATE_BOUNDARY, 138 UTF16.TRAIL_SURROGATE_BOUNDARY}; 139 140 start = 6; 141 limit = 9; 142 for (int i = 0; i < limit - start; i ++) { 143 if (UTF16.bounds(array, start, limit, i) != subboundtype2[i]) { 144 errln("FAILED Subarray bounds in [" + start + ", " + limit + 145 "] expected " + subboundtype2[i] + " at offset " + i); 146 } 147 } 148 149 // ends in the mid of a supplementary character 150 int subboundtype3[] = {UTF16.LEAD_SURROGATE_BOUNDARY, 151 UTF16.TRAIL_SURROGATE_BOUNDARY, 152 UTF16.SINGLE_CHAR_BOUNDARY}; 153 start = 5; 154 limit = 8; 155 for (int i = 0; i < limit - start; i ++) { 156 if (UTF16.bounds(array, start, limit, i) != subboundtype3[i]) { 157 errln("FAILED Subarray bounds in [" + start + ", " + limit + 158 "] expected " + subboundtype3[i] + " at offset " + i); 159 } 160 } 161 } 162 163 /** 164 * Testing UTF16 class methods charAt and charAtCodePoint 165 */ TestCharAt()166 public void TestCharAt() 167 { 168 StringBuffer strbuff = 169 new StringBuffer("12345\ud800\udc0167890\ud800\udc02"); 170 if (UTF16.charAt(strbuff, 0) != '1' || UTF16.charAt(strbuff, 2) != '3' 171 || UTF16.charAt(strbuff, 5) != 0x10001 || 172 UTF16.charAt(strbuff, 6) != 0x10001 || 173 UTF16.charAt(strbuff, 12) != 0x10002 || 174 UTF16.charAt(strbuff, 13) != 0x10002) { 175 errln("FAIL Getting character from string buffer error" ); 176 } 177 String str = strbuff.toString(); 178 if (UTF16.charAt(str, 0) != '1' || UTF16.charAt(str, 2) != '3' || 179 UTF16.charAt(str, 5) != 0x10001 || UTF16.charAt(str, 6) != 0x10001 180 || UTF16.charAt(str, 12) != 0x10002 || 181 UTF16.charAt(str, 13) != 0x10002) 182 { 183 errln("FAIL Getting character from string error" ); 184 } 185 char array[] = str.toCharArray(); 186 int start = 0; 187 int limit = str.length(); 188 if (UTF16.charAt(array, start, limit, 0) != '1' || 189 UTF16.charAt(array, start, limit, 2) != '3' || 190 UTF16.charAt(array, start, limit, 5) != 0x10001 || 191 UTF16.charAt(array, start, limit, 6) != 0x10001 || 192 UTF16.charAt(array, start, limit, 12) != 0x10002 || 193 UTF16.charAt(array, start, limit, 13) != 0x10002) { 194 errln("FAIL Getting character from array error" ); 195 } 196 // check the sub array here. 197 start = 6; 198 limit = 13; 199 try { 200 UTF16.charAt(array, start, limit, -1); 201 errln("FAIL out of bounds error expected"); 202 } catch (Exception e) { 203 System.out.print(""); 204 } 205 try { 206 UTF16.charAt(array, start, limit, 8); 207 errln("FAIL out of bounds error expected"); 208 } catch (Exception e) { 209 System.out.print(""); 210 } 211 if (UTF16.charAt(array, start, limit, 0) != 0xdc01) { 212 errln("FAIL Expected result in subarray 0xdc01"); 213 } 214 if (UTF16.charAt(array, start, limit, 6) != 0xd800) { 215 errln("FAIL Expected result in subarray 0xd800"); 216 } 217 ReplaceableString replaceable = new ReplaceableString(str); 218 if (UTF16.charAt(replaceable, 0) != '1' || 219 UTF16.charAt(replaceable, 2) != '3' || 220 UTF16.charAt(replaceable, 5) != 0x10001 || 221 UTF16.charAt(replaceable, 6) != 0x10001 || 222 UTF16.charAt(replaceable, 12) != 0x10002 || 223 UTF16.charAt(replaceable, 13) != 0x10002) { 224 errln("FAIL Getting character from replaceable error" ); 225 } 226 227 StringBuffer strbuffer = new StringBuffer("0xD805"); 228 UTF16.charAt((CharSequence)strbuffer, 0); 229 } 230 231 /** 232 * Testing UTF16 class methods countCodePoint 233 */ TestCountCodePoint()234 public void TestCountCodePoint() 235 { 236 StringBuffer strbuff = new StringBuffer(""); 237 char array[] = null; 238 if (UTF16.countCodePoint(strbuff) != 0 || 239 UTF16.countCodePoint("") != 0 || 240 UTF16.countCodePoint(array,0 ,0) != 0) { 241 errln("FAIL Counting code points for empty strings"); 242 } 243 244 strbuff = new StringBuffer("this is a string "); 245 String str = strbuff.toString(); 246 array = str.toCharArray(); 247 int size = str.length(); 248 249 if (UTF16.countCodePoint(array, 0, 0) != 0) { 250 errln("FAIL Counting code points for 0 offset array"); 251 } 252 253 if (UTF16.countCodePoint(str) != size || 254 UTF16.countCodePoint(strbuff) != size || 255 UTF16.countCodePoint(array, 0, size) != size) { 256 errln("FAIL Counting code points"); 257 } 258 259 UTF16.append(strbuff, 0x10000); 260 str = strbuff.toString(); 261 array = str.toCharArray(); 262 if (UTF16.countCodePoint(str) != size + 1 || 263 UTF16.countCodePoint(strbuff) != size + 1 || 264 UTF16.countCodePoint(array, 0, size + 1) != size + 1 || 265 UTF16.countCodePoint(array, 0, size + 2) != size + 1) { 266 errln("FAIL Counting code points"); 267 } 268 UTF16.append(strbuff, 0x61); 269 str = strbuff.toString(); 270 array = str.toCharArray(); 271 if (UTF16.countCodePoint(str) != size + 2 || 272 UTF16.countCodePoint(strbuff) != size + 2 || 273 UTF16.countCodePoint(array, 0, size + 1) != size + 1 || 274 UTF16.countCodePoint(array, 0, size + 2) != size + 1 || 275 UTF16.countCodePoint(array, 0, size + 3) != size + 2) { 276 errln("FAIL Counting code points"); 277 } 278 } 279 280 /** 281 * Testing UTF16 class methods delete 282 */ TestDelete()283 public void TestDelete() 284 { //01234567890123456 285 StringBuffer strbuff = new StringBuffer("these are strings"); 286 int size = strbuff.length(); 287 char array[] = strbuff.toString().toCharArray(); 288 289 UTF16.delete(strbuff, 3); 290 UTF16.delete(strbuff, 3); 291 UTF16.delete(strbuff, 3); 292 UTF16.delete(strbuff, 3); 293 UTF16.delete(strbuff, 3); 294 UTF16.delete(strbuff, 3); 295 try { 296 UTF16.delete(strbuff, strbuff.length()); 297 errln("FAIL deleting out of bounds character should fail"); 298 } catch (Exception e) { 299 System.out.print(""); 300 } 301 UTF16.delete(strbuff, strbuff.length() - 1); 302 if (!strbuff.toString().equals("the string")) { 303 errln("FAIL expected result after deleting characters is " + 304 "\"the string\""); 305 } 306 307 size = UTF16.delete(array, size, 3); 308 size = UTF16.delete(array, size, 3); 309 size = UTF16.delete(array, size, 3); 310 size = UTF16.delete(array, size, 3); 311 size = UTF16.delete(array, size, 3); 312 size = UTF16.delete(array, size, 3); 313 try { 314 UTF16.delete(array, size, size); 315 errln("FAIL deleting out of bounds character should fail"); 316 } catch (Exception e) { 317 System.out.print(""); 318 } 319 size = UTF16.delete(array, size, size - 1); 320 String str = new String(array, 0, size); 321 if (!str.equals("the string")) { 322 errln("FAIL expected result after deleting characters is " + 323 "\"the string\""); 324 } 325 //012345678 9 01 2 3 4 326 strbuff = new StringBuffer("string: \ud800\udc00 \ud801\udc01 \ud801\udc01"); 327 size = strbuff.length(); 328 array = strbuff.toString().toCharArray(); 329 330 UTF16.delete(strbuff, 8); 331 UTF16.delete(strbuff, 8); 332 UTF16.delete(strbuff, 9); 333 UTF16.delete(strbuff, 8); 334 UTF16.delete(strbuff, 9); 335 UTF16.delete(strbuff, 6); 336 UTF16.delete(strbuff, 6); 337 if (!strbuff.toString().equals("string")) { 338 errln("FAIL expected result after deleting characters is \"string\""); 339 } 340 341 size = UTF16.delete(array, size, 8); 342 size = UTF16.delete(array, size, 8); 343 size = UTF16.delete(array, size, 9); 344 size = UTF16.delete(array, size, 8); 345 size = UTF16.delete(array, size, 9); 346 size = UTF16.delete(array, size, 6); 347 size = UTF16.delete(array, size, 6); 348 str = new String(array, 0, size); 349 if (!str.equals("string")) { 350 errln("FAIL expected result after deleting characters is \"string\""); 351 } 352 } 353 354 /** 355 * Testing findOffsetFromCodePoint and findCodePointOffset 356 */ TestfindOffset()357 public void TestfindOffset() 358 { 359 // jitterbug 47 360 String str = "a\uD800\uDC00b"; 361 StringBuffer strbuff = new StringBuffer(str); 362 char array[] = str.toCharArray(); 363 int limit = str.length(); 364 if (UTF16.findCodePointOffset(str, 0) != 0 || 365 UTF16.findOffsetFromCodePoint(str, 0) != 0 || 366 UTF16.findCodePointOffset(strbuff, 0) != 0 || 367 UTF16.findOffsetFromCodePoint(strbuff, 0) != 0 || 368 UTF16.findCodePointOffset(array, 0, limit, 0) != 0 || 369 UTF16.findOffsetFromCodePoint(array, 0, limit, 0) != 0) { 370 errln("FAIL Getting the first codepoint offset to a string with " + 371 "supplementary characters"); 372 } 373 if (UTF16.findCodePointOffset(str, 1) != 1 || 374 UTF16.findOffsetFromCodePoint(str, 1) != 1 || 375 UTF16.findCodePointOffset(strbuff, 1) != 1 || 376 UTF16.findOffsetFromCodePoint(strbuff, 1) != 1 || 377 UTF16.findCodePointOffset(array, 0, limit, 1) != 1 || 378 UTF16.findOffsetFromCodePoint(array, 0, limit, 1) != 1) { 379 errln("FAIL Getting the second codepoint offset to a string with " + 380 "supplementary characters"); 381 } 382 if (UTF16.findCodePointOffset(str, 2) != 1 || 383 UTF16.findOffsetFromCodePoint(str, 2) != 3 || 384 UTF16.findCodePointOffset(strbuff, 2) != 1 || 385 UTF16.findOffsetFromCodePoint(strbuff, 2) != 3 || 386 UTF16.findCodePointOffset(array, 0, limit, 2) != 1 || 387 UTF16.findOffsetFromCodePoint(array, 0, limit, 2) != 3) { 388 errln("FAIL Getting the third codepoint offset to a string with " + 389 "supplementary characters"); 390 } 391 if (UTF16.findCodePointOffset(str, 3) != 2 || 392 UTF16.findOffsetFromCodePoint(str, 3) != 4 || 393 UTF16.findCodePointOffset(strbuff, 3) != 2 || 394 UTF16.findOffsetFromCodePoint(strbuff, 3) != 4 || 395 UTF16.findCodePointOffset(array, 0, limit, 3) != 2 || 396 UTF16.findOffsetFromCodePoint(array, 0, limit, 3) != 4) { 397 errln("FAIL Getting the last codepoint offset to a string with " + 398 "supplementary characters"); 399 } 400 if (UTF16.findCodePointOffset(str, 4) != 3 || 401 UTF16.findCodePointOffset(strbuff, 4) != 3 || 402 UTF16.findCodePointOffset(array, 0, limit, 4) != 3) { 403 errln("FAIL Getting the length offset to a string with " + 404 "supplementary characters"); 405 } 406 try { 407 UTF16.findCodePointOffset(str, 5); 408 errln("FAIL Getting the a non-existence codepoint to a string " + 409 "with supplementary characters"); 410 } catch (Exception e) { 411 // this is a success 412 logln("Passed out of bounds codepoint offset"); 413 } 414 try { 415 UTF16.findOffsetFromCodePoint(str, 4); 416 errln("FAIL Getting the a non-existence codepoint to a string " + 417 "with supplementary characters"); 418 } catch (Exception e) { 419 // this is a success 420 logln("Passed out of bounds codepoint offset"); 421 } 422 try { 423 UTF16.findCodePointOffset(strbuff, 5); 424 errln("FAIL Getting the a non-existence codepoint to a string " + 425 "with supplementary characters"); 426 } catch (Exception e) { 427 // this is a success 428 logln("Passed out of bounds codepoint offset"); 429 } 430 try { 431 UTF16.findOffsetFromCodePoint(strbuff, 4); 432 errln("FAIL Getting the a non-existence codepoint to a string " + 433 "with supplementary characters"); 434 } catch (Exception e) { 435 // this is a success 436 logln("Passed out of bounds codepoint offset"); 437 } 438 try { 439 UTF16.findCodePointOffset(array, 0, limit, 5); 440 errln("FAIL Getting the a non-existence codepoint to a string " + 441 "with supplementary characters"); 442 } catch (Exception e) { 443 // this is a success 444 logln("Passed out of bounds codepoint offset"); 445 } 446 try { 447 UTF16.findOffsetFromCodePoint(array, 0, limit, 4); 448 errln("FAIL Getting the a non-existence codepoint to a string " + 449 "with supplementary characters"); 450 } catch (Exception e) { 451 // this is a success 452 logln("Passed out of bounds codepoint offset"); 453 } 454 455 if (UTF16.findCodePointOffset(array, 1, 3, 0) != 0 || 456 UTF16.findOffsetFromCodePoint(array, 1, 3, 0) != 0 || 457 UTF16.findCodePointOffset(array, 1, 3, 1) != 0 || 458 UTF16.findCodePointOffset(array, 1, 3, 2) != 1 || 459 UTF16.findOffsetFromCodePoint(array, 1, 3, 1) != 2) { 460 errln("FAIL Getting valid codepoint offset in sub array"); 461 } 462 } 463 464 /** 465 * Testing UTF16 class methods getCharCount, *Surrogate 466 */ TestGetCharCountSurrogate()467 public void TestGetCharCountSurrogate() 468 { 469 if (UTF16.getCharCount(0x61) != 1 || 470 UTF16.getCharCount(0x10000) != 2) { 471 errln("FAIL getCharCount result failure"); 472 } 473 if (UTF16.getLeadSurrogate(0x61) != 0 || 474 UTF16.getTrailSurrogate(0x61) != 0x61 || 475 UTF16.isLeadSurrogate((char)0x61) || 476 UTF16.isTrailSurrogate((char)0x61) || 477 UTF16.getLeadSurrogate(0x10000) != 0xd800 || 478 UTF16.getTrailSurrogate(0x10000) != 0xdc00 || 479 UTF16.isLeadSurrogate((char)0xd800) != true || 480 UTF16.isTrailSurrogate((char)0xd800) || 481 UTF16.isLeadSurrogate((char)0xdc00) || 482 UTF16.isTrailSurrogate((char)0xdc00) != true) { 483 errln("FAIL *Surrogate result failure"); 484 } 485 486 if (UTF16.isSurrogate((char)0x61) || !UTF16.isSurrogate((char)0xd800) 487 || !UTF16.isSurrogate((char)0xdc00)) { 488 errln("FAIL isSurrogate result failure"); 489 } 490 } 491 492 /** 493 * Testing UTF16 class method insert 494 */ TestInsert()495 public void TestInsert() 496 { 497 StringBuffer strbuff = new StringBuffer("0123456789"); 498 char array[] = new char[128]; 499 int srcEnd = strbuff.length(); 500 if (0 != srcEnd) { 501 strbuff.getChars(0, srcEnd, array, 0); 502 } 503 int length = 10; 504 UTF16.insert(strbuff, 5, 't'); 505 UTF16.insert(strbuff, 5, 's'); 506 UTF16.insert(strbuff, 5, 'e'); 507 UTF16.insert(strbuff, 5, 't'); 508 if (!(strbuff.toString().equals("01234test56789"))) { 509 errln("FAIL inserting \"test\""); 510 } 511 length = UTF16.insert(array, length, 5, 't'); 512 length = UTF16.insert(array, length, 5, 's'); 513 length = UTF16.insert(array, length, 5, 'e'); 514 length = UTF16.insert(array, length, 5, 't'); 515 String str = new String(array, 0, length); 516 if (!(str.equals("01234test56789"))) { 517 errln("FAIL inserting \"test\""); 518 } 519 UTF16.insert(strbuff, 0, 0x10000); 520 UTF16.insert(strbuff, 11, 0x10000); 521 UTF16.insert(strbuff, strbuff.length(), 0x10000); 522 if (!(strbuff.toString().equals( 523 "\ud800\udc0001234test\ud800\udc0056789\ud800\udc00"))) { 524 errln("FAIL inserting supplementary characters"); 525 } 526 length = UTF16.insert(array, length, 0, 0x10000); 527 length = UTF16.insert(array, length, 11, 0x10000); 528 length = UTF16.insert(array, length, length, 0x10000); 529 str = new String(array, 0, length); 530 if (!(str.equals( 531 "\ud800\udc0001234test\ud800\udc0056789\ud800\udc00"))) { 532 errln("FAIL inserting supplementary characters"); 533 } 534 535 try { 536 UTF16.insert(strbuff, -1, 0); 537 errln("FAIL invalid insertion offset"); 538 } catch (Exception e) { 539 System.out.print(""); 540 } 541 try { 542 UTF16.insert(strbuff, 64, 0); 543 errln("FAIL invalid insertion offset"); 544 } catch (Exception e) { 545 System.out.print(""); 546 } 547 try { 548 UTF16.insert(array, length, -1, 0); 549 errln("FAIL invalid insertion offset"); 550 } catch (Exception e) { 551 System.out.print(""); 552 } 553 try { 554 UTF16.insert(array, length, 64, 0); 555 errln("FAIL invalid insertion offset"); 556 } catch (Exception e) { 557 System.out.print(""); 558 } 559 try { 560 // exceeded array size 561 UTF16.insert(array, array.length, 64, 0); 562 errln("FAIL invalid insertion offset"); 563 } catch (Exception e) { 564 System.out.print(""); 565 } 566 } 567 568 /* 569 * Testing moveCodePointOffset APIs 570 */ 571 572 // 573 // checkMoveCodePointOffset 574 // Run a single test case through each of the moveCodePointOffset() functions. 575 // Parameters - 576 // s The string to work in. 577 // startIdx The starting position within the string. 578 // amount The number of code points to move. 579 // expectedResult The string index after the move, or -1 if the 580 // function should throw an exception. checkMoveCodePointOffset(String s, int startIdx, int amount, int expectedResult)581 private void checkMoveCodePointOffset(String s, int startIdx, int amount, int expectedResult) { 582 // Test with the String flavor of moveCodePointOffset 583 try { 584 int result = UTF16.moveCodePointOffset(s, startIdx, amount); 585 if (result != expectedResult) { 586 errln("FAIL: UTF16.moveCodePointOffset(String \"" + s + "\", " + startIdx + ", " + amount + ")" + 587 " returned " + result + ", expected result was " + 588 (expectedResult==-1 ? "exception" : Integer.toString(expectedResult))); 589 } 590 } 591 catch (IndexOutOfBoundsException e) { 592 if (expectedResult != -1) { 593 errln("FAIL: UTF16.moveCodePointOffset(String \"" + s + "\", " + startIdx + ", " + amount + ")" + 594 " returned exception" + ", expected result was " + expectedResult); 595 } 596 } 597 598 // Test with the StringBuffer flavor of moveCodePointOffset 599 StringBuffer sb = new StringBuffer(s); 600 try { 601 int result = UTF16.moveCodePointOffset(sb, startIdx, amount); 602 if (result != expectedResult) { 603 errln("FAIL: UTF16.moveCodePointOffset(StringBuffer \"" + s + "\", " + startIdx + ", " + amount + ")" + 604 " returned " + result + ", expected result was " + 605 (expectedResult==-1 ? "exception" : Integer.toString(expectedResult))); 606 } 607 } 608 catch (IndexOutOfBoundsException e) { 609 if (expectedResult != -1) { 610 errln("FAIL: UTF16.moveCodePointOffset(StringBuffer \"" + s + "\", " + startIdx + ", " + amount + ")" + 611 " returned exception" + ", expected result was " + expectedResult); 612 } 613 } 614 615 // Test with the char[] flavor of moveCodePointOffset 616 char ca[] = s.toCharArray(); 617 try { 618 int result = UTF16.moveCodePointOffset(ca, 0, s.length(), startIdx, amount); 619 if (result != expectedResult) { 620 errln("FAIL: UTF16.moveCodePointOffset(char[] \"" + s + "\", 0, " + s.length() 621 + ", " + startIdx + ", " + amount + ")" + 622 " returned " + result + ", expected result was " + 623 (expectedResult==-1 ? "exception" : Integer.toString(expectedResult))); 624 } 625 } 626 catch (IndexOutOfBoundsException e) { 627 if (expectedResult != -1) { 628 errln("FAIL: UTF16.moveCodePointOffset(char[] \"" + s + "\", 0, " + s.length() 629 + ", " + startIdx + ", " + amount + ")" + 630 " returned exception" + ", expected result was " + expectedResult); 631 } 632 } 633 634 // Put the test string into the interior of a char array, 635 // run test on the subsection of the array. 636 char ca2[] = new char[s.length()+2]; 637 ca2[0] = (char)0xd800; 638 ca2[s.length()+1] = (char)0xd8ff; 639 s.getChars(0, s.length(), ca2, 1); 640 try { 641 int result = UTF16.moveCodePointOffset(ca2, 1, s.length()+1, startIdx, amount); 642 if (result != expectedResult) { 643 errln("UTF16.moveCodePointOffset(char[] \"" + "." + s + ".\", 1, " + (s.length()+1) 644 + ", " + startIdx + ", " + amount + ")" + 645 " returned " + result + ", expected result was " + 646 (expectedResult==-1 ? "exception" : Integer.toString(expectedResult))); 647 } 648 } 649 catch (IndexOutOfBoundsException e) { 650 if (expectedResult != -1) { 651 errln("UTF16.moveCodePointOffset(char[] \"" + "." + s + ".\", 1, " + (s.length()+1) 652 + ", " + startIdx + ", " + amount + ")" + 653 " returned exception" + ", expected result was " + expectedResult); 654 } 655 } 656 657 } 658 659 TestMoveCodePointOffset()660 public void TestMoveCodePointOffset() 661 { 662 // checkMoveCodePointOffset(String, startIndex, amount, expected ); expected=-1 for exception. 663 664 // No Supplementary chars 665 checkMoveCodePointOffset("abc", 1, 1, 2); 666 checkMoveCodePointOffset("abc", 1, -1, 0); 667 checkMoveCodePointOffset("abc", 1, -2, -1); 668 checkMoveCodePointOffset("abc", 1, 2, 3); 669 checkMoveCodePointOffset("abc", 1, 3, -1); 670 checkMoveCodePointOffset("abc", 1, 0, 1); 671 672 checkMoveCodePointOffset("abc", 3, 0, 3); 673 checkMoveCodePointOffset("abc", 4, 0, -1); 674 checkMoveCodePointOffset("abc", 0, 0, 0); 675 checkMoveCodePointOffset("abc", -1, 0, -1); 676 677 checkMoveCodePointOffset("", 0, 0, 0); 678 checkMoveCodePointOffset("", 0, -1, -1); 679 checkMoveCodePointOffset("", 0, 1, -1); 680 681 checkMoveCodePointOffset("a", 0, 0, 0); 682 checkMoveCodePointOffset("a", 1, 0, 1); 683 checkMoveCodePointOffset("a", 0, 1, 1); 684 checkMoveCodePointOffset("a", 1, -1, 0); 685 686 687 // Supplementary in middle of string 688 checkMoveCodePointOffset("a\ud800\udc00b", 0, 1, 1); 689 checkMoveCodePointOffset("a\ud800\udc00b", 0, 2, 3); 690 checkMoveCodePointOffset("a\ud800\udc00b", 0, 3, 4); 691 checkMoveCodePointOffset("a\ud800\udc00b", 0, 4, -1); 692 693 checkMoveCodePointOffset("a\ud800\udc00b", 4, -1, 3); 694 checkMoveCodePointOffset("a\ud800\udc00b", 4, -2, 1); 695 checkMoveCodePointOffset("a\ud800\udc00b", 4, -3, 0); 696 checkMoveCodePointOffset("a\ud800\udc00b", 4, -4, -1); 697 698 // Supplementary at start of string 699 checkMoveCodePointOffset("\ud800\udc00ab", 0, 1, 2); 700 checkMoveCodePointOffset("\ud800\udc00ab", 1, 1, 2); 701 checkMoveCodePointOffset("\ud800\udc00ab", 2, 1, 3); 702 checkMoveCodePointOffset("\ud800\udc00ab", 2, -1, 0); 703 checkMoveCodePointOffset("\ud800\udc00ab", 1, -1, 0); 704 checkMoveCodePointOffset("\ud800\udc00ab", 0, -1, -1); 705 706 707 // Supplementary at end of string 708 checkMoveCodePointOffset("ab\ud800\udc00", 1, 1, 2); 709 checkMoveCodePointOffset("ab\ud800\udc00", 2, 1, 4); 710 checkMoveCodePointOffset("ab\ud800\udc00", 3, 1, 4); 711 checkMoveCodePointOffset("ab\ud800\udc00", 4, 1, -1); 712 713 checkMoveCodePointOffset("ab\ud800\udc00", 5, -2, -1); 714 checkMoveCodePointOffset("ab\ud800\udc00", 4, -1, 2); 715 checkMoveCodePointOffset("ab\ud800\udc00", 3, -1, 2); 716 checkMoveCodePointOffset("ab\ud800\udc00", 2, -1, 1); 717 checkMoveCodePointOffset("ab\ud800\udc00", 1, -1, 0); 718 719 // Unpaired surrogate in middle 720 checkMoveCodePointOffset("a\ud800b", 0, 1, 1); 721 checkMoveCodePointOffset("a\ud800b", 1, 1, 2); 722 checkMoveCodePointOffset("a\ud800b", 2, 1, 3); 723 724 checkMoveCodePointOffset("a\udc00b", 0, 1, 1); 725 checkMoveCodePointOffset("a\udc00b", 1, 1, 2); 726 checkMoveCodePointOffset("a\udc00b", 2, 1, 3); 727 728 checkMoveCodePointOffset("a\udc00\ud800b", 0, 1, 1); 729 checkMoveCodePointOffset("a\udc00\ud800b", 1, 1, 2); 730 checkMoveCodePointOffset("a\udc00\ud800b", 2, 1, 3); 731 checkMoveCodePointOffset("a\udc00\ud800b", 3, 1, 4); 732 733 checkMoveCodePointOffset("a\ud800b", 1, -1, 0); 734 checkMoveCodePointOffset("a\ud800b", 2, -1, 1); 735 checkMoveCodePointOffset("a\ud800b", 3, -1, 2); 736 737 checkMoveCodePointOffset("a\udc00b", 1, -1, 0); 738 checkMoveCodePointOffset("a\udc00b", 2, -1, 1); 739 checkMoveCodePointOffset("a\udc00b", 3, -1, 2); 740 741 checkMoveCodePointOffset("a\udc00\ud800b", 1, -1, 0); 742 checkMoveCodePointOffset("a\udc00\ud800b", 2, -1, 1); 743 checkMoveCodePointOffset("a\udc00\ud800b", 3, -1, 2); 744 checkMoveCodePointOffset("a\udc00\ud800b", 4, -1, 3); 745 746 // Unpaired surrogate at start 747 checkMoveCodePointOffset("\udc00ab", 0, 1, 1); 748 checkMoveCodePointOffset("\ud800ab", 0, 2, 2); 749 checkMoveCodePointOffset("\ud800\ud800ab", 0, 3, 3); 750 checkMoveCodePointOffset("\udc00\udc00ab", 0, 4, 4); 751 752 checkMoveCodePointOffset("\udc00ab", 2, -1, 1); 753 checkMoveCodePointOffset("\ud800ab", 1, -1, 0); 754 checkMoveCodePointOffset("\ud800ab", 1, -2, -1); 755 checkMoveCodePointOffset("\ud800\ud800ab", 2, -1, 1); 756 checkMoveCodePointOffset("\udc00\udc00ab", 2, -2, 0); 757 checkMoveCodePointOffset("\udc00\udc00ab", 2, -3, -1); 758 759 // Unpaired surrogate at end 760 checkMoveCodePointOffset("ab\udc00\udc00ab", 3, 1, 4); 761 checkMoveCodePointOffset("ab\udc00\udc00ab", 2, 1, 3); 762 checkMoveCodePointOffset("ab\udc00\udc00ab", 1, 1, 2); 763 764 checkMoveCodePointOffset("ab\udc00\udc00ab", 4, -1, 3); 765 checkMoveCodePointOffset("ab\udc00\udc00ab", 3, -1, 2); 766 checkMoveCodePointOffset("ab\udc00\udc00ab", 2, -1, 1); 767 768 769 //01234567890 1 2 3 45678901234 770 String str = new String("0123456789\ud800\udc00\ud801\udc010123456789"); 771 int move1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 772 12, 12, 14, 14, 15, 16, 17, 18, 19, 20, 773 21, 22, 23, 24}; 774 int move2[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 775 14, 14, 15, 15, 16, 17, 18, 19, 20, 21, 776 22, 23, 24, -1}; 777 int move3[] = { 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 778 15, 15, 16, 16, 17, 18, 19, 20, 21, 22, 779 23, 24, -1, -1}; 780 int size = str.length(); 781 for (int i = 0; i < size; i ++) { 782 checkMoveCodePointOffset(str, i, 1, move1[i]); 783 checkMoveCodePointOffset(str, i, 2, move2[i]); 784 checkMoveCodePointOffset(str, i, 3, move3[i]); 785 } 786 787 char strarray[] = str.toCharArray(); 788 if (UTF16.moveCodePointOffset(strarray, 9, 13, 0, 2) != 3) { 789 errln("FAIL: Moving offset 0 by 2 codepoint in subarray [9, 13] " + 790 "expected result 3"); 791 } 792 if (UTF16.moveCodePointOffset(strarray, 9, 13, 1, 2) != 4) { 793 errln("FAIL: Moving offset 1 by 2 codepoint in subarray [9, 13] " + 794 "expected result 4"); 795 } 796 if (UTF16.moveCodePointOffset(strarray, 11, 14, 0, 2) != 3) { 797 errln("FAIL: Moving offset 0 by 2 codepoint in subarray [11, 14] " 798 + "expected result 3"); 799 } 800 } 801 802 /** 803 * Testing UTF16 class methods setCharAt 804 */ TestSetCharAt()805 public void TestSetCharAt() 806 { 807 StringBuffer strbuff = new StringBuffer("012345"); 808 char array[] = new char[128]; 809 int srcEnd = strbuff.length(); 810 if (0 != srcEnd) { 811 strbuff.getChars(0, srcEnd, array, 0); 812 } 813 int length = 6; 814 for (int i = 0; i < length; i ++) { 815 UTF16.setCharAt(strbuff, i, '0'); 816 UTF16.setCharAt(array, length, i, '0'); 817 } 818 String str = new String(array, 0, length); 819 if (!(strbuff.toString().equals("000000")) || 820 !(str.equals("000000"))) { 821 errln("FAIL: setChar to '0' failed"); 822 } 823 UTF16.setCharAt(strbuff, 0, 0x10000); 824 UTF16.setCharAt(strbuff, 4, 0x10000); 825 UTF16.setCharAt(strbuff, 7, 0x10000); 826 if (!(strbuff.toString().equals( 827 "\ud800\udc0000\ud800\udc000\ud800\udc00"))) { 828 errln("FAIL: setChar to 0x10000 failed"); 829 } 830 length = UTF16.setCharAt(array, length, 0, 0x10000); 831 length = UTF16.setCharAt(array, length, 4, 0x10000); 832 length = UTF16.setCharAt(array, length, 7, 0x10000); 833 str = new String(array, 0, length); 834 if (!(str.equals("\ud800\udc0000\ud800\udc000\ud800\udc00"))) { 835 errln("FAIL: setChar to 0x10000 failed"); 836 } 837 UTF16.setCharAt(strbuff, 0, '0'); 838 UTF16.setCharAt(strbuff, 1, '1'); 839 UTF16.setCharAt(strbuff, 2, '2'); 840 UTF16.setCharAt(strbuff, 4, '3'); 841 UTF16.setCharAt(strbuff, 4, '4'); 842 UTF16.setCharAt(strbuff, 5, '5'); 843 if (!strbuff.toString().equals("012345")) { 844 errln("Fail converting supplementaries in StringBuffer to BMP " + 845 "characters"); 846 } 847 length = UTF16.setCharAt(array, length, 0, '0'); 848 length = UTF16.setCharAt(array, length, 1, '1'); 849 length = UTF16.setCharAt(array, length, 2, '2'); 850 length = UTF16.setCharAt(array, length, 4, '3'); 851 length = UTF16.setCharAt(array, length, 4, '4'); 852 length = UTF16.setCharAt(array, length, 5, '5'); 853 str = new String(array, 0, length); 854 if (!str.equals("012345")) { 855 errln("Fail converting supplementaries in array to BMP " + 856 "characters"); 857 } 858 try { 859 UTF16.setCharAt(strbuff, -1, 0); 860 errln("FAIL: setting character at invalid offset"); 861 } catch (Exception e) { 862 System.out.print(""); 863 } 864 try { 865 UTF16.setCharAt(array, length, -1, 0); 866 errln("FAIL: setting character at invalid offset"); 867 } catch (Exception e) { 868 System.out.print(""); 869 } 870 try { 871 UTF16.setCharAt(strbuff, length, 0); 872 errln("FAIL: setting character at invalid offset"); 873 } catch (Exception e) { 874 System.out.print(""); 875 } 876 try { 877 UTF16.setCharAt(array, length, length, 0); 878 errln("FAIL: setting character at invalid offset"); 879 } catch (Exception e) { 880 System.out.print(""); 881 } 882 } 883 884 /** 885 * Testing UTF16 valueof APIs 886 */ TestValueOf()887 public void TestValueOf() 888 { 889 if(UCharacter.getCodePoint('\ud800','\udc00')!=0x10000){ 890 errln("FAIL: getCodePoint('\ud800','\udc00')"); 891 } 892 if (!UTF16.valueOf(0x61).equals("a") || 893 !UTF16.valueOf(0x10000).equals("\ud800\udc00")) { 894 errln("FAIL: valueof(char32)"); 895 } 896 String str = new String("01234\ud800\udc0056789"); 897 StringBuffer strbuff = new StringBuffer(str); 898 char array[] = str.toCharArray(); 899 int length = str.length(); 900 901 String expected[] = {"0", "1", "2", "3", "4", "\ud800\udc00", 902 "\ud800\udc00", "5", "6", "7", "8", "9"}; 903 for (int i = 0; i < length; i ++) { 904 if (!UTF16.valueOf(str, i).equals(expected[i]) || 905 !UTF16.valueOf(strbuff, i).equals(expected[i]) || 906 !UTF16.valueOf(array, 0, length, i).equals(expected[i])) { 907 errln("FAIL: valueOf() expected " + expected[i]); 908 } 909 } 910 try { 911 UTF16.valueOf(str, -1); 912 errln("FAIL: out of bounds error expected"); 913 } catch (Exception e) { 914 System.out.print(""); 915 } 916 try { 917 UTF16.valueOf(strbuff, -1); 918 errln("FAIL: out of bounds error expected"); 919 } catch (Exception e) { 920 System.out.print(""); 921 } 922 try { 923 UTF16.valueOf(array, 0, length, -1); 924 errln("FAIL: out of bounds error expected"); 925 } catch (Exception e) { 926 System.out.print(""); 927 } 928 try { 929 UTF16.valueOf(str, length); 930 errln("FAIL: out of bounds error expected"); 931 } catch (Exception e) { 932 System.out.print(""); 933 } 934 try { 935 UTF16.valueOf(strbuff, length); 936 errln("FAIL: out of bounds error expected"); 937 } catch (Exception e) { 938 System.out.print(""); 939 } 940 try { 941 UTF16.valueOf(array, 0, length, length); 942 errln("FAIL: out of bounds error expected"); 943 } catch (Exception e) { 944 System.out.print(""); 945 } 946 if (!UTF16.valueOf(array, 6, length, 0).equals("\udc00") || 947 !UTF16.valueOf(array, 0, 6, 5).equals("\ud800")) { 948 errln("FAIL: error getting partial supplementary character"); 949 } 950 try { 951 UTF16.valueOf(array, 3, 5, -1); 952 errln("FAIL: out of bounds error expected"); 953 } catch (Exception e) { 954 System.out.print(""); 955 } 956 try { 957 UTF16.valueOf(array, 3, 5, 3); 958 errln("FAIL: out of bounds error expected"); 959 } catch (Exception e) { 960 System.out.print(""); 961 } 962 } 963 TestIndexOf()964 public void TestIndexOf() 965 { 966 //012345678901234567890123456789012345 967 String test1 = "test test ttest tetest testesteststt"; 968 String test2 = "test"; 969 int testChar1 = 0x74; 970 int testChar2 = 0x20402; 971 // int testChar3 = 0xdc02; 972 // int testChar4 = 0xd841; 973 String test3 = "\ud841\udc02\u0071\udc02\ud841\u0071\ud841\udc02\u0071\u0072\ud841\udc02\u0071\ud841\udc02\u0071\udc02\ud841\u0073"; 974 String test4 = UCharacter.toString(testChar2); 975 976 if (UTF16.indexOf(test1, test2) != 0 || 977 UTF16.indexOf(test1, test2, 0) != 0) { 978 errln("indexOf failed: expected to find '" + test2 + 979 "' at position 0 in text '" + test1 + "'"); 980 } 981 if (UTF16.indexOf(test1, testChar1) != 0 || 982 UTF16.indexOf(test1, testChar1, 0) != 0) { 983 errln("indexOf failed: expected to find 0x" + 984 Integer.toHexString(testChar1) + 985 " at position 0 in text '" + test1 + "'"); 986 } 987 if (UTF16.indexOf(test3, testChar2) != 0 || 988 UTF16.indexOf(test3, testChar2, 0) != 0) { 989 errln("indexOf failed: expected to find 0x" + 990 Integer.toHexString(testChar2) + 991 " at position 0 in text '" + Utility.hex(test3) + "'"); 992 } 993 String test5 = "\ud841\ud841\udc02"; 994 if (UTF16.indexOf(test5, testChar2) != 1 || 995 UTF16.indexOf(test5, testChar2, 0) != 1) { 996 errln("indexOf failed: expected to find 0x" + 997 Integer.toHexString(testChar2) + 998 " at position 0 in text '" + Utility.hex(test3) + "'"); 999 } 1000 if (UTF16.lastIndexOf(test1, test2) != 29 || 1001 UTF16.lastIndexOf(test1, test2, test1.length()) != 29) { 1002 errln("lastIndexOf failed: expected to find '" + test2 + 1003 "' at position 29 in text '" + test1 + "'"); 1004 } 1005 if (UTF16.lastIndexOf(test1, testChar1) != 35 || 1006 UTF16.lastIndexOf(test1, testChar1, test1.length()) != 35) { 1007 errln("lastIndexOf failed: expected to find 0x" + 1008 Integer.toHexString(testChar1) + 1009 " at position 35 in text '" + test1 + "'"); 1010 } 1011 if (UTF16.lastIndexOf(test3, testChar2) != 13 || 1012 UTF16.lastIndexOf(test3, testChar2, test3.length()) != 13) { 1013 errln("indexOf failed: expected to find 0x" + 1014 Integer.toHexString(testChar2) + 1015 " at position 13 in text '" + Utility.hex(test3) + "'"); 1016 } 1017 int occurrences = 0; 1018 for (int startPos = 0; startPos != -1 && startPos < test1.length();) 1019 { 1020 startPos = UTF16.indexOf(test1, test2, startPos); 1021 if (startPos >= 0) { 1022 ++ occurrences; 1023 startPos += 4; 1024 } 1025 } 1026 if (occurrences != 6) { 1027 errln("indexOf failed: expected to find 6 occurrences, found " 1028 + occurrences); 1029 } 1030 1031 occurrences = 0; 1032 for (int startPos = 10; startPos != -1 && startPos < test1.length();) 1033 { 1034 startPos = UTF16.indexOf(test1, test2, startPos); 1035 if (startPos >= 0) { 1036 ++ occurrences; 1037 startPos += 4; 1038 } 1039 } 1040 if (occurrences != 4) { 1041 errln("indexOf with starting offset failed: expected to find 4 occurrences, found " 1042 + occurrences); 1043 } 1044 1045 occurrences = 0; 1046 for (int startPos = 0; 1047 startPos != -1 && startPos < test3.length();) { 1048 startPos = UTF16.indexOf(test3, test4, startPos); 1049 if (startPos != -1) { 1050 ++ occurrences; 1051 startPos += 2; 1052 } 1053 } 1054 if (occurrences != 4) { 1055 errln("indexOf failed: expected to find 4 occurrences, found " 1056 + occurrences); 1057 } 1058 1059 occurrences = 0; 1060 for (int startPos = 10; 1061 startPos != -1 && startPos < test3.length();) { 1062 startPos = UTF16.indexOf(test3, test4, startPos); 1063 if (startPos != -1) { 1064 ++ occurrences; 1065 startPos += 2; 1066 } 1067 } 1068 if (occurrences != 2) { 1069 errln("indexOf failed: expected to find 2 occurrences, found " 1070 + occurrences); 1071 } 1072 1073 occurrences = 0; 1074 for (int startPos = 0; 1075 startPos != -1 && startPos < test1.length();) { 1076 startPos = UTF16.indexOf(test1, testChar1, startPos); 1077 if (startPos != -1) { 1078 ++ occurrences; 1079 startPos += 1; 1080 } 1081 } 1082 if (occurrences != 16) { 1083 errln("indexOf with character failed: expected to find 16 occurrences, found " 1084 + occurrences); 1085 } 1086 1087 occurrences = 0; 1088 for (int startPos = 10; 1089 startPos != -1 && startPos < test1.length();) { 1090 startPos = UTF16.indexOf(test1, testChar1, startPos); 1091 if (startPos != -1) { 1092 ++ occurrences; 1093 startPos += 1; 1094 } 1095 } 1096 if (occurrences != 12) { 1097 errln("indexOf with character & start offset failed: expected to find 12 occurrences, found " 1098 + occurrences); 1099 } 1100 1101 occurrences = 0; 1102 for (int startPos = 0; 1103 startPos != -1 && startPos < test3.length();) { 1104 startPos = UTF16.indexOf(test3, testChar2, startPos); 1105 if (startPos != -1) { 1106 ++ occurrences; 1107 startPos += 1; 1108 } 1109 } 1110 if (occurrences != 4) { 1111 errln("indexOf failed: expected to find 4 occurrences, found " 1112 + occurrences); 1113 } 1114 1115 occurrences = 0; 1116 for (int startPos = 5; startPos != -1 && startPos < test3.length();) { 1117 startPos = UTF16.indexOf(test3, testChar2, startPos); 1118 if (startPos != -1) { 1119 ++ occurrences; 1120 startPos += 1; 1121 } 1122 } 1123 if (occurrences != 3) { 1124 errln("indexOf with character & start & end offsets failed: expected to find 2 occurrences, found " 1125 + occurrences); 1126 } 1127 occurrences = 0; 1128 for (int startPos = 32; startPos != -1;) { 1129 startPos = UTF16.lastIndexOf(test1, test2, startPos); 1130 if (startPos != -1) { 1131 ++ occurrences; 1132 startPos -= 5; 1133 } 1134 } 1135 if (occurrences != 6) { 1136 errln("lastIndexOf with starting and ending offsets failed: expected to find 4 occurrences, found " 1137 + occurrences); 1138 } 1139 occurrences = 0; 1140 for (int startPos = 32; startPos != -1;) { 1141 startPos = UTF16.lastIndexOf(test1, testChar1, startPos); 1142 if (startPos != -1) { 1143 ++ occurrences; 1144 startPos -= 5; 1145 } 1146 } 1147 if (occurrences != 7) { 1148 errln("lastIndexOf with character & start & end offsets failed: expected to find 11 occurrences, found " 1149 + occurrences); 1150 } 1151 1152 //testing UChar32 1153 occurrences = 0; 1154 for (int startPos = test3.length(); startPos != -1;) { 1155 startPos = UTF16.lastIndexOf(test3, testChar2, startPos - 5); 1156 if (startPos != -1) { 1157 ++ occurrences; 1158 } 1159 } 1160 if (occurrences != 3) { 1161 errln("lastIndexOf with character & start & end offsets failed: expected to find 3 occurrences, found " 1162 + occurrences); 1163 } 1164 1165 // testing supplementary 1166 for (int i = 0; i < INDEXOF_SUPPLEMENTARY_CHAR_.length; i ++) { 1167 int ch = INDEXOF_SUPPLEMENTARY_CHAR_[i]; 1168 for (int j = 0; j < INDEXOF_SUPPLEMENTARY_CHAR_INDEX_[i].length; 1169 j ++) { 1170 int index = 0; 1171 int expected = INDEXOF_SUPPLEMENTARY_CHAR_INDEX_[i][j]; 1172 if (j > 0) { 1173 index = INDEXOF_SUPPLEMENTARY_CHAR_INDEX_[i][j - 1] + 1; 1174 } 1175 if (UTF16.indexOf(INDEXOF_SUPPLEMENTARY_STRING_, ch, index) != 1176 expected || 1177 UTF16.indexOf(INDEXOF_SUPPLEMENTARY_STRING_, 1178 UCharacter.toString(ch), index) != 1179 expected) { 1180 errln("Failed finding index for supplementary 0x" + 1181 Integer.toHexString(ch)); 1182 } 1183 index = INDEXOF_SUPPLEMENTARY_STRING_.length(); 1184 if (j < INDEXOF_SUPPLEMENTARY_CHAR_INDEX_[i].length - 1) { 1185 index = INDEXOF_SUPPLEMENTARY_CHAR_INDEX_[i][j + 1] - 1; 1186 } 1187 if (UTF16.lastIndexOf(INDEXOF_SUPPLEMENTARY_STRING_, ch, 1188 index) != expected || 1189 UTF16.lastIndexOf(INDEXOF_SUPPLEMENTARY_STRING_, 1190 UCharacter.toString(ch), index) 1191 != expected) 1192 { 1193 errln("Failed finding last index for supplementary 0x" + 1194 Integer.toHexString(ch)); 1195 } 1196 } 1197 } 1198 1199 for (int i = 0; i < INDEXOF_SUPPLEMENTARY_STR_INDEX_.length; i ++) { 1200 int index = 0; 1201 int expected = INDEXOF_SUPPLEMENTARY_STR_INDEX_[i]; 1202 if (i > 0) { 1203 index = INDEXOF_SUPPLEMENTARY_STR_INDEX_[i - 1] + 1; 1204 } 1205 if (UTF16.indexOf(INDEXOF_SUPPLEMENTARY_STRING_, 1206 INDEXOF_SUPPLEMENTARY_STR_, index) != expected) { 1207 errln("Failed finding index for supplementary string " + 1208 hex(INDEXOF_SUPPLEMENTARY_STRING_)); 1209 } 1210 index = INDEXOF_SUPPLEMENTARY_STRING_.length(); 1211 if (i < INDEXOF_SUPPLEMENTARY_STR_INDEX_.length - 1) { 1212 index = INDEXOF_SUPPLEMENTARY_STR_INDEX_[i + 1] - 1; 1213 } 1214 if (UTF16.lastIndexOf(INDEXOF_SUPPLEMENTARY_STRING_, 1215 INDEXOF_SUPPLEMENTARY_STR_, index) != expected) { 1216 errln("Failed finding last index for supplementary string " + 1217 hex(INDEXOF_SUPPLEMENTARY_STRING_)); 1218 } 1219 } 1220 } 1221 TestReplace()1222 public void TestReplace() 1223 { 1224 String test1 = "One potato, two potato, three potato, four\n"; 1225 String test2 = "potato"; 1226 String test3 = "MISSISSIPPI"; 1227 1228 String result = UTF16.replace(test1, test2, test3); 1229 String expectedValue = 1230 "One MISSISSIPPI, two MISSISSIPPI, three MISSISSIPPI, four\n"; 1231 if (!result.equals(expectedValue)) { 1232 errln("findAndReplace failed: expected \"" + expectedValue + 1233 "\", got \"" + test1 + "\"."); 1234 } 1235 result = UTF16.replace(test1, test3, test2); 1236 expectedValue = test1; 1237 if (!result.equals(expectedValue)) { 1238 errln("findAndReplace failed: expected \"" + expectedValue + 1239 "\", got \"" + test1 + "\"."); 1240 } 1241 1242 result = UTF16.replace(test1, ',', 'e'); 1243 expectedValue = "One potatoe two potatoe three potatoe four\n"; 1244 if (!result.equals(expectedValue)) { 1245 errln("findAndReplace failed: expected \"" + expectedValue + 1246 "\", got \"" + test1 + "\"."); 1247 } 1248 1249 result = UTF16.replace(test1, ',', 0x10000); 1250 expectedValue = "One potato\ud800\udc00 two potato\ud800\udc00 three potato\ud800\udc00 four\n"; 1251 if (!result.equals(expectedValue)) { 1252 errln("findAndReplace failed: expected \"" + expectedValue + 1253 "\", got \"" + test1 + "\"."); 1254 } 1255 1256 result = UTF16.replace(test1, "potato", "\ud800\udc00\ud801\udc01"); 1257 expectedValue = "One \ud800\udc00\ud801\udc01, two \ud800\udc00\ud801\udc01, three \ud800\udc00\ud801\udc01, four\n"; 1258 if (!result.equals(expectedValue)) { 1259 errln("findAndReplace failed: expected \"" + expectedValue + 1260 "\", got \"" + test1 + "\"."); 1261 } 1262 1263 String test4 = "\ud800\ud800\udc00\ud800\udc00\udc00\ud800\ud800\udc00\ud800\udc00\udc00"; 1264 result = UTF16.replace(test4, 0xd800, 'A'); 1265 expectedValue = "A\ud800\udc00\ud800\udc00\udc00A\ud800\udc00\ud800\udc00\udc00"; 1266 if (!result.equals(expectedValue)) { 1267 errln("findAndReplace failed: expected \"" + expectedValue + 1268 "\", got \"" + test1 + "\"."); 1269 } 1270 1271 result = UTF16.replace(test4, 0xdC00, 'A'); 1272 expectedValue = "\ud800\ud800\udc00\ud800\udc00A\ud800\ud800\udc00\ud800\udc00A"; 1273 if (!result.equals(expectedValue)) { 1274 errln("findAndReplace failed: expected \"" + expectedValue + 1275 "\", got \"" + test1 + "\"."); 1276 } 1277 1278 result = UTF16.replace(test4, 0x10000, 'A'); 1279 expectedValue = "\ud800AA\udc00\ud800AA\udc00"; 1280 if (!result.equals(expectedValue)) { 1281 errln("findAndReplace failed: expected \"" + expectedValue + 1282 "\", got \"" + test1 + "\"."); 1283 } 1284 } 1285 TestReverse()1286 public void TestReverse() 1287 { 1288 StringBuffer test = new StringBuffer( 1289 "backwards words say to used I"); 1290 1291 StringBuffer result = UTF16.reverse(test); 1292 if (!result.toString().equals("I desu ot yas sdrow sdrawkcab")) { 1293 errln("reverse() failed: Expected \"I desu ot yas sdrow sdrawkcab\",\n got \"" 1294 + result + "\""); 1295 } 1296 StringBuffer testbuffer = new StringBuffer(); 1297 UTF16.append(testbuffer, 0x2f999); 1298 UTF16.append(testbuffer, 0x1d15f); 1299 UTF16.append(testbuffer, 0x00c4); 1300 UTF16.append(testbuffer, 0x1ed0); 1301 result = UTF16.reverse(testbuffer); 1302 if (result.charAt(0) != 0x1ed0 || 1303 result.charAt(1) != 0xc4 || 1304 UTF16.charAt(result, 2) != 0x1d15f || 1305 UTF16.charAt(result, 4)!=0x2f999) { 1306 errln("reverse() failed with supplementary characters"); 1307 } 1308 } 1309 1310 /** 1311 * Testing the setter and getter apis for StringComparator 1312 */ TestStringComparator()1313 public void TestStringComparator() 1314 { 1315 UTF16.StringComparator compare = new UTF16.StringComparator(); 1316 if (compare.getCodePointCompare() != false) { 1317 errln("Default string comparator should be code unit compare"); 1318 } 1319 if (compare.getIgnoreCase() != false) { 1320 errln("Default string comparator should be case sensitive compare"); 1321 } 1322 if (compare.getIgnoreCaseOption() 1323 != UTF16.StringComparator.FOLD_CASE_DEFAULT) { 1324 errln("Default string comparator should have fold case default compare"); 1325 } 1326 compare.setCodePointCompare(true); 1327 if (compare.getCodePointCompare() != true) { 1328 errln("Error setting code point compare"); 1329 } 1330 compare.setCodePointCompare(false); 1331 if (compare.getCodePointCompare() != false) { 1332 errln("Error setting code point compare"); 1333 } 1334 compare.setIgnoreCase(true, UTF16.StringComparator.FOLD_CASE_DEFAULT); 1335 if (compare.getIgnoreCase() != true 1336 || compare.getIgnoreCaseOption() 1337 != UTF16.StringComparator.FOLD_CASE_DEFAULT) { 1338 errln("Error setting ignore case and options"); 1339 } 1340 compare.setIgnoreCase(false, UTF16.StringComparator.FOLD_CASE_EXCLUDE_SPECIAL_I); 1341 if (compare.getIgnoreCase() != false 1342 || compare.getIgnoreCaseOption() 1343 != UTF16.StringComparator.FOLD_CASE_EXCLUDE_SPECIAL_I) { 1344 errln("Error setting ignore case and options"); 1345 } 1346 compare.setIgnoreCase(true, UTF16.StringComparator.FOLD_CASE_EXCLUDE_SPECIAL_I); 1347 if (compare.getIgnoreCase() != true 1348 || compare.getIgnoreCaseOption() 1349 != UTF16.StringComparator.FOLD_CASE_EXCLUDE_SPECIAL_I) { 1350 errln("Error setting ignore case and options"); 1351 } 1352 compare.setIgnoreCase(false, UTF16.StringComparator.FOLD_CASE_DEFAULT); 1353 if (compare.getIgnoreCase() != false 1354 || compare.getIgnoreCaseOption() 1355 != UTF16.StringComparator.FOLD_CASE_DEFAULT) { 1356 errln("Error setting ignore case and options"); 1357 } 1358 } 1359 TestCodePointCompare()1360 public void TestCodePointCompare() 1361 { 1362 // these strings are in ascending order 1363 String str[] = {"\u0061", "\u20ac\ud801", "\u20ac\ud800\udc00", 1364 "\ud800", "\ud800\uff61", "\udfff", 1365 "\uff61\udfff", "\uff61\ud800\udc02", "\ud800\udc02", 1366 "\ud84d\udc56"}; 1367 UTF16.StringComparator cpcompare 1368 = new UTF16.StringComparator(true, false, 1369 UTF16.StringComparator.FOLD_CASE_DEFAULT); 1370 UTF16.StringComparator cucompare 1371 = new UTF16.StringComparator(); 1372 for (int i = 0; i < str.length - 1; ++ i) { 1373 if (cpcompare.compare(str[i], str[i + 1]) >= 0) { 1374 errln("error: compare() in code point order fails for string " 1375 + Utility.hex(str[i]) + " and " 1376 + Utility.hex(str[i + 1])); 1377 } 1378 // test code unit compare 1379 if (cucompare.compare(str[i], str[i + 1]) 1380 != str[i].compareTo(str[i + 1])) { 1381 errln("error: compare() in code unit order fails for string " 1382 + Utility.hex(str[i]) + " and " 1383 + Utility.hex(str[i + 1])); 1384 } 1385 } 1386 } 1387 TestCaseCompare()1388 public void TestCaseCompare() 1389 { 1390 String mixed = "\u0061\u0042\u0131\u03a3\u00df\ufb03\ud93f\udfff"; 1391 String otherDefault = "\u0041\u0062\u0131\u03c3\u0073\u0053\u0046\u0066\u0049\ud93f\udfff"; 1392 String otherExcludeSpecialI = "\u0041\u0062\u0131\u03c3\u0053\u0073\u0066\u0046\u0069\ud93f\udfff"; 1393 String different = "\u0041\u0062\u0131\u03c3\u0073\u0053\u0046\u0066\u0049\ud93f\udffd"; 1394 1395 UTF16.StringComparator compare = new UTF16.StringComparator(); 1396 compare.setIgnoreCase(true, UTF16.StringComparator.FOLD_CASE_DEFAULT); 1397 // test u_strcasecmp() 1398 int result = compare.compare(mixed, otherDefault); 1399 if (result != 0) { 1400 errln("error: default compare(mixed, other) = " + result 1401 + " instead of 0"); 1402 } 1403 1404 // test u_strcasecmp() - exclude special i 1405 compare.setIgnoreCase(true, 1406 UTF16.StringComparator.FOLD_CASE_EXCLUDE_SPECIAL_I); 1407 result = compare.compare(mixed, otherExcludeSpecialI); 1408 if (result != 0) { 1409 errln("error: exclude_i compare(mixed, other) = " + result 1410 + " instead of 0"); 1411 } 1412 1413 // test u_strcasecmp() 1414 compare.setIgnoreCase(true, 1415 UTF16.StringComparator.FOLD_CASE_DEFAULT); 1416 result = compare.compare(mixed, different); 1417 if (result <= 0) { 1418 errln("error: default compare(mixed, different) = " + result 1419 + " instead of positive"); 1420 } 1421 1422 // test substrings - stop before the sharp s (U+00df) 1423 compare.setIgnoreCase(true, 1424 UTF16.StringComparator.FOLD_CASE_DEFAULT); 1425 result = compare.compare(mixed.substring(0, 4), 1426 different.substring(0, 4)); 1427 if (result != 0) { 1428 errln("error: default compare(mixed substring, different substring) = " 1429 + result + " instead of 0"); 1430 } 1431 // test substrings - stop in the middle of the sharp s (U+00df) 1432 compare.setIgnoreCase(true, 1433 UTF16.StringComparator.FOLD_CASE_DEFAULT); 1434 result = compare.compare(mixed.substring(0, 5), 1435 different.substring(0, 5)); 1436 if (result <= 0) { 1437 errln("error: default compare(mixed substring, different substring) = " 1438 + result + " instead of positive"); 1439 } 1440 } 1441 TestHasMoreCodePointsThan()1442 public void TestHasMoreCodePointsThan() 1443 { 1444 String str = "\u0061\u0062\ud800\udc00\ud801\udc01\u0063\ud802\u0064" 1445 + "\udc03\u0065\u0066\ud804\udc04\ud805\udc05\u0067"; 1446 int length = str.length(); 1447 while (length >= 0) { 1448 for (int i = 0; i <= length; ++ i) { 1449 String s = str.substring(0, i); 1450 for (int number = -1; number <= ((length - i) + 2); ++ number) { 1451 boolean flag = UTF16.hasMoreCodePointsThan(s, number); 1452 if (flag != (UTF16.countCodePoint(s) > number)) { 1453 errln("hasMoreCodePointsThan(" + Utility.hex(s) 1454 + ", " + number + ") = " + flag + " is wrong"); 1455 } 1456 } 1457 } 1458 -- length; 1459 } 1460 1461 // testing for null bad input 1462 for(length = -1; length <= 1; ++ length) { 1463 for (int i = 0; i <= length; ++ i) { 1464 for (int number = -2; number <= 2; ++ number) { 1465 boolean flag = UTF16.hasMoreCodePointsThan((String)null, 1466 number); 1467 if (flag != (UTF16.countCodePoint((String)null) > number)) { 1468 errln("hasMoreCodePointsThan(null, " + number + ") = " 1469 + flag + " is wrong"); 1470 } 1471 } 1472 } 1473 } 1474 1475 length = str.length(); 1476 while (length >= 0) { 1477 for (int i = 0; i <= length; ++ i) { 1478 StringBuffer s = new StringBuffer(str.substring(0, i)); 1479 for (int number = -1; number <= ((length - i) + 2); ++ number) { 1480 boolean flag = UTF16.hasMoreCodePointsThan(s, number); 1481 if (flag != (UTF16.countCodePoint(s) > number)) { 1482 errln("hasMoreCodePointsThan(" + Utility.hex(s) 1483 + ", " + number + ") = " + flag + " is wrong"); 1484 } 1485 } 1486 } 1487 -- length; 1488 } 1489 1490 // testing for null bad input 1491 for (length = -1; length <= 1; ++ length) { 1492 for (int i = 0; i <= length; ++ i) { 1493 for (int number = -2; number <= 2; ++ number) { 1494 boolean flag = UTF16.hasMoreCodePointsThan( 1495 (StringBuffer)null, number); 1496 if (flag 1497 != (UTF16.countCodePoint((StringBuffer)null) > number)) 1498 { 1499 errln("hasMoreCodePointsThan(null, " + number + ") = " 1500 + flag + " is wrong"); 1501 } 1502 } 1503 } 1504 } 1505 1506 char strarray[] = str.toCharArray(); 1507 while (length >= 0) { 1508 for (int limit = 0; limit <= length; ++ limit) { 1509 for (int start = 0; start <= limit; ++ start) { 1510 for (int number = -1; number <= ((limit - start) + 2); 1511 ++ number) { 1512 boolean flag = UTF16.hasMoreCodePointsThan(strarray, 1513 start, limit, number); 1514 if (flag != (UTF16.countCodePoint(strarray, start, 1515 limit) > number)) { 1516 errln("hasMoreCodePointsThan(" 1517 + Utility.hex(str.substring(start, limit)) 1518 + ", " + start + ", " + limit + ", " + number 1519 + ") = " + flag + " is wrong"); 1520 } 1521 } 1522 } 1523 } 1524 -- length; 1525 } 1526 1527 // testing for null bad input 1528 for (length = -1; length <= 1; ++ length) { 1529 for (int i = 0; i <= length; ++ i) { 1530 for (int number = -2; number <= 2; ++ number) { 1531 boolean flag = UTF16.hasMoreCodePointsThan( 1532 (StringBuffer)null, number); 1533 if (flag 1534 != (UTF16.countCodePoint((StringBuffer)null) > number)) 1535 { 1536 errln("hasMoreCodePointsThan(null, " + number + ") = " 1537 + flag + " is wrong"); 1538 } 1539 } 1540 } 1541 } 1542 1543 // bad input 1544 try { 1545 UTF16.hasMoreCodePointsThan(strarray, -2, -1, 5); 1546 errln("hasMoreCodePointsThan(chararray) with negative indexes has to throw an exception"); 1547 } catch (Exception e) { 1548 logln("PASS: UTF16.hasMoreCodePointsThan failed as expected"); 1549 } 1550 try { 1551 UTF16.hasMoreCodePointsThan(strarray, 5, 2, 5); 1552 errln("hasMoreCodePointsThan(chararray) with limit less than start index has to throw an exception"); 1553 } catch (Exception e) { 1554 logln("PASS: UTF16.hasMoreCodePointsThan failed as expected"); 1555 } 1556 try { 1557 if (UTF16.hasMoreCodePointsThan(strarray, -2, 2, 5)) { 1558 errln("hasMoreCodePointsThan(chararray) with negative start indexes can't return true"); 1559 } 1560 } catch (Exception e) { 1561 } 1562 } 1563 TestUtilities()1564 public void TestUtilities() { 1565 String[] tests = { 1566 "a", 1567 "\uFFFF", 1568 "", 1569 "\uD800", 1570 "\uDC00", 1571 "\uDBFF\uDfff", 1572 "", 1573 "\u0000", 1574 "\uDC00\uD800", 1575 "ab", 1576 "a", 1577 null, 1578 }; 1579 StringComparator sc = new UTF16.StringComparator(true,false,0); 1580 for (String item1 : tests) { 1581 String nonNull1 = item1 == null ? "" : item1; 1582 int count = UTF16.countCodePoint(nonNull1); 1583 int expected = count == 0 || count > 1 ? -1 : nonNull1.codePointAt(0); 1584 assertEquals("codepoint test " + Utility.hex(nonNull1), expected, UTF16.getSingleCodePoint(item1)); 1585 if (expected == -1) { 1586 continue; 1587 } 1588 for (String item2 : tests) { 1589 String nonNull2 = item2 == null ? "" : item2; 1590 int scValue = Integer.signum(sc.compare(nonNull1, nonNull2)); 1591 int fValue = Integer.signum(UTF16.compareCodePoint(expected, item2)); 1592 assertEquals("comparison " + Utility.hex(nonNull1) + ", " + Utility.hex(nonNull2), scValue, fValue); 1593 } 1594 } 1595 } 1596 TestNewString()1597 public void TestNewString() { 1598 final int[] codePoints = { 1599 UCharacter.toCodePoint(UCharacter.MIN_HIGH_SURROGATE, UCharacter.MAX_LOW_SURROGATE), 1600 UCharacter.toCodePoint(UCharacter.MAX_HIGH_SURROGATE, UCharacter.MIN_LOW_SURROGATE), 1601 UCharacter.MAX_HIGH_SURROGATE, 1602 'A', 1603 -1, 1604 }; 1605 1606 1607 final String cpString = "" + 1608 UCharacter.MIN_HIGH_SURROGATE + 1609 UCharacter.MAX_LOW_SURROGATE + 1610 UCharacter.MAX_HIGH_SURROGATE + 1611 UCharacter.MIN_LOW_SURROGATE + 1612 UCharacter.MAX_HIGH_SURROGATE + 1613 'A'; 1614 1615 final int[][] tests = { 1616 { 0, 1, 0, 2 }, 1617 { 0, 2, 0, 4 }, 1618 { 1, 1, 2, 2 }, 1619 { 1, 2, 2, 3 }, 1620 { 1, 3, 2, 4 }, 1621 { 2, 2, 4, 2 }, 1622 { 2, 3, 0, -1 }, 1623 { 4, 5, 0, -1 }, 1624 { 3, -1, 0, -1 } 1625 }; 1626 1627 for (int i = 0; i < tests.length; ++i) { 1628 int[] t = tests[i]; 1629 int s = t[0]; 1630 int c = t[1]; 1631 int rs = t[2]; 1632 int rc = t[3]; 1633 1634 Exception e = null; 1635 try { 1636 String str = UTF16.newString(codePoints, s, c); 1637 if (rc == -1 || !str.equals(cpString.substring(rs, rs+rc))) { 1638 errln("failed codePoints iter: " + i + " start: " + s + " len: " + c); 1639 } 1640 continue; 1641 } 1642 catch (IndexOutOfBoundsException e1) { 1643 e = e1; 1644 } 1645 catch (IllegalArgumentException e2) { 1646 e = e2; 1647 } 1648 if (rc != -1) { 1649 errln(e.getMessage()); 1650 } 1651 } 1652 } 1653 main(String[] arg)1654 public static void main(String[] arg) 1655 { 1656 try 1657 { 1658 UTF16Test test = new UTF16Test(); 1659 test.run(arg); 1660 // test.TestCaseCompare(); 1661 } 1662 catch (Exception e) 1663 { 1664 e.printStackTrace(); 1665 } 1666 } 1667 1668 1669 // private data members ---------------------------------------------- 1670 1671 private final static String INDEXOF_SUPPLEMENTARY_STRING_ = 1672 "\ud841\udc02\u0071\udc02\ud841\u0071\ud841\udc02\u0071\u0072" + 1673 "\ud841\udc02\u0071\ud841\udc02\u0071\udc02\ud841\u0073"; 1674 private final static int INDEXOF_SUPPLEMENTARY_CHAR_[] = 1675 {0x71, 0xd841, 0xdc02, 1676 UTF16Util.getRawSupplementary((char)0xd841, 1677 (char)0xdc02)}; 1678 private final static int INDEXOF_SUPPLEMENTARY_CHAR_INDEX_[][] = 1679 {{2, 5, 8, 12, 15}, 1680 {4, 17}, 1681 {3, 16}, 1682 {0, 6, 10, 13} 1683 }; 1684 private final static String INDEXOF_SUPPLEMENTARY_STR_ = "\udc02\ud841"; 1685 private final static int INDEXOF_SUPPLEMENTARY_STR_INDEX_[] = 1686 {3, 16}; 1687 1688 // private methods --------------------------------------------------- 1689 } 1690 1691