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 and
6 * others. All Rights Reserved.
7 ********************************************************************
8 * File TMSGFMT.CPP
9 *
10 * Modification History:
11 *
12 * Date Name Description
13 * 03/24/97 helena Converted from Java.
14 * 07/11/97 helena Updated to work on AIX.
15 * 08/04/97 jfitz Updated to intltest
16 *******************************************************************/
17
18 #include "unicode/utypes.h"
19
20 #if !UCONFIG_NO_FORMATTING
21
22 #include "tmsgfmt.h"
23 #include "cmemory.h"
24
25 #include "unicode/format.h"
26 #include "unicode/decimfmt.h"
27 #include "unicode/localpointer.h"
28 #include "unicode/locid.h"
29 #include "unicode/msgfmt.h"
30 #include "unicode/numfmt.h"
31 #include "unicode/choicfmt.h"
32 #include "unicode/messagepattern.h"
33 #include "unicode/selfmt.h"
34 #include "unicode/gregocal.h"
35 #include "unicode/strenum.h"
36 #include <stdio.h>
37
38 void
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)39 TestMessageFormat::runIndexedTest(int32_t index, UBool exec,
40 const char* &name, char* /*par*/) {
41 TESTCASE_AUTO_BEGIN;
42 TESTCASE_AUTO(testBug1);
43 TESTCASE_AUTO(testBug2);
44 TESTCASE_AUTO(sample);
45 TESTCASE_AUTO(PatternTest);
46 TESTCASE_AUTO(testStaticFormat);
47 TESTCASE_AUTO(testSimpleFormat);
48 TESTCASE_AUTO(testMsgFormatChoice);
49 TESTCASE_AUTO(testCopyConstructor);
50 TESTCASE_AUTO(testAssignment);
51 TESTCASE_AUTO(testClone);
52 TESTCASE_AUTO(testEquals);
53 TESTCASE_AUTO(testNotEquals);
54 TESTCASE_AUTO(testSetLocale);
55 TESTCASE_AUTO(testFormat);
56 TESTCASE_AUTO(testParse);
57 TESTCASE_AUTO(testAdopt);
58 TESTCASE_AUTO(testCopyConstructor2);
59 TESTCASE_AUTO(TestUnlimitedArgsAndSubformats);
60 TESTCASE_AUTO(TestRBNF);
61 TESTCASE_AUTO(TestTurkishCasing);
62 TESTCASE_AUTO(testAutoQuoteApostrophe);
63 TESTCASE_AUTO(testMsgFormatPlural);
64 TESTCASE_AUTO(testMsgFormatSelect);
65 TESTCASE_AUTO(testApostropheInPluralAndSelect);
66 TESTCASE_AUTO(TestApostropheMode);
67 TESTCASE_AUTO(TestCompatibleApostrophe);
68 TESTCASE_AUTO(testCoverage);
69 TESTCASE_AUTO(testGetFormatNames);
70 TESTCASE_AUTO(TestTrimArgumentName);
71 TESTCASE_AUTO(TestSelectOrdinal);
72 TESTCASE_AUTO(TestDecimals);
73 TESTCASE_AUTO(TestArgIsPrefixOfAnother);
74 TESTCASE_AUTO(TestMessageFormatNumberSkeleton);
75 TESTCASE_AUTO_END;
76 }
77
testBug3()78 void TestMessageFormat::testBug3()
79 {
80 double myNumber = -123456;
81 DecimalFormat *form = 0;
82 Locale locale[] = {
83 Locale("ar", "", ""),
84 Locale("be", "", ""),
85 Locale("bg", "", ""),
86 Locale("ca", "", ""),
87 Locale("cs", "", ""),
88 Locale("da", "", ""),
89 Locale("de", "", ""),
90 Locale("de", "AT", ""),
91 Locale("de", "CH", ""),
92 Locale("el", "", ""), // 10
93 Locale("en", "CA", ""),
94 Locale("en", "GB", ""),
95 Locale("en", "IE", ""),
96 Locale("en", "US", ""),
97 Locale("es", "", ""),
98 Locale("et", "", ""),
99 Locale("fi", "", ""),
100 Locale("fr", "", ""),
101 Locale("fr", "BE", ""),
102 Locale("fr", "CA", ""), // 20
103 Locale("fr", "CH", ""),
104 Locale("he", "", ""),
105 Locale("hr", "", ""),
106 Locale("hu", "", ""),
107 Locale("is", "", ""),
108 Locale("it", "", ""),
109 Locale("it", "CH", ""),
110 Locale("ja", "", ""),
111 Locale("ko", "", ""),
112 Locale("lt", "", ""), // 30
113 Locale("lv", "", ""),
114 Locale("mk", "", ""),
115 Locale("nl", "", ""),
116 Locale("nl", "BE", ""),
117 Locale("no", "", ""),
118 Locale("pl", "", ""),
119 Locale("pt", "", ""),
120 Locale("ro", "", ""),
121 Locale("ru", "", ""),
122 Locale("sh", "", ""), // 40
123 Locale("sk", "", ""),
124 Locale("sl", "", ""),
125 Locale("sq", "", ""),
126 Locale("sr", "", ""),
127 Locale("sv", "", ""),
128 Locale("tr", "", ""),
129 Locale("uk", "", ""),
130 Locale("zh", "", ""),
131 Locale("zh", "TW", "") // 49
132 };
133 int32_t i;
134 for (i= 0; i < 49; i++) {
135 UnicodeString buffer;
136 logln(locale[i].getDisplayName(buffer));
137 UErrorCode success = U_ZERO_ERROR;
138 // form = (DecimalFormat*)NumberFormat::createCurrencyInstance(locale[i], success);
139 form = (DecimalFormat*)NumberFormat::createInstance(locale[i], success);
140 if (U_FAILURE(success)) {
141 errln("Err: Number Format ");
142 logln("Number format creation failed.");
143 continue;
144 }
145 Formattable result;
146 FieldPosition pos(FieldPosition::DONT_CARE);
147 buffer.remove();
148 form->format(myNumber, buffer, pos);
149 success = U_ZERO_ERROR;
150 ParsePosition parsePos;
151 form->parse(buffer, result, parsePos);
152 logln(UnicodeString(" -> ") /* + << dec*/ + toString(result) + UnicodeString("[supposed output for result]"));
153 if (U_FAILURE(success)) {
154 errln("Err: Number Format parse");
155 logln("Number format parse failed.");
156 }
157 delete form;
158 }
159 }
160
testBug1()161 void TestMessageFormat::testBug1()
162 {
163 const double limit[] = {0.0, 1.0, 2.0};
164 const UnicodeString formats[] = {"0.0<=Arg<1.0",
165 "1.0<=Arg<2.0",
166 "2.0<-Arg"};
167 ChoiceFormat *cf = new ChoiceFormat(limit, formats, 3);
168 FieldPosition status(FieldPosition::DONT_CARE);
169 UnicodeString toAppendTo;
170 cf->format((int32_t)1, toAppendTo, status);
171 if (toAppendTo != "1.0<=Arg<2.0") {
172 errln("ChoiceFormat cmp in testBug1");
173 }
174 logln(toAppendTo);
175 delete cf;
176 }
177
testBug2()178 void TestMessageFormat::testBug2()
179 {
180 UErrorCode status = U_ZERO_ERROR;
181 UnicodeString result;
182 // {sfb} use double format in pattern, so result will match (not strictly necessary)
183 const UnicodeString pattern = "There {0,choice,0#are no files|1#is one file|1<are {0, number} files} on disk {1}. ";
184 logln("The input pattern : " + pattern);
185 MessageFormat *fmt = new MessageFormat(pattern, status);
186 if (U_FAILURE(status)) {
187 dataerrln("MessageFormat pattern creation failed. - %s", u_errorName(status));
188 return;
189 }
190 logln("The output pattern is : " + fmt->toPattern(result));
191 if (pattern != result) {
192 errln("MessageFormat::toPattern() failed.");
193 }
194 delete fmt;
195 }
196
197 #if 0
198 #if defined(_DEBUG)
199 //----------------------------------------------------
200 // console I/O
201 //----------------------------------------------------
202
203 #include <iostream>
204 std::ostream& operator<<(std::ostream& stream, const Formattable& obj);
205
206 #include "unicode/datefmt.h"
207 #include <stdlib.h>
208 #include <string.h>
209
210 IntlTest&
211 operator<<( IntlTest& stream,
212 const Formattable& obj)
213 {
214 static DateFormat *defDateFormat = 0;
215
216 UnicodeString buffer;
217 switch(obj.getType()) {
218 case Formattable::kDate :
219 if (defDateFormat == 0) {
220 defDateFormat = DateFormat::createInstance();
221 }
222 defDateFormat->format(obj.getDate(), buffer);
223 stream << buffer;
224 break;
225 case Formattable::kDouble :
226 char convert[20];
227 sprintf( convert, "%lf", obj.getDouble() );
228 stream << convert << "D";
229 break;
230 case Formattable::kLong :
231 stream << obj.getLong() << "L";
232 break;
233 case Formattable::kString:
234 stream << "\"" << obj.getString(buffer) << "\"";
235 break;
236 case Formattable::kArray:
237 int32_t i, count;
238 const Formattable* array;
239 array = obj.getArray(count);
240 stream << "[";
241 for (i=0; i<count; ++i) stream << array[i] << ( (i==(count-1)) ? "" : ", " );
242 stream << "]";
243 break;
244 default:
245 stream << "INVALID_Formattable";
246 }
247 return stream;
248 }
249 #endif /* defined(_DEBUG) */
250 #endif
251
PatternTest()252 void TestMessageFormat::PatternTest()
253 {
254 Formattable testArgs[] = {
255 Formattable(double(1)), Formattable(double(3456)),
256 Formattable("Disk"), Formattable(UDate((int32_t)1000000000L), Formattable::kIsDate)
257 };
258 UnicodeString testCases[] = {
259 "Quotes '', '{', 'a' {0} '{0}'",
260 "Quotes '', '{', 'a' {0,number} '{0}'",
261 "'{'1,number,'#',##} {1,number,'#',##}",
262 "There are {1} files on {2} at {3}.",
263 "On {2}, there are {1} files, with {0,number,currency}.",
264 "'{1,number,percent}', {1,number,percent},",
265 "'{1,date,full}', {1,date,full},",
266 "'{3,date,full}', {3,date,full},",
267 "'{1,number,#,##}' {1,number,#,##}",
268 };
269
270 // ICU 4.8 returns the original pattern (testCases),
271 // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns).
272 /*UnicodeString testResultPatterns[] = {
273 "Quotes '', '{', a {0} '{'0}",
274 "Quotes '', '{', a {0,number} '{'0}",
275 "'{'1,number,#,##} {1,number,'#'#,##}",
276 "There are {1} files on {2} at {3}.",
277 "On {2}, there are {1} files, with {0,number,currency}.",
278 "'{'1,number,percent}, {1,number,percent},",
279 "'{'1,date,full}, {1,date,full},",
280 "'{'3,date,full}, {3,date,full},",
281 "'{'1,number,#,##} {1,number,#,##}"
282 };*/
283
284 UnicodeString testResultStrings[] = {
285 "Quotes ', {, 'a' 1 {0}",
286 "Quotes ', {, 'a' 1 {0}",
287 "{1,number,'#',##} #34,56",
288 "There are 3,456 files on Disk at 1/12/70, 5:46 AM.",
289 "On Disk, there are 3,456 files, with $1.00.",
290 "{1,number,percent}, 345,600%,",
291 "{1,date,full}, Wednesday, December 31, 1969,",
292 "{3,date,full}, Monday, January 12, 1970,",
293 "{1,number,#,##} 34,56"
294 };
295
296
297 for (int32_t i = 0; i < 9; ++i) {
298 //it_out << "\nPat in: " << testCases[i]);
299
300 MessageFormat *form = 0;
301 UErrorCode success = U_ZERO_ERROR;
302 UnicodeString buffer;
303 form = new MessageFormat(testCases[i], Locale::getUS(), success);
304 if (U_FAILURE(success)) {
305 dataerrln("MessageFormat creation failed.#1 - %s", u_errorName(success));
306 logln(((UnicodeString)"MessageFormat for ") + testCases[i] + " creation failed.\n");
307 continue;
308 }
309 // ICU 4.8 returns the original pattern (testCases),
310 // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns).
311 if (form->toPattern(buffer) != testCases[i]) {
312 // Note: An alternative test would be to build MessagePattern objects for
313 // both the input and output patterns and compare them, taking SKIP_SYNTAX etc.
314 // into account.
315 // (Too much trouble...)
316 errln(UnicodeString("TestMessageFormat::PatternTest failed test #2, i = ") + i);
317 //form->toPattern(buffer);
318 errln(((UnicodeString)" Orig: ") + testCases[i]);
319 errln(((UnicodeString)" Exp: ") + testCases[i]);
320 errln(((UnicodeString)" Got: ") + buffer);
321 }
322
323 //it_out << "Pat out: " << form->toPattern(buffer));
324 UnicodeString result;
325 int32_t count = 4;
326 FieldPosition fieldpos(FieldPosition::DONT_CARE);
327 form->format(testArgs, count, result, fieldpos, success);
328 if (U_FAILURE(success)) {
329 dataerrln("MessageFormat failed test #3 - %s", u_errorName(success));
330 logln("TestMessageFormat::PatternTest failed test #3");
331 continue;
332 }
333 if (result != testResultStrings[i]) {
334 errln("TestMessageFormat::PatternTest failed test #4");
335 logln("TestMessageFormat::PatternTest failed #4.");
336 logln(UnicodeString(" Result: ") + result );
337 logln(UnicodeString(" Expected: ") + testResultStrings[i] );
338 }
339
340
341 //it_out << "Result: " << result);
342 #if 0
343 /* TODO: Look at this test and see if this is still a valid test */
344 logln("---------------- test parse ----------------");
345
346 form->toPattern(buffer);
347 logln("MSG pattern for parse: " + buffer);
348
349 int32_t parseCount = 0;
350 Formattable* values = form->parse(result, parseCount, success);
351 if (U_FAILURE(success)) {
352 errln("MessageFormat failed test #5");
353 logln(UnicodeString("MessageFormat failed test #5 with error code ")+(int32_t)success);
354 } else if (parseCount != count) {
355 errln("MSG count not %d as expected. Got %d", count, parseCount);
356 }
357 UBool failed = FALSE;
358 for (int32_t j = 0; j < parseCount; ++j) {
359 if (values == 0 || testArgs[j] != values[j]) {
360 errln(((UnicodeString)"MSG testargs[") + j + "]: " + toString(testArgs[j]));
361 errln(((UnicodeString)"MSG values[") + j + "] : " + toString(values[j]));
362 failed = TRUE;
363 }
364 }
365 if (failed)
366 errln("MessageFormat failed test #6");
367 #endif
368 delete form;
369 }
370 }
371
sample()372 void TestMessageFormat::sample()
373 {
374 MessageFormat *form = 0;
375 UnicodeString buffer1, buffer2;
376 UErrorCode success = U_ZERO_ERROR;
377 form = new MessageFormat("There are {0} files on {1}", success);
378 if (U_FAILURE(success)) {
379 errln("Err: Message format creation failed");
380 logln("Sample message format creation failed.");
381 return;
382 }
383 UnicodeString abc("abc");
384 UnicodeString def("def");
385 Formattable testArgs1[] = { abc, def };
386 FieldPosition fieldpos(FieldPosition::DONT_CARE);
387 assertEquals("format",
388 "There are abc files on def",
389 form->format(testArgs1, 2, buffer2, fieldpos, success));
390 assertSuccess("format", success);
391 delete form;
392 }
393
testStaticFormat()394 void TestMessageFormat::testStaticFormat()
395 {
396 UErrorCode err = U_ZERO_ERROR;
397 Formattable arguments[] = {
398 (int32_t)7,
399 Formattable(UDate(8.71068e+011), Formattable::kIsDate),
400 "a disturbance in the Force"
401 };
402
403 UnicodeString result;
404 result = MessageFormat::format(
405 "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
406 arguments,
407 3,
408 result,
409 err);
410
411 if (U_FAILURE(err)) {
412 dataerrln("TestMessageFormat::testStaticFormat #1 - %s", u_errorName(err));
413 logln(UnicodeString("TestMessageFormat::testStaticFormat failed test #1 with error code ")+(int32_t)err);
414 return;
415 }
416
417 const UnicodeString expected(
418 "At 12:20:00 PM on Aug 8, 1997, there was a disturbance in the Force on planet 7.", "");
419 if (result != expected) {
420 errln("TestMessageFormat::testStaticFormat failed on test");
421 logln( UnicodeString(" Result: ") + result );
422 logln( UnicodeString(" Expected: ") + expected );
423 }
424 }
425
426 /* When the default locale is tr, make sure that the pattern can still be parsed. */
TestTurkishCasing()427 void TestMessageFormat::TestTurkishCasing()
428 {
429 UErrorCode err = U_ZERO_ERROR;
430 Locale saveDefaultLocale;
431 Locale::setDefault( Locale("tr"), err );
432
433 Formattable arguments[] = {
434 (int32_t)7,
435 Formattable(UDate(8.71068e+011), Formattable::kIsDate),
436 "a disturbance in the Force"
437 };
438
439 UnicodeString result;
440 result = MessageFormat::format(
441 "At {1,TIME} on {1,DATE,SHORT}, there was {2} on planet {0,NUMBER,INTEGER}.",
442 arguments,
443 3,
444 result,
445 err);
446
447 if (U_FAILURE(err)) {
448 dataerrln("TestTurkishCasing #1 with error code %s", u_errorName(err));
449 return;
450 }
451
452 const UnicodeString expected(
453 "At 12:20:00 on 8.08.1997, there was a disturbance in the Force on planet 7.", "");
454 if (result != expected) {
455 errln("TestTurkishCasing failed on test");
456 errln( UnicodeString(" Result: ") + result );
457 errln( UnicodeString(" Expected: ") + expected );
458 }
459 Locale::setDefault( saveDefaultLocale, err );
460 }
461
testSimpleFormat()462 void TestMessageFormat::testSimpleFormat(/* char* par */)
463 {
464 logln("running TestMessageFormat::testSimpleFormat");
465
466 UErrorCode err = U_ZERO_ERROR;
467
468 Formattable testArgs1[] = {(int32_t)0, "MyDisk"};
469 Formattable testArgs2[] = {(int32_t)1, "MyDisk"};
470 Formattable testArgs3[] = {(int32_t)12, "MyDisk"};
471
472 MessageFormat* form = new MessageFormat(
473 "The disk \"{1}\" contains {0} file(s).", err);
474
475 UnicodeString string;
476 FieldPosition ignore(FieldPosition::DONT_CARE);
477 form->format(testArgs1, 2, string, ignore, err);
478 if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 0 file(s).") {
479 dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #1 - ") + u_errorName(err));
480 }
481
482 ignore.setField(FieldPosition::DONT_CARE);
483 string.remove();
484 form->format(testArgs2, 2, string, ignore, err);
485 if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 1 file(s).") {
486 logln(string);
487 dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #2")+string + " - " + u_errorName(err));
488 }
489
490 ignore.setField(FieldPosition::DONT_CARE);
491 string.remove();
492 form->format(testArgs3, 2, string, ignore, err);
493 if (U_FAILURE(err) || string != "The disk \"MyDisk\" contains 12 file(s).") {
494 dataerrln(UnicodeString("TestMessageFormat::testSimpleFormat failed on test #3")+string + " - " + u_errorName(err));
495 }
496
497 delete form;
498 }
499
testMsgFormatChoice()500 void TestMessageFormat::testMsgFormatChoice(/* char* par */)
501 {
502 logln("running TestMessageFormat::testMsgFormatChoice");
503
504 UErrorCode err = U_ZERO_ERROR;
505
506 MessageFormat* form = new MessageFormat("The disk \"{1}\" contains {0}.", err);
507 double filelimits[] = {0,1,2};
508 UnicodeString filepart[] = {"no files","one file","{0,number} files"};
509 ChoiceFormat* fileform = new ChoiceFormat(filelimits, filepart, 3);
510 form->setFormat(1,*fileform); // NOT zero, see below
511 //is the format adopted?
512
513 FieldPosition ignore(FieldPosition::DONT_CARE);
514 UnicodeString string;
515 Formattable testArgs1[] = {(int32_t)0, "MyDisk"};
516 form->format(testArgs1, 2, string, ignore, err);
517 if (string != "The disk \"MyDisk\" contains no files.") {
518 errln("TestMessageFormat::testMsgFormatChoice failed on test #1");
519 }
520
521 ignore.setField(FieldPosition::DONT_CARE);
522 string.remove();
523 Formattable testArgs2[] = {(int32_t)1, "MyDisk"};
524 form->format(testArgs2, 2, string, ignore, err);
525 if (string != "The disk \"MyDisk\" contains one file.") {
526 errln("TestMessageFormat::testMsgFormatChoice failed on test #2");
527 }
528
529 ignore.setField(FieldPosition::DONT_CARE);
530 string.remove();
531 Formattable testArgs3[] = {(int32_t)1273, "MyDisk"};
532 form->format(testArgs3, 2, string, ignore, err);
533 if (string != "The disk \"MyDisk\" contains 1,273 files.") {
534 dataerrln("TestMessageFormat::testMsgFormatChoice failed on test #3 - %s", u_errorName(err));
535 }
536
537 delete form;
538 delete fileform;
539 }
540
541
testMsgFormatPlural()542 void TestMessageFormat::testMsgFormatPlural(/* char* par */)
543 {
544 logln("running TestMessageFormat::testMsgFormatPlural");
545
546 UErrorCode err = U_ZERO_ERROR;
547 UnicodeString t1("{0, plural, one{C''est # fichier} other{Ce sont # fichiers}} dans la liste.");
548 UnicodeString t2("{argument, plural, one{C''est # fichier} other {Ce sont # fichiers}} dans la liste.");
549 UnicodeString t3("There {0, plural, one{is # zavod}few{are {0, number,###.0} zavoda} other{are # zavodov}} in the directory.");
550 UnicodeString t4("There {argument, plural, one{is # zavod}few{are {argument, number,###.0} zavoda} other{are #zavodov}} in the directory.");
551 UnicodeString t5("{0, plural, one {{0, number,C''est #,##0.0# fichier}} other {Ce sont # fichiers}} dans la liste.");
552 MessageFormat* mfNum = new MessageFormat(t1, Locale("fr"), err);
553 if (U_FAILURE(err)) {
554 dataerrln("TestMessageFormat::testMsgFormatPlural #1 - argumentIndex - %s", u_errorName(err));
555 logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #1 with error code ")+(int32_t)err);
556 return;
557 }
558 Formattable testArgs1((int32_t)0);
559 FieldPosition ignore(FieldPosition::DONT_CARE);
560 UnicodeString numResult1;
561 mfNum->format(&testArgs1, 1, numResult1, ignore, err);
562
563 MessageFormat* mfAlpha = new MessageFormat(t2, Locale("fr"), err);
564 UnicodeString argName[] = {UnicodeString("argument")};
565 UnicodeString argNameResult;
566 mfAlpha->format(argName, &testArgs1, 1, argNameResult, err);
567 if (U_FAILURE(err)) {
568 dataerrln("TestMessageFormat::testMsgFormatPlural #1 - argumentName - %s", u_errorName(err));
569 logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #1 with error code ")+(int32_t)err);
570 delete mfNum;
571 return;
572 }
573 if ( numResult1 != argNameResult){
574 errln("TestMessageFormat::testMsgFormatPlural #1");
575 logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
576 }
577 if ( numResult1 != UnicodeString("C\'est 0 fichier dans la liste.")) {
578 errln("TestMessageFormat::testMsgFormatPlural #1");
579 logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
580 }
581 err = U_ZERO_ERROR;
582
583 delete mfNum;
584 delete mfAlpha;
585
586 MessageFormat* mfNum2 = new MessageFormat(t3, Locale("uk"), err);
587 numResult1.remove();
588 Formattable testArgs2((int32_t)4);
589 mfNum2->format(&testArgs2, 1, numResult1, ignore, err);
590 MessageFormat* mfAlpha2 = new MessageFormat(t4, Locale("uk"), err);
591 argNameResult.remove();
592 mfAlpha2->format(argName, &testArgs2, 1, argNameResult, err);
593
594 if (U_FAILURE(err)) {
595 errln("TestMessageFormat::testMsgFormatPlural #2 - argumentName");
596 logln(UnicodeString("TestMessageFormat::testMsgFormatPlural #2 with error code ")+(int32_t)err);
597 delete mfNum2;
598 return;
599 }
600 if ( numResult1 != argNameResult){
601 errln("TestMessageFormat::testMsgFormatPlural #2");
602 logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
603 }
604 if ( numResult1 != UnicodeString("There are 4,0 zavoda in the directory.")) {
605 errln("TestMessageFormat::testMsgFormatPlural #2");
606 logln(UnicodeString("The results of argumentName and argumentIndex are not the same."));
607 }
608
609 delete mfNum2;
610 delete mfAlpha2;
611
612 // nested formats
613 err = U_ZERO_ERROR;
614 MessageFormat* msgFmt = new MessageFormat(t5, Locale("fr"), err);
615 if (U_FAILURE(err)) {
616 errln("TestMessageFormat::test nested PluralFormat with argumentName");
617 logln(UnicodeString("TestMessageFormat::test nested PluralFormat with error code ")+(int32_t)err);
618 delete msgFmt;
619 return;
620 }
621 Formattable testArgs3((int32_t)0);
622 argNameResult.remove();
623 msgFmt->format(&testArgs3, 1, argNameResult, ignore, err);
624 if (U_FAILURE(err)) {
625 errln("TestMessageFormat::test nested PluralFormat with argumentName");
626 }
627 if ( argNameResult!= UnicodeString("C'est 0,0 fichier dans la liste.")) {
628 errln(UnicodeString("TestMessageFormat::test nested named PluralFormat: ") + argNameResult);
629 logln(UnicodeString("The unexpected nested named PluralFormat."));
630 }
631 delete msgFmt;
632 }
633
testApostropheInPluralAndSelect()634 void TestMessageFormat::testApostropheInPluralAndSelect() {
635 UErrorCode errorCode = U_ZERO_ERROR;
636 MessageFormat msgFmt(UNICODE_STRING_SIMPLE(
637 "abc_{0,plural,other{#'#'#'{'#''}}_def_{1,select,other{sel'}'ect''}}_xyz"),
638 Locale::getEnglish(),
639 errorCode);
640 if (U_FAILURE(errorCode)) {
641 errln("MessageFormat constructor failed - %s\n", u_errorName(errorCode));
642 return;
643 }
644 UnicodeString expected = UNICODE_STRING_SIMPLE("abc_3#3{3'_def_sel}ect'_xyz");
645 Formattable args[] = { (int32_t)3, UNICODE_STRING_SIMPLE("x") };
646 internalFormat(
647 &msgFmt, args, 2, expected,
648 "MessageFormat with apostrophes in plural/select arguments failed:\n");
649 }
650
internalFormat(MessageFormat * msgFmt,Formattable * args,int32_t numOfArgs,UnicodeString expected,const char * errMsg)651 void TestMessageFormat::internalFormat(MessageFormat* msgFmt ,
652 Formattable* args , int32_t numOfArgs ,
653 UnicodeString expected, const char* errMsg)
654 {
655 UnicodeString result;
656 FieldPosition ignore(FieldPosition::DONT_CARE);
657 UErrorCode status = U_ZERO_ERROR;
658
659 //Format with passed arguments
660 msgFmt->format( args , numOfArgs , result, ignore, status);
661 if (U_FAILURE(status)) {
662 dataerrln( "%s error while formatting with ErrorCode as %s" ,errMsg, u_errorName(status) );
663 }
664 //Compare expected with obtained result
665 if ( result!= expected ) {
666 UnicodeString err = UnicodeString(errMsg);
667 err+= UnicodeString(":Unexpected Result \n Expected: " + expected + "\n Obtained: " + result + "\n");
668 dataerrln(err);
669 }
670 }
671
internalCreate(UnicodeString pattern,Locale locale,UErrorCode & status,char * errMsg)672 MessageFormat* TestMessageFormat::internalCreate(
673 UnicodeString pattern ,Locale locale ,UErrorCode &status , char* errMsg)
674 {
675 //Create the MessageFormat with simple SelectFormat
676 MessageFormat* msgFmt = new MessageFormat(pattern, locale, status);
677 if (U_FAILURE(status)) {
678 dataerrln( "%s error while constructing with ErrorCode as %s" ,errMsg, u_errorName(status) );
679 logln(UnicodeString("TestMessageFormat::testMsgFormatSelect #1 with error code ")+(int32_t)status);
680 return NULL;
681 }
682 return msgFmt;
683 }
684
testMsgFormatSelect()685 void TestMessageFormat::testMsgFormatSelect(/* char* par */)
686 {
687 logln("running TestMessageFormat::testMsgFormatSelect");
688
689 UErrorCode err = U_ZERO_ERROR;
690 //French Pattern
691 UnicodeString t1("{0} est {1, select, female {all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.");
692
693 err = U_ZERO_ERROR;
694 //Create the MessageFormat with simple French pattern
695 MessageFormat* msgFmt1 = internalCreate(t1.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t1");
696 if (!U_FAILURE(err)) {
697 //Arguments
698 Formattable testArgs10[] = {"Kirti","female"};
699 Formattable testArgs11[] = {"Victor","other"};
700 Formattable testArgs12[] = {"Ash","unknown"};
701 Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
702 UnicodeString exp[] = {
703 "Kirti est all\\u00E9e \\u00E0 Paris." ,
704 "Victor est all\\u00E9 \\u00E0 Paris.",
705 "Ash est all\\u00E9 \\u00E0 Paris."};
706 //Format
707 for( int i=0; i< 3; i++){
708 internalFormat( msgFmt1 , testArgs[i], 2, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t1");
709 }
710 }
711 delete msgFmt1;
712
713 //Quoted French Pattern
714 UnicodeString t2("{0} est {1, select, female {all\\u00E9e c''est} other {all\\u00E9 c''est}} \\u00E0 Paris.");
715 err = U_ZERO_ERROR;
716 //Create the MessageFormat with Quoted French pattern
717 MessageFormat* msgFmt2 = internalCreate(t2.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t2");
718 if (!U_FAILURE(err)) {
719 //Arguments
720 Formattable testArgs10[] = {"Kirti","female"};
721 Formattable testArgs11[] = {"Victor","other"};
722 Formattable testArgs12[] = {"Ash","male"};
723 Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
724 UnicodeString exp[] = {
725 "Kirti est all\\u00E9e c'est \\u00E0 Paris." ,
726 "Victor est all\\u00E9 c'est \\u00E0 Paris.",
727 "Ash est all\\u00E9 c'est \\u00E0 Paris."};
728 //Format
729 for( int i=0; i< 3; i++){
730 internalFormat( msgFmt2 , testArgs[i], 2, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t2");
731 }
732 }
733 delete msgFmt2;
734
735 //English Pattern
736 UnicodeString t3("{0, select , male {MALE FR company} female {FEMALE FR company} other {FR otherValue}} published new books.");
737 err = U_ZERO_ERROR;
738 //Create the MessageFormat with English pattern
739 MessageFormat* msgFmt3 = internalCreate(t3, Locale("en"),err,(char*)"From TestMessageFormat::TestSelectFormat create t3");
740 if (!U_FAILURE(err)) {
741 //Arguments
742 Formattable testArgs10[] = {"female"};
743 Formattable testArgs11[] = {"other"};
744 Formattable testArgs12[] = {"male"};
745 Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
746 UnicodeString exp[] = {
747 "FEMALE FR company published new books." ,
748 "FR otherValue published new books.",
749 "MALE FR company published new books."};
750 //Format
751 for( int i=0; i< 3; i++){
752 internalFormat( msgFmt3 , testArgs[i], 1, exp[i] ,(char*)"From TestMessageFormat::testSelectFormat format t3");
753 }
754 }
755 delete msgFmt3;
756
757 //Nested patterns with plural, number ,choice ,select format etc.
758 //Select Format with embedded number format
759 UnicodeString t4("{0} est {1, select, female {{2,number,integer} all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.");
760 err = U_ZERO_ERROR;
761 //Create the MessageFormat with Select Format with embedded number format (nested pattern)
762 MessageFormat* msgFmt4 = internalCreate(t4.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t4");
763 if (!U_FAILURE(err)) {
764 //Arguments
765 Formattable testArgs10[] = {"Kirti","female",(int32_t)6};
766 Formattable testArgs11[] = {"Kirti","female",100.100};
767 Formattable testArgs12[] = {"Kirti","other",(int32_t)6};
768 Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12};
769 UnicodeString exp[] = {
770 "Kirti est 6 all\\u00E9e \\u00E0 Paris." ,
771 "Kirti est 100 all\\u00E9e \\u00E0 Paris.",
772 "Kirti est all\\u00E9 \\u00E0 Paris."};
773 //Format
774 for( int i=0; i< 3; i++){
775 internalFormat( msgFmt4 , testArgs[i], 3, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t4");
776 }
777 }
778 delete msgFmt4;
779
780 //Plural format with embedded select format
781 UnicodeString t5("{0} {1, plural, one {est {2, select, female {all\\u00E9e} other {all\\u00E9}}} other {sont {2, select, female {all\\u00E9es} other {all\\u00E9s}}}} \\u00E0 Paris.");
782 err = U_ZERO_ERROR;
783 //Create the MessageFormat with Plural format with embedded select format(nested pattern)
784 MessageFormat* msgFmt5 = internalCreate(t5.unescape(), Locale("fr"),err,(char*)"From TestMessageFormat::TestSelectFormat create t5");
785 // with no data the above should fail but it seems to construct an invalid MessageFormat with no reported error. See #13079
786 if (!U_FAILURE(err)) {
787 //Arguments
788 Formattable testArgs10[] = {"Kirti",(int32_t)6,"female"};
789 Formattable testArgs11[] = {"Kirti",(int32_t)1,"female"};
790 Formattable testArgs12[] = {"Ash",(int32_t)1,"other"};
791 Formattable testArgs13[] = {"Ash",(int32_t)5,"other"};
792 Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12,testArgs13};
793 UnicodeString exp[] = {
794 "Kirti sont all\\u00E9es \\u00E0 Paris." ,
795 "Kirti est all\\u00E9e \\u00E0 Paris.",
796 "Ash est all\\u00E9 \\u00E0 Paris.",
797 "Ash sont all\\u00E9s \\u00E0 Paris."};
798 //Format
799 for( int i=0; i< 4; i++){
800 internalFormat( msgFmt5 , testArgs[i], 3, exp[i].unescape() ,(char*)"From TestMessageFormat::testSelectFormat format t5");
801 }
802 }
803 delete msgFmt5;
804
805 err = U_ZERO_ERROR;
806 //Select, plural, and number formats heavily nested
807 UnicodeString t6("{0} und {1, select, female {{2, plural, one {{3, select, female {ihre Freundin} other {ihr Freund}} } other {ihre {2, number, integer} {3, select, female {Freundinnen} other {Freunde}} } }} other{{2, plural, one {{3, select, female {seine Freundin} other {sein Freund}}} other {seine {2, number, integer} {3, select, female {Freundinnen} other {Freunde}}}}} } gingen nach Paris.");
808 //Create the MessageFormat with Select, plural, and number formats heavily nested
809 MessageFormat* msgFmt6 = internalCreate(t6, Locale("de"),err,(char*)"From TestMessageFormat::TestSelectFormat create t6");
810 if (!U_FAILURE(err)) {
811 //Arguments
812 Formattable testArgs10[] = {"Kirti","other",(int32_t)1,"other"};
813 Formattable testArgs11[] = {"Kirti","other",(int32_t)6,"other"};
814 Formattable testArgs12[] = {"Kirti","other",(int32_t)1,"female"};
815 Formattable testArgs13[] = {"Kirti","other",(int32_t)3,"female"};
816 Formattable testArgs14[] = {"Kirti","female",(int32_t)1,"female"};
817 Formattable testArgs15[] = {"Kirti","female",(int32_t)5,"female"};
818 Formattable testArgs16[] = {"Kirti","female",(int32_t)1,"other"};
819 Formattable testArgs17[] = {"Kirti","female",(int32_t)5,"other"};
820 Formattable testArgs18[] = {"Kirti","mixed",(int32_t)1,"mixed"};
821 Formattable testArgs19[] = {"Kirti","mixed",(int32_t)1,"other"};
822 Formattable testArgs20[] = {"Kirti","female",(int32_t)1,"mixed"};
823 Formattable testArgs21[] = {"Kirti","mixed",(int32_t)5,"mixed"};
824 Formattable testArgs22[] = {"Kirti","mixed",(int32_t)5,"other"};
825 Formattable testArgs23[] = {"Kirti","female",(int32_t)5,"mixed"};
826 Formattable* testArgs[] = {testArgs10,testArgs11,testArgs12,testArgs13,
827 testArgs14,testArgs15,testArgs16,testArgs17,
828 testArgs18,testArgs19,testArgs20,testArgs21,
829 testArgs22,testArgs23 };
830 UnicodeString exp[] = {
831 "Kirti und sein Freund gingen nach Paris." ,
832 "Kirti und seine 6 Freunde gingen nach Paris." ,
833 "Kirti und seine Freundin gingen nach Paris.",
834 "Kirti und seine 3 Freundinnen gingen nach Paris.",
835 "Kirti und ihre Freundin gingen nach Paris.",
836 "Kirti und ihre 5 Freundinnen gingen nach Paris.",
837 "Kirti und ihr Freund gingen nach Paris.",
838 "Kirti und ihre 5 Freunde gingen nach Paris.",
839 "Kirti und sein Freund gingen nach Paris.",
840 "Kirti und sein Freund gingen nach Paris.",
841 "Kirti und ihr Freund gingen nach Paris.",
842 "Kirti und seine 5 Freunde gingen nach Paris." ,
843 "Kirti und seine 5 Freunde gingen nach Paris." ,
844 "Kirti und ihre 5 Freunde gingen nach Paris."
845 };
846 //Format
847 for( int i=0; i< 14; i++){
848 internalFormat( msgFmt6 , testArgs[i], 4, exp[i] ,(char*)"From TestMessageFormat::testSelectFormat format t6");
849 }
850 }
851 delete msgFmt6;
852 }
853
854 //---------------------------------
855 // API Tests
856 //---------------------------------
857
testCopyConstructor()858 void TestMessageFormat::testCopyConstructor()
859 {
860 UErrorCode success = U_ZERO_ERROR;
861 MessageFormat *x = new MessageFormat("There are {0} files on {1}", success);
862 MessageFormat *z = new MessageFormat("There are {0} files on {1} created", success);
863 MessageFormat *y = 0;
864 y = new MessageFormat(*x);
865 if ( (*x == *y) &&
866 (*x != *z) &&
867 (*y != *z) )
868 logln("First test (operator ==): Passed!");
869 else {
870 errln("TestMessageFormat::testCopyConstructor failed #1");
871 logln("First test (operator ==): Failed!");
872 }
873 if ( ((*x == *y) && (*y == *x)) &&
874 ((*x != *z) && (*z != *x)) &&
875 ((*y != *z) && (*z != *y)) )
876 logln("Second test (equals): Passed!");
877 else {
878 errln("TestMessageFormat::testCopyConstructor failed #2");
879 logln("Second test (equals): Failed!");
880 }
881
882 delete x;
883 delete y;
884 delete z;
885 }
886
887
testAssignment()888 void TestMessageFormat::testAssignment()
889 {
890 UErrorCode success = U_ZERO_ERROR;
891 MessageFormat *x = new MessageFormat("There are {0} files on {1}", success);
892 MessageFormat *z = new MessageFormat("There are {0} files on {1} created", success);
893 MessageFormat *y = new MessageFormat("There are {0} files on {1} created", success);
894 *y = *x;
895 if ( (*x == *y) &&
896 (*x != *z) &&
897 (*y != *z) )
898 logln("First test (operator ==): Passed!");
899 else {
900 errln( "TestMessageFormat::testAssignment failed #1");
901 logln("First test (operator ==): Failed!");
902 }
903 if ( ((*x == *y) && (*y == *x)) &&
904 ((*x != *z) && (*z != *x)) &&
905 ((*y != *z) && (*z != *y)) )
906 logln("Second test (equals): Passed!");
907 else {
908 errln("TestMessageFormat::testAssignment failed #2");
909 logln("Second test (equals): Failed!");
910 }
911
912 delete x;
913 delete y;
914 delete z;
915 }
916
testClone()917 void TestMessageFormat::testClone()
918 {
919 UErrorCode success = U_ZERO_ERROR;
920 MessageFormat *x = new MessageFormat("There are {0} files on {1}", success);
921 MessageFormat *z = new MessageFormat("There are {0} files on {1} created", success);
922 MessageFormat *y = 0;
923 y = (MessageFormat*)x->clone();
924 if ( (*x == *y) &&
925 (*x != *z) &&
926 (*y != *z) )
927 logln("First test (operator ==): Passed!");
928 else {
929 errln("TestMessageFormat::testClone failed #1");
930 logln("First test (operator ==): Failed!");
931 }
932 if ( ((*x == *y) && (*y == *x)) &&
933 ((*x != *z) && (*z != *x)) &&
934 ((*y != *z) && (*z != *y)) )
935 logln("Second test (equals): Passed!");
936 else {
937 errln("TestMessageFormat::testClone failed #2");
938 logln("Second test (equals): Failed!");
939 }
940
941 delete x;
942 delete y;
943 delete z;
944 }
945
testEquals()946 void TestMessageFormat::testEquals()
947 {
948 UErrorCode success = U_ZERO_ERROR;
949 MessageFormat x("There are {0} files on {1}", success);
950 MessageFormat y("There are {0} files on {1}", success);
951 if (!(x == y)) {
952 errln( "TestMessageFormat::testEquals failed #1");
953 logln("First test (operator ==): Failed!");
954 }
955
956 }
957
testNotEquals()958 void TestMessageFormat::testNotEquals()
959 {
960 UErrorCode success = U_ZERO_ERROR;
961 MessageFormat x("There are {0} files on {1}", success);
962 MessageFormat y(x);
963 y.setLocale(Locale("fr"));
964 if (!(x != y)) {
965 errln( "TestMessageFormat::testEquals failed #1");
966 logln("First test (operator !=): Failed!");
967 }
968 y = x;
969 y.applyPattern("There are {0} files on {1} the disk", success);
970 if (!(x != y)) {
971 errln( "TestMessageFormat::testEquals failed #1");
972 logln("Second test (operator !=): Failed!");
973 }
974 }
975
976
testSetLocale()977 void TestMessageFormat::testSetLocale()
978 {
979 UErrorCode err = U_ZERO_ERROR;
980 GregorianCalendar cal(err);
981 Formattable arguments[] = {
982 456.83,
983 Formattable(UDate(8.71068e+011), Formattable::kIsDate),
984 "deposit"
985 };
986
987 UnicodeString result;
988
989 //UnicodeString formatStr = "At {1,time} on {1,date}, you made a {2} of {0,number,currency}.";
990 UnicodeString formatStr = "At <time> on {1,date}, you made a {2} of {0,number,currency}.";
991 // {sfb} to get $, would need Locale::US, not Locale::ENGLISH
992 // Just use unlocalized currency symbol.
993 //UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of $456.83.";
994 UnicodeString compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of ";
995 compareStrEng += (UChar) 0x00a4;
996 compareStrEng += "456.83.";
997 // {sfb} to get DM, would need Locale::GERMANY, not Locale::GERMAN
998 // Just use unlocalized currency symbol.
999 //UnicodeString compareStrGer = "At <time> on 08.08.1997, you made a deposit of 456,83 DM.";
1000 UnicodeString compareStrGer = "At <time> on 08.08.1997, you made a deposit of ";
1001 compareStrGer += "456,83";
1002 compareStrGer += (UChar) 0x00a0;
1003 compareStrGer += "XXX.";
1004
1005 MessageFormat msg( formatStr, err);
1006 result = "";
1007 FieldPosition pos(FieldPosition::DONT_CARE);
1008 result = msg.format(
1009 arguments,
1010 3,
1011 result,
1012 pos,
1013 err);
1014
1015 logln(result);
1016 if (result != compareStrEng) {
1017 char bbuf[96];
1018 result.extract(0, result.length(), bbuf, sizeof(bbuf));
1019 dataerrln("*** MSG format err. - %s; result was %s", u_errorName(err), bbuf);
1020 }
1021
1022 msg.setLocale(Locale::getEnglish());
1023 UBool getLocale_ok = TRUE;
1024 if (msg.getLocale() != Locale::getEnglish()) {
1025 errln("*** MSG getLocale err.");
1026 getLocale_ok = FALSE;
1027 }
1028
1029 msg.setLocale(Locale::getGerman());
1030
1031 if (msg.getLocale() != Locale::getGerman()) {
1032 errln("*** MSG getLocal err.");
1033 getLocale_ok = FALSE;
1034 }
1035
1036 msg.applyPattern( formatStr, err);
1037
1038 pos.setField(0);
1039 result = "";
1040 result = msg.format(
1041 arguments,
1042 3,
1043 result,
1044 pos,
1045 err);
1046
1047 logln(result);
1048 if (result == compareStrGer) {
1049 logln("MSG setLocale tested.");
1050 }else{
1051 dataerrln( "*** MSG setLocale err. - %s", u_errorName(err));
1052 }
1053
1054 if (getLocale_ok) {
1055 logln("MSG getLocale tested.");
1056 }
1057 }
1058
testFormat()1059 void TestMessageFormat::testFormat()
1060 {
1061 UErrorCode err = U_ZERO_ERROR;
1062 GregorianCalendar cal(err);
1063
1064 const Formattable ftarray[] =
1065 {
1066 Formattable( UDate(8.71068e+011), Formattable::kIsDate )
1067 };
1068 const int32_t ft_cnt = UPRV_LENGTHOF(ftarray);
1069 Formattable ft_arr( ftarray, ft_cnt );
1070
1071 Formattable* fmt = new Formattable(UDate(8.71068e+011), Formattable::kIsDate);
1072
1073 UnicodeString result;
1074
1075 //UnicodeString formatStr = "At {1,time} on {1,date}, you made a {2} of {0,number,currency}.";
1076 UnicodeString formatStr = "On {0,date}, it began.";
1077 UnicodeString compareStr = "On Aug 8, 1997, it began.";
1078
1079 err = U_ZERO_ERROR;
1080 MessageFormat msg( formatStr, err);
1081 FieldPosition fp(FieldPosition::DONT_CARE);
1082
1083 result = "";
1084 fp = 0;
1085 result = msg.format(
1086 *fmt,
1087 result,
1088 //FieldPosition(0),
1089 fp,
1090 err);
1091
1092 if (err != U_ILLEGAL_ARGUMENT_ERROR) {
1093 dataerrln("*** MSG format without expected error code. - %s", u_errorName(err));
1094 }
1095 err = U_ZERO_ERROR;
1096
1097 result = "";
1098 fp = 0;
1099 result = msg.format(
1100 ft_arr,
1101 result,
1102 //FieldPosition(0),
1103 fp,
1104 err);
1105
1106 logln("MSG format( Formattable&, ... ) expected:" + compareStr);
1107 logln("MSG format( Formattable&, ... ) result:" + result);
1108 if (result != compareStr) {
1109 dataerrln("*** MSG format( Formattable&, .... ) err. - %s", u_errorName(err));
1110 }else{
1111 logln("MSG format( Formattable&, ... ) tested.");
1112 }
1113
1114 delete fmt;
1115
1116 }
1117
testParse()1118 void TestMessageFormat::testParse()
1119 {
1120 UErrorCode err = U_ZERO_ERROR;
1121 int32_t count;
1122 UnicodeString msgFormatString = "{0} =sep= {1}";
1123 MessageFormat msg( msgFormatString, err);
1124 UnicodeString source = "abc =sep= def";
1125 UnicodeString tmp1, tmp2;
1126
1127 Formattable* fmt_arr = msg.parse( source, count, err );
1128 if (U_FAILURE(err) || (!fmt_arr)) {
1129 errln("*** MSG parse (ustring, count, err) error.");
1130 }else{
1131 logln("MSG parse -- count: %d", count);
1132 if (count != 2) {
1133 errln("*** MSG parse (ustring, count, err) count err.");
1134 }else{
1135 if ((fmt_arr[0].getType() == Formattable::kString)
1136 && (fmt_arr[1].getType() == Formattable::kString)
1137 && (fmt_arr[0].getString(tmp1) == "abc")
1138 && (fmt_arr[1].getString(tmp2) == "def")) {
1139 logln("MSG parse (ustring, count, err) tested.");
1140 }else{
1141 errln("*** MSG parse (ustring, count, err) result err.");
1142 }
1143 }
1144 }
1145 delete[] fmt_arr;
1146
1147 ParsePosition pp(0);
1148
1149 fmt_arr = msg.parse( source, pp, count );
1150 if ((pp == 0) || (!fmt_arr)) {
1151 errln("*** MSG parse (ustring, parsepos., count) error.");
1152 }else{
1153 logln("MSG parse -- count: %d", count);
1154 if (count != 2) {
1155 errln("*** MSG parse (ustring, parsepos., count) count err.");
1156 }else{
1157 if ((fmt_arr[0].getType() == Formattable::kString)
1158 && (fmt_arr[1].getType() == Formattable::kString)
1159 && (fmt_arr[0].getString(tmp1) == "abc")
1160 && (fmt_arr[1].getString(tmp2) == "def")) {
1161 logln("MSG parse (ustring, parsepos., count) tested.");
1162 }else{
1163 errln("*** MSG parse (ustring, parsepos., count) result err.");
1164 }
1165 }
1166 }
1167 delete[] fmt_arr;
1168
1169 pp = 0;
1170 Formattable fmta;
1171
1172 msg.parseObject( source, fmta, pp );
1173 if (pp == 0) {
1174 errln("*** MSG parse (ustring, Formattable, parsepos ) error.");
1175 }else{
1176 logln("MSG parse -- count: %d", count);
1177 fmta.getArray(count);
1178 if (count != 2) {
1179 errln("*** MSG parse (ustring, Formattable, parsepos ) count err.");
1180 }else{
1181 if ((fmta[0].getType() == Formattable::kString)
1182 && (fmta[1].getType() == Formattable::kString)
1183 && (fmta[0].getString(tmp1) == "abc")
1184 && (fmta[1].getString(tmp2) == "def")) {
1185 logln("MSG parse (ustring, Formattable, parsepos ) tested.");
1186 }else{
1187 errln("*** MSG parse (ustring, Formattable, parsepos ) result err.");
1188 }
1189 }
1190 }
1191 }
1192
1193
testAdopt()1194 void TestMessageFormat::testAdopt()
1195 {
1196 UErrorCode err = U_ZERO_ERROR;
1197
1198 UnicodeString formatStr("{0,date},{1},{2,number}", "");
1199 UnicodeString formatStrChange("{0,number},{1,number},{2,date}", "");
1200 err = U_ZERO_ERROR;
1201 MessageFormat msg( formatStr, err);
1202 MessageFormat msgCmp( formatStr, err);
1203 if (U_FAILURE(err)) {
1204 dataerrln("Unable to instantiate MessageFormat - %s", u_errorName(err));
1205 return;
1206 }
1207 int32_t count, countCmp;
1208 const Format** formats = msg.getFormats(count);
1209 const Format** formatsCmp = msgCmp.getFormats(countCmp);
1210 const Format** formatsChg = 0;
1211 const Format** formatsAct = 0;
1212 int32_t countAct;
1213 const Format* a;
1214 const Format* b;
1215 UnicodeString patCmp;
1216 UnicodeString patAct;
1217 Format** formatsToAdopt;
1218
1219 if (!formats || !formatsCmp || (count <= 0) || (count != countCmp)) {
1220 dataerrln("Error getting Formats");
1221 return;
1222 }
1223
1224 int32_t i;
1225
1226 for (i = 0; i < count; i++) {
1227 a = formats[i];
1228 b = formatsCmp[i];
1229 if ((a != NULL) && (b != NULL)) {
1230 if (*a != *b) {
1231 errln("a != b");
1232 return;
1233 }
1234 }else if ((a != NULL) || (b != NULL)) {
1235 errln("(a != NULL) || (b != NULL)");
1236 return;
1237 }
1238 }
1239
1240 msg.applyPattern( formatStrChange, err ); //set msg formats to something different
1241 int32_t countChg;
1242 formatsChg = msg.getFormats(countChg); // tested function
1243 if (!formatsChg || (countChg != count)) {
1244 errln("Error getting Formats");
1245 return;
1246 }
1247
1248 UBool diff;
1249 diff = TRUE;
1250 for (i = 0; i < count; i++) {
1251 a = formatsChg[i];
1252 b = formatsCmp[i];
1253 if ((a != NULL) && (b != NULL)) {
1254 if (*a == *b) {
1255 logln("formatsChg == formatsCmp at index %d", i);
1256 diff = FALSE;
1257 }
1258 }
1259 }
1260 if (!diff) {
1261 errln("*** MSG getFormats diff err.");
1262 return;
1263 }
1264
1265 logln("MSG getFormats tested.");
1266
1267 msg.setFormats( formatsCmp, countCmp ); //tested function
1268
1269 formatsAct = msg.getFormats(countAct);
1270 if (!formatsAct || (countAct <=0) || (countAct != countCmp)) {
1271 errln("Error getting Formats");
1272 return;
1273 }
1274
1275 assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove()));
1276 // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
1277 // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove()));
1278 msg.toPattern(patCmp.remove());
1279 if (!patCmp.isBogus()) {
1280 errln("msg.setFormat().toPattern() succeeds.");
1281 }
1282
1283 for (i = 0; i < countAct; i++) {
1284 a = formatsAct[i];
1285 b = formatsCmp[i];
1286 if ((a != NULL) && (b != NULL)) {
1287 if (*a != *b) {
1288 logln("formatsAct != formatsCmp at index %d", i);
1289 errln("a != b");
1290 return;
1291 }
1292 }else if ((a != NULL) || (b != NULL)) {
1293 errln("(a != NULL) || (b != NULL)");
1294 return;
1295 }
1296 }
1297 logln("MSG setFormats tested.");
1298
1299 //----
1300
1301 msg.applyPattern( formatStrChange, err ); //set msg formats to something different
1302
1303 formatsToAdopt = new Format* [countCmp];
1304 if (!formatsToAdopt) {
1305 errln("memory allocation error");
1306 return;
1307 }
1308
1309 for (i = 0; i < countCmp; i++) {
1310 if (formatsCmp[i] == NULL) {
1311 formatsToAdopt[i] = NULL;
1312 }else{
1313 formatsToAdopt[i] = formatsCmp[i]->clone();
1314 if (!formatsToAdopt[i]) {
1315 errln("Can't clone format at index %d", i);
1316 return;
1317 }
1318 }
1319 }
1320 msg.adoptFormats( formatsToAdopt, countCmp ); // function to test
1321 delete[] formatsToAdopt;
1322
1323 assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove()));
1324 // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
1325 // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove()));
1326
1327 formatsAct = msg.getFormats(countAct);
1328 if (!formatsAct || (countAct <=0) || (countAct != countCmp)) {
1329 errln("Error getting Formats");
1330 return;
1331 }
1332
1333 for (i = 0; i < countAct; i++) {
1334 a = formatsAct[i];
1335 b = formatsCmp[i];
1336 if ((a != NULL) && (b != NULL)) {
1337 if (*a != *b) {
1338 errln("a != b");
1339 return;
1340 }
1341 }else if ((a != NULL) || (b != NULL)) {
1342 errln("(a != NULL) || (b != NULL)");
1343 return;
1344 }
1345 }
1346 logln("MSG adoptFormats tested.");
1347
1348 //---- adoptFormat
1349
1350 msg.applyPattern( formatStrChange, err ); //set msg formats to something different
1351
1352 formatsToAdopt = new Format* [countCmp];
1353 if (!formatsToAdopt) {
1354 errln("memory allocation error");
1355 return;
1356 }
1357
1358 for (i = 0; i < countCmp; i++) {
1359 if (formatsCmp[i] == NULL) {
1360 formatsToAdopt[i] = NULL;
1361 }else{
1362 formatsToAdopt[i] = formatsCmp[i]->clone();
1363 if (!formatsToAdopt[i]) {
1364 errln("Can't clone format at index %d", i);
1365 return;
1366 }
1367 }
1368 }
1369
1370 for ( i = 0; i < countCmp; i++ ) {
1371 msg.adoptFormat( i, formatsToAdopt[i] ); // function to test
1372 }
1373 delete[] formatsToAdopt; // array itself not needed in this case;
1374
1375 assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern(patCmp.remove()));
1376 // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
1377 // assertEquals("msg.toPattern()", formatStr, msg.toPattern(patAct.remove()));
1378
1379 formatsAct = msg.getFormats(countAct);
1380 if (!formatsAct || (countAct <=0) || (countAct != countCmp)) {
1381 errln("Error getting Formats");
1382 return;
1383 }
1384
1385 for (i = 0; i < countAct; i++) {
1386 a = formatsAct[i];
1387 b = formatsCmp[i];
1388 if ((a != NULL) && (b != NULL)) {
1389 if (*a != *b) {
1390 errln("a != b");
1391 return;
1392 }
1393 }else if ((a != NULL) || (b != NULL)) {
1394 errln("(a != NULL) || (b != NULL)");
1395 return;
1396 }
1397 }
1398 logln("MSG adoptFormat tested.");
1399 }
1400
1401 // This test is a regression test for a fixed bug in the copy constructor.
1402 // It is kept as a global function rather than as a method since the test depends on memory values.
1403 // (At least before the bug was fixed, whether it showed up or not depended on memory contents,
1404 // which is probably why it didn't show up in the regular test for the copy constructor.)
1405 // For this reason, the test isn't changed even though it contains function calls whose results are
1406 // not tested and had no problems. Actually, the test failed by *crashing*.
_testCopyConstructor2()1407 static void _testCopyConstructor2()
1408 {
1409 UErrorCode status = U_ZERO_ERROR;
1410 UnicodeString formatStr("Hello World on {0,date,full}", "");
1411 UnicodeString resultStr(" ", "");
1412 UnicodeString result;
1413 FieldPosition fp(FieldPosition::DONT_CARE);
1414 UDate d = Calendar::getNow();
1415 const Formattable fargs( d, Formattable::kIsDate );
1416
1417 MessageFormat* fmt1 = new MessageFormat( formatStr, status );
1418 MessageFormat* fmt2 = NULL;
1419 MessageFormat* fmt3 = NULL;
1420 MessageFormat* fmt4 = NULL;
1421
1422 if (fmt1 == NULL) {
1423 it_err("testCopyConstructor2: (fmt1 != NULL)");
1424 goto cleanup;
1425 }
1426
1427 fmt2 = new MessageFormat( *fmt1 );
1428 result = fmt1->format( &fargs, 1, resultStr, fp, status );
1429
1430 if (fmt2 == NULL) {
1431 it_err("testCopyConstructor2: (fmt2 != NULL)");
1432 goto cleanup;
1433 }
1434
1435 fmt3 = (MessageFormat*) fmt1->clone();
1436 fmt4 = (MessageFormat*) fmt2->clone();
1437
1438 if (fmt3 == NULL) {
1439 it_err("testCopyConstructor2: (fmt3 != NULL)");
1440 goto cleanup;
1441 }
1442 if (fmt4 == NULL) {
1443 it_err("testCopyConstructor2: (fmt4 != NULL)");
1444 goto cleanup;
1445 }
1446
1447 result = fmt1->format( &fargs, 1, resultStr, fp, status );
1448 result = fmt2->format( &fargs, 1, resultStr, fp, status );
1449 result = fmt3->format( &fargs, 1, resultStr, fp, status );
1450 result = fmt4->format( &fargs, 1, resultStr, fp, status );
1451
1452 cleanup:
1453 delete fmt1;
1454 delete fmt2;
1455 delete fmt3;
1456 delete fmt4;
1457 }
1458
testCopyConstructor2()1459 void TestMessageFormat::testCopyConstructor2() {
1460 _testCopyConstructor2();
1461 }
1462
1463 /**
1464 * Verify that MessageFormat accomodates more than 10 arguments and
1465 * more than 10 subformats.
1466 */
TestUnlimitedArgsAndSubformats()1467 void TestMessageFormat::TestUnlimitedArgsAndSubformats() {
1468 UErrorCode ec = U_ZERO_ERROR;
1469 const UnicodeString pattern =
1470 "On {0,date} (aka {0,date,short}, aka {0,date,long}) "
1471 "at {0,time} (aka {0,time,short}, aka {0,time,long}) "
1472 "there were {1,number} werjes "
1473 "(a {3,number,percent} increase over {2,number}) "
1474 "despite the {4}''s efforts "
1475 "and to delight of {5}, {6}, {7}, {8}, {9}, and {10} {11}.";
1476 MessageFormat msg(pattern, ec);
1477 if (U_FAILURE(ec)) {
1478 dataerrln("FAIL: constructor failed - %s", u_errorName(ec));
1479 return;
1480 }
1481
1482 const Formattable ARGS[] = {
1483 Formattable(UDate(1e13), Formattable::kIsDate),
1484 Formattable((int32_t)1303),
1485 Formattable((int32_t)1202),
1486 Formattable(1303.0/1202 - 1),
1487 Formattable("Glimmung"),
1488 Formattable("the printers"),
1489 Formattable("Nick"),
1490 Formattable("his father"),
1491 Formattable("his mother"),
1492 Formattable("the spiddles"),
1493 Formattable("of course"),
1494 Formattable("Horace"),
1495 };
1496 const int32_t ARGS_LENGTH = UPRV_LENGTHOF(ARGS);
1497 Formattable ARGS_OBJ(ARGS, ARGS_LENGTH);
1498
1499 UnicodeString expected =
1500 "On Nov 20, 2286 (aka 11/20/86, aka November 20, 2286) "
1501 "at 9:46:40 AM (aka 9:46 AM, aka 9:46:40 AM PST) "
1502 "there were 1,303 werjes "
1503 "(a 8% increase over 1,202) "
1504 "despite the Glimmung's efforts "
1505 "and to delight of the printers, Nick, his father, "
1506 "his mother, the spiddles, and of course Horace.";
1507 UnicodeString result;
1508 msg.format(ARGS_OBJ, result, ec);
1509 if (result == expected) {
1510 logln(result);
1511 } else {
1512 errln((UnicodeString)"FAIL: Got " + result +
1513 ", expected " + expected);
1514 }
1515 }
1516
1517 // test RBNF extensions to message format
TestRBNF(void)1518 void TestMessageFormat::TestRBNF(void) {
1519 // WARNING: this depends on the RBNF formats for en_US
1520 Locale locale("en", "US", "");
1521
1522 UErrorCode ec = U_ZERO_ERROR;
1523
1524 UnicodeString values[] = {
1525 // decimal values do not format completely for ordinal or duration, and
1526 // do not always parse, so do not include them
1527 "0", "1", "12", "100", "123", "1001", "123,456", "-17",
1528 };
1529 int32_t values_count = UPRV_LENGTHOF(values);
1530
1531 UnicodeString formats[] = {
1532 "There are {0,spellout} files to search.",
1533 "There are {0,spellout,%simplified} files to search.",
1534 "The bogus spellout {0,spellout,%BOGUS} files behaves like the default.",
1535 "This is the {0,ordinal} file to search.",
1536 "Searching this file will take {0,duration} to complete.",
1537 "Searching this file will take {0,duration,%with-words} to complete.",
1538 };
1539 int32_t formats_count = UPRV_LENGTHOF(formats);
1540
1541 Formattable args[1];
1542
1543 NumberFormat* numFmt = NumberFormat::createInstance(locale, ec);
1544 if (U_FAILURE(ec)) {
1545 dataerrln("Error calling NumberFormat::createInstance()");
1546 return;
1547 }
1548
1549 for (int i = 0; i < formats_count; ++i) {
1550 MessageFormat* fmt = new MessageFormat(formats[i], locale, ec);
1551 logln((UnicodeString)"Testing format pattern: '" + formats[i] + "'");
1552
1553 for (int j = 0; j < values_count; ++j) {
1554 ec = U_ZERO_ERROR;
1555 numFmt->parse(values[j], args[0], ec);
1556 if (U_FAILURE(ec)) {
1557 errln((UnicodeString)"Failed to parse test argument " + values[j]);
1558 } else {
1559 FieldPosition fp(FieldPosition::DONT_CARE);
1560 UnicodeString result;
1561 fmt->format(args, 1, result, fp, ec);
1562 logln((UnicodeString)"value: " + toString(args[0]) + " --> " + result + UnicodeString(" ec: ") + u_errorName(ec));
1563
1564 int32_t count = 0;
1565 Formattable* parseResult = fmt->parse(result, count, ec);
1566 if (count != 1) {
1567 errln((UnicodeString)"parse returned " + count + " args");
1568 } else if (parseResult[0] != args[0]) {
1569 errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0]));
1570 }
1571 delete []parseResult;
1572 }
1573 }
1574 delete fmt;
1575 }
1576 delete numFmt;
1577 }
1578
GetPatternAndSkipSyntax(const MessagePattern & pattern)1579 UnicodeString TestMessageFormat::GetPatternAndSkipSyntax(const MessagePattern& pattern) {
1580 UnicodeString us(pattern.getPatternString());
1581 int count = pattern.countParts();
1582 for (int i = count; i > 0;) {
1583 const MessagePattern::Part& part = pattern.getPart(--i);
1584 if (part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
1585 us.remove(part.getIndex(), part.getLimit() - part.getIndex());
1586 }
1587 }
1588 return us;
1589 }
1590
TestApostropheMode()1591 void TestMessageFormat::TestApostropheMode() {
1592 UErrorCode status = U_ZERO_ERROR;
1593 MessagePattern *ado_mp = new MessagePattern(UMSGPAT_APOS_DOUBLE_OPTIONAL, status);
1594 MessagePattern *adr_mp = new MessagePattern(UMSGPAT_APOS_DOUBLE_REQUIRED, status);
1595 if (ado_mp->getApostropheMode() != UMSGPAT_APOS_DOUBLE_OPTIONAL) {
1596 errln("wrong value from ado_mp->getApostropheMode().");
1597 }
1598 if (adr_mp->getApostropheMode() != UMSGPAT_APOS_DOUBLE_REQUIRED) {
1599 errln("wrong value from adr_mp->getApostropheMode().");
1600 }
1601
1602
1603 UnicodeString tuples[] = {
1604 // Desired output
1605 // DOUBLE_OPTIONAL pattern
1606 // DOUBLE_REQUIRED pattern (empty=same as DOUBLE_OPTIONAL)
1607 "I see {many}", "I see '{many}'", "",
1608 "I said {'Wow!'}", "I said '{''Wow!''}'", "",
1609 "I dont know", "I dont know", "I don't know",
1610 "I don't know", "I don't know", "I don''t know",
1611 "I don't know", "I don''t know", "I don''t know"
1612 };
1613 int32_t tuples_count = UPRV_LENGTHOF(tuples);
1614
1615 for (int i = 0; i < tuples_count; i += 3) {
1616 UnicodeString& desired = tuples[i];
1617 UnicodeString& ado_pattern = tuples[i + 1];
1618 UErrorCode status = U_ZERO_ERROR;
1619 assertEquals("DOUBLE_OPTIONAL failure",
1620 desired,
1621 GetPatternAndSkipSyntax(ado_mp->parse(ado_pattern, NULL, status)));
1622 UnicodeString& adr_pattern = tuples[i + 2].isEmpty() ? ado_pattern : tuples[i + 2];
1623 assertEquals("DOUBLE_REQUIRED failure", desired,
1624 GetPatternAndSkipSyntax(adr_mp->parse(adr_pattern, NULL, status)));
1625 }
1626 delete adr_mp;
1627 delete ado_mp;
1628 }
1629
1630
1631 // Compare behavior of DOUBLE_OPTIONAL (new default) and DOUBLE_REQUIRED JDK-compatibility mode.
TestCompatibleApostrophe()1632 void TestMessageFormat::TestCompatibleApostrophe() {
1633 // Message with choice argument which does not contain another argument.
1634 // The JDK performs only one apostrophe-quoting pass on this pattern.
1635 UnicodeString pattern = "ab{0,choice,0#1'2''3'''4''''.}yz";
1636
1637 UErrorCode ec = U_ZERO_ERROR;
1638 MessageFormat compMsg("", Locale::getUS(), ec);
1639 compMsg.applyPattern(pattern, UMSGPAT_APOS_DOUBLE_REQUIRED, NULL, ec);
1640 if (compMsg.getApostropheMode() != UMSGPAT_APOS_DOUBLE_REQUIRED) {
1641 errln("wrong value from compMsg.getApostropheMode().");
1642 }
1643
1644 MessageFormat icuMsg("", Locale::getUS(), ec);
1645 icuMsg.applyPattern(pattern, UMSGPAT_APOS_DOUBLE_OPTIONAL, NULL, ec);
1646 if (icuMsg.getApostropheMode() != UMSGPAT_APOS_DOUBLE_OPTIONAL) {
1647 errln("wrong value from icuMsg.getApostropheMode().");
1648 }
1649
1650 Formattable zero0[] = { (int32_t)0 };
1651 FieldPosition fieldpos(FieldPosition::DONT_CARE);
1652 UnicodeString buffer1, buffer2;
1653 assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
1654 "ab12'3'4''.yz",
1655 compMsg.format(zero0, 1, buffer1, fieldpos, ec));
1656 assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior",
1657 "ab1'2'3''4''.yz",
1658 icuMsg.format(zero0, 1, buffer2, fieldpos, ec));
1659
1660 // Message with choice argument which contains a nested simple argument.
1661 // The DOUBLE_REQUIRED version performs two apostrophe-quoting passes.
1662 buffer1.remove();
1663 buffer2.remove();
1664 pattern = "ab{0,choice,0#1'2''3'''4''''.{0,number,'#x'}}yz";
1665 compMsg.applyPattern(pattern, ec);
1666 icuMsg.applyPattern(pattern, ec);
1667 if (U_FAILURE(ec)) {
1668 dataerrln("Unable to applyPattern - %s", u_errorName(ec));
1669 } else {
1670 assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
1671 "ab1234'.0xyz",
1672 compMsg.format(zero0, 1, buffer1, fieldpos, ec));
1673 assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior",
1674 "ab1'2'3''4''.#x0yz",
1675 icuMsg.format(zero0, 1, buffer2, fieldpos, ec));
1676 }
1677
1678 // This part is copied over from Java tests but cannot be properly tested here
1679 // because we do not have a live reference implementation with JDK behavior.
1680 // The JDK ChoiceFormat itself always performs one apostrophe-quoting pass.
1681 /*
1682 ChoiceFormat choice = new ChoiceFormat("0#1'2''3'''4''''.");
1683 assertEquals("unexpected JDK ChoiceFormat apostrophe behavior",
1684 "12'3'4''.",
1685 choice.format(0));
1686 choice.applyPattern("0#1'2''3'''4''''.{0,number,'#x'}");
1687 assertEquals("unexpected JDK ChoiceFormat apostrophe behavior",
1688 "12'3'4''.{0,number,#x}",
1689 choice.format(0));
1690 */
1691 }
1692
testAutoQuoteApostrophe(void)1693 void TestMessageFormat::testAutoQuoteApostrophe(void) {
1694 const char* patterns[] = { // pattern, expected pattern
1695 "'", "''",
1696 "''", "''",
1697 "'{", "'{'",
1698 "' {", "'' {",
1699 "'a", "''a",
1700 "'{'a", "'{'a",
1701 "'{a'", "'{a'",
1702 "'{}", "'{}'",
1703 "{'", "{'",
1704 "{'a", "{'a",
1705 "{'a{}'a}'a", "{'a{}'a}''a",
1706 "'}'", "'}'",
1707 "'} '{'}'", "'} '{'}''",
1708 "'} {{{''", "'} {{{'''",
1709 };
1710 int32_t pattern_count = UPRV_LENGTHOF(patterns);
1711
1712 for (int i = 0; i < pattern_count; i += 2) {
1713 UErrorCode status = U_ZERO_ERROR;
1714 UnicodeString result = MessageFormat::autoQuoteApostrophe(patterns[i], status);
1715 UnicodeString target(patterns[i+1]);
1716 if (target != result) {
1717 const int BUF2_LEN = 64;
1718 char buf[256];
1719 char buf2[BUF2_LEN];
1720 int32_t len = result.extract(0, result.length(), buf2, BUF2_LEN);
1721 if (len >= BUF2_LEN) {
1722 buf2[BUF2_LEN-1] = 0;
1723 }
1724 sprintf(buf, "[%2d] test \"%s\": target (\"%s\") != result (\"%s\")\n", i/2, patterns[i], patterns[i+1], buf2);
1725 errln(buf);
1726 }
1727 }
1728 }
1729
testCoverage(void)1730 void TestMessageFormat::testCoverage(void) {
1731 UErrorCode status = U_ZERO_ERROR;
1732 UnicodeString testformat("{argument, plural, one{C''est # fichier} other {Ce sont # fichiers}} dans la liste.");
1733 MessageFormat *msgfmt = new MessageFormat(testformat, Locale("fr"), status);
1734 if (msgfmt == NULL || U_FAILURE(status)) {
1735 dataerrln("FAIL: Unable to create MessageFormat.: %s", u_errorName(status));
1736 return;
1737 }
1738 if (!msgfmt->usesNamedArguments()) {
1739 errln("FAIL: Unable to detect usage of named arguments.");
1740 }
1741 const double limit[] = {0.0, 1.0, 2.0};
1742 const UnicodeString formats[] = {"0.0<=Arg<1.0",
1743 "1.0<=Arg<2.0",
1744 "2.0<-Arg"};
1745 ChoiceFormat cf(limit, formats, 3);
1746
1747 msgfmt->setFormat("set", cf, status);
1748
1749 StringEnumeration *en = msgfmt->getFormatNames(status);
1750 if (en == NULL || U_FAILURE(status)) {
1751 errln("FAIL: Unable to get format names enumeration.");
1752 } else {
1753 int32_t count = 0;
1754 en->reset(status);
1755 count = en->count(status);
1756 if (U_FAILURE(status)) {
1757 errln("FAIL: Unable to get format name enumeration count.");
1758 } else {
1759 for (int32_t i = 0; i < count; i++) {
1760 en->snext(status);
1761 if (U_FAILURE(status)) {
1762 errln("FAIL: Error enumerating through names.");
1763 break;
1764 }
1765 }
1766 }
1767 }
1768
1769 // adoptFormat() takes ownership of the input Format object.
1770 // We need to clone the stack-allocated cf so that we do not attempt to delete cf.
1771 Format *cfClone = cf.clone();
1772 msgfmt->adoptFormat("adopt", cfClone, status);
1773
1774 delete en;
1775 delete msgfmt;
1776
1777 msgfmt = new MessageFormat("'", status);
1778 if (msgfmt == NULL || U_FAILURE(status)) {
1779 errln("FAIL: Unable to create MessageFormat.");
1780 return;
1781 }
1782 if (msgfmt->usesNamedArguments()) {
1783 errln("FAIL: Unable to detect usage of named arguments.");
1784 }
1785
1786 // Starting with ICU 4.8, we support setFormat(name, ...) and getFormatNames()
1787 // on a MessageFormat without named arguments.
1788 msgfmt->setFormat("formatName", cf, status);
1789 if (U_FAILURE(status)) {
1790 errln("FAIL: Should work to setFormat(name, ...) regardless of pattern.");
1791 }
1792 status = U_ZERO_ERROR;
1793 en = msgfmt->getFormatNames(status);
1794 if (U_FAILURE(status)) {
1795 errln("FAIL: Should work to get format names enumeration regardless of pattern.");
1796 }
1797
1798 delete en;
1799 delete msgfmt;
1800 }
1801
testGetFormatNames()1802 void TestMessageFormat::testGetFormatNames() {
1803 IcuTestErrorCode errorCode(*this, "testGetFormatNames");
1804 MessageFormat msgfmt("Hello, {alice,number} {oops,date,full} {zip,spellout} World.", Locale::getRoot(), errorCode);
1805 if(errorCode.errDataIfFailureAndReset("MessageFormat() failed")) {
1806 return;
1807 }
1808 LocalPointer<StringEnumeration> names(msgfmt.getFormatNames(errorCode));
1809 if(errorCode.errIfFailureAndReset("msgfmt.getFormatNames() failed")) {
1810 return;
1811 }
1812 const UnicodeString *name;
1813 name = names->snext(errorCode);
1814 if (name == NULL || errorCode.isFailure()) {
1815 errln("msgfmt.getFormatNames()[0] failed: %s", errorCode.errorName());
1816 errorCode.reset();
1817 return;
1818 }
1819 if (!assertEquals("msgfmt.getFormatNames()[0]", UNICODE_STRING_SIMPLE("alice"), *name)) {
1820 return;
1821 }
1822 name = names->snext(errorCode);
1823 if (name == NULL || errorCode.isFailure()) {
1824 errln("msgfmt.getFormatNames()[1] failed: %s", errorCode.errorName());
1825 errorCode.reset();
1826 return;
1827 }
1828 if (!assertEquals("msgfmt.getFormatNames()[1]", UNICODE_STRING_SIMPLE("oops"), *name)) {
1829 return;
1830 }
1831 name = names->snext(errorCode);
1832 if (name == NULL || errorCode.isFailure()) {
1833 errln("msgfmt.getFormatNames()[2] failed: %s", errorCode.errorName());
1834 errorCode.reset();
1835 return;
1836 }
1837 if (!assertEquals("msgfmt.getFormatNames()[2]", UNICODE_STRING_SIMPLE("zip"), *name)) {
1838 return;
1839 }
1840 name = names->snext(errorCode);
1841 if (name != NULL) {
1842 errln(UnicodeString("msgfmt.getFormatNames()[3] should be NULL but is: ") + *name);
1843 return;
1844 }
1845 }
1846
TestTrimArgumentName()1847 void TestMessageFormat::TestTrimArgumentName() {
1848 // ICU 4.8 allows and ignores white space around argument names and numbers.
1849 IcuTestErrorCode errorCode(*this, "TestTrimArgumentName");
1850 MessageFormat m("a { 0 , number , '#,#'#.0 } z", Locale::getEnglish(), errorCode);
1851 if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
1852 return;
1853 }
1854 Formattable args[1] = { (int32_t)2 };
1855 FieldPosition ignore(FieldPosition::DONT_CARE);
1856 UnicodeString result;
1857 assertEquals("trim-numbered-arg format() failed", "a #,#2.0 z",
1858 m.format(args, 1, result, ignore, errorCode));
1859
1860 m.applyPattern("x { _oOo_ , number , integer } y", errorCode);
1861 UnicodeString argName = UNICODE_STRING_SIMPLE("_oOo_");
1862 args[0].setLong(3);
1863 result.remove();
1864 assertEquals("trim-named-arg format() failed", "x 3 y",
1865 m.format(&argName, args, 1, result, errorCode));
1866 }
1867
TestSelectOrdinal()1868 void TestMessageFormat::TestSelectOrdinal() {
1869 IcuTestErrorCode errorCode(*this, "TestSelectOrdinal");
1870 // Test plural & ordinal together,
1871 // to make sure that we get the correct cached PluralSelector for each.
1872 MessageFormat m(
1873 "{0,plural,one{1 file}other{# files}}, "
1874 "{0,selectordinal,one{#st file}two{#nd file}few{#rd file}other{#th file}}",
1875 Locale::getEnglish(), errorCode);
1876 if (errorCode.errDataIfFailureAndReset("Unable to instantiate MessageFormat")) {
1877 return;
1878 }
1879 Formattable args[1] = { (int32_t)21 };
1880 FieldPosition ignore(FieldPosition::DONT_CARE);
1881 UnicodeString result;
1882 assertEquals("plural-and-ordinal format(21) failed", "21 files, 21st file",
1883 m.format(args, 1, result, ignore, errorCode), TRUE);
1884
1885 args[0].setLong(2);
1886 assertEquals("plural-and-ordinal format(2) failed", "2 files, 2nd file",
1887 m.format(args, 1, result.remove(), ignore, errorCode), TRUE);
1888
1889 args[0].setLong(1);
1890 assertEquals("plural-and-ordinal format(1) failed", "1 file, 1st file",
1891 m.format(args, 1, result.remove(), ignore, errorCode), TRUE);
1892
1893 args[0].setLong(3);
1894 assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file",
1895 m.format(args, 1, result.remove(), ignore, errorCode), TRUE);
1896
1897 errorCode.errDataIfFailureAndReset("");
1898 }
1899
TestDecimals()1900 void TestMessageFormat::TestDecimals() {
1901 IcuTestErrorCode errorCode(*this, "TestDecimals");
1902 // Simple number replacement.
1903 MessageFormat m(
1904 "{0,plural,one{one meter}other{# meters}}",
1905 Locale::getEnglish(), errorCode);
1906 Formattable args[1] = { (int32_t)1 };
1907 FieldPosition ignore;
1908 UnicodeString result;
1909 assertEquals("simple format(1)", "one meter",
1910 m.format(args, 1, result, ignore, errorCode), TRUE);
1911
1912 args[0] = (double)1.5;
1913 result.remove();
1914 assertEquals("simple format(1.5)", "1.5 meters",
1915 m.format(args, 1, result, ignore, errorCode), TRUE);
1916
1917 // Simple but explicit.
1918 MessageFormat m0(
1919 "{0,plural,one{one meter}other{{0} meters}}",
1920 Locale::getEnglish(), errorCode);
1921 args[0] = (int32_t)1;
1922 result.remove();
1923 assertEquals("explicit format(1)", "one meter",
1924 m0.format(args, 1, result, ignore, errorCode), TRUE);
1925
1926 args[0] = (double)1.5;
1927 result.remove();
1928 assertEquals("explicit format(1.5)", "1.5 meters",
1929 m0.format(args, 1, result, ignore, errorCode), TRUE);
1930
1931 // With offset and specific simple format with optional decimals.
1932 MessageFormat m1(
1933 "{0,plural,offset:1 one{another meter}other{{0,number,00.#} meters}}",
1934 Locale::getEnglish(), errorCode);
1935 args[0] = (int32_t)1;
1936 result.remove();
1937 assertEquals("offset format(1)", "01 meters",
1938 m1.format(args, 1, result, ignore, errorCode), TRUE);
1939
1940 args[0] = (int32_t)2;
1941 result.remove();
1942 assertEquals("offset format(1)", "another meter",
1943 m1.format(args, 1, result, ignore, errorCode), TRUE);
1944
1945 args[0] = (double)2.5;
1946 result.remove();
1947 assertEquals("offset format(1)", "02.5 meters",
1948 m1.format(args, 1, result, ignore, errorCode), TRUE);
1949
1950 // With offset and specific simple format with forced decimals.
1951 MessageFormat m2(
1952 "{0,plural,offset:1 one{another meter}other{{0,number,0.0} meters}}",
1953 Locale::getEnglish(), errorCode);
1954 args[0] = (int32_t)1;
1955 result.remove();
1956 assertEquals("offset-decimals format(1)", "1.0 meters",
1957 m2.format(args, 1, result, ignore, errorCode), TRUE);
1958
1959 args[0] = (int32_t)2;
1960 result.remove();
1961 assertEquals("offset-decimals format(1)", "2.0 meters",
1962 m2.format(args, 1, result, ignore, errorCode), TRUE);
1963
1964 args[0] = (double)2.5;
1965 result.remove();
1966 assertEquals("offset-decimals format(1)", "2.5 meters",
1967 m2.format(args, 1, result, ignore, errorCode), TRUE);
1968 errorCode.reset();
1969 }
1970
TestArgIsPrefixOfAnother()1971 void TestMessageFormat::TestArgIsPrefixOfAnother() {
1972 IcuTestErrorCode errorCode(*this, "TestArgIsPrefixOfAnother");
1973 // Ticket #11952
1974 MessageFormat mf1("{0,select,a{A}ab{AB}abc{ABC}other{?}}", Locale::getEnglish(), errorCode);
1975 Formattable args[3];
1976 FieldPosition ignore;
1977 UnicodeString result;
1978 args[0].setString("a");
1979 assertEquals("a", "A", mf1.format(args, 1, result, ignore, errorCode));
1980 args[0].setString("ab");
1981 assertEquals("ab", "AB", mf1.format(args, 1, result.remove(), ignore, errorCode));
1982 args[0].setString("abc");
1983 assertEquals("abc", "ABC", mf1.format(args, 1, result.remove(), ignore, errorCode));
1984
1985 // Ticket #12172
1986 MessageFormat mf2("{a} {aa} {aaa}", Locale::getEnglish(), errorCode);
1987 UnicodeString argNames[3] = { "a", "aa", "aaa" };
1988 args[0].setString("A");
1989 args[1].setString("AB");
1990 args[2].setString("ABC");
1991 assertEquals("a aa aaa", "A AB ABC", mf2.format(argNames, args, 3, result.remove(), errorCode));
1992
1993 // Ticket #12172
1994 MessageFormat mf3("{aa} {aaa}", Locale::getEnglish(), errorCode);
1995 assertEquals("aa aaa", "AB ABC", mf3.format(argNames + 1, args + 1, 2, result.remove(), errorCode));
1996 }
1997
TestMessageFormatNumberSkeleton()1998 void TestMessageFormat::TestMessageFormatNumberSkeleton() {
1999 IcuTestErrorCode status(*this, "TestMessageFormatNumberSkeleton");
2000
2001 static const struct TestCase {
2002 const char16_t* messagePattern;
2003 const char* localeName;
2004 double arg;
2005 const char16_t* expected;
2006 } cases[] = {
2007 { u"{0,number,::percent}", "en", 50, u"50%" },
2008 { u"{0,number,::percent scale/100}", "en", 0.5, u"50%" },
2009 { u"{0,number, :: percent scale/100 }", "en", 0.5, u"50%" },
2010 { u"{0,number,::currency/USD}", "en", 23, u"$23.00" },
2011 { u"{0,number,::precision-integer}", "en", 514.23, u"514" },
2012 { u"{0,number,::.000}", "en", 514.23, u"514.230" },
2013 { u"{0,number,::.}", "en", 514.23, u"514" },
2014 { u"{0,number,::}", "fr", 514.23, u"514,23" },
2015 { u"Cost: {0,number,::currency/EUR}.", "en", 4.3, u"Cost: €4.30." },
2016 { u"{0,number,'::'0.00}", "en", 50, u"::50.00" }, // pattern literal
2017 };
2018
2019 for (auto& cas : cases) {
2020 status.setScope(cas.messagePattern);
2021 MessageFormat msgf(cas.messagePattern, cas.localeName, status);
2022 UnicodeString sb;
2023 FieldPosition fpos(0);
2024 Formattable argsArray[] = {{cas.arg}};
2025 Formattable args(argsArray, 1);
2026 msgf.format(args, sb, status);
2027
2028 assertEquals(cas.messagePattern, cas.expected, sb);
2029 }
2030 }
2031
2032 #endif /* #if !UCONFIG_NO_FORMATTING */
2033