• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2016 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 /*
5 **********************************************************************
6 * Copyright (c) 2004-2016, International Business Machines
7 * Corporation and others.  All Rights Reserved.
8 **********************************************************************
9 * Author: Alan Liu
10 * Created: April 6, 2004
11 * Since: ICU 3.0
12 **********************************************************************
13 */
14 package ohos.global.icu.dev.test.format;
15 
16 import java.text.AttributedCharacterIterator;
17 import java.text.AttributedString;
18 import java.text.ChoiceFormat;
19 import java.text.FieldPosition;
20 import java.text.Format;
21 import java.text.ParseException;
22 import java.text.ParsePosition;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.TreeMap;
30 
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 import org.junit.runners.JUnit4;
34 
35 import ohos.global.icu.dev.test.TestFmwk;
36 import ohos.global.icu.text.DateFormat;
37 import ohos.global.icu.text.DecimalFormat;
38 import ohos.global.icu.text.DecimalFormatSymbols;
39 import ohos.global.icu.text.MessageFormat;
40 import ohos.global.icu.text.MessagePattern;
41 import ohos.global.icu.text.NumberFormat;
42 import ohos.global.icu.text.SimpleDateFormat;
43 import ohos.global.icu.text.UFormat;
44 import ohos.global.icu.util.Calendar;
45 import ohos.global.icu.util.GregorianCalendar;
46 import ohos.global.icu.util.TimeZone;
47 import ohos.global.icu.util.ULocale;
48 
49 
50 
51 @RunWith(JUnit4.class)
52 public class TestMessageFormat extends TestFmwk {
53     @Test
TestBug3()54     public void TestBug3()
55     {
56         double myNumber = -123456;
57         DecimalFormat form = null;
58         Locale locale[] = {
59             new Locale("ar", "", ""),
60             new Locale("be", "", ""),
61             new Locale("bg", "", ""),
62             new Locale("ca", "", ""),
63             new Locale("cs", "", ""),
64             new Locale("da", "", ""),
65             new Locale("de", "", ""),
66             new Locale("de", "AT", ""),
67             new Locale("de", "CH", ""),
68             new Locale("el", "", ""),       // 10
69             new Locale("en", "CA", ""),
70             new Locale("en", "GB", ""),
71             new Locale("en", "IE", ""),
72             new Locale("en", "US", ""),
73             new Locale("es", "", ""),
74             new Locale("et", "", ""),
75             new Locale("fi", "", ""),
76             new Locale("fr", "", ""),
77             new Locale("fr", "BE", ""),
78             new Locale("fr", "CA", ""),     // 20
79             new Locale("fr", "CH", ""),
80             new Locale("he", "", ""),
81             new Locale("hr", "", ""),
82             new Locale("hu", "", ""),
83             new Locale("is", "", ""),
84             new Locale("it", "", ""),
85             new Locale("it", "CH", ""),
86             new Locale("ja", "", ""),
87             new Locale("ko", "", ""),
88             new Locale("lt", "", ""),       // 30
89             new Locale("lv", "", ""),
90             new Locale("mk", "", ""),
91             new Locale("nl", "", ""),
92             new Locale("nl", "BE", ""),
93             new Locale("no", "", ""),
94             new Locale("pl", "", ""),
95             new Locale("pt", "", ""),
96             new Locale("ro", "", ""),
97             new Locale("ru", "", ""),
98             new Locale("sh", "", ""),       // 40
99             new Locale("sk", "", ""),
100             new Locale("sl", "", ""),
101             new Locale("sq", "", ""),
102             new Locale("sr", "", ""),
103             new Locale("sv", "", ""),
104             new Locale("tr", "", ""),
105             new Locale("uk", "", ""),
106             new Locale("zh", "", ""),
107             new Locale("zh", "TW", "")      // 49
108         };
109         StringBuffer buffer = new StringBuffer();
110         ParsePosition parsePos = new ParsePosition(0);
111         int i;
112         for (i= 0; i < 49; i++) {
113     //        form = (DecimalFormat)NumberFormat.getCurrencyInstance(locale[i]);
114             form = (DecimalFormat)NumberFormat.getInstance(locale[i]);
115             if (form == null) {
116                 errln("Number format creation failed for " + locale[i].getDisplayName());
117                 continue;
118             }
119             FieldPosition pos = new FieldPosition(FieldPosition_DONT_CARE);
120             buffer.setLength(0);
121             form.format(myNumber, buffer, pos);
122             parsePos.setIndex(0);
123             Object result = form.parse(buffer.toString(), parsePos);
124             logln(locale[i].getDisplayName() + " -> " + result);
125             if (parsePos.getIndex() != buffer.length()) {
126                 errln("Number format parse failed.");
127             }
128         }
129     }
130 
131     @Test
TestBug1()132     public void TestBug1()
133     {
134         final double limit[] = {0.0, 1.0, 2.0};
135         final String formats[] = {"0.0<=Arg<1.0",
136                                   "1.0<=Arg<2.0",
137                                   "2.0<-Arg"};
138         ChoiceFormat cf = new ChoiceFormat(limit, formats);
139         assertEquals("ChoiceFormat.format", formats[1], cf.format(1));
140     }
141 
142     @Test
TestBug2()143     public void TestBug2()
144     {
145         // {sfb} use double format in pattern, so result will match (not strictly necessary)
146         final String pattern = "There {0,choice,0.0#are no files|1.0#is one file|1.0<are {0, number} files} on disk {1}. ";
147         logln("The input pattern : " + pattern);
148         try {
149             MessageFormat fmt = new MessageFormat(pattern);
150             assertEquals("toPattern", pattern, fmt.toPattern());
151         } catch (IllegalArgumentException e) {
152             errln("MessageFormat pattern creation failed.");
153         }
154     }
155 
156     @Test
TestPattern()157     public void TestPattern() // aka PatternTest()
158     {
159         Object testArgs[] = {
160             new Double(1), new Double(3456),
161             "Disk", new Date(1000000000L)
162         };
163         String testCases[] = {
164            "Quotes '', '{', 'a' {0} '{0}'",
165            "Quotes '', '{', 'a' {0,number} '{0}'",
166            "'{'1,number,'#',##} {1,number,'#',##}",
167            "There are {1} files on {2} at {3}.",
168            "On {2}, there are {1} files, with {0,number,currency}.",
169            "'{1,number,percent}', {1,number,percent},",
170            "'{1,date,full}', {1,date,full},",
171            "'{3,date,full}', {3,date,full},",
172            "'{1,number,#,##}' {1,number,#,##}",
173         };
174 
175         // ICU 4.8 returns the original pattern (testCases)
176         // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns).
177         /*String testResultPatterns[] = {
178             "Quotes '', '{', a {0} '{'0}",
179             "Quotes '', '{', a {0,number} '{'0}",
180             "'{'1,number,#,##} {1,number,'#'#,##}",
181             "There are {1} files on {2} at {3}.",
182             "On {2}, there are {1} files, with {0,number,currency}.",
183             "'{'1,number,percent}, {1,number,percent},",
184             "'{'1,date,full}, {1,date,full},",
185             "'{'3,date,full}, {3,date,full},",
186             "'{'1,number,#,##} {1,number,#,##}"
187         };*/
188 
189         String testResultStrings[] = {
190             "Quotes ', {, 'a' 1 {0}",
191             "Quotes ', {, 'a' 1 {0}",
192             "{1,number,'#',##} #34,56",
193             "There are 3,456 files on Disk at 1/12/70, 5:46 AM.",
194             "On Disk, there are 3,456 files, with $1.00.",
195             "{1,number,percent}, 345,600%,",
196             "{1,date,full}, Wednesday, December 31, 1969,",
197             "{3,date,full}, Monday, January 12, 1970,",
198             "{1,number,#,##} 34,56"
199         };
200 
201         for (int i = 0; i < 9; ++i) {
202             //it_out << "\nPat in:  " << testCases[i]);
203 
204             //String buffer;
205             MessageFormat form = null;
206             try {
207                 form = new MessageFormat(testCases[i], Locale.US);
208             } catch (IllegalArgumentException e1) {
209                 errln("MessageFormat for " + testCases[i] + " creation failed.");
210                 continue;
211             }
212             // ICU 4.8 returns the original pattern (testCases)
213             // rather than toPattern() reconstituting a new, equivalent pattern string (testResultPatterns).
214             // assertEquals("\"" + testCases[i] + "\".toPattern()", testResultPatterns[i], form.toPattern());
215             assertEquals("\"" + testCases[i] + "\".toPattern()", testCases[i], form.toPattern());
216             // Note: An alternative test would be to build MessagePattern objects for
217             // both the input and output patterns and compare them, taking SKIP_SYNTAX etc.
218             // into account.
219             // (Too much trouble...)
220 
221             //it_out << "Pat out: " << form.toPattern(buffer));
222             StringBuffer result = new StringBuffer();
223             FieldPosition fieldpos = new FieldPosition(FieldPosition_DONT_CARE);
224             form.format(testArgs, result, fieldpos);
225             assertEquals("format", testResultStrings[i], result.toString());
226 
227             //it_out << "Result:  " << result);
228     //        /* TODO: Look at this test and see if this is still a valid test */
229     //        logln("---------------- test parse ----------------");
230     //
231     //        int count = 4;
232     //        form.toPattern(buffer);
233     //        logln("MSG pattern for parse: " + buffer);
234     //
235     //        int parseCount = 0;
236     //        Formattable* values = form.parse(result, parseCount, success);
237     //        if (U_FAILURE(success)) {
238     //            errln("MessageFormat failed test #5");
239     //            logln(String("MessageFormat failed test #5 with error code ")+(int)success);
240     //        } else if (parseCount != count) {
241     //            errln("MSG count not %d as expected. Got %d", count, parseCount);
242     //        }
243     //        UBool failed = FALSE;
244     //        for (int j = 0; j < parseCount; ++j) {
245     //             if (values == 0 || testArgs[j] != values[j]) {
246     //                errln(((String)"MSG testargs[") + j + "]: " + toString(testArgs[j]));
247     //                errln(((String)"MSG values[") + j + "]  : " + toString(values[j]));
248     //                failed = TRUE;
249     //             }
250     //        }
251     //        if (failed)
252     //            errln("MessageFormat failed test #6");
253         }
254     }
255 
256     @Test
TestSample()257     public void TestSample() // aka sample()
258     {
259         MessageFormat form = null;
260         StringBuffer buffer2 = new StringBuffer();
261         try {
262             form = new MessageFormat("There are {0} files on {1}");
263         } catch (IllegalArgumentException e1) {
264             errln("Sample message format creation failed.");
265             return;
266         }
267         Object testArgs1[] = { "abc", "def" };
268         FieldPosition fieldpos = new FieldPosition(FieldPosition_DONT_CARE);
269         assertEquals("format",
270                      "There are abc files on def",
271                      form.format(testArgs1, buffer2, fieldpos).toString());
272     }
273 
274     @Test
TestStaticFormat()275     public void TestStaticFormat()
276     {
277         Object arguments[] = {
278             new Integer(7),
279             new Date(871068000000L),
280             "a disturbance in the Force"
281         };
282 
283         assertEquals("format",
284             "At 12:20:00 PM on Aug 8, 1997, there was a disturbance in the Force on planet 7.",
285             MessageFormat.format("At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
286                                  arguments));
287     }
288 
289     static final int FieldPosition_DONT_CARE = -1;
290 
291     @Test
TestSimpleFormat()292     public void TestSimpleFormat()
293     {
294         Object testArgs1[] = {new Integer(0), "MyDisk"};
295         Object testArgs2[] = {new Integer(1), "MyDisk"};
296         Object testArgs3[] = {new Integer(12), "MyDisk"};
297 
298         MessageFormat form = new MessageFormat(
299             "The disk \"{1}\" contains {0} file(s).");
300 
301         StringBuffer string = new StringBuffer();
302         FieldPosition ignore = new FieldPosition(FieldPosition_DONT_CARE);
303         form.format(testArgs1, string, ignore);
304         assertEquals("format",
305                      "The disk \"MyDisk\" contains 0 file(s).",
306                      string.toString());
307 
308         string.setLength(0);
309         form.format(testArgs2, string, ignore);
310         assertEquals("format",
311                      "The disk \"MyDisk\" contains 1 file(s).",
312                      string.toString());
313 
314         string.setLength(0);
315         form.format(testArgs3, string, ignore);
316         assertEquals("format",
317                      "The disk \"MyDisk\" contains 12 file(s).",
318                      string.toString());
319     }
320 
321     @Test
TestMsgFormatChoice()322     public void TestMsgFormatChoice()
323     {
324         MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
325         double filelimits[] = {0,1,2};
326         String filepart[] = {"no files","one file","{0,number} files"};
327         ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
328         form.setFormat(1, fileform); // NOT zero, see below
329 
330         FieldPosition ignore = new FieldPosition(FieldPosition_DONT_CARE);
331         StringBuffer string = new StringBuffer();
332         Object testArgs1[] = {new Integer(0), "MyDisk"};
333         form.format(testArgs1, string, ignore);
334         assertEquals("format#1",
335                      "The disk \"MyDisk\" contains no files.",
336                      string.toString());
337 
338         string.setLength(0);
339         Object testArgs2[] = {new Integer(1), "MyDisk"};
340         form.format(testArgs2, string, ignore);
341         assertEquals("format#2",
342                      "The disk \"MyDisk\" contains one file.",
343                      string.toString());
344 
345         string.setLength(0);
346         Object testArgs3[] = {new Integer(1273), "MyDisk"};
347         form.format(testArgs3, string, ignore);
348         assertEquals("format#3",
349                      "The disk \"MyDisk\" contains 1,273 files.",
350                      string.toString());
351     }
352 
353     //---------------------------------
354     //  API Tests
355     //---------------------------------
356 
357     @Test
TestClone()358     public void TestClone()
359     {
360         MessageFormat x = new MessageFormat("There are {0} files on {1}");
361         MessageFormat z = new MessageFormat("There are {0} files on {1} created");
362         MessageFormat y = null;
363         y = (MessageFormat)x.clone();
364         if (x.equals(y) &&
365             !x.equals(z) &&
366             !y.equals(z) )
367             logln("First test (operator ==): Passed!");
368         else {
369             errln("First test (operator ==): Failed!");
370         }
371         if ((x.equals(y) && y.equals(x)) &&
372             (!x.equals(z) && !z.equals(x)) &&
373             (!y.equals(z) && !z.equals(y)) )
374             logln("Second test (equals): Passed!");
375         else {
376             errln("Second test (equals): Failed!");
377         }
378 
379     }
380 
381     @Test
TestEquals()382     public void TestEquals()
383     {
384         MessageFormat x = new MessageFormat("There are {0} files on {1}");
385         MessageFormat y = new MessageFormat("There are {0} files on {1}");
386         if (!x.equals(y)) {
387             errln("First test (operator ==): Failed!");
388         }
389 
390     }
391 
392     @Test
TestNotEquals()393     public void TestNotEquals()
394     {
395         MessageFormat x = new MessageFormat("There are {0} files on {1}");
396         MessageFormat y = new MessageFormat("There are {0} files on {1}");
397         y.setLocale(Locale.FRENCH);
398         if (x.equals(y)) {
399             errln("First test (operator !=): Failed!");
400         }
401         y = new MessageFormat("There are {0} files on {1}");
402         y.applyPattern("There are {0} files on {1} the disk");
403         if (x.equals(y)) {
404             errln("Second test (operator !=): Failed!");
405         }
406     }
407 
408     @Test
TestHashCode()409     public void TestHashCode()
410     {
411         ULocale save = ULocale.getDefault();
412         ULocale.setDefault(ULocale.US);
413 
414         MessageFormat x = new MessageFormat("There are {0} files on {1}");
415         MessageFormat z = new MessageFormat("There are {0} files on {1}");
416         MessageFormat y = null;
417         y = (MessageFormat)x.clone();
418         if (x.hashCode() != y.hashCode())
419             errln("FAIL: identical objects have different hashcodes");
420         if (x.hashCode() != z.hashCode())
421             errln("FAIL: identical objects have different hashcodes");
422 
423     /* These are not errors
424         y.setLocale(ULocale.FRENCH);
425         if (x.hashCode() == y.hashCode())
426             errln("FAIL: different objects have same hashcodes. Locale ignored");
427 
428         z.applyPattern("There are {0} files on {1} the disk");
429         if (x.hashCode() == z.hashCode())
430             errln("FAIL: different objects have same hashcodes. Pattern ignored");
431     */
432 
433         ULocale.setDefault(save);
434     }
435 
436     @Test
TestSetLocale()437     public void TestSetLocale()
438     {
439         Object arguments[] = {
440             new Double(456.83),
441             new Date(871068000000L),
442             "deposit"
443             };
444 
445         StringBuffer result = new StringBuffer();
446 
447         //String formatStr = "At {1,time} on {1,date}, you made a {2} of {0,number,currency}.";
448         String formatStr = "At <time> on {1,date}, you made a {2} of {0,number,currency}.";
449         // {sfb} to get $, would need Locale::US, not Locale::ENGLISH
450         // Just use unlocalized currency symbol.
451         // ICU 62: use the unknown currency symbol XXX.
452         //String compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of $456.83.";
453         String compareStrEng = "At <time> on Aug 8, 1997, you made a deposit of ";
454         compareStrEng += "\u00a4";
455         compareStrEng += "456.83.";
456         // {sfb} to get DM, would need Locale::GERMANY, not Locale::GERMAN
457         // Just use unlocalized currency symbol.
458         //String compareStrGer = "At <time> on 08.08.1997, you made a deposit of 456,83 DM.";
459         String compareStrGer = "At <time> on 08.08.1997, you made a deposit of ";
460         compareStrGer += "456,83\u00a0";
461         compareStrGer += "XXX";
462         compareStrGer += ".";
463 
464         MessageFormat msg = new MessageFormat(formatStr, Locale.ENGLISH);
465         result.setLength(0);
466         FieldPosition pos = new FieldPosition(FieldPosition_DONT_CARE);
467         result = msg.format(
468             arguments,
469             result,
470             pos);
471         assertEquals("format", compareStrEng, result.toString());
472 
473         msg.setLocale(Locale.ENGLISH);
474         assertEquals("getLocale", Locale.ENGLISH, msg.getLocale());
475 
476         msg.setLocale(Locale.GERMAN);
477         assertEquals("getLocale", Locale.GERMAN, msg.getLocale());
478 
479         msg.applyPattern(formatStr);
480         result.setLength(0);
481         result = msg.format(
482             arguments,
483             result,
484             pos);
485         assertEquals("format", compareStrGer, result.toString());
486 
487         //Cover getULocale()
488         logln("Testing set/get ULocale ...");
489         msg.setLocale(ULocale.ENGLISH);
490         assertEquals("getULocale", ULocale.ENGLISH, msg.getULocale());
491 
492         msg.setLocale(ULocale.GERMAN);
493         assertEquals("getULocale", ULocale.GERMAN, msg.getULocale());
494 
495         msg.applyPattern(formatStr);
496         result.setLength(0);
497         result = msg.format(
498             arguments,
499             result,
500             pos);
501         assertEquals("format", compareStrGer, result.toString());
502     }
503 
504     @SuppressWarnings("static-access")
505     @Test
TestFormat()506     public void TestFormat()
507     {
508         final Object ft_arr[] =
509         {
510             new Date(871068000000L)
511         };
512 
513         StringBuffer result = new StringBuffer();
514 
515         //String formatStr = "At {1,time} on {1,date}, you made a {2} of {0,number,currency}.";
516         String formatStr = "On {0,date}, it began.";
517         String compareStr = "On Aug 8, 1997, it began.";
518 
519         MessageFormat msg = new MessageFormat(formatStr);
520         FieldPosition fp = new FieldPosition(FieldPosition_DONT_CARE);
521 
522         try {
523             msg.format(new Date(871068000000L),
524                        result,
525                        fp);
526             errln("*** MSG format without expected error code.");
527         } catch (Exception e1) {
528         }
529 
530         result.setLength(0);
531         result = msg.format(
532             ft_arr,
533             result,
534             fp);
535         assertEquals("format", compareStr, result.toString());
536 
537         Map<String,Object> map = new HashMap<String,Object>();
538         try{
539             msg.format("", map);
540         } catch(Exception e){
541             errln("MessageFormat.format(String,Map) was not suppose to return " +
542                     "an exception.");
543         }
544     }
545 
546     @Test
TestParse()547     public void TestParse()
548     {
549         String msgFormatString = "{0} =sep= {1}";
550         MessageFormat msg = new MessageFormat(msgFormatString);
551         String source = "abc =sep= def";
552 
553         try {
554             Object[] fmt_arr = msg.parse(source);
555             if (fmt_arr.length != 2) {
556                 errln("*** MSG parse (ustring, count, err) count err.");
557             } else {
558                 // TODO: This if statement seems to be redundant. [tschumann]
559                 if (fmt_arr.length != 2) {
560                     errln("*** MSG parse (ustring, parsepos., count) count err.");
561                 } else {
562                     assertEquals("parse()[0]", "abc", fmt_arr[0]);
563                     assertEquals("parse()[1]", "def", fmt_arr[1]);
564                 }
565             }
566         } catch (ParseException e1) {
567             errln("*** MSG parse (ustring, count, err) error.");
568         }
569 
570         ParsePosition pp = new ParsePosition(0);
571 
572         Object[] fmt_arr = msg.parse(source, pp);
573         if (pp.getIndex()==0 || fmt_arr==null) {
574             errln("*** MSG parse (ustring, parsepos., count) error.");
575         } else {
576             if (fmt_arr.length != 2) {
577                 errln("*** MSG parse (ustring, parsepos., count) count err.");
578             } else {
579                 assertEquals("parse()[0]", "abc", fmt_arr[0]);
580                 assertEquals("parse()[1]", "def", fmt_arr[1]);
581             }
582         }
583 
584         pp.setIndex(0);
585         Object[] fmta;
586 
587         fmta = (Object[]) msg.parseObject( source, pp );
588         if (pp.getIndex() == 0) {
589             errln("*** MSG parse (ustring, Object, parsepos ) error.");
590         } else {
591             if (fmta.length != 2) {
592                 errln("*** MSG parse (ustring, count, err) count err.");
593             } else {
594                 // TODO: Don't we want to check fmta?
595                 //       In this case this if statement would be redundant, too.
596                 //       [tschumann]
597                 if (fmt_arr.length != 2) {
598                     errln("*** MSG parse (ustring, parsepos., count) count err.");
599                 } else {
600                     // TODO: Don't we want to check fmta? [tschumann]
601                     assertEquals("parse()[0]", "abc", fmt_arr[0]);
602                     assertEquals("parse()[1]", "def", fmt_arr[1]);
603                 }
604             }
605         }
606     }
607 
608     /**
609      * Of course, in Java there is no adopt, but we retain the same
610      * method name. [alan]
611      */
612     @Test
TestAdopt()613     public void TestAdopt()
614     {
615         String formatStr = "{0,date},{1},{2,number}";
616         String formatStrChange = "{0,number},{1,number},{2,date}";
617         MessageFormat msg = new MessageFormat(formatStr);
618         MessageFormat msgCmp = new MessageFormat(formatStr);
619         Format[] formats = msg.getFormats();
620         Format[] formatsCmp = msgCmp.getFormats();
621         Format[] formatsChg = null;
622         Format[] formatsAct = null;
623         Format a = null;
624         Format b = null;
625         Format[] formatsToAdopt = null;
626 
627         if (formats==null || formatsCmp==null || (formats.length <= 0) || (formats.length != formatsCmp.length)) {
628             errln("Error getting Formats");
629             return;
630         }
631 
632         int i;
633 
634         for (i = 0; i < formats.length; i++) {
635             a = formats[i];
636             b = formatsCmp[i];
637             if ((a != null) && (b != null)) {
638                 if (!a.equals(b)) {
639                     errln("a != b");
640                     return;
641                 }
642             } else if ((a != null) || (b != null)) {
643                 errln("(a != null) || (b != null)");
644                 return;
645             }
646         }
647 
648         msg.applyPattern( formatStrChange ); //set msg formats to something different
649         formatsChg = msg.getFormats(); // tested function
650         if (formatsChg==null || (formatsChg.length != formats.length)) {
651             errln("Error getting Formats");
652             return;
653         }
654 
655         boolean diff;
656         diff = true;
657         for (i = 0; i < formats.length; i++) {
658             a = formatsChg[i];
659             b = formatsCmp[i];
660             if ((a != null) && (b != null)) {
661                 if (a.equals(b)) {
662                     logln("formatsChg == formatsCmp at index " + i);
663                     diff = false;
664                 }
665             }
666         }
667         if (!diff) {
668             errln("*** MSG getFormats diff err.");
669             return;
670         }
671 
672         logln("MSG getFormats tested.");
673 
674         msg.setFormats( formatsCmp ); //tested function
675 
676         formatsAct = msg.getFormats();
677         if (formatsAct==null || (formatsAct.length <=0) || (formatsAct.length != formatsCmp.length)) {
678             errln("Error getting Formats");
679             return;
680         }
681 
682         assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern());
683         // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
684         // assertEquals("msg.toPattern()", formatStr, msg.toPattern());
685         try {
686             msg.toPattern();
687             errln("msg.setFormat().toPattern() does not throw an IllegalStateException");
688         } catch(IllegalStateException e) {
689             // ok
690         }
691 
692         for (i = 0; i < formatsAct.length; i++) {
693             a = formatsAct[i];
694             b = formatsCmp[i];
695             if ((a != null) && (b != null)) {
696                 if (!a.equals(b)) {
697                     errln("formatsAct != formatsCmp at index " + i);
698                     return;
699                 }
700             } else if ((a != null) || (b != null)) {
701                 errln("(a != null) || (b != null)");
702                 return;
703             }
704         }
705         logln("MSG setFormats tested.");
706 
707         //----
708 
709         msg.applyPattern( formatStrChange ); //set msg formats to something different
710 
711         formatsToAdopt = new Format[formatsCmp.length];
712         if (formatsToAdopt==null) {
713             errln("memory allocation error");
714             return;
715         }
716 
717         for (i = 0; i < formatsCmp.length; i++) {
718             if (formatsCmp[i] == null) {
719                 formatsToAdopt[i] = null;
720             } else {
721                 formatsToAdopt[i] = (Format) formatsCmp[i].clone();
722                 if (formatsToAdopt[i]==null) {
723                     errln("Can't clone format at index " + i);
724                     return;
725                 }
726             }
727         }
728         msg.setFormats( formatsToAdopt ); // function to test
729 
730         assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern());
731         // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
732         // assertEquals("msg.toPattern()", formatStr, msg.toPattern());
733 
734         formatsAct = msg.getFormats();
735         if (formatsAct==null || (formatsAct.length <=0) || (formatsAct.length != formatsCmp.length)) {
736             errln("Error getting Formats");
737             return;
738         }
739 
740         for (i = 0; i < formatsAct.length; i++) {
741             a = formatsAct[i];
742             b = formatsCmp[i];
743             if ((a != null) && (b != null)) {
744                 if (!a.equals(b)) {
745                     errln("a != b");
746                     return;
747                 }
748             } else if ((a != null) || (b != null)) {
749                 errln("(a != null) || (b != null)");
750                 return;
751             }
752         }
753         logln("MSG adoptFormats tested.");
754 
755         //---- adoptFormat
756 
757         msg.applyPattern( formatStrChange ); //set msg formats to something different
758 
759         formatsToAdopt = new Format[formatsCmp.length];
760         if (formatsToAdopt==null) {
761             errln("memory allocation error");
762             return;
763         }
764 
765         for (i = 0; i < formatsCmp.length; i++) {
766             if (formatsCmp[i] == null) {
767                 formatsToAdopt[i] = null;
768             } else {
769                 formatsToAdopt[i] = (Format) formatsCmp[i].clone();
770                 if (formatsToAdopt[i]==null) {
771                     errln("Can't clone format at index " + i);
772                     return;
773                 }
774             }
775         }
776 
777         for ( i = 0; i < formatsCmp.length; i++ ) {
778             msg.setFormat( i, formatsToAdopt[i] ); // function to test
779         }
780 
781         assertEquals("msgCmp.toPattern()", formatStr, msgCmp.toPattern());
782         // ICU 4.8 does not support toPattern() when there are custom formats (from setFormat() etc.).
783         // assertEquals("msg.toPattern()", formatStr, msg.toPattern());
784 
785         formatsAct = msg.getFormats();
786         if (formatsAct==null || (formatsAct.length <=0) || (formatsAct.length != formatsCmp.length)) {
787             errln("Error getting Formats");
788             return;
789         }
790 
791         for (i = 0; i < formatsAct.length; i++) {
792             a = formatsAct[i];
793             b = formatsCmp[i];
794             if ((a != null) && (b != null)) {
795                 if (!a.equals(b)) {
796                     errln("a != b");
797                     return;
798                 }
799             } else if ((a != null) || (b != null)) {
800                 errln("(a != null) || (b != null)");
801                 return;
802             }
803         }
804         logln("MSG adoptFormat tested.");
805     }
806 
807     /**
808      * Verify that MessageFormat accomodates more than 10 arguments and
809      * more than 10 subformats.
810      */
811     @Test
TestUnlimitedArgsAndSubformats()812     public void TestUnlimitedArgsAndSubformats() {
813         final String pattern =
814             "On {0,date} (aka {0,date,short}, aka {0,date,long}) "+
815             "at {0,time} (aka {0,time,short}, aka {0,time,long}) "+
816             "there were {1,number} werjes "+
817             "(a {3,number,percent} increase over {2,number}) "+
818             "despite the {4}''s efforts "+
819             "and to delight of {5}, {6}, {7}, {8}, {9}, and {10} {11}.";
820         try {
821             MessageFormat msg = new MessageFormat(pattern);
822 
823             final Object ARGS[] = {
824                 new Date(10000000000000L),
825                 new Integer(1303),
826                 new Integer(1202),
827                 new Double(1303.0/1202 - 1),
828                 "Glimmung",
829                 "the printers",
830                 "Nick",
831                 "his father",
832                 "his mother",
833                 "the spiddles",
834                 "of course",
835                 "Horace"
836             };
837 
838             String expected =
839                 "On Nov 20, 2286 (aka 11/20/86, aka November 20, 2286) "+
840                 "at 9:46:40 AM (aka 9:46 AM, aka 9:46:40 AM PST) "+
841                 "there were 1,303 werjes "+
842                 "(a 8% increase over 1,202) "+
843                 "despite the Glimmung's efforts "+
844                 "and to delight of the printers, Nick, his father, "+
845                 "his mother, the spiddles, and of course Horace.";
846             assertEquals("format", expected, msg.format(ARGS));
847         } catch (IllegalArgumentException e1) {
848             errln("FAIL: constructor failed");
849         }
850     }
851 
852     // test RBNF extensions to message format
853     @Test
TestRBNF()854     public void TestRBNF() {
855         // WARNING: this depends on the RBNF formats for en_US
856         Locale locale = Locale.US;
857         String[] values = {
858             // decimal values do not format completely for ordinal or duration, and
859             // do not always parse, so do not include them
860             "0", "1", "12", "100", "123", "1001", "123,456", "-17",
861         };
862         String[] formats = {
863             "There are {0,spellout} files to search.",
864             "There are {0,spellout,%simplified} files to search.",
865             "The bogus spellout {0,spellout,%BOGUS} files behaves like the default.",
866             "This is the {0,ordinal} file to search.", // TODO fix bug, ordinal does not parse
867             "Searching this file will take {0,duration} to complete.",
868             "Searching this file will take {0,duration,%with-words} to complete.",
869         };
870         final NumberFormat numFmt = NumberFormat.getInstance(locale);
871         Object[] args = new Object[1];
872         Number num = null;
873         for (int i = 0; i < formats.length; ++i) {
874             MessageFormat fmt = new MessageFormat(formats[i], locale);
875             logln("Testing format pattern: '" + formats[i] + "'");
876             for (int j = 0; j < values.length; ++j) {
877                 try {
878                     num = numFmt.parse(values[j]);
879                 }
880                 catch (Exception e) {
881                     throw new IllegalStateException("failed to parse test argument");
882                 }
883                 args[0] = num;
884                 String result = fmt.format(args);
885                 logln("value: " + num + " --> " + result);
886 
887                 if (i != 3) { // TODO: fix this, for now skip ordinal parsing (format string at index 3)
888                     try {
889                         Object[] parsedArgs = fmt.parse(result);
890                         if (parsedArgs.length != 1) {
891                             errln("parse returned " + parsedArgs.length + " args");
892                         } else if (!parsedArgs[0].equals(num)) {
893                             errln("parsed argument " + parsedArgs[0] + " != " + num);
894                         }
895                     }
896                     catch (ParseException e) {
897                         errln("parse of '" + result + "' returned exception: "
898                                 + e.getMessage() + " " + e.getErrorOffset());
899                     }
900                 }
901             }
902         }
903     }
904 
905     @Test
TestSetGetFormats()906     public void TestSetGetFormats()
907     {
908         Object arguments[] = {
909             new Double(456.83),
910             new Date(871068000000L),
911             "deposit"
912             };
913 
914         StringBuffer result = new StringBuffer();
915 
916         String formatStr = "At <time> on {1,date}, you made a {2} of {0,number,currency}.";
917         // original expected format result
918         String compareStr = "At <time> on Aug 8, 1997, you made a deposit of $456.83.";
919         // the date being German-style, but the currency being English-style
920         String compareStr2 = "At <time> on 08.08.1997, you made a deposit of ";
921         compareStr2 += "\u00A4";
922         compareStr2 += "456.83.";
923         // both date and currency formats are German-style
924         String compareStr3 = "At <time> on 08.08.1997, you made a deposit of ";
925         compareStr3 += "456,83\u00a0";
926         compareStr3 += "XXX";
927         compareStr3 += ".";
928 
929         MessageFormat msg = new MessageFormat(formatStr, ULocale.US);
930         result.setLength(0);
931         FieldPosition pos = new FieldPosition(FieldPosition_DONT_CARE);
932         result = msg.format(
933             arguments,
934             result,
935             pos);
936         assertEquals("format", compareStr, result.toString());
937 
938         // constructs a Format array with a English-style Currency formatter
939         //                            and a German-style Date formatter
940         //      might not meaningful, just for testing setFormatsByArgIndex
941         Format[] fmts = new Format[] {
942             NumberFormat.getCurrencyInstance(ULocale.ENGLISH),
943             DateFormat.getDateInstance(DateFormat.DEFAULT, ULocale.GERMAN)
944             };
945 
946         msg.setFormatsByArgumentIndex(fmts);
947         result.setLength(0);
948         pos = new FieldPosition(FieldPosition_DONT_CARE);
949         result = msg.format(
950             arguments,
951             result,
952             pos);
953         assertEquals("format", compareStr2, result.toString());
954 
955         // Construct a German-style Currency formatter, replace the corresponding one
956         // Thus both formatters should format objects with German-style
957         Format newFmt = NumberFormat.getCurrencyInstance(ULocale.GERMAN);
958         msg.setFormatByArgumentIndex(0, newFmt);
959         result.setLength(0);
960         pos = new FieldPosition(FieldPosition_DONT_CARE);
961         result = msg.format(
962             arguments,
963             result,
964             pos);
965         assertEquals("format", compareStr3, result.toString());
966 
967         // verify getFormatsByArgumentIndex
968         //   you should got three formats by that
969         //          - DecimalFormat     locale: de
970         //          - SimpleDateFormat  locale: de
971         //          - null
972         Format[] fmts2 = msg.getFormatsByArgumentIndex();
973         assertEquals("1st subformmater: Format Class", "ohos.global.icu.text.DecimalFormat", fmts2[0].getClass().getName());
974         assertEquals("1st subformmater: its Locale", ULocale.GERMAN, ((UFormat)fmts2[0]).getLocale(ULocale.VALID_LOCALE));
975         assertEquals("2nd subformatter: Format Class", "ohos.global.icu.text.SimpleDateFormat", fmts2[1].getClass().getName());
976         assertEquals("2nd subformmater: its Locale", ULocale.GERMAN, ((UFormat)fmts2[1]).getLocale(ULocale.VALID_LOCALE));
977         assertTrue("The third subFormatter is null", null == fmts2[2]);
978     }
979 
980     // Test the fix pattern api
981     @Test
TestAutoQuoteApostrophe()982     public void TestAutoQuoteApostrophe() {
983         final String[] patterns = { // new pattern, expected pattern
984             "'", "''",
985             "''", "''",
986             "'{", "'{'",
987             "' {", "'' {",
988             "'a", "''a",
989             "'{'a", "'{'a",
990             "'{a'", "'{a'",
991             "'{}", "'{}'",
992             "{'", "{'",
993             "{'a", "{'a",
994             "{'a{}'a}'a", "{'a{}'a}''a",
995             "'}'", "'}'",
996             "'} '{'}'", "'} '{'}''",
997             "'} {{{''", "'} {{{'''",
998         };
999         for (int i = 0; i < patterns.length; i += 2) {
1000             assertEquals("[" + (i/2) + "] \"" + patterns[i] + "\"", patterns[i+1], MessageFormat.autoQuoteApostrophe(patterns[i]));
1001         }
1002     }
1003 
1004     // This tests passing named arguments instead of numbers to format().
1005     @Test
testFormatNamedArguments()1006     public void testFormatNamedArguments() {
1007         Map arguments = new HashMap();
1008         arguments.put("startDate", new Date(871068000000L));
1009 
1010         StringBuffer result = new StringBuffer();
1011 
1012         String formatStr = "On {startDate,date}, it began.";
1013         String compareStr = "On Aug 8, 1997, it began.";
1014 
1015         MessageFormat msg = new MessageFormat(formatStr);
1016         FieldPosition fp = new FieldPosition(FieldPosition_DONT_CARE);
1017 
1018         try {
1019             msg.format(arguments.get("startDate"), result, fp);
1020             errln("*** MSG format without expected error code.");
1021         } catch (Exception e1) {
1022         }
1023 
1024         result.setLength(0);
1025         result = msg.format(
1026             arguments,
1027             result,
1028             fp);
1029         assertEquals("format", compareStr, result.toString());
1030     }
1031 
1032     // This tests parsing formatted messages with named arguments instead of
1033     // numbers.
1034     @Test
testParseNamedArguments()1035     public void testParseNamedArguments() {
1036         String msgFormatString = "{foo} =sep= {bar}";
1037         MessageFormat msg = new MessageFormat(msgFormatString);
1038         String source = "abc =sep= def";
1039 
1040         try {
1041             Map fmt_map = msg.parseToMap(source);
1042             if (fmt_map.keySet().size() != 2) {
1043                 errln("*** MSG parse (ustring, count, err) count err.");
1044             } else {
1045                 assertEquals("parse()[0]", "abc", fmt_map.get("foo"));
1046                 assertEquals("parse()[1]", "def", fmt_map.get("bar"));
1047             }
1048         } catch (ParseException e1) {
1049             errln("*** MSG parse (ustring, count, err) error.");
1050         }
1051 
1052         ParsePosition pp = new ParsePosition(0);
1053         Map fmt_map = msg.parseToMap(source, pp);
1054         if (pp.getIndex()==0 || fmt_map==null) {
1055             errln("*** MSG parse (ustring, parsepos., count) error.");
1056         } else {
1057             if (fmt_map.keySet().size() != 2) {
1058                 errln("*** MSG parse (ustring, parsepos., count) count err.");
1059             } else {
1060                 assertEquals("parse()[0]", "abc", fmt_map.get("foo"));
1061                 assertEquals("parse()[1]", "def", fmt_map.get("bar"));
1062             }
1063         }
1064 
1065         pp.setIndex(0);
1066 
1067         Map fmta = (Map) msg.parseObject( source, pp );
1068         if (pp.getIndex() == 0) {
1069             errln("*** MSG parse (ustring, Object, parsepos ) error.");
1070         } else {
1071             if (fmta.keySet().size() != 2) {
1072                 errln("*** MSG parse (ustring, count, err) count err.");
1073             } else {
1074                 assertEquals("parse()[0]", "abc", fmta.get("foo"));
1075                 assertEquals("parse()[1]", "def", fmta.get("bar"));
1076             }
1077         }
1078     }
1079 
1080     // Ensure that methods designed for numeric arguments only, will throw
1081     // an exception when called on MessageFormat objects created with
1082     // named arguments.
1083     @Test
testNumericOnlyMethods()1084     public void testNumericOnlyMethods() {
1085         MessageFormat msg = new MessageFormat("Number of files: {numfiles}");
1086         boolean gotException = false;
1087         try {
1088             Format fmts[] = {new DecimalFormat()};
1089             msg.setFormatsByArgumentIndex(fmts);
1090         } catch (IllegalArgumentException e) {
1091             gotException = true;
1092         }
1093         if (!gotException) {
1094             errln("MessageFormat.setFormatsByArgumentIndex() should throw an " +
1095                   "IllegalArgumentException when called on formats with " +
1096                   "named arguments but did not!");
1097         }
1098 
1099         gotException = false;
1100         try {
1101             msg.setFormatByArgumentIndex(0, new DecimalFormat());
1102         } catch (IllegalArgumentException e) {
1103             gotException = true;
1104         }
1105         if (!gotException) {
1106             errln("MessageFormat.setFormatByArgumentIndex() should throw an " +
1107                   "IllegalArgumentException when called on formats with " +
1108                   "named arguments but did not!");
1109         }
1110 
1111         gotException = false;
1112         try {
1113             msg.getFormatsByArgumentIndex();
1114         } catch (IllegalArgumentException e) {
1115             gotException = true;
1116         }
1117         if (!gotException) {
1118             errln("MessageFormat.getFormatsByArgumentIndex() should throw an " +
1119                   "IllegalArgumentException when called on formats with " +
1120                   "named arguments but did not!");
1121         }
1122 
1123         gotException = false;
1124         try {
1125             Object args[] = {new Long(42)};
1126             msg.format(args, new StringBuffer(), new FieldPosition(FieldPosition_DONT_CARE));
1127         } catch (IllegalArgumentException e) {
1128             gotException = true;
1129         }
1130         if (!gotException) {
1131             errln("MessageFormat.format(Object[], StringBuffer, FieldPosition) " +
1132                   "should throw an IllegalArgumentException when called on " +
1133                   "formats with named arguments but did not!");
1134         }
1135 
1136         gotException = false;
1137         try {
1138             Object args[] = {new Long(42)};
1139             msg.format((Object) args, new StringBuffer(), new FieldPosition(FieldPosition_DONT_CARE));
1140         } catch (IllegalArgumentException e) {
1141             gotException = true;
1142         }
1143         if (!gotException) {
1144             errln("MessageFormat.format(Object, StringBuffer, FieldPosition) " +
1145                   "should throw an IllegalArgumentException when called with " +
1146                   "non-Map object as argument on formats with named " +
1147                   "arguments but did not!");
1148         }
1149 
1150         gotException = false;
1151         try {
1152             msg.parse("Number of files: 5", new ParsePosition(0));
1153         } catch (IllegalArgumentException e) {
1154             gotException = true;
1155         }
1156         if (!gotException) {
1157             errln("MessageFormat.parse(String, ParsePosition) " +
1158                   "should throw an IllegalArgumentException when called with " +
1159                   "non-Map object as argument on formats with named " +
1160                   "arguments but did not!");
1161         }
1162 
1163         gotException = false;
1164         try {
1165             msg.parse("Number of files: 5");
1166         } catch (IllegalArgumentException e) {
1167             gotException = true;
1168         } catch (ParseException e) {
1169             errln("Wrong exception thrown.");
1170         }
1171         if (!gotException) {
1172             errln("MessageFormat.parse(String) " +
1173                   "should throw an IllegalArgumentException when called with " +
1174                   "non-Map object as argument on formats with named " +
1175                   "arguments but did not!");
1176         }
1177     }
1178 
1179     @Test
testNamedArguments()1180     public void testNamedArguments() {
1181         // ICU 4.8 allows mixing named and numbered arguments.
1182         assertTrue(
1183                 "has some named arguments",
1184                 new MessageFormat("Number of files in folder {0}: {numfiles}").usesNamedArguments());
1185         assertTrue(
1186                 "has some named arguments",
1187                 new MessageFormat("Number of files in folder {folder}: {1}").usesNamedArguments());
1188 
1189         // Test named arguments.
1190         MessageFormat mf = new MessageFormat("Number of files in folder {folder}: {numfiles}");
1191         if (!mf.usesNamedArguments()) {
1192             errln("message format 1 should have used named arguments");
1193         }
1194         mf = new MessageFormat("Wavelength:  {\u028EValue\uFF14}");
1195         if (!mf.usesNamedArguments()) {
1196             errln("message format 2 should have used named arguments");
1197         }
1198 
1199         // Test argument names with invalid start characters.
1200         // Modified: ICU 4.8 allows all characters except for Pattern_White_Space and Pattern_Syntax.
1201         try {
1202             new MessageFormat("Wavelength:  {^\u028EValue\uFF14}");
1203             errln("Creating a MessageFormat with invalid argument names " +
1204             "should throw an IllegalArgumentException but did not!");
1205         } catch (IllegalArgumentException e) {}
1206 
1207         try {
1208             new MessageFormat("Wavelength:  {\uFE45\u028EValue}");
1209             errln("Creating a MessageFormat with invalid argument names " +
1210             "should throw an IllegalArgumentException but did not!");
1211         } catch (IllegalArgumentException e) {}
1212 
1213         // Test argument names with invalid continue characters.
1214         // Modified: ICU 4.8 allows all characters except for Pattern_White_Space and Pattern_Syntax.
1215         try {
1216             new MessageFormat("Wavelength:  {Value@\uFF14}");
1217             errln("Creating a MessageFormat with invalid argument names " +
1218             "should throw an IllegalArgumentException but did not!");
1219         } catch (IllegalArgumentException e) {}
1220 
1221         try {
1222             new MessageFormat("Wavelength:  {Value(\uFF14)}");
1223             errln("Creating a MessageFormat with invalid argument names " +
1224             "should throw an IllegalArgumentException but did not!");
1225         } catch (IllegalArgumentException e) {}
1226     }
1227 
1228     @Test
testNumericFormatWithMap()1229     public void testNumericFormatWithMap() {
1230         MessageFormat mf = new MessageFormat("X:{2} Y:{1}");
1231         if (mf.usesNamedArguments()) {
1232             errln("should not use named arguments");
1233         }
1234 
1235         Map map12 = new HashMap();
1236         map12.put("1", "one");
1237         map12.put("2", "two");
1238 
1239         String target = "X:two Y:one";
1240         String result = mf.format(map12);
1241         if (!target.equals(result)) {
1242             errln("expected '" + target + "' but got '" + result + "'");
1243         }
1244 
1245         try {
1246             Map mapResult = mf.parseToMap(target);
1247             if (!map12.equals(mapResult)) {
1248                 errln("expected " + map12 + " but got " + mapResult);
1249             }
1250         } catch (ParseException e) {
1251             errln("unexpected exception: " + e.getMessage());
1252         }
1253 
1254         Map map10 = new HashMap();
1255         map10.put("1", "one");
1256         map10.put("0", "zero");
1257         target = "X:{2} Y:one";
1258         result = mf.format(map10);
1259         if (!target.equals(result)) {
1260             errln("expected '" + target + "' but got '" + result + "'");
1261         }
1262 
1263         DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
1264         DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM);
1265         Map fmtMap = new HashMap();
1266         fmtMap.put("1", dateFormat);
1267         fmtMap.put("2", timeFormat);
1268         mf.setFormatsByArgumentName(fmtMap);
1269         Date date = new Date(661439820000L);
1270 
1271         try {
1272             result = mf.format(map12); // should fail, wrong argument type
1273             fail("expected exception but got '" + result + "'");
1274         } catch (IllegalArgumentException e) {
1275             // expect this
1276         }
1277 
1278         Map argMap = new HashMap();
1279         argMap.put("1", date);
1280         argMap.put("2", date);
1281         target = "X:5:17:00 AM Y:Dec 17, 1990";
1282         result = mf.format(argMap);
1283         if (!target.equals(result)) {
1284             errln("expected '" + target + "' but got '" + result + "'");
1285         }
1286     }
1287 
1288     // This tests nested Formats inside PluralFormat.
1289     @Test
testNestedFormatsInPluralFormat()1290     public void testNestedFormatsInPluralFormat() {
1291         try {
1292             MessageFormat msgFmt = new MessageFormat(
1293                     "{0, plural, one {{0, number,C''est #,##0.0# fichier}} " +
1294                     "other {Ce sont # fichiers}} dans la liste.",
1295                     new ULocale("fr"));
1296             Object objArray[] = {new Long(0)};
1297             HashMap objMap = new HashMap();
1298             objMap.put("argument", objArray[0]);
1299             String result = msgFmt.format(objArray);
1300             if (!result.equals("C'est 0,0 fichier dans la liste.")) {
1301                 errln("PluralFormat produced wrong message string.");
1302             }
1303         } catch (Exception e) {
1304             e.printStackTrace();
1305             throw new RuntimeException(e.getMessage());
1306         }
1307     }
1308 
1309     // This tests PluralFormats used inside MessageFormats.
1310     @Test
testPluralFormat()1311     public void testPluralFormat() {
1312         {
1313             MessageFormat mfNum = new MessageFormat(
1314                     "{0, plural, one{C''est # fichier} other " +
1315                       "{Ce sont # fichiers}} dans la liste.",
1316                     new ULocale("fr"));
1317             MessageFormat mfAlpha = new MessageFormat(
1318                     "{argument, plural, one{C''est # fichier} other {Ce " +
1319                       "sont # fichiers}} dans la liste.",
1320                     new ULocale("fr"));
1321             Object objArray[] = {new Long(0)};
1322             HashMap objMap = new HashMap();
1323             objMap.put("argument", objArray[0]);
1324             String result = mfNum.format(objArray);
1325             if (!result.equals(mfAlpha.format(objMap))) {
1326                 errln("PluralFormat's output differs when using named " +
1327                         "arguments instead of numbers!");
1328             }
1329             if (!result.equals("C'est 0 fichier dans la liste.")) {
1330                 errln("PluralFormat produced wrong message string.");
1331             }
1332         }
1333         {
1334             MessageFormat mfNum = new MessageFormat (
1335                     "There {0, plural, one{is # zavod}few{are {0, " +
1336                       "number,###.0} zavoda} other{are # zavodov}} in the " +
1337                       "directory.",
1338                     new ULocale("uk"));
1339             MessageFormat mfAlpha = new MessageFormat (
1340                     "There {argument, plural, one{is # zavod}few{" +
1341                       "are {argument, number,###.0} zavoda} other{are # " +
1342                       "zavodov}} in the directory.",
1343                     new ULocale("uk"));
1344             Object objArray[] = {new Long(4)};
1345             HashMap objMap = new HashMap();
1346             objMap.put("argument", objArray[0]);
1347             String result = mfNum.format(objArray);
1348             if (!result.equals(mfAlpha.format(objMap))) {
1349                 errln("PluralFormat's output differs when using named " +
1350                         "arguments instead of numbers!");
1351             }
1352             if (!result.equals("There are 4,0 zavoda in the directory.")) {
1353                 errln("PluralFormat produced wrong message string.");
1354             }
1355         }
1356     }
1357 
1358     @Test
testApostropheInPluralAndSelect()1359     public void testApostropheInPluralAndSelect() {
1360         MessageFormat fmt = new MessageFormat(
1361                 "abc_{0,plural,other{#'#'#'{'#''}}_def_{1,select,other{sel'}'ect''}}_xyz",
1362                 Locale.ENGLISH);
1363         String expected = "abc_3#3{3'_def_sel}ect'_xyz";
1364         String result = fmt.format(new Object[] { 3, "x" });
1365         if (!result.equals(expected)) {
1366             errln("MessageFormat with apostrophes in plural/select arguments failed:\n" +
1367                   "Expected "+expected+"\n" +
1368                   "Got      "+result);
1369         }
1370     }
1371 
1372   // Test toPattern when there is a PluralFormat
1373     @Test
testPluralFormatToPattern()1374   public void testPluralFormatToPattern() {
1375     String[] patterns = {
1376       "Beware of vicious {0, plural, one {hamster} other {hamsters}}.",
1377       "{0, plural, one {{0, number,C''''est #,##0.0# fichier}} other {Ce sont # fichiers}} dans la liste.",
1378       "{0, plural, one {C''est # fichier} other {Ce sont # fichiers}} dans la liste.",
1379     };
1380 
1381     for (int i = 0; i < patterns.length; ++i) {
1382       String pattern = patterns[i];
1383       MessageFormat mf = new MessageFormat(pattern);
1384       MessageFormat mf2 = new MessageFormat(mf.toPattern());
1385       if (!mf.equals(mf2)) {
1386         errln("message formats not equal for pattern:\n*** '" + pattern + "'\n*** '" +
1387               mf.toPattern() + "'");
1388       }
1389     }
1390   }
1391 
1392     /**
1393      * This tests SelectFormats used inside MessageFormats.
1394      */
1395     @Test
testSelectFormat()1396     public void testSelectFormat() {
1397         String pattern = null;
1398         MessageFormat msgFmt = null ;
1399 
1400         //Create the MessageFormat with simple French pattern
1401         pattern = "{0} est {1, select, female {all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.";
1402         msgFmt = new MessageFormat(pattern);
1403         assertNotNull( "ERROR:Failure in constructing with simple French pattern", msgFmt);
1404 
1405         //Format
1406         Object testArgs[][] ={
1407             {"Kirti","female"} ,
1408             {"Victor","other"} ,
1409             {"Ash","unknown"} ,
1410         };
1411         String exp[] = {
1412             "Kirti est all\\u00E9e \\u00E0 Paris." ,
1413             "Victor est all\\u00E9 \\u00E0 Paris.",
1414             "Ash est all\\u00E9 \\u00E0 Paris."
1415         };
1416         for ( int i=0; i< 3; i++){
1417             assertEquals("ERROR:Failure in format with simple French Pattern" ,
1418                       exp[i] , msgFmt.format(testArgs[i]) );
1419         }
1420 
1421         //Create the MessageFormat with Quoted French Pattern
1422         pattern = "{0} est {1, select, female {all\\u00E9e c''est} other {all\\u00E9 c''est}} \\u00E0 Paris.";
1423         msgFmt = new MessageFormat(pattern);
1424         assertNotNull( "ERROR:Failure in constructing with quoted French pattern", msgFmt);
1425 
1426         //Format
1427         Object testArgs1[][] ={
1428             {"Kirti","female"} ,
1429             {"Victor","other"} ,
1430             {"Ash","male"} ,
1431         };
1432         String exp1[] = {
1433             "Kirti est all\\u00E9e c'est \\u00E0 Paris." ,
1434             "Victor est all\\u00E9 c'est \\u00E0 Paris.",
1435             "Ash est all\\u00E9 c'est \\u00E0 Paris."
1436         };
1437         for ( int i=0; i< 3; i++){
1438             assertEquals("ERROR:Failure in format with quoted French Pattern" ,
1439                           exp1[i] , msgFmt.format(testArgs1[i]) );
1440         }
1441 
1442         //Nested patterns with plural, number ,choice ,select format etc.
1443         //Select Format with embedded number format
1444         pattern = "{0} est {1, select, female {{2,number,integer} all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.";
1445         msgFmt = new MessageFormat(pattern);
1446         assertNotNull( "ERROR:Failure in constructing with nested pattern 1", msgFmt);
1447 
1448         //Format
1449         Object testArgs3[][] ={
1450             {"Kirti", "female", 6} ,
1451             {"Kirti", "female", 100.100} ,
1452             {"Kirti", "other", 6} ,
1453         };
1454         String exp3[] = {
1455             "Kirti est 6 all\\u00E9e \\u00E0 Paris." ,
1456             "Kirti est 100 all\\u00E9e \\u00E0 Paris.",
1457             "Kirti est all\\u00E9 \\u00E0 Paris."
1458         };
1459 
1460         for ( int i=0; i< 3; i++){
1461             assertEquals("ERROR:Failure in format with nested Pattern 1" ,
1462                           exp3[i] , msgFmt.format(testArgs3[i]) );
1463         }
1464 
1465         //Plural format with embedded select format
1466         pattern = "{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.";
1467         msgFmt = new MessageFormat(pattern);
1468         assertNotNull( "ERROR:Failure in constructing with nested pattern 2", msgFmt);
1469 
1470         //Format
1471         Object testArgs4[][] ={
1472             {"Kirti",6,"female"},
1473             {"Kirti",1,"female"},
1474             {"Ash",1,"other"},
1475             {"Ash",5,"other"},
1476         };
1477         String exp4[] = {
1478             "Kirti sont all\\u00E9es \\u00E0 Paris." ,
1479             "Kirti est all\\u00E9e \\u00E0 Paris.",
1480             "Ash est all\\u00E9 \\u00E0 Paris.",
1481             "Ash sont all\\u00E9s \\u00E0 Paris."
1482         };
1483         for ( int i=0; i< 4; i++){
1484             assertEquals("ERROR:Failure in format with nested Pattern 2" ,
1485                           exp4[i] , msgFmt.format(testArgs4[i]) );
1486         }
1487 
1488         //Select, plural, and number formats heavily nested
1489         pattern = "{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.";
1490         msgFmt = new MessageFormat(pattern);
1491         assertNotNull( "ERROR:Failure in constructing with nested pattern 3", msgFmt);
1492 
1493         //Format
1494         Object testArgs5[][] ={
1495             {"Kirti","other",1,"other"},
1496             {"Kirti","other",6,"other"},
1497             {"Kirti","other",1,"female"},
1498             {"Kirti","other",3,"female"},
1499             {"Kirti","female",1,"female"},
1500             {"Kirti","female",5,"female"},
1501             {"Kirti","female",1,"other"},
1502             {"Kirti","female",5,"other"},
1503             {"Kirti","mixed",1,"mixed"},
1504             {"Kirti","mixed",1,"other"},
1505             {"Kirti","female",1,"mixed"},
1506             {"Kirti","mixed",5,"mixed"},
1507             {"Kirti","mixed",5,"other"},
1508             {"Kirti","female",5,"mixed"},
1509         };
1510         String exp5[] = {
1511             "Kirti und sein Freund gingen nach Paris." ,
1512             "Kirti und seine 6 Freunde gingen nach Paris." ,
1513             "Kirti und seine Freundin gingen nach Paris.",
1514             "Kirti und seine 3 Freundinnen gingen nach Paris.",
1515             "Kirti und ihre Freundin  gingen nach Paris.",
1516             "Kirti und ihre 5 Freundinnen  gingen nach Paris.",
1517             "Kirti und ihr Freund  gingen nach Paris.",
1518             "Kirti und ihre 5 Freunde  gingen nach Paris.",
1519             "Kirti und sein Freund gingen nach Paris.",
1520             "Kirti und sein Freund gingen nach Paris.",
1521             "Kirti und ihr Freund  gingen nach Paris.",
1522             "Kirti und seine 5 Freunde gingen nach Paris." ,
1523             "Kirti und seine 5 Freunde gingen nach Paris." ,
1524             "Kirti und ihre 5 Freunde  gingen nach Paris."
1525         };
1526         //Format
1527         for ( int i=0; i< 14; i++){
1528             assertEquals("ERROR:Failure in format with nested Pattern 3" ,
1529                           exp5[i] , msgFmt.format(testArgs5[i]) );
1530         }
1531     }
1532 
1533     /**
1534      * Test toPattern when there is a SelectFormat
1535      */
1536     @Test
testSelectFormatToPattern()1537     public void testSelectFormatToPattern() {
1538         String[] patterns = {
1539           //Pattern with some text at start and at end
1540           "{0} est {1,select, female {all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.",
1541           //Pattern with some text at start
1542           "{0} est {1,select, female {all\\u00E9e} other {all\\u00E9}}",
1543           //Pattern with some text at end
1544           "{1, select,female {all\\u00E9e} other {all\\u00E9}} \\u00E0 Paris.",
1545           //Pattern with no text at any  end
1546           "{1, select,female {all\\u00E9e} other {all\\u00E9}}.",
1547           //Quoted French pattern
1548           "{0} est {1,select, female {all\\u00E9e c''est} other {all\\u00E9 c''est}} \\u00E0 Paris.",
1549         };
1550 
1551         for (int i = 0; i < patterns.length; ++i) {
1552             String pattern = patterns[i];
1553             MessageFormat mf = new MessageFormat(pattern);
1554             MessageFormat mf2 = new MessageFormat(mf.toPattern());
1555             if (!mf.equals(mf2)) {
1556                 errln("message formats not equal for pattern:\n*** '"
1557                      + pattern + "'\n*** '" + mf.toPattern() + "'");
1558             }
1559         }
1560     }
1561 
1562     // Test case for null arguments.
1563     // Ticket#6361
1564     @Test
TestNullArgs()1565     public void TestNullArgs() {
1566         MessageFormat msgfmt = new MessageFormat("{0} - {1}");
1567         Object[][] TEST_CASES = {
1568             {null,                          "{0} - {1}"},
1569             {new Object[] {null},           "null - {1}"},
1570             {new Object[] {null, null},     "null - null"},
1571             {new Object[] {"one"},          "one - {1}"},
1572             {new Object[] {"one", null},    "one - null"},
1573             {new Object[] {null, "two"},    "null - two"},
1574         };
1575 
1576         for (int i = 0; i < TEST_CASES.length; i++) {
1577             String text = msgfmt.format(TEST_CASES[i][0]);
1578             if (!text.equals(TEST_CASES[i][1])) {
1579                 errln("FAIL: Returned[" + text + "] Expected[" + TEST_CASES[i][1] + "]");
1580             }
1581         }
1582     }
1583 
1584     @Test
TestSetFormat()1585     public void TestSetFormat() {
1586         MessageFormat ms = new MessageFormat("{number} {date}", ULocale.ENGLISH);
1587         final DecimalFormat decimalFormat = new DecimalFormat("000.000", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
1588         ms.setFormatByArgumentName("number", decimalFormat);
1589         final SimpleDateFormat dateFormat = new SimpleDateFormat("'year:'yy 'month:'MM 'day:'dd");
1590         dateFormat.setTimeZone(TimeZone.getTimeZone("Etc/GMT"));
1591         ms.setFormatByArgumentName("date", dateFormat);
1592         Map map = new HashMap();
1593         map.put("number", new Integer(1234));
1594         map.put("date", new Date(0,0,0));
1595         String result = ms.format(map);
1596         assertEquals("setFormatByArgumentName", "1234.000 year:99 month:12 day:31", result);
1597         Set formatNames = ms.getArgumentNames();
1598         assertEquals("Format Names match", formatNames, map.keySet());
1599         assertEquals("Decimal", decimalFormat, ms.getFormatByArgumentName("number"));
1600         assertEquals("Date", dateFormat, ms.getFormatByArgumentName("date"));
1601     }
1602 
1603     // Test case for formatToCharacterIterator
1604     @Test
TestFormatToCharacterIterator()1605     public void TestFormatToCharacterIterator() {
1606         MessageFormat[] msgfmts = {
1607                 new MessageFormat(
1608                         "The {3,ordinal} folder ''{0}'' contains {2,number} file(s), created at {1,time} on {1,date}."),
1609                 new MessageFormat(
1610                         "The {arg3,ordinal} folder ''{arg0}'' contains {arg2,number} file(s), created at {arg1,time} on {arg1,date}."), // same
1611                                                                                                                                         // as
1612                                                                                                                                         // above,
1613                                                                                                                                         // but
1614                                                                                                                                         // named
1615                                                                                                                                         // args
1616                 new MessageFormat("The folder contains {0}.") };
1617 
1618         double filelimits[] = { 0, 1, 2 };
1619         String filepart[] = { "no files", "one file", "{0,number} files" };
1620         ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
1621         msgfmts[2].setFormat(0, fileform);
1622 
1623         Object[] args0 = new Object[] { "tmp", new Date(1184777888000L), new Integer(15), new Integer(2) };
1624 
1625         HashMap args1 = new HashMap();
1626         args1.put("arg0", "tmp");
1627         args1.put("arg1", new Date(1184777888000L));
1628         args1.put("arg2", new Integer(15));
1629         args1.put("arg3", new Integer(2));
1630 
1631         Object[] args2 = new Object[] { new Integer(34) };
1632 
1633         Object[] args = { args0, args1, args2 };
1634 
1635         String[] expectedStrings = {
1636                 "The 2nd folder 'tmp' contains 15 file(s), created at 9:58:08 AM on Jul 18, 2007.",
1637                 "The 2nd folder 'tmp' contains 15 file(s), created at 9:58:08 AM on Jul 18, 2007.",
1638                 "The folder contains 34 files." };
1639 
1640         AttributedString[] expectedAttributedStrings = { new AttributedString(expectedStrings[0]),
1641                 new AttributedString(expectedStrings[1]), new AttributedString(expectedStrings[2]) };
1642 
1643         // Add expected attributes to the expectedAttributedStrings[0]
1644         expectedAttributedStrings[0].addAttribute(MessageFormat.Field.ARGUMENT, new Integer(3), 4, 7);
1645         expectedAttributedStrings[0].addAttribute(MessageFormat.Field.ARGUMENT, new Integer(0), 16, 19);
1646         expectedAttributedStrings[0].addAttribute(MessageFormat.Field.ARGUMENT, new Integer(2), 30, 32);
1647         expectedAttributedStrings[0].addAttribute(NumberFormat.Field.INTEGER, NumberFormat.Field.INTEGER, 30, 32);
1648         expectedAttributedStrings[0].addAttribute(MessageFormat.Field.ARGUMENT, new Integer(1), 53, 63);
1649         expectedAttributedStrings[0].addAttribute(DateFormat.Field.HOUR1, DateFormat.Field.HOUR1, 53, 54);
1650         //expectedAttributedStrings[0].addAttribute(DateFormat.Field.TIME_SEPARATOR, DateFormat.Field.TIME_SEPARATOR, 54, 55);
1651         expectedAttributedStrings[0].addAttribute(DateFormat.Field.MINUTE, DateFormat.Field.MINUTE, 55, 57);
1652         //expectedAttributedStrings[0].addAttribute(DateFormat.Field.TIME_SEPARATOR, DateFormat.Field.TIME_SEPARATOR, 57, 58);
1653         expectedAttributedStrings[0].addAttribute(DateFormat.Field.SECOND, DateFormat.Field.SECOND, 58, 60);
1654         expectedAttributedStrings[0].addAttribute(DateFormat.Field.AM_PM, DateFormat.Field.AM_PM, 61, 63);
1655         expectedAttributedStrings[0].addAttribute(MessageFormat.Field.ARGUMENT, new Integer(1), 67, 79);
1656         expectedAttributedStrings[0].addAttribute(DateFormat.Field.MONTH, DateFormat.Field.MONTH, 67, 70);
1657         expectedAttributedStrings[0].addAttribute(DateFormat.Field.DAY_OF_MONTH, DateFormat.Field.DAY_OF_MONTH, 71, 73);
1658         expectedAttributedStrings[0].addAttribute(DateFormat.Field.YEAR, DateFormat.Field.YEAR, 75, 79);
1659 
1660         // Add expected attributes to the expectedAttributedStrings[1]
1661         expectedAttributedStrings[1].addAttribute(MessageFormat.Field.ARGUMENT, "arg3", 4, 7);
1662         expectedAttributedStrings[1].addAttribute(MessageFormat.Field.ARGUMENT, "arg0", 16, 19);
1663         expectedAttributedStrings[1].addAttribute(MessageFormat.Field.ARGUMENT, "arg2", 30, 32);
1664         expectedAttributedStrings[1].addAttribute(NumberFormat.Field.INTEGER, NumberFormat.Field.INTEGER, 30, 32);
1665         expectedAttributedStrings[1].addAttribute(MessageFormat.Field.ARGUMENT, "arg1", 53, 63);
1666         expectedAttributedStrings[1].addAttribute(DateFormat.Field.HOUR1, DateFormat.Field.HOUR1, 53, 54);
1667         //expectedAttributedStrings[1].addAttribute(DateFormat.Field.TIME_SEPARATOR, DateFormat.Field.TIME_SEPARATOR, 54, 55);
1668         expectedAttributedStrings[1].addAttribute(DateFormat.Field.MINUTE, DateFormat.Field.MINUTE, 55, 57);
1669         //expectedAttributedStrings[1].addAttribute(DateFormat.Field.TIME_SEPARATOR, DateFormat.Field.TIME_SEPARATOR, 57, 58);
1670         expectedAttributedStrings[1].addAttribute(DateFormat.Field.SECOND, DateFormat.Field.SECOND, 58, 60);
1671         expectedAttributedStrings[1].addAttribute(DateFormat.Field.AM_PM, DateFormat.Field.AM_PM, 61, 63);
1672         expectedAttributedStrings[1].addAttribute(MessageFormat.Field.ARGUMENT, "arg1", 67, 79);
1673         expectedAttributedStrings[1].addAttribute(DateFormat.Field.MONTH, DateFormat.Field.MONTH, 67, 70);
1674         expectedAttributedStrings[1].addAttribute(DateFormat.Field.DAY_OF_MONTH, DateFormat.Field.DAY_OF_MONTH, 71, 73);
1675         expectedAttributedStrings[1].addAttribute(DateFormat.Field.YEAR, DateFormat.Field.YEAR, 75, 79);
1676 
1677         // Add expected attributes to the expectedAttributedStrings[2]
1678         expectedAttributedStrings[2].addAttribute(MessageFormat.Field.ARGUMENT, new Integer(0), 20, 28);
1679         expectedAttributedStrings[2].addAttribute(NumberFormat.Field.INTEGER, NumberFormat.Field.INTEGER, 20, 22);
1680 
1681         for (int i = 0; i < msgfmts.length; i++) {
1682             AttributedCharacterIterator acit = msgfmts[i].formatToCharacterIterator(args[i]);
1683             AttributedCharacterIterator expectedAcit = expectedAttributedStrings[i].getIterator();
1684 
1685             // Check available attributes
1686             Set attrSet = acit.getAllAttributeKeys();
1687             Set expectedAttrSet = expectedAcit.getAllAttributeKeys();
1688             if (attrSet.size() != expectedAttrSet.size()) {
1689                 errln("FAIL: Number of attribute keys is " + attrSet.size() + " expected: " + expectedAttrSet.size());
1690             }
1691             Iterator attrIterator = attrSet.iterator();
1692             while (attrIterator.hasNext()) {
1693                 AttributedCharacterIterator.Attribute attr = (AttributedCharacterIterator.Attribute) attrIterator
1694                         .next();
1695                 if (!expectedAttrSet.contains(attr)) {
1696                     errln("FAIL: The attribute " + attr + " is not expected.");
1697                 }
1698             }
1699 
1700             StringBuffer buf = new StringBuffer();
1701             int index = acit.getBeginIndex();
1702             int end = acit.getEndIndex();
1703             int indexExp = expectedAcit.getBeginIndex();
1704             int expectedLen = expectedAcit.getEndIndex() - indexExp;
1705             if (end - index != expectedLen) {
1706                 errln("FAIL: Length of the result attributed string is " + (end - index) + " expected: " + expectedLen);
1707             } else {
1708                 // Check attributes associated with each character
1709                 while (index < end) {
1710                     char c = acit.setIndex(index);
1711                     buf.append(c);
1712                     expectedAcit.setIndex(indexExp);
1713 
1714                     Map attrs = acit.getAttributes();
1715                     Map attrsExp = expectedAcit.getAttributes();
1716                     if (attrs.size() != attrsExp.size()) {
1717                         errln("FAIL: Number of attributes associated with index " + index + " is " + attrs.size()
1718                                 + " expected: " + attrsExp.size());
1719                     } else {
1720                         // Check all attributes at the index
1721                         Iterator entryIterator = attrsExp.entrySet().iterator();
1722                         while (entryIterator.hasNext()) {
1723                             Map.Entry entry = (Map.Entry) entryIterator.next();
1724                             if (attrs.containsKey(entry.getKey())) {
1725                                 Object value = attrs.get(entry.getKey());
1726                                 assertEquals("Attribute value at index " + index, entry.getValue(), value);
1727                             } else {
1728                                 errln("FAIL: Attribute " + entry.getKey() + " is missing at index " + index);
1729                             }
1730                         }
1731                     }
1732                     index++;
1733                     indexExp++;
1734                 }
1735                 assertEquals("AttributedString contents", expectedStrings[i], buf.toString());
1736             }
1737         }
1738 
1739         // Tests when "if (arguments == null)" is true
1740         try {
1741             MessageFormat mf = new MessageFormat("");
1742             mf.formatToCharacterIterator(null);
1743             errln("MessageFormat.formatToCharacterIterator(Object) was suppose "
1744                     + "to return an exception when null is passed.");
1745         } catch (Exception e) {
1746         }
1747     }
1748 
1749     /*
1750      * Tests the method public Format getFormatByArgumentName(String argumentName)
1751      */
1752     @Test
TestGetFormatByArgumentName()1753     public void TestGetFormatByArgumentName() {
1754         MessageFormat mf = new MessageFormat("");
1755         if (mf.getFormatByArgumentName("") != null) {
1756             errln("MessageFormat.getFormatByArgumentName(String) was suppose "
1757                     + "to return an null if argumentName was not found.");
1758         }
1759     }
1760 
getPatternAndSkipSyntax(MessagePattern pattern)1761     public String getPatternAndSkipSyntax(MessagePattern pattern) {
1762         StringBuilder sb = new StringBuilder(pattern.getPatternString());
1763         int count = pattern.countParts();
1764         for (int i = count; i > 0;) {
1765             MessagePattern.Part part = pattern.getPart(--i);
1766             if (part.getType() == MessagePattern.Part.Type.SKIP_SYNTAX) {
1767                 sb.delete(part.getIndex(), part.getLimit());
1768             }
1769         }
1770         return sb.toString();
1771     }
1772 
1773     @Test
TestApostropheMode()1774     public void TestApostropheMode() {
1775         MessagePattern ado_mp = new MessagePattern(MessagePattern.ApostropheMode.DOUBLE_OPTIONAL);
1776         MessagePattern adr_mp = new MessagePattern(MessagePattern.ApostropheMode.DOUBLE_REQUIRED);
1777         assertEquals("wrong value",
1778                 MessagePattern.ApostropheMode.DOUBLE_OPTIONAL,
1779                 ado_mp.getApostropheMode());
1780         assertEquals("wrong value",
1781                 MessagePattern.ApostropheMode.DOUBLE_REQUIRED,
1782                 adr_mp.getApostropheMode());
1783         assertNotEquals("MessagePatterns with different ApostropheMode (no pattern)", ado_mp, adr_mp);
1784         assertNotEquals("MessagePatterns with different ApostropheMode (a)",
1785                 ado_mp.parse("a"), adr_mp.parse("a"));
1786 
1787         String[] tuples = new String[] {
1788             // Desired output
1789             // DOUBLE_OPTIONAL pattern
1790             // DOUBLE_REQUIRED pattern (null=same as DOUBLE_OPTIONAL)
1791             "I see {many}", "I see '{many}'", null,
1792             "I said {'Wow!'}", "I said '{''Wow!''}'", null,
1793             "I dont know", "I dont know", "I don't know",
1794             "I don't know", "I don't know", "I don''t know",
1795             "I don't know", "I don''t know", "I don''t know",
1796         };
1797         for (int i = 0; i < tuples.length; i += 3) {
1798             String desired = tuples[i];
1799             String ado_pattern = tuples[i + 1];
1800             assertEquals("DOUBLE_OPTIONAL failure", desired,
1801                     getPatternAndSkipSyntax(ado_mp.parse(ado_pattern)));
1802             String adr_pattern = tuples[i + 2];
1803             if (adr_pattern == null) {
1804                 adr_pattern = ado_pattern;
1805             }
1806             assertEquals("DOUBLE_REQUIRED failure", desired,
1807                     getPatternAndSkipSyntax(adr_mp.parse(adr_pattern)));
1808         }
1809     }
1810 
1811     // Compare behavior of JDK and ICU's DOUBLE_REQUIRED compatibility mode.
1812     @Test
TestCompatibleApostrophe()1813     public void TestCompatibleApostrophe() {
1814         // Message with choice argument which does not contain another argument.
1815         // The JDK performs only one apostrophe-quoting pass on this pattern.
1816         String pattern = "ab{0,choice,0#1'2''3'''4''''.}yz";
1817         java.text.MessageFormat jdkMsg =
1818             new java.text.MessageFormat(pattern, Locale.ENGLISH);
1819 
1820         MessageFormat compMsg = new MessageFormat("", Locale.ENGLISH);
1821         compMsg.applyPattern(pattern, MessagePattern.ApostropheMode.DOUBLE_REQUIRED);
1822         assertEquals("wrong value",
1823                 MessagePattern.ApostropheMode.DOUBLE_REQUIRED,
1824                 compMsg.getApostropheMode());
1825 
1826         MessageFormat icuMsg = new MessageFormat("", Locale.ENGLISH);
1827         icuMsg.applyPattern(pattern, MessagePattern.ApostropheMode.DOUBLE_OPTIONAL);
1828         assertEquals("wrong value",
1829                 MessagePattern.ApostropheMode.DOUBLE_OPTIONAL,
1830                 icuMsg.getApostropheMode());
1831 
1832         Object[] zero0 = new Object[] { 0 };
1833         assertEquals("unexpected JDK MessageFormat apostrophe behavior",
1834                 "ab12'3'4''.yz",
1835                 jdkMsg.format(zero0));
1836         assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
1837                 "ab12'3'4''.yz",
1838                 compMsg.format(zero0));
1839         assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior",
1840                 "ab1'2'3''4''.yz",
1841                 icuMsg.format(zero0));
1842 
1843         // Message with choice argument which contains a nested simple argument.
1844         // The JDK performs two apostrophe-quoting passes.
1845         pattern = "ab{0,choice,0#1'2''3'''4''''.{0,number,'#x'}}yz";
1846         jdkMsg.applyPattern(pattern);
1847         compMsg.applyPattern(pattern);
1848         icuMsg.applyPattern(pattern);
1849         assertEquals("unexpected JDK MessageFormat apostrophe behavior",
1850                 "ab1234'.0xyz",
1851                 jdkMsg.format(zero0));
1852         assertEquals("incompatible ICU MessageFormat compatibility-apostrophe behavior",
1853                 "ab1234'.0xyz",
1854                 compMsg.format(zero0));
1855         assertEquals("unexpected ICU MessageFormat double-apostrophe-optional behavior",
1856                 "ab1'2'3''4''.#x0yz",
1857                 icuMsg.format(zero0));
1858 
1859         // Message with choice argument which contains a nested choice argument.
1860         // The JDK fails to parse this pattern.
1861         // jdkMsg.applyPattern("cd{0,choice,0#ef{0,choice,0#1'2''3'''4''''.}uv}wx");
1862         // For lack of comparison, we do not test ICU with this pattern.
1863 
1864         // The JDK ChoiceFormat itself always performs one apostrophe-quoting pass.
1865         ChoiceFormat choice = new ChoiceFormat("0#1'2''3'''4''''.");
1866         assertEquals("unexpected JDK ChoiceFormat apostrophe behavior",
1867                 "12'3'4''.",
1868                 choice.format(0));
1869         choice.applyPattern("0#1'2''3'''4''''.{0,number,'#x'}");
1870         assertEquals("unexpected JDK ChoiceFormat apostrophe behavior",
1871                 "12'3'4''.{0,number,#x}",
1872                 choice.format(0));
1873     }
1874 
1875     @Test
TestTrimArgumentName()1876     public void TestTrimArgumentName() {
1877         // ICU 4.8 allows and ignores white space around argument names and numbers.
1878         MessageFormat m = new MessageFormat("a { 0 , number , '#,#'#.0 } z", Locale.ENGLISH);
1879         assertEquals("trim-numbered-arg format() failed", "a  #,#2.0  z", m.format(new Object[] { 2 }));
1880 
1881         m.applyPattern("x { _oOo_ , number , integer } y");
1882         Map<String, Object> map = new HashMap<String, Object>();
1883         map.put("_oOo_", new Integer(3));
1884         StringBuffer result = new StringBuffer();
1885         assertEquals("trim-named-arg format() failed", "x 3 y",
1886                      m.format(map, result, new FieldPosition(FieldPosition_DONT_CARE)).toString());
1887     }
1888 
1889     @Test
TestSelectOrdinal()1890     public void TestSelectOrdinal() {
1891         // Test plural & ordinal together,
1892         // to make sure that we get the correct cached PluralSelector for each.
1893         MessageFormat m = new MessageFormat(
1894             "{0,plural,one{1 file}other{# files}}, " +
1895             "{0,selectordinal,one{#st file}two{#nd file}few{#rd file}other{#th file}}",
1896             ULocale.ENGLISH);
1897         Object[] args = new Object[] { 21 };
1898         FieldPosition ignore = null;
1899         StringBuffer result = new StringBuffer();
1900         assertEquals("plural-and-ordinal format(21)", "21 files, 21st file",
1901                      m.format(args, result, ignore).toString());
1902 
1903         args[0] = 2;
1904         result.delete(0, result.length());
1905         assertEquals("plural-and-ordinal format(2) failed", "2 files, 2nd file",
1906                      m.format(args, result, ignore).toString());
1907 
1908         args[0] = 1;
1909         result.delete(0, result.length());
1910         assertEquals("plural-and-ordinal format(1) failed", "1 file, 1st file",
1911                      m.format(args, result, ignore).toString());
1912 
1913         args[0] = 3;
1914         result.delete(0, result.length());
1915         assertEquals("plural-and-ordinal format(3) failed", "3 files, 3rd file",
1916                      m.format(args, result, ignore).toString());
1917     }
1918 
1919     @Test
TestDecimals()1920     public void TestDecimals() {
1921         // Simple number replacement.
1922         MessageFormat m = new MessageFormat(
1923                 "{0,plural,one{one meter}other{# meters}}",
1924                 ULocale.ENGLISH);
1925         Object[] args = new Object[] { 1 };
1926         FieldPosition ignore = null;
1927         StringBuffer result = new StringBuffer();
1928         assertEquals("simple format(1)", "one meter",
1929                 m.format(args, result, ignore).toString());
1930 
1931         args[0] = 1.5;
1932         result.delete(0, result.length());
1933         assertEquals("simple format(1.5)", "1.5 meters",
1934                 m.format(args, result, ignore).toString());
1935 
1936         // Simple but explicit.
1937         MessageFormat m0 = new MessageFormat(
1938                 "{0,plural,one{one meter}other{{0} meters}}",
1939                 ULocale.ENGLISH);
1940         args[0] = 1;
1941         result.delete(0, result.length());
1942         assertEquals("explicit format(1)", "one meter",
1943                 m0.format(args, result, ignore).toString());
1944 
1945         args[0] = 1.5;
1946         result.delete(0, result.length());
1947         assertEquals("explicit format(1.5)", "1.5 meters",
1948                 m0.format(args, result, ignore).toString());
1949 
1950         // With offset and specific simple format with optional decimals.
1951         MessageFormat m1 = new MessageFormat(
1952                 "{0,plural,offset:1 one{another meter}other{{0,number,00.#} meters}}",
1953                 ULocale.ENGLISH);
1954         args[0] = 1;
1955         result.delete(0, result.length());
1956         assertEquals("offset format(1)", "01 meters",
1957                 m1.format(args, result, ignore).toString());
1958 
1959         args[0] = 2;
1960         result.delete(0, result.length());
1961         assertEquals("offset format(1)", "another meter",
1962                 m1.format(args, result, ignore).toString());
1963 
1964         args[0] = 2.5;
1965         result.delete(0, result.length());
1966         assertEquals("offset format(1)", "02.5 meters",
1967                 m1.format(args, result, ignore).toString());
1968 
1969         // With offset and specific simple format with forced decimals.
1970         MessageFormat m2 = new MessageFormat(
1971                 "{0,plural,offset:1 one{another meter}other{{0,number,0.0} meters}}",
1972                 ULocale.ENGLISH);
1973         args[0] = 1;
1974         result.delete(0, result.length());
1975         assertEquals("offset-decimals format(1)", "1.0 meters",
1976                 m2.format(args, result, ignore).toString());
1977 
1978         args[0] = 2;
1979         result.delete(0, result.length());
1980         assertEquals("offset-decimals format(1)", "2.0 meters",
1981                 m2.format(args, result, ignore).toString());
1982 
1983         args[0] = 2.5;
1984         result.delete(0, result.length());
1985         assertEquals("offset-decimals format(1)", "2.5 meters",
1986                 m2.format(args, result, ignore).toString());
1987     }
1988 
1989     @Test
TestArgIsPrefixOfAnother()1990     public void TestArgIsPrefixOfAnother() {
1991         // Ticket #11952
1992         MessageFormat mf1 = new MessageFormat(
1993                 "{0,select,a{A}ab{AB}abc{ABC}other{?}}", ULocale.ENGLISH);
1994         assertEquals("a", "A", mf1.format(new Object[] { "a" }));
1995         assertEquals("ab", "AB", mf1.format(new Object[] { "ab" }));
1996         assertEquals("abc", "ABC", mf1.format(new Object[] { "abc" }));
1997 
1998         // Ticket #12172
1999         MessageFormat mf2 = new MessageFormat("{a} {aa} {aaa}", ULocale.ENGLISH);
2000         Map<String, Object> args = new TreeMap<String, Object>();
2001         args.put("a", "A");
2002         args.put("aa", "AB");
2003         args.put("aaa", "ABC");
2004         assertEquals("a aa aaa", "A AB ABC", mf2.format(args, new StringBuffer(), null).toString());
2005 
2006         // Ticket #12172
2007         MessageFormat mf3 = new MessageFormat("{aa} {aaa}", ULocale.ENGLISH);
2008         assertEquals("aa aaa", "AB ABC", mf3.format(args, new StringBuffer(), null).toString());
2009     }
2010 
2011     @Test
TestMessagePatternAutoQuoteApostropheDeep()2012     public void TestMessagePatternAutoQuoteApostropheDeep() {
2013         // Example input & output taken from API docs.
2014         MessagePattern pattern = new MessagePattern(
2015                 "I don't '{know}' {gender,select,female{h''er}other{h'im}}.");
2016         assertEquals("autoQuoteApostropheDeep()",
2017                 "I don''t '{know}' {gender,select,female{h''er}other{h''im}}.",
2018                 pattern.autoQuoteApostropheDeep());
2019     }
2020 
2021     @Test
TestMessagePatternFreezable()2022     public void TestMessagePatternFreezable() {
2023         MessagePattern pattern = new MessagePattern();
2024         assertFalse("just constructed, not yet frozen", pattern.isFrozen());
2025         pattern.parse("fee");
2026         assertTrue("parsed, not empty", pattern.countParts() > 0);
2027         pattern.freeze();
2028         assertTrue("just frozen", pattern.isFrozen());
2029         try {
2030             pattern.parse("fi");
2031             fail("MessagePattern.freeze().parse() did not fail");
2032         } catch (Exception expected) {
2033         }
2034         assertEquals("frozen+parse: no change", "fee", pattern.autoQuoteApostropheDeep());
2035         MessagePattern thawed = pattern.cloneAsThawed();
2036         assertFalse("thawed", thawed.isFrozen());
2037         assertTrue("still frozen", pattern.isFrozen());
2038         assertTrue("frozen!=thawed", pattern != thawed);
2039         thawed.parse("fo");
2040         assertEquals("thawed+parse", "fo", thawed.autoQuoteApostropheDeep());
2041     }
2042 
2043     @Test
TestMessagePatternNamedAndNumberedArguments()2044     public void TestMessagePatternNamedAndNumberedArguments() {
2045         MessagePattern pattern = new MessagePattern();
2046         pattern.parse("fee");
2047         assertFalse("fee no named args", pattern.hasNamedArguments());
2048         assertFalse("fee no numbered args", pattern.hasNumberedArguments());
2049         pattern.parse("fi {0}");
2050         assertFalse("fi {0} no named args", pattern.hasNamedArguments());
2051         assertTrue("fi {0} has numbered args", pattern.hasNumberedArguments());
2052         pattern.parse("fo {name}");
2053         assertTrue("fo {name} has named args", pattern.hasNamedArguments());
2054         assertFalse("fo {name} no numbered args", pattern.hasNumberedArguments());
2055         pattern.parse("fum {0} {name}");
2056         assertTrue("fum {0} {name} has named args", pattern.hasNamedArguments());
2057         assertTrue("fum {0} {name} no numbered args", pattern.hasNumberedArguments());
2058     }
2059 
2060     @Test
TestMessagePatternPartCoverage()2061     public void TestMessagePatternPartCoverage() {
2062         MessagePattern pattern = new MessagePattern("ab{17}c");
2063         assertEquals("msg start { arg number } msg limit", 5, pattern.countParts());
2064         MessagePattern.Part arg = pattern.getPart(2);
2065         assertEquals("arg number", MessagePattern.Part.Type.ARG_NUMBER, arg.getType());
2066         assertEquals("arg number start", 3, arg.getIndex());
2067         assertEquals("arg number length", 2, arg.getLength());
2068         assertEquals("arg number limit", 5, arg.getLimit());
2069         assertEquals("arg number 17", 17, arg.getValue());
2070     }
2071 
2072     @Test
TestMessagePatternParseChoiceStyle()2073     public void TestMessagePatternParseChoiceStyle() {
2074         // This would be tested by ChoiceFormat if ICU4J had its own version of that,
2075         // like ICU4C does.
2076         // Instead, there is only java.text.ChoiceFormat.
2077         // Most of the implementation gets covered by testing with a MessageFormat
2078         // that contains a nested ChoiceFormat pattern,
2079         // but that does not call this public API method.
2080         MessagePattern pattern = new MessagePattern();
2081         // Example string from java.text.ChoiceFormat class docs.
2082         pattern.parseChoiceStyle(
2083                 "-1#is negative| 0#is zero or fraction | 1#is one |" +
2084                 "1.0<is 1+ |2#is two |2<is more than 2.");
2085         // Only simple API coverage. The parser implementation is tested via MessageFormat.
2086         assertTrue("many parts", pattern.countParts() > 10);
2087     }
2088 
2089     // This is mostly a code coverage test with verification minimized to what can be plausibly assumed: different
2090     // hash values for distinctly different objects.
2091     @Test
TestDateFormatHashCode()2092     public void TestDateFormatHashCode() {
2093         DateFormat testDF1 = DateFormat.getDateInstance(DateFormat.DEFAULT, ULocale.GERMAN);
2094         DateFormat testDF2 = DateFormat.getDateInstance(DateFormat.DEFAULT, ULocale.FRENCH);
2095 
2096         int actualHashResult1 = testDF1.hashCode();
2097         int actualHashResult2 = testDF2.hashCode();
2098         assertNotEquals("DateFormat hashCode() test: really the same hashcode?", actualHashResult1, actualHashResult2);
2099     }
2100 
2101     @Test
TestMessageFormatNumberSkeleton()2102     public void TestMessageFormatNumberSkeleton() {
2103         Object[][] cases = new Object[][] {
2104                 { "{0,number,::percent}", ULocale.ENGLISH, 50, "50%" },
2105                 { "{0,number,::percent scale/100}", ULocale.ENGLISH, 0.5, "50%" },
2106                 { "{0,number,   ::   percent   scale/100   }", ULocale.ENGLISH, 0.5, "50%" },
2107                 { "{0,number,::currency/USD}", ULocale.ENGLISH, 23, "$23.00" },
2108                 { "{0,number,::precision-integer}", ULocale.ENGLISH, 514.23, "514" },
2109                 { "{0,number,::.000}", ULocale.ENGLISH, 514.23, "514.230" },
2110                 { "{0,number,::.}", ULocale.ENGLISH, 514.23, "514" },
2111                 { "{0,number,::}", ULocale.FRENCH, 514.23, "514,23" },
2112                 { "Cost: {0,number,::currency/EUR}.", ULocale.ENGLISH, 4.3, "Cost: €4.30." },
2113                 { "{0,number,'::'0.00}", ULocale.ENGLISH, 50, "::50.00" }, // pattern literal
2114         };
2115 
2116         for (Object[] cas : cases) {
2117             String messagePattern = (String) cas[0];
2118             ULocale locale = (ULocale) cas[1];
2119             Number arg = (Number) cas[2];
2120             String expected = (String) cas[3];
2121 
2122             MessageFormat msgf = new MessageFormat(messagePattern, locale);
2123             StringBuffer sb = new StringBuffer();
2124             FieldPosition fpos = new FieldPosition(FieldPosition_DONT_CARE);
2125             msgf.format(new Object[] { arg }, sb, fpos);
2126 
2127             assertEquals(messagePattern, expected, sb.toString());
2128         }
2129     }
2130 
doTheRealDateTimeSkeletonTesting(Date date, String messagePattern, ULocale locale, String expected)2131     private static void doTheRealDateTimeSkeletonTesting(Date date, String messagePattern, ULocale locale, String expected) {
2132 
2133         MessageFormat msgf = new MessageFormat(messagePattern, locale);
2134         StringBuffer sb = new StringBuffer();
2135         FieldPosition fpos = new FieldPosition(FieldPosition_DONT_CARE);
2136         msgf.format(new Object[] { date }, sb, fpos);
2137 
2138         assertEquals(messagePattern, expected, sb.toString());
2139     }
2140 
2141     @Test
TestMessageFormatDateSkeleton()2142     public void TestMessageFormatDateSkeleton() {
2143         Date date = new GregorianCalendar(2021, Calendar.NOVEMBER, 23, 16, 42, 55).getTime();
2144 
2145         doTheRealDateTimeSkeletonTesting(date, "{0,date,::MMMMd}", ULocale.ENGLISH, "November 23");
2146         doTheRealDateTimeSkeletonTesting(date, "{0,date,::yMMMMdjm}", ULocale.ENGLISH, "November 23, 2021, 4:42 PM");
2147         doTheRealDateTimeSkeletonTesting(date, "{0,date,   ::   yMMMMd   }", ULocale.ENGLISH, "November 23, 2021");
2148         doTheRealDateTimeSkeletonTesting(date, "{0,date,::yMMMMd}", ULocale.FRENCH, "23 novembre 2021");
2149         doTheRealDateTimeSkeletonTesting(date, "Expiration: {0,date,::yMMM}!", ULocale.ENGLISH, "Expiration: Nov 2021!");
2150         doTheRealDateTimeSkeletonTesting(date, "{0,date,'::'yMMMMd}", ULocale.ENGLISH, "::2021November23"); // pattern literal
2151     }
2152 
2153     @Test
TestMessageFormatTimeSkeleton()2154     public void TestMessageFormatTimeSkeleton() {
2155         Date date = new GregorianCalendar(2021, Calendar.NOVEMBER, 23, 16, 42, 55).getTime();
2156 
2157         doTheRealDateTimeSkeletonTesting(date, "{0,time,::MMMMd}", ULocale.ENGLISH, "November 23");
2158         doTheRealDateTimeSkeletonTesting(date, "{0,time,::yMMMMdjm}", ULocale.ENGLISH, "November 23, 2021, 4:42 PM");
2159         doTheRealDateTimeSkeletonTesting(date, "{0,time,   ::   yMMMMd   }", ULocale.ENGLISH, "November 23, 2021");
2160         doTheRealDateTimeSkeletonTesting(date, "{0,time,::yMMMMd}", ULocale.FRENCH, "23 novembre 2021");
2161         doTheRealDateTimeSkeletonTesting(date, "Expiration: {0,time,::yMMM}!", ULocale.ENGLISH, "Expiration: Nov 2021!");
2162         doTheRealDateTimeSkeletonTesting(date, "{0,time,'::'yMMMMd}", ULocale.ENGLISH, "::2021November23"); // pattern literal
2163     }
2164 }
2165