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