1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /*********************************************************************** 4 * COPYRIGHT: 5 * Copyright (c) 1997-2016, International Business Machines Corporation 6 * and others. All Rights Reserved. 7 ***********************************************************************/ 8 9 #include "unicode/utypes.h" 10 11 #if !UCONFIG_NO_FORMATTING 12 13 #include "msfmrgts.h" 14 15 #include "unicode/format.h" 16 #include "unicode/decimfmt.h" 17 #include "unicode/locid.h" 18 #include "unicode/msgfmt.h" 19 #include "unicode/numfmt.h" 20 #include "unicode/choicfmt.h" 21 #include "unicode/gregocal.h" 22 #include "cmemory.h" 23 #include "putilimp.h" 24 25 // ***************************************************************************** 26 // class MessageFormatRegressionTest 27 // ***************************************************************************** 28 29 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break; 30 31 void 32 MessageFormatRegressionTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) 33 { 34 TESTCASE_AUTO_BEGIN; 35 TESTCASE_AUTO(Test4074764) 36 //TESTCASE_AUTO(Test4058973) -- disabled/obsolete in ICU 4.8 37 TESTCASE_AUTO(Test4031438) 38 TESTCASE_AUTO(Test4052223) 39 TESTCASE_AUTO(Test4104976) 40 TESTCASE_AUTO(Test4106659) 41 TESTCASE_AUTO(Test4106660) 42 TESTCASE_AUTO(Test4111739) 43 TESTCASE_AUTO(Test4114743) 44 TESTCASE_AUTO(Test4116444) 45 TESTCASE_AUTO(Test4114739) 46 TESTCASE_AUTO(Test4113018) 47 TESTCASE_AUTO(Test4106661) 48 TESTCASE_AUTO(Test4094906) 49 TESTCASE_AUTO(Test4118592) 50 TESTCASE_AUTO(Test4118594) 51 TESTCASE_AUTO(Test4105380) 52 TESTCASE_AUTO(Test4120552) 53 TESTCASE_AUTO(Test4142938) 54 TESTCASE_AUTO(TestChoicePatternQuote) 55 TESTCASE_AUTO(Test4112104) 56 TESTCASE_AUTO(TestAPI) 57 TESTCASE_AUTO_END; 58 } 59 60 UBool 61 MessageFormatRegressionTest::failure(UErrorCode status, const char* msg, UBool possibleDataError) 62 { 63 if(U_FAILURE(status)) { 64 if (possibleDataError) { 65 dataerrln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status)); 66 } else { 67 errln(UnicodeString("FAIL: ") + msg + " failed, error " + u_errorName(status)); 68 } 69 return TRUE; 70 } 71 72 return FALSE; 73 } 74 75 /* @bug 4074764 76 * Null exception when formatting pattern with MessageFormat 77 * with no parameters. 78 */ 79 void MessageFormatRegressionTest::Test4074764() { 80 UnicodeString pattern [] = { 81 "Message without param", 82 "Message with param:{0}", 83 "Longer Message with param {0}" 84 }; 85 //difference between the two param strings are that 86 //in the first one, the param position is within the 87 //length of the string without param while it is not so 88 //in the other case. 89 90 UErrorCode status = U_ZERO_ERROR; 91 MessageFormat *messageFormatter = new MessageFormat("", status); 92 93 failure(status, "couldn't create MessageFormat"); 94 95 //try { 96 //Apply pattern with param and print the result 97 messageFormatter->applyPattern(pattern[1], status); 98 failure(status, "messageFormat->applyPattern"); 99 //Object[] params = {new UnicodeString("BUG"), new Date()}; 100 Formattable params [] = { 101 Formattable(UnicodeString("BUG")), 102 Formattable(0, Formattable::kIsDate) 103 }; 104 UnicodeString tempBuffer; 105 FieldPosition pos(FieldPosition::DONT_CARE); 106 tempBuffer = messageFormatter->format(params, 2, tempBuffer, pos, status); 107 if( tempBuffer != "Message with param:BUG" || failure(status, "messageFormat->format")) 108 errln("MessageFormat with one param test failed."); 109 logln("Formatted with one extra param : " + tempBuffer); 110 111 //Apply pattern without param and print the result 112 messageFormatter->applyPattern(pattern[0], status); 113 failure(status, "messageFormatter->applyPattern"); 114 115 // {sfb} how much does this apply in C++? 116 // do we want to verify that the Formattable* array is not NULL, 117 // or is that the user's responsibility? 118 // additionally, what should be the item count? 119 // for bug testing purposes, assume that something was set to 120 // NULL by mistake, and that the length should be non-zero 121 122 //tempBuffer = messageFormatter->format(NULL, 1, tempBuffer, FieldPosition(FieldPosition::DONT_CARE), status); 123 tempBuffer.remove(); 124 tempBuffer = messageFormatter->format(NULL, 0, tempBuffer, pos, status); 125 126 if( tempBuffer != "Message without param" || failure(status, "messageFormat->format")) 127 errln("MessageFormat with no param test failed."); 128 logln("Formatted with no params : " + tempBuffer); 129 130 tempBuffer.remove(); 131 tempBuffer = messageFormatter->format(params, 2, tempBuffer, pos, status); 132 if (tempBuffer != "Message without param" || failure(status, "messageFormat->format")) 133 errln("Formatted with arguments > subsitution failed. result = " + tempBuffer); 134 logln("Formatted with extra params : " + tempBuffer); 135 //This statement gives an exception while formatting... 136 //If we use pattern[1] for the message with param, 137 //we get an NullPointerException in MessageFormat.java(617) 138 //If we use pattern[2] for the message with param, 139 //we get an StringArrayIndexOutOfBoundsException in MessageFormat.java(614) 140 //Both are due to maxOffset not being reset to -1 141 //in applyPattern() when the pattern does not 142 //contain any param. 143 /*} catch (Exception foo) { 144 errln("Exception when formatting with no params."); 145 }*/ 146 147 delete messageFormatter; 148 } 149 150 /* @bug 4058973 151 * MessageFormat.toPattern has weird rounding behavior. 152 * 153 * ICU 4.8: This test is commented out because toPattern() has been changed to return 154 * the original pattern string, rather than reconstituting a new (equivalent) one. 155 * This trivially eliminates issues with rounding or any other pattern string differences. 156 */ 157 /* 158 void MessageFormatRegressionTest::Test4058973() 159 { 160 UErrorCode status = U_ZERO_ERROR; 161 MessageFormat *fmt = new MessageFormat("{0,choice,0#no files|1#one file|1< {0,number,integer} files}", status); 162 failure(status, "new MessageFormat"); 163 164 UnicodeString pat; 165 pat = fmt->toPattern(pat); 166 UnicodeString exp("{0,choice,0#no files|1#one file|1< {0,number,integer} files}"); 167 if (pat != exp) { 168 errln("MessageFormat.toPattern failed"); 169 errln("Exp: " + exp); 170 errln("Got: " + pat); 171 } 172 173 delete fmt; 174 }*/ 175 /* @bug 4031438 176 * More robust message formats. 177 */ 178 void MessageFormatRegressionTest::Test4031438() 179 { 180 UErrorCode status = U_ZERO_ERROR; 181 182 UnicodeString pattern1("Impossible {1} has occurred -- status code is {0} and message is {2}."); 183 UnicodeString pattern2("Double '' Quotes {0} test and quoted '{1}' test plus 'other {2} stuff'."); 184 185 MessageFormat *messageFormatter = new MessageFormat("", status); 186 failure(status, "new MessageFormat"); 187 188 const UBool possibleDataError = TRUE; 189 190 //try { 191 logln("Apply with pattern : " + pattern1); 192 messageFormatter->applyPattern(pattern1, status); 193 failure(status, "messageFormat->applyPattern"); 194 //Object[] params = {new Integer(7)}; 195 Formattable params []= { 196 Formattable((int32_t)7) 197 }; 198 UnicodeString tempBuffer; 199 FieldPosition pos(FieldPosition::DONT_CARE); 200 tempBuffer = messageFormatter->format(params, 1, tempBuffer, pos, status); 201 if(tempBuffer != "Impossible {1} has occurred -- status code is 7 and message is {2}." || failure(status, "MessageFormat::format")) 202 dataerrln("Tests arguments < substitution failed"); 203 logln("Formatted with 7 : " + tempBuffer); 204 ParsePosition pp(0); 205 int32_t count = 0; 206 Formattable *objs = messageFormatter->parse(tempBuffer, pp, count); 207 //if(objs[7/*params.length*/] != NULL) 208 // errln("Parse failed with more than expected arguments"); 209 210 NumberFormat *fmt = 0; 211 UnicodeString temp, temp1; 212 213 for (int i = 0; i < count; i++) { 214 215 // convert to string if not already 216 Formattable obj = objs[i]; 217 temp.remove(); 218 if(obj.getType() == Formattable::kString) 219 temp = obj.getString(temp); 220 else { 221 fmt = NumberFormat::createInstance(status); 222 switch (obj.getType()) { 223 case Formattable::kLong: fmt->format(obj.getLong(), temp); break; 224 case Formattable::kInt64: fmt->format(obj.getInt64(), temp); break; 225 case Formattable::kDouble: fmt->format(obj.getDouble(), temp); break; 226 default: break; 227 } 228 } 229 230 // convert to string if not already 231 Formattable obj1 = params[i]; 232 temp1.remove(); 233 if(obj1.getType() == Formattable::kString) 234 temp1 = obj1.getString(temp1); 235 else { 236 fmt = NumberFormat::createInstance(status); 237 switch (obj1.getType()) { 238 case Formattable::kLong: fmt->format(obj1.getLong(), temp1); break; 239 case Formattable::kInt64: fmt->format(obj1.getInt64(), temp1); break; 240 case Formattable::kDouble: fmt->format(obj1.getDouble(), temp1); break; 241 default: break; 242 } 243 } 244 245 //if (objs[i] != NULL && objs[i].getString(temp1) != params[i].getString(temp2)) { 246 if (temp != temp1) { 247 errln("Parse failed on object " + objs[i].getString(temp1) + " at index : " + i); 248 } 249 } 250 251 delete fmt; 252 delete [] objs; 253 254 // {sfb} does this apply? no way to really pass a null Formattable, 255 // only a null array 256 257 /*tempBuffer = messageFormatter->format(null, tempBuffer, FieldPosition(FieldPosition::DONT_CARE), status); 258 if (tempBuffer != "Impossible {1} has occurred -- status code is {0} and message is {2}." || failure(status, "messageFormat->format")) 259 errln("Tests with no arguments failed"); 260 logln("Formatted with null : " + tempBuffer);*/ 261 logln("Apply with pattern : " + pattern2); 262 messageFormatter->applyPattern(pattern2, status); 263 failure(status, "messageFormatter->applyPattern", possibleDataError); 264 tempBuffer.remove(); 265 tempBuffer = messageFormatter->format(params, 1, tempBuffer, pos, status); 266 if (tempBuffer != "Double ' Quotes 7 test and quoted {1} test plus 'other {2} stuff'.") 267 dataerrln("quote format test (w/ params) failed. - %s", u_errorName(status)); 268 logln("Formatted with params : " + tempBuffer); 269 270 /*tempBuffer = messageFormatter->format(null); 271 if (!tempBuffer.equals("Double ' Quotes {0} test and quoted {1} test plus other {2} stuff.")) 272 errln("quote format test (w/ null) failed."); 273 logln("Formatted with null : " + tempBuffer); 274 logln("toPattern : " + messageFormatter.toPattern());*/ 275 /*} catch (Exception foo) { 276 errln("Exception when formatting in bug 4031438. "+foo.getMessage()); 277 }*/ 278 delete messageFormatter; 279 } 280 281 void MessageFormatRegressionTest::Test4052223() 282 { 283 284 ParsePosition pos(0); 285 if (pos.getErrorIndex() != -1) { 286 errln("ParsePosition.getErrorIndex initialization failed."); 287 } 288 289 UErrorCode status = U_ZERO_ERROR; 290 MessageFormat *fmt = new MessageFormat("There are {0} apples growing on the {1} tree.", status); 291 failure(status, "new MessageFormat"); 292 UnicodeString str("There is one apple growing on the peach tree."); 293 294 int32_t count = 0; 295 fmt->parse(str, pos, count); 296 297 logln(UnicodeString("unparsable string , should fail at ") + pos.getErrorIndex()); 298 if (pos.getErrorIndex() == -1) 299 errln("Bug 4052223 failed : parsing string " + str); 300 pos.setErrorIndex(4); 301 if (pos.getErrorIndex() != 4) 302 errln(UnicodeString("setErrorIndex failed, got ") + pos.getErrorIndex() + " instead of 4"); 303 304 ChoiceFormat *f = new ChoiceFormat( 305 "-1#are negative|0#are no or fraction|1#is one|1.0<is 1+|2#are two|2<are more than 2.", status); 306 failure(status, "new ChoiceFormat"); 307 pos.setIndex(0); 308 pos.setErrorIndex(-1); 309 Formattable obj; 310 f->parse("are negative", obj, pos); 311 if (pos.getErrorIndex() != -1 && obj.getDouble() == -1.0) 312 errln(UnicodeString("Parse with \"are negative\" failed, at ") + pos.getErrorIndex()); 313 pos.setIndex(0); 314 pos.setErrorIndex(-1); 315 f->parse("are no or fraction ", obj, pos); 316 if (pos.getErrorIndex() != -1 && obj.getDouble() == 0.0) 317 errln(UnicodeString("Parse with \"are no or fraction\" failed, at ") + pos.getErrorIndex()); 318 pos.setIndex(0); 319 pos.setErrorIndex(-1); 320 f->parse("go postal", obj, pos); 321 if (pos.getErrorIndex() == -1 && ! uprv_isNaN(obj.getDouble())) 322 errln(UnicodeString("Parse with \"go postal\" failed, at ") + pos.getErrorIndex()); 323 324 delete fmt; 325 delete f; 326 } 327 /* @bug 4104976 328 * ChoiceFormat.equals(null) throws NullPointerException 329 */ 330 331 // {sfb} not really applicable in C++?? (kind of silly) 332 333 void MessageFormatRegressionTest::Test4104976() 334 { 335 double limits [] = {1, 20}; 336 UnicodeString formats [] = { 337 UnicodeString("xyz"), 338 UnicodeString("abc") 339 }; 340 int32_t formats_length = UPRV_LENGTHOF(formats); 341 UErrorCode status = U_ZERO_ERROR; 342 ChoiceFormat *cf = new ChoiceFormat(limits, formats, formats_length); 343 failure(status, "new ChoiceFormat"); 344 //try { 345 log("Compares to null is always false, returned : "); 346 logln(cf == NULL ? "TRUE" : "FALSE"); 347 /*} catch (Exception foo) { 348 errln("ChoiceFormat.equals(null) throws exception."); 349 }*/ 350 351 delete cf; 352 } 353 354 /* @bug 4106659 355 * ChoiceFormat.ctor(double[], String[]) doesn't check 356 * whether lengths of input arrays are equal. 357 */ 358 359 // {sfb} again, not really applicable in C++ 360 361 void MessageFormatRegressionTest::Test4106659() 362 { 363 /* 364 double limits [] = { 365 1, 2, 3 366 }; 367 UnicodeString formats [] = { 368 "one", "two" 369 }; 370 ChoiceFormat *cf = NULL; 371 //try { 372 // cf = new ChoiceFormat(limits, formats, 3); 373 //} catch (Exception foo) { 374 // logln("ChoiceFormat constructor should check for the array lengths"); 375 // cf = null; 376 //} 377 //if (cf != null) 378 // errln(cf->format(5)); 379 // 380 delete cf; 381 */ 382 } 383 384 /* @bug 4106660 385 * ChoiceFormat.ctor(double[], String[]) allows unordered double array. 386 * This is not a bug, added javadoc to emphasize the use of limit 387 * array must be in ascending order. 388 */ 389 void MessageFormatRegressionTest::Test4106660() 390 { 391 double limits [] = {3, 1, 2}; 392 UnicodeString formats [] = { 393 UnicodeString("Three"), 394 UnicodeString("One"), 395 UnicodeString("Two") 396 }; 397 ChoiceFormat *cf = new ChoiceFormat(limits, formats, 3); 398 double d = 5.0; 399 UnicodeString str; 400 FieldPosition pos(FieldPosition::DONT_CARE); 401 str = cf->format(d, str, pos); 402 if (str != "Two") 403 errln( (UnicodeString) "format(" + d + ") = " + str); 404 405 delete cf; 406 } 407 408 /* @bug 4111739 409 * MessageFormat is incorrectly serialized/deserialized. 410 */ 411 412 // {sfb} doesn't apply in C++ 413 414 void MessageFormatRegressionTest::Test4111739() 415 { 416 /*MessageFormat format1 = null; 417 MessageFormat format2 = null; 418 ObjectOutputStream ostream = null; 419 ByteArrayOutputStream baos = null; 420 ObjectInputStream istream = null; 421 422 try { 423 baos = new ByteArrayOutputStream(); 424 ostream = new ObjectOutputStream(baos); 425 } catch(IOException e) { 426 errln("Unexpected exception : " + e.getMessage()); 427 return; 428 } 429 430 try { 431 format1 = new MessageFormat("pattern{0}"); 432 ostream.writeObject(format1); 433 ostream.flush(); 434 435 byte bytes[] = baos.toByteArray(); 436 437 istream = new ObjectInputStream(new ByteArrayInputStream(bytes)); 438 format2 = (MessageFormat)istream.readObject(); 439 } catch(Exception e) { 440 errln("Unexpected exception : " + e.getMessage()); 441 } 442 443 if (!format1.equals(format2)) { 444 errln("MessageFormats before and after serialization are not" + 445 " equal\nformat1 = " + format1 + "(" + format1.toPattern() + ")\nformat2 = " + 446 format2 + "(" + format2.toPattern() + ")"); 447 } else { 448 logln("Serialization for MessageFormat is OK."); 449 }*/ 450 } 451 /* @bug 4114743 452 * MessageFormat.applyPattern allows illegal patterns. 453 */ 454 void MessageFormatRegressionTest::Test4114743() 455 { 456 UnicodeString originalPattern("initial pattern"); 457 UErrorCode status = U_ZERO_ERROR; 458 MessageFormat *mf = new MessageFormat(originalPattern, status); 459 failure(status, "new MessageFormat"); 460 //try { 461 UnicodeString illegalPattern("ab { '}' de"); 462 mf->applyPattern(illegalPattern, status); 463 if( ! U_FAILURE(status)) 464 errln("illegal pattern: \"" + illegalPattern + "\""); 465 /*} catch (IllegalArgumentException foo) { 466 if (!originalPattern.equals(mf.toPattern())) 467 errln("pattern after: \"" + mf.toPattern() + "\""); 468 }*/ 469 delete mf; 470 } 471 472 /* @bug 4116444 473 * MessageFormat.parse has different behavior in case of null. 474 */ 475 void MessageFormatRegressionTest::Test4116444() 476 { 477 UnicodeString patterns [] = { 478 (UnicodeString)"", 479 (UnicodeString)"one", 480 (UnicodeString) "{0,date,short}" 481 }; 482 483 UErrorCode status = U_ZERO_ERROR; 484 MessageFormat *mf = new MessageFormat("", status); 485 failure(status, "new MessageFormat"); 486 487 for (int i = 0; i < 3; i++) { 488 UnicodeString pattern = patterns[i]; 489 mf->applyPattern(pattern, status); 490 failure(status, "mf->applyPattern", TRUE); 491 492 //try { 493 int32_t count = 0; 494 ParsePosition pp(0); 495 Formattable *array = mf->parse(UnicodeString(""), pp, count); 496 logln("pattern: \"" + pattern + "\""); 497 log(" parsedObjects: "); 498 if (array != NULL) { 499 log("{"); 500 for (int j = 0; j < count; j++) { 501 //if (array[j] != null) 502 UnicodeString dummy; 503 dataerrln("\"" + array[j].getString(dummy) + "\""); 504 //else 505 // log("null"); 506 if (j < count- 1) 507 log(","); 508 } 509 log("}") ; 510 delete[] array; 511 } else { 512 log("null"); 513 } 514 logln(""); 515 /*} catch (Exception e) { 516 errln("pattern: \"" + pattern + "\""); 517 errln(" Exception: " + e.getMessage()); 518 }*/ 519 } 520 521 delete mf; 522 } 523 /* @bug 4114739 (FIX and add javadoc) 524 * MessageFormat.format has undocumented behavior about empty format objects. 525 */ 526 527 // {sfb} doesn't apply in C++? 528 void MessageFormatRegressionTest::Test4114739() 529 { 530 531 UErrorCode status = U_ZERO_ERROR; 532 MessageFormat *mf = new MessageFormat("<{0}>", status); 533 failure(status, "new MessageFormat"); 534 535 Formattable *objs1 = NULL; 536 //Formattable objs2 [] = {}; 537 //Formattable *objs3 [] = {NULL}; 538 //try { 539 UnicodeString pat; 540 UnicodeString res; 541 logln("pattern: \"" + mf->toPattern(pat) + "\""); 542 log("format(null) : "); 543 FieldPosition pos(FieldPosition::DONT_CARE); 544 logln("\"" + mf->format(objs1, 0, res, pos, status) + "\""); 545 failure(status, "mf->format"); 546 /*log("format({}) : "); 547 logln("\"" + mf->format(objs2, 0, res, FieldPosition(FieldPosition::DONT_CARE), status) + "\""); 548 failure(status, "mf->format"); 549 log("format({null}) :"); 550 logln("\"" + mf->format(objs3, 0, res, FieldPosition(FieldPosition::DONT_CARE), status) + "\""); 551 failure(status, "mf->format");*/ 552 /*} catch (Exception e) { 553 errln("Exception thrown for null argument tests."); 554 }*/ 555 556 delete mf; 557 } 558 559 /* @bug 4113018 560 * MessageFormat.applyPattern works wrong with illegal patterns. 561 */ 562 void MessageFormatRegressionTest::Test4113018() 563 { 564 UnicodeString originalPattern("initial pattern"); 565 UErrorCode status = U_ZERO_ERROR; 566 MessageFormat *mf = new MessageFormat(originalPattern, status); 567 failure(status, "new messageFormat"); 568 UnicodeString illegalPattern("format: {0, xxxYYY}"); 569 UnicodeString pat; 570 logln("pattern before: \"" + mf->toPattern(pat) + "\""); 571 logln("illegal pattern: \"" + illegalPattern + "\""); 572 //try { 573 mf->applyPattern(illegalPattern, status); 574 if( ! U_FAILURE(status)) 575 errln("Should have thrown IllegalArgumentException for pattern : " + illegalPattern); 576 /*} catch (IllegalArgumentException e) { 577 if (!originalPattern.equals(mf.toPattern())) 578 errln("pattern after: \"" + mf.toPattern() + "\""); 579 }*/ 580 delete mf; 581 } 582 583 /* @bug 4106661 584 * ChoiceFormat is silent about the pattern usage in javadoc. 585 */ 586 void MessageFormatRegressionTest::Test4106661() 587 { 588 UErrorCode status = U_ZERO_ERROR; 589 ChoiceFormat *fmt = new ChoiceFormat( 590 "-1#are negative| 0#are no or fraction | 1#is one |1.0<is 1+ |2#are two |2<are more than 2.", status); 591 failure(status, "new ChoiceFormat"); 592 UnicodeString pat; 593 logln("Formatter Pattern : " + fmt->toPattern(pat)); 594 595 FieldPosition bogus(FieldPosition::DONT_CARE); 596 UnicodeString str; 597 598 // Will this work for -inf? 599 logln("Format with -INF : " + fmt->format(Formattable(-uprv_getInfinity()), str, bogus, status)); 600 failure(status, "fmt->format"); 601 str.remove(); 602 logln("Format with -1.0 : " + fmt->format(Formattable(-1.0), str, bogus, status)); 603 failure(status, "fmt->format"); 604 str.remove(); 605 logln("Format with -1.0 : " + fmt->format(Formattable(-1.0), str, bogus, status)); 606 failure(status, "fmt->format"); 607 str.remove(); 608 logln("Format with 0 : " + fmt->format(Formattable((int32_t)0), str, bogus, status)); 609 failure(status, "fmt->format"); 610 str.remove(); 611 logln("Format with 0.9 : " + fmt->format(Formattable(0.9), str, bogus, status)); 612 failure(status, "fmt->format"); 613 str.remove(); 614 logln("Format with 1.0 : " + fmt->format(Formattable(1.0), str, bogus, status)); 615 failure(status, "fmt->format"); 616 str.remove(); 617 logln("Format with 1.5 : " + fmt->format(Formattable(1.5), str, bogus, status)); 618 failure(status, "fmt->format"); 619 str.remove(); 620 logln("Format with 2 : " + fmt->format(Formattable((int32_t)2), str, bogus, status)); 621 failure(status, "fmt->format"); 622 str.remove(); 623 logln("Format with 2.1 : " + fmt->format(Formattable(2.1), str, bogus, status)); 624 failure(status, "fmt->format"); 625 str.remove(); 626 logln("Format with NaN : " + fmt->format(Formattable(uprv_getNaN()), str, bogus, status)); 627 failure(status, "fmt->format"); 628 str.remove(); 629 logln("Format with +INF : " + fmt->format(Formattable(uprv_getInfinity()), str, bogus, status)); 630 failure(status, "fmt->format"); 631 632 delete fmt; 633 } 634 635 /* @bug 4094906 636 * ChoiceFormat should accept \u221E as eq. to INF. 637 */ 638 void MessageFormatRegressionTest::Test4094906() 639 { 640 UErrorCode status = U_ZERO_ERROR; 641 UnicodeString pattern("-"); 642 pattern += (UChar) 0x221E; 643 pattern += "<are negative|0<are no or fraction|1#is one|1<is 1+|"; 644 pattern += (UChar) 0x221E; 645 pattern += "<are many."; 646 647 ChoiceFormat *fmt = new ChoiceFormat(pattern, status); 648 failure(status, "new ChoiceFormat"); 649 UnicodeString pat; 650 if (fmt->toPattern(pat) != pattern) { 651 errln( (UnicodeString) "Formatter Pattern : " + pat); 652 errln( (UnicodeString) "Expected Pattern : " + pattern); 653 } 654 FieldPosition bogus(FieldPosition::DONT_CARE); 655 UnicodeString str; 656 657 // Will this work for -inf? 658 logln("Format with -INF : " + fmt->format(Formattable(-uprv_getInfinity()), str, bogus, status)); 659 failure(status, "fmt->format"); 660 str.remove(); 661 logln("Format with -1.0 : " + fmt->format(Formattable(-1.0), str, bogus, status)); 662 failure(status, "fmt->format"); 663 str.remove(); 664 logln("Format with -1.0 : " + fmt->format(Formattable(-1.0), str, bogus, status)); 665 failure(status, "fmt->format"); 666 str.remove(); 667 logln("Format with 0 : " + fmt->format(Formattable((int32_t)0), str, bogus, status)); 668 failure(status, "fmt->format"); 669 str.remove(); 670 logln("Format with 0.9 : " + fmt->format(Formattable(0.9), str, bogus, status)); 671 failure(status, "fmt->format"); 672 str.remove(); 673 logln("Format with 1.0 : " + fmt->format(Formattable(1.0), str, bogus, status)); 674 failure(status, "fmt->format"); 675 str.remove(); 676 logln("Format with 1.5 : " + fmt->format(Formattable(1.5), str, bogus, status)); 677 failure(status, "fmt->format"); 678 str.remove(); 679 logln("Format with 2 : " + fmt->format(Formattable((int32_t)2), str, bogus, status)); 680 failure(status, "fmt->format"); 681 str.remove(); 682 logln("Format with 2.1 : " + fmt->format(Formattable(2.1), str, bogus, status)); 683 failure(status, "fmt->format"); 684 str.remove(); 685 logln("Format with NaN : " + fmt->format(Formattable(uprv_getNaN()), str, bogus, status)); 686 failure(status, "fmt->format"); 687 str.remove(); 688 logln("Format with +INF : " + fmt->format(Formattable(uprv_getInfinity()), str, bogus, status)); 689 failure(status, "fmt->format"); 690 691 delete fmt; 692 } 693 694 /* @bug 4118592 695 * MessageFormat.parse fails with ChoiceFormat. 696 */ 697 void MessageFormatRegressionTest::Test4118592() 698 { 699 UErrorCode status = U_ZERO_ERROR; 700 MessageFormat *mf = new MessageFormat("", status); 701 failure(status, "new messageFormat"); 702 UnicodeString pattern("{0,choice,1#YES|2#NO}"); 703 UnicodeString prefix(""); 704 Formattable *objs = 0; 705 706 for (int i = 0; i < 5; i++) { 707 UnicodeString formatted; 708 formatted = prefix + "YES"; 709 mf->applyPattern(prefix + pattern, status); 710 failure(status, "mf->applyPattern"); 711 prefix += "x"; 712 //Object[] objs = mf.parse(formatted, new ParsePosition(0)); 713 int32_t count = 0; 714 ParsePosition pp(0); 715 objs = mf->parse(formatted, pp, count); 716 UnicodeString pat; 717 logln(UnicodeString("") + i + ". pattern :\"" + mf->toPattern(pat) + "\""); 718 log(" \"" + formatted + "\" parsed as "); 719 if (objs == NULL) 720 logln(" null"); 721 else { 722 UnicodeString temp; 723 if(objs[0].getType() == Formattable::kString) 724 logln((UnicodeString)" " + objs[0].getString(temp)); 725 else 726 logln((UnicodeString)" " + (objs[0].getType() == Formattable::kLong ? objs[0].getLong() : objs[0].getDouble())); 727 delete[] objs; 728 729 } 730 } 731 732 delete mf; 733 } 734 /* @bug 4118594 735 * MessageFormat.parse fails for some patterns. 736 */ 737 void MessageFormatRegressionTest::Test4118594() 738 { 739 UErrorCode status = U_ZERO_ERROR; 740 const UBool possibleDataError = TRUE; 741 MessageFormat *mf = new MessageFormat("{0}, {0}, {0}", status); 742 failure(status, "new MessageFormat"); 743 UnicodeString forParsing("x, y, z"); 744 //Object[] objs = mf.parse(forParsing, new ParsePosition(0)); 745 int32_t count = 0; 746 ParsePosition pp(0); 747 Formattable *objs = mf->parse(forParsing, pp, count); 748 UnicodeString pat; 749 logln("pattern: \"" + mf->toPattern(pat) + "\""); 750 logln("text for parsing: \"" + forParsing + "\""); 751 UnicodeString str; 752 if (objs[0].getString(str) != "z") 753 errln("argument0: \"" + objs[0].getString(str) + "\""); 754 mf->applyPattern("{0,number,#.##}, {0,number,#.#}", status); 755 failure(status, "mf->applyPattern", possibleDataError); 756 //Object[] oldobjs = {new Double(3.1415)}; 757 Formattable oldobjs [] = {Formattable(3.1415)}; 758 UnicodeString result; 759 FieldPosition pos(FieldPosition::DONT_CARE); 760 result = mf->format( oldobjs, 1, result, pos, status ); 761 failure(status, "mf->format", possibleDataError); 762 pat.remove(); 763 logln("pattern: \"" + mf->toPattern(pat) + "\""); 764 logln("text for parsing: \"" + result + "\""); 765 // result now equals "3.14, 3.1" 766 if (result != "3.14, 3.1") 767 dataerrln("result = " + result + " - " + u_errorName(status)); 768 //Object[] newobjs = mf.parse(result, new ParsePosition(0)); 769 int32_t count1 = 0; 770 pp.setIndex(0); 771 Formattable *newobjs = mf->parse(result, pp, count1); 772 // newobjs now equals {new Double(3.1)} 773 if (newobjs == NULL) { 774 dataerrln("Error calling MessageFormat::parse"); 775 } else { 776 if (newobjs[0].getDouble() != 3.1) 777 errln( UnicodeString("newobjs[0] = ") + newobjs[0].getDouble()); 778 } 779 780 delete [] objs; 781 delete [] newobjs; 782 delete mf; 783 } 784 /* @bug 4105380 785 * When using ChoiceFormat, MessageFormat is not good for I18n. 786 */ 787 void MessageFormatRegressionTest::Test4105380() 788 { 789 UnicodeString patternText1("The disk \"{1}\" contains {0}."); 790 UnicodeString patternText2("There are {0} on the disk \"{1}\""); 791 UErrorCode status = U_ZERO_ERROR; 792 const UBool possibleDataError = TRUE; 793 MessageFormat *form1 = new MessageFormat(patternText1, status); 794 failure(status, "new MessageFormat"); 795 MessageFormat *form2 = new MessageFormat(patternText2, status); 796 failure(status, "new MessageFormat"); 797 double filelimits [] = {0,1,2}; 798 UnicodeString filepart [] = { 799 (UnicodeString)"no files", 800 (UnicodeString)"one file", 801 (UnicodeString)"{0,number} files" 802 }; 803 ChoiceFormat *fileform = new ChoiceFormat(filelimits, filepart, 3); 804 form1->setFormat(1, *fileform); 805 form2->setFormat(0, *fileform); 806 //Object[] testArgs = {new Long(12373), "MyDisk"}; 807 Formattable testArgs [] = { 808 Formattable((int32_t)12373), 809 Formattable((UnicodeString)"MyDisk") 810 }; 811 812 FieldPosition bogus(FieldPosition::DONT_CARE); 813 814 UnicodeString result; 815 logln(form1->format(testArgs, 2, result, bogus, status)); 816 failure(status, "form1->format", possibleDataError); 817 result.remove(); 818 logln(form2->format(testArgs, 2, result, bogus, status)); 819 failure(status, "form1->format", possibleDataError); 820 821 delete form1; 822 delete form2; 823 delete fileform; 824 } 825 /* @bug 4120552 826 * MessageFormat.parse incorrectly sets errorIndex. 827 */ 828 void MessageFormatRegressionTest::Test4120552() 829 { 830 UErrorCode status = U_ZERO_ERROR; 831 MessageFormat *mf = new MessageFormat("pattern", status); 832 failure(status, "new MessageFormat"); 833 UnicodeString texts[] = { 834 (UnicodeString)"pattern", 835 (UnicodeString)"pat", 836 (UnicodeString)"1234" 837 }; 838 UnicodeString pat; 839 logln("pattern: \"" + mf->toPattern(pat) + "\""); 840 for (int i = 0; i < 3; i++) { 841 ParsePosition pp(0); 842 //Object[] objs = mf.parse(texts[i], pp); 843 int32_t count = 0; 844 Formattable *objs = mf->parse(texts[i], pp, count); 845 log(" text for parsing: \"" + texts[i] + "\""); 846 if (objs == NULL) { 847 logln(" (incorrectly formatted string)"); 848 if (pp.getErrorIndex() == -1) 849 errln(UnicodeString("Incorrect error index: ") + pp.getErrorIndex()); 850 } else { 851 logln(" (correctly formatted string)"); 852 delete[] objs; 853 } 854 } 855 delete mf; 856 } 857 858 /** 859 * @bug 4142938 860 * MessageFormat handles single quotes in pattern wrong. 861 * This is actually a problem in ChoiceFormat; it doesn't 862 * understand single quotes. 863 */ 864 void MessageFormatRegressionTest::Test4142938() 865 { 866 UnicodeString pat = CharsToUnicodeString("''Vous'' {0,choice,0#n''|1#}avez s\\u00E9lectionn\\u00E9 " 867 "{0,choice,0#aucun|1#{0}} client{0,choice,0#s|1#|2#s} " 868 "personnel{0,choice,0#s|1#|2#s}."); 869 UErrorCode status = U_ZERO_ERROR; 870 MessageFormat *mf = new MessageFormat(pat, status); 871 failure(status, "new MessageFormat"); 872 873 UnicodeString PREFIX [] = { 874 CharsToUnicodeString("'Vous' n'avez s\\u00E9lectionn\\u00E9 aucun clients personnels."), 875 CharsToUnicodeString("'Vous' avez s\\u00E9lectionn\\u00E9 "), 876 CharsToUnicodeString("'Vous' avez s\\u00E9lectionn\\u00E9 ") 877 }; 878 UnicodeString SUFFIX [] = { 879 UnicodeString(), 880 UNICODE_STRING(" client personnel.", 18), 881 UNICODE_STRING(" clients personnels.", 20) 882 }; 883 884 for (int i=0; i<3; i++) { 885 UnicodeString out; 886 //out = mf->format(new Object[]{new Integer(i)}); 887 Formattable objs [] = { 888 Formattable((int32_t)i) 889 }; 890 FieldPosition pos(FieldPosition::DONT_CARE); 891 out = mf->format(objs, 1, out, pos, status); 892 if (!failure(status, "mf->format", TRUE)) { 893 if (SUFFIX[i] == "") { 894 if (out != PREFIX[i]) 895 errln((UnicodeString)"" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\""); 896 } 897 else { 898 if (!out.startsWith(PREFIX[i]) || 899 !out.endsWith(SUFFIX[i])) 900 errln((UnicodeString)"" + i + ": Got \"" + out + "\"; Want \"" + PREFIX[i] + "\"...\"" + 901 SUFFIX[i] + "\""); 902 } 903 } 904 } 905 906 delete mf; 907 } 908 909 /** 910 * @bug 4142938 911 * Test the applyPattern and toPattern handling of single quotes 912 * by ChoiceFormat. (This is in here because this was a bug reported 913 * against MessageFormat.) The single quote is used to quote the 914 * pattern characters '|', '#', '<', and '\u2264'. Two quotes in a row 915 * is a quote literal. 916 */ 917 void MessageFormatRegressionTest::TestChoicePatternQuote() 918 { 919 // ICU 4.8 ChoiceFormat (like PluralFormat & SelectFormat) 920 // returns the chosen string unmodified, so that it is usable in a MessageFormat. 921 // We modified the test strings accordingly. 922 // Note: Without further formatting/trimming/etc., it is not possible 923 // to get a single apostrophe as the last character of a non-final choice sub-message 924 // because the single apostrophe before the pipe '|' would start quoted text. 925 // Normally, ChoiceFormat is used inside a MessageFormat, where a double apostrophe 926 // can be used in that case and will be formatted as a single one. 927 // (Better: Use a "real" apostrophe, U+2019.) 928 UnicodeString DATA [] = { 929 // Pattern 0 value 1 value 930 // {sfb} hacked - changed \u2264 to = (copied from Character Map) 931 "0#can't|1#can", "can't", "can", 932 "0#pound(#)='#''|1#xyz", "pound(#)='#''", "xyz", 933 "0#1<2 '| 1=1'|1#'", "1<2 '| 1=1'", "'", 934 }; 935 for (int i=0; i<9; i+=3) { 936 //try { 937 UErrorCode status = U_ZERO_ERROR; 938 ChoiceFormat *cf = new ChoiceFormat(DATA[i], status); 939 failure(status, "new ChoiceFormat"); 940 for (int j=0; j<=1; ++j) { 941 UnicodeString out; 942 FieldPosition pos(FieldPosition::DONT_CARE); 943 out = cf->format((double)j, out, pos); 944 if (out != DATA[i+1+j]) 945 errln("Fail: Pattern \"" + DATA[i] + "\" x "+j+" -> " + 946 out + "; want \"" + DATA[i+1+j] + "\""); 947 } 948 UnicodeString pat; 949 pat = cf->toPattern(pat); 950 UnicodeString pat2; 951 ChoiceFormat *cf2 = new ChoiceFormat(pat, status); 952 pat2 = cf2->toPattern(pat2); 953 if (pat != pat2) 954 errln("Fail: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + "\""); 955 else 956 logln("Ok: Pattern \"" + DATA[i] + "\" x toPattern -> \"" + pat + "\""); 957 /*} 958 catch (IllegalArgumentException e) { 959 errln("Fail: Pattern \"" + DATA[i] + "\" -> " + e); 960 }*/ 961 962 delete cf; 963 delete cf2; 964 } 965 } 966 967 /** 968 * @bug 4112104 969 * MessageFormat.equals(null) throws a NullPointerException. The JLS states 970 * that it should return false. 971 */ 972 void MessageFormatRegressionTest::Test4112104() 973 { 974 UErrorCode status = U_ZERO_ERROR; 975 MessageFormat *format = new MessageFormat("", status); 976 failure(status, "new MessageFormat"); 977 //try { 978 // This should NOT throw an exception 979 if (format == NULL) { 980 // It also should return false 981 errln("MessageFormat.equals(null) returns false"); 982 } 983 /*} 984 catch (NullPointerException e) { 985 errln("MessageFormat.equals(null) throws " + e); 986 }*/ 987 delete format; 988 } 989 990 void MessageFormatRegressionTest::TestAPI() { 991 UErrorCode status = U_ZERO_ERROR; 992 MessageFormat *format = new MessageFormat("", status); 993 failure(status, "new MessageFormat"); 994 995 // Test adoptFormat 996 MessageFormat *fmt = new MessageFormat("",status); 997 format->adoptFormat("some_name",fmt,status); // Must at least pass a valid identifier. 998 failure(status, "adoptFormat"); 999 1000 // Test getFormat 1001 format->setFormat((int32_t)0,*fmt); 1002 format->getFormat("some_other_name",status); // Must at least pass a valid identifier. 1003 failure(status, "getFormat"); 1004 delete format; 1005 } 1006 1007 #endif /* #if !UCONFIG_NO_FORMATTING */ 1008