1/*
2 *******************************************************************************
3 * Copyright (C) 1996-2015, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.dev.test.format;
8
9import java.math.BigInteger;
10import java.text.ParseException;
11import java.util.Locale;
12import java.util.Random;
13
14import com.ibm.icu.dev.test.TestFmwk;
15import com.ibm.icu.text.DecimalFormat;
16import com.ibm.icu.text.DecimalFormatSymbols;
17import com.ibm.icu.text.DisplayContext;
18import com.ibm.icu.text.NumberFormat;
19import com.ibm.icu.text.RuleBasedNumberFormat;
20import com.ibm.icu.util.ULocale;
21
22/**
23 * This does not test lenient parse mode, since testing the default implementation
24 * introduces a dependency on collation.  See RbnfLenientScannerTest.
25 */
26public class RbnfTest extends TestFmwk {
27    public static void main(String[] args) {
28        RbnfTest test = new RbnfTest();
29
30        try {
31            test.run(args);
32        }
33        catch (Throwable e) {
34            System.out.println("Entire test failed because of exception: "
35                               + e.toString());
36            e.printStackTrace();
37        }
38    }
39
40    static String fracRules =
41        "%main:\n" +
42        // this rule formats the number if it's 1 or more.  It formats
43        // the integral part using a DecimalFormat ("#,##0" puts
44        // thousands separators in the right places) and the fractional
45        // part using %%frac.  If there is no fractional part, it
46        // just shows the integral part.
47        "    x.0: <#,##0<[ >%%frac>];\n" +
48        // this rule formats the number if it's between 0 and 1.  It
49        // shows only the fractional part (0.5 shows up as "1/2," not
50        // "0 1/2")
51        "    0.x: >%%frac>;\n" +
52        // the fraction rule set.  This works the same way as the one in the
53        // preceding example: We multiply the fractional part of the number
54        // being formatted by each rule's base value and use the rule that
55        // produces the result closest to 0 (or the first rule that produces 0).
56        // Since we only provide rules for the numbers from 2 to 10, we know
57        // we'll get a fraction with a denominator between 2 and 10.
58        // "<0<" causes the numerator of the fraction to be formatted
59        // using numerals
60        "%%frac:\n" +
61        "    2: 1/2;\n" +
62        "    3: <0</3;\n" +
63        "    4: <0</4;\n" +
64        "    5: <0</5;\n" +
65        "    6: <0</6;\n" +
66        "    7: <0</7;\n" +
67        "    8: <0</8;\n" +
68        "    9: <0</9;\n" +
69        "   10: <0</10;\n";
70
71    public void TestCoverage() {
72        String durationInSecondsRules =
73                // main rule set for formatting with words
74                "%with-words:\n"
75                        // take care of singular and plural forms of "second"
76                        + "    0 seconds; 1 second; =0= seconds;\n"
77                        // use %%min to format values greater than 60 seconds
78                        + "    60/60: <%%min<[, >>];\n"
79                        // use %%hr to format values greater than 3,600 seconds
80                        // (the ">>>" below causes us to see the number of minutes
81                        // when when there are zero minutes)
82                        + "    3600/60: <%%hr<[, >>>];\n"
83                        // this rule set takes care of the singular and plural forms
84                        // of "minute"
85                        + "%%min:\n"
86                        + "    0 minutes; 1 minute; =0= minutes;\n"
87                        // this rule set takes care of the singular and plural forms
88                        // of "hour"
89                        + "%%hr:\n"
90                        + "    0 hours; 1 hour; =0= hours;\n"
91
92                        // main rule set for formatting in numerals
93                        + "%in-numerals:\n"
94                        // values below 60 seconds are shown with "sec."
95                        + "    =0= sec.;\n"
96                        // higher values are shown with colons: %%min-sec is used for
97                        // values below 3,600 seconds...
98                        + "    60: =%%min-sec=;\n"
99                        // ...and %%hr-min-sec is used for values of 3,600 seconds
100                        // and above
101                        + "    3600: =%%hr-min-sec=;\n"
102                        // this rule causes values of less than 10 minutes to show without
103                        // a leading zero
104                        + "%%min-sec:\n"
105                        + "    0: :=00=;\n"
106                        + "    60/60: <0<>>;\n"
107                        // this rule set is used for values of 3,600 or more.  Minutes are always
108                        // shown, and always shown with two digits
109                        + "%%hr-min-sec:\n"
110                        + "    0: :=00=;\n"
111                        + "    60/60: <00<>>;\n"
112                        + "    3600/60: <#,##0<:>>>;\n"
113                        // the lenient-parse rules allow several different characters to be used
114                        // as delimiters between hours, minutes, and seconds
115                        + "%%lenient-parse:\n"
116                        + "    & : = . = ' ' = -;\n";
117
118        // extra calls to boost coverage numbers
119        RuleBasedNumberFormat fmt0 = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
120        RuleBasedNumberFormat fmt1 = (RuleBasedNumberFormat)fmt0.clone();
121        RuleBasedNumberFormat fmt2 = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
122        if (!fmt0.equals(fmt0)) {
123            errln("self equality fails");
124        }
125        if (!fmt0.equals(fmt1)) {
126            errln("clone equality fails");
127        }
128        if (!fmt0.equals(fmt2)) {
129            errln("duplicate equality fails");
130        }
131        String str = fmt0.toString();
132        logln(str);
133
134        RuleBasedNumberFormat fmt3 =  new RuleBasedNumberFormat(durationInSecondsRules);
135
136        if (fmt0.equals(fmt3)) {
137            errln("nonequal fails");
138        }
139        if (!fmt3.equals(fmt3)) {
140            errln("self equal 2 fails");
141        }
142        str = fmt3.toString();
143        logln(str);
144
145        String[] names = fmt3.getRuleSetNames();
146
147        try {
148            fmt3.setDefaultRuleSet(null);
149            fmt3.setDefaultRuleSet("%%foo");
150            errln("sdrf %%foo didn't fail");
151        }
152        catch (Exception e) {
153            logln("Got the expected exception");
154        }
155
156        try {
157            fmt3.setDefaultRuleSet("%bogus");
158            errln("sdrf %bogus didn't fail");
159        }
160        catch (Exception e) {
161            logln("Got the expected exception");
162        }
163
164        try {
165            str = fmt3.format(2.3, names[0]);
166            logln(str);
167            str = fmt3.format(2.3, "%%foo");
168            errln("format double %%foo didn't fail");
169        }
170        catch (Exception e) {
171            logln("Got the expected exception");
172        }
173
174        try {
175            str = fmt3.format(123L, names[0]);
176            logln(str);
177            str = fmt3.format(123L, "%%foo");
178            errln("format double %%foo didn't fail");
179        }
180        catch (Exception e) {
181            logln("Got the expected exception");
182        }
183
184        RuleBasedNumberFormat fmt4 = new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
185        RuleBasedNumberFormat fmt5 = new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
186        str = fmt4.toString();
187        logln(str);
188        if (!fmt4.equals(fmt5)) {
189            errln("duplicate 2 equality failed");
190        }
191        str = fmt4.format(123L);
192        logln(str);
193        try {
194            Number num = fmt4.parse(str);
195            logln(num.toString());
196        }
197        catch (Exception e) {
198            errln("parse caught exception");
199        }
200
201        str = fmt4.format(.000123);
202        logln(str);
203        try {
204            Number num = fmt4.parse(str);
205            logln(num.toString());
206        }
207        catch (Exception e) {
208            errln("parse caught exception");
209        }
210
211        str = fmt4.format(456.000123);
212        logln(str);
213        try {
214            Number num = fmt4.parse(str);
215            logln(num.toString());
216        }
217        catch (Exception e) {
218            errln("parse caught exception");
219        }
220    }
221
222    public void TestUndefinedSpellout() {
223        Locale greek = new Locale("el", "", "");
224        RuleBasedNumberFormat[] formatters = {
225                new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.SPELLOUT),
226                new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.ORDINAL),
227                new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.DURATION),
228        };
229
230        String[] data = {
231                "0",
232                "1",
233                "15",
234                "20",
235                "23",
236                "73",
237                "88",
238                "100",
239                "106",
240                "127",
241                "200",
242                "579",
243                "1,000",
244                "2,000",
245                "3,004",
246                "4,567",
247                "15,943",
248                "105,000",
249                "2,345,678",
250                "-36",
251                "-36.91215",
252                "234.56789"
253        };
254
255        NumberFormat decFormat = NumberFormat.getInstance(Locale.US);
256        for (int j = 0; j < formatters.length; ++j) {
257            com.ibm.icu.text.NumberFormat formatter = formatters[j];
258            logln("formatter[" + j + "]");
259            for (int i = 0; i < data.length; ++i) {
260                try {
261                    String result = formatter.format(decFormat.parse(data[i]));
262                    logln("[" + i + "] " + data[i] + " ==> " + result);
263                }
264                catch (Exception e) {
265                    errln("formatter[" + j + "], data[" + i + "] " + data[i] + " threw exception " + e.getMessage());
266                }
267            }
268        }
269    }
270
271    /**
272     * Perform a simple spot check on the English spellout rules
273     */
274    public void TestEnglishSpellout() {
275        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.US,
276                RuleBasedNumberFormat.SPELLOUT);
277        String[][] testData = {
278                { "1", "one" },
279                { "15", "fifteen" },
280                { "20", "twenty" },
281                { "23", "twenty-three" },
282                { "73", "seventy-three" },
283                { "88", "eighty-eight" },
284                { "100", "one hundred" },
285                { "106", "one hundred six" },
286                { "127", "one hundred twenty-seven" },
287                { "200", "two hundred" },
288                { "579", "five hundred seventy-nine" },
289                { "1,000", "one thousand" },
290                { "2,000", "two thousand" },
291                { "3,004", "three thousand four" },
292                { "4,567", "four thousand five hundred sixty-seven" },
293                { "15,943", "fifteen thousand nine hundred forty-three" },
294                { "2,345,678", "two million three hundred forty-five "
295                        + "thousand six hundred seventy-eight" },
296                { "-36", "minus thirty-six" },
297                { "234.567", "two hundred thirty-four point five six seven" }
298        };
299
300        doTest(formatter, testData, true);
301    }
302
303    /**
304     * Perform a simple spot check on the English ordinal-abbreviation rules
305     */
306    public void TestOrdinalAbbreviations() {
307        RuleBasedNumberFormat formatter= new RuleBasedNumberFormat(Locale.US,
308                RuleBasedNumberFormat.ORDINAL);
309        String[][] testData = {
310                { "1", "1st" },
311                { "2", "2nd" },
312                { "3", "3rd" },
313                { "4", "4th" },
314                { "7", "7th" },
315                { "10", "10th" },
316                { "11", "11th" },
317                { "13", "13th" },
318                { "20", "20th" },
319                { "21", "21st" },
320                { "22", "22nd" },
321                { "23", "23rd" },
322                { "24", "24th" },
323                { "33", "33rd" },
324                { "102", "102nd" },
325                { "312", "312th" },
326                { "12,345", "12,345th" }
327        };
328
329        doTest(formatter, testData, false);
330    }
331
332    /**
333     * Perform a simple spot check on the duration-formatting rules
334     */
335    public void TestDurations() {
336        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.US,
337                RuleBasedNumberFormat.DURATION);
338        String[][] testData = {
339                { "3,600", "1:00:00" },             //move me and I fail
340                { "0", "0 sec." },
341                { "1", "1 sec." },
342                { "24", "24 sec." },
343                { "60", "1:00" },
344                { "73", "1:13" },
345                { "145", "2:25" },
346                { "666", "11:06" },
347                //            { "3,600", "1:00:00" },
348                { "3,740", "1:02:20" },
349                { "10,293", "2:51:33" }
350        };
351
352        doTest(formatter, testData, true);
353    }
354
355    /**
356     * Perform a simple spot check on the Spanish spellout rules
357     */
358    public void TestSpanishSpellout() {
359        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(new Locale("es", "es",
360                ""), RuleBasedNumberFormat.SPELLOUT);
361        String[][] testData = {
362                { "1", "uno" },
363                { "6", "seis" },
364                { "16", "diecis\u00e9is" },
365                { "20", "veinte" },
366                { "24", "veinticuatro" },
367                { "26", "veintis\u00e9is" },
368                { "73", "setenta y tres" },
369                { "88", "ochenta y ocho" },
370                { "100", "cien" },
371                { "106", "ciento seis" },
372                { "127", "ciento veintisiete" },
373                { "200", "doscientos" },
374                { "579", "quinientos setenta y nueve" },
375                { "1,000", "mil" },
376                { "2,000", "dos mil" },
377                { "3,004", "tres mil cuatro" },
378                { "4,567", "cuatro mil quinientos sesenta y siete" },
379                { "15,943", "quince mil novecientos cuarenta y tres" },
380                { "2,345,678", "dos millones trescientos cuarenta y cinco mil "
381                        + "seiscientos setenta y ocho"},
382                { "-36", "menos treinta y seis" },
383                { "234.567", "doscientos treinta y cuatro coma cinco seis siete" }
384        };
385
386        doTest(formatter, testData, true);
387    }
388
389    /**
390     * Perform a simple spot check on the French spellout rules
391     */
392    public void TestFrenchSpellout() {
393        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.FRANCE,
394                RuleBasedNumberFormat.SPELLOUT);
395        String[][] testData = {
396                { "1", "un" },
397                { "15", "quinze" },
398                { "20", "vingt" },
399                { "21", "vingt-et-un" },
400                { "23", "vingt-trois" },
401                { "62", "soixante-deux" },
402                { "70", "soixante-dix" },
403                { "71", "soixante-et-onze" },
404                { "73", "soixante-treize" },
405                { "80", "quatre-vingts" },
406                { "88", "quatre-vingt-huit" },
407                { "100", "cent" },
408                { "106", "cent six" },
409                { "127", "cent vingt-sept" },
410                { "200", "deux cents" },
411                { "579", "cinq cent soixante-dix-neuf" },
412                { "1,000", "mille" },
413                { "1,123", "mille cent vingt-trois" },
414                { "1,594", "mille cinq cent quatre-vingt-quatorze" },
415                { "2,000", "deux mille" },
416                { "3,004", "trois mille quatre" },
417                { "4,567", "quatre mille cinq cent soixante-sept" },
418                { "15,943", "quinze mille neuf cent quarante-trois" },
419                { "2,345,678", "deux millions trois cent quarante-cinq mille "
420                        + "six cent soixante-dix-huit" },
421                { "-36", "moins trente-six" },
422                { "234.567", "deux cent trente-quatre virgule cinq six sept" }
423        };
424
425        doTest(formatter, testData, true);
426    }
427
428    /**
429     * Perform a simple spot check on the Swiss French spellout rules
430     */
431    public void TestSwissFrenchSpellout() {
432        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(new Locale("fr", "CH"),
433                RuleBasedNumberFormat.SPELLOUT);
434        String[][] testData = {
435                { "1", "un" },
436                { "15", "quinze" },
437                { "20", "vingt" },
438                { "21", "vingt-et-un" },
439                { "23", "vingt-trois" },
440                { "62", "soixante-deux" },
441                { "70", "septante" },
442                { "71", "septante-et-un" },
443                { "73", "septante-trois" },
444                { "80", "huitante" },
445                { "88", "huitante-huit" },
446                { "100", "cent" },
447                { "106", "cent six" },
448                { "127", "cent vingt-sept" },
449                { "200", "deux cents" },
450                { "579", "cinq cent septante-neuf" },
451                { "1,000", "mille" },
452                { "1,123", "mille cent vingt-trois" },
453                { "1,594", "mille cinq cent nonante-quatre" },
454                { "2,000", "deux mille" },
455                { "3,004", "trois mille quatre" },
456                { "4,567", "quatre mille cinq cent soixante-sept" },
457                { "15,943", "quinze mille neuf cent quarante-trois" },
458                { "2,345,678", "deux millions trois cent quarante-cinq mille "
459                        + "six cent septante-huit" },
460                { "-36", "moins trente-six" },
461                { "234.567", "deux cent trente-quatre virgule cinq six sept" }
462        };
463
464        doTest(formatter, testData, true);
465    }
466
467    /**
468     * Perform a simple spot check on the Italian spellout rules
469     */
470    public void TestItalianSpellout() {
471        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.ITALIAN,
472                RuleBasedNumberFormat.SPELLOUT);
473        String[][] testData = {
474                { "1", "uno" },
475                { "15", "quindici" },
476                { "20", "venti" },
477                { "23", "venti\u00ADtr\u00E9" },
478                { "73", "settanta\u00ADtr\u00E9" },
479                { "88", "ottant\u00ADotto" },
480                { "100", "cento" },
481                { "106", "cento\u00ADsei" },
482                { "108", "cent\u00ADotto" },
483                { "127", "cento\u00ADventi\u00ADsette" },
484                { "181", "cent\u00ADottant\u00ADuno" },
485                { "200", "due\u00ADcento" },
486                { "579", "cinque\u00ADcento\u00ADsettanta\u00ADnove" },
487                { "1,000", "mille" },
488                { "2,000", "due\u00ADmila" },
489                { "3,004", "tre\u00ADmila\u00ADquattro" },
490                { "4,567", "quattro\u00ADmila\u00ADcinque\u00ADcento\u00ADsessanta\u00ADsette" },
491                { "15,943", "quindici\u00ADmila\u00ADnove\u00ADcento\u00ADquaranta\u00ADtr\u00E9" },
492                { "-36", "meno trenta\u00ADsei" },
493                { "234.567", "due\u00ADcento\u00ADtrenta\u00ADquattro virgola cinque sei sette" }
494        };
495
496        doTest(formatter, testData, true);
497    }
498
499    /**
500     * Perform a simple spot check on the German spellout rules
501     */
502    public void TestGermanSpellout() {
503        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.GERMANY,
504                RuleBasedNumberFormat.SPELLOUT);
505        String[][] testData = {
506                { "1", "eins" },
507                { "15", "f\u00fcnfzehn" },
508                { "20", "zwanzig" },
509                { "23", "drei\u00ADund\u00ADzwanzig" },
510                { "73", "drei\u00ADund\u00ADsiebzig" },
511                { "88", "acht\u00ADund\u00ADachtzig" },
512                { "100", "ein\u00ADhundert" },
513                { "106", "ein\u00ADhundert\u00ADsechs" },
514                { "127", "ein\u00ADhundert\u00ADsieben\u00ADund\u00ADzwanzig" },
515                { "200", "zwei\u00ADhundert" },
516                { "579", "f\u00fcnf\u00ADhundert\u00ADneun\u00ADund\u00ADsiebzig" },
517                { "1,000", "ein\u00ADtausend" },
518                { "2,000", "zwei\u00ADtausend" },
519                { "3,004", "drei\u00ADtausend\u00ADvier" },
520                { "4,567", "vier\u00ADtausend\u00ADf\u00fcnf\u00ADhundert\u00ADsieben\u00ADund\u00ADsechzig" },
521                { "15,943", "f\u00fcnfzehn\u00ADtausend\u00ADneun\u00ADhundert\u00ADdrei\u00ADund\u00ADvierzig" },
522                { "2,345,678", "zwei Millionen drei\u00ADhundert\u00ADf\u00fcnf\u00ADund\u00ADvierzig\u00ADtausend\u00AD"
523                        + "sechs\u00ADhundert\u00ADacht\u00ADund\u00ADsiebzig" }
524        };
525
526        doTest(formatter, testData, true);
527    }
528
529    /**
530     * Perform a simple spot check on the Thai spellout rules
531     */
532    public void TestThaiSpellout() {
533        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(new Locale("th", "TH"),
534                RuleBasedNumberFormat.SPELLOUT);
535        String[][] testData = {
536                { "0", "\u0e28\u0e39\u0e19\u0e22\u0e4c" },
537                { "1", "\u0e2b\u0e19\u0e36\u0e48\u0e07" },
538                { "10", "\u0e2a\u0e34\u0e1a" },
539                { "11", "\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14" },
540                { "21", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14" },
541                { "101", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e23\u0e49\u0e2d\u0e22\u200b\u0e2b\u0e19\u0e36\u0e48\u0e07" },
542                { "1.234", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e2d\u0e07\u0e2a\u0e32\u0e21\u0e2a\u0e35\u0e48" },
543                { "21.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
544                { "22.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e2d\u0e07\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
545                { "23.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e32\u0e21\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
546                { "123.45", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e23\u0e49\u0e2d\u0e22\u200b\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e32\u0e21\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
547                { "12,345.678", "\u0E2B\u0E19\u0E36\u0E48\u0E07\u200b\u0E2B\u0E21\u0E37\u0E48\u0E19\u200b\u0E2A\u0E2D\u0E07\u200b\u0E1E\u0E31\u0E19\u200b\u0E2A\u0E32\u0E21\u200b\u0E23\u0E49\u0E2D\u0E22\u200b\u0E2A\u0E35\u0E48\u200b\u0E2A\u0E34\u0E1A\u200b\u0E2B\u0E49\u0E32\u200b\u0E08\u0E38\u0E14\u200b\u0E2B\u0E01\u0E40\u0E08\u0E47\u0E14\u0E41\u0E1B\u0E14" },
548        };
549
550        doTest(formatter, testData, true);
551    }
552
553    /**
554     * Perform a simple spot check on the ordinal spellout rules
555     */
556    public void TestPluralRules() {
557        String enRules = "%digits-ordinal:"
558                + "-x: −>>;"
559                + "0: =#,##0=$(ordinal,one{st}two{nd}few{rd}other{th})$;";
560        RuleBasedNumberFormat enFormatter = new RuleBasedNumberFormat(enRules, ULocale.ENGLISH);
561        String[][] enTestData = {
562                { "1", "1st" },
563                { "2", "2nd" },
564                { "3", "3rd" },
565                { "4", "4th" },
566                { "11", "11th" },
567                { "12", "12th" },
568                { "13", "13th" },
569                { "14", "14th" },
570                { "21", "21st" },
571                { "22", "22nd" },
572                { "23", "23rd" },
573                { "24", "24th" },
574        };
575
576        doTest(enFormatter, enTestData, true);
577
578        // This is trying to model the feminine form, but don't worry about the details too much.
579        // We're trying to test the plural rules.
580        String ruRules = "%spellout-numbering:"
581                + "-x: минус >>;"
582                + "x.x: << запятая >>;"
583                + "0: ноль;"
584                + "1: один;"
585                + "2: два;"
586                + "3: три;"
587                + "4: четыре;"
588                + "5: пять;"
589                + "6: шесть;"
590                + "7: семь;"
591                + "8: восемь;"
592                + "9: девять;"
593                + "10: десять;"
594                + "11: одиннадцать;"
595                + "12: двенадцать;"
596                + "13: тринадцать;"
597                + "14: четырнадцать;"
598                + "15: пятнадцать;"
599                + "16: шестнадцать;"
600                + "17: семнадцать;"
601                + "18: восемнадцать;"
602                + "19: девятнадцать;"
603                + "20: двадцать[ >>];"
604                + "30: тридцать[ >>];"
605                + "40: сорок[ >>];"
606                + "50: пятьдесят[ >>];"
607                + "60: шестьдесят[ >>];"
608                + "70: семьдесят[ >>];"
609                + "80: восемьдесят[ >>];"
610                + "90: девяносто[ >>];"
611                + "100: сто[ >>];"
612                + "200: <<сти[ >>];"
613                + "300: <<ста[ >>];"
614                + "500: <<сот[ >>];"
615                + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})$[ >>];"
616                + "1000000: << $(cardinal,one{миллион}few{миллионы}other{миллионов})$[ >>];";
617        RuleBasedNumberFormat ruFormatter = new RuleBasedNumberFormat(ruRules, new ULocale("ru"));
618        String[][] ruTestData = {
619                { "1", "один" },
620                { "100", "сто" },
621                { "125", "сто двадцать пять" },
622                { "399", "триста девяносто девять" },
623                { "1,000", "один тысяча" },
624                { "1,001", "один тысяча один" },
625                { "2,000", "два тысячи" },
626                { "2,001", "два тысячи один" },
627                { "2,002", "два тысячи два" },
628                { "3,333", "три тысячи триста тридцать три" },
629                { "5,000", "пять тысяч" },
630                { "11,000", "одиннадцать тысяч" },
631                { "21,000", "двадцать один тысяча" },
632                { "22,000", "двадцать два тысячи" },
633                { "25,001", "двадцать пять тысяч один" },
634        };
635
636        doTest(ruFormatter, ruTestData, true);
637
638        // Make sure there are no divide by 0 errors.
639        String result = new RuleBasedNumberFormat(ruRules, new ULocale("ru")).format(21000);
640        if (!"двадцать один тысяча".equals(result)) {
641            errln("Got " + result + " for 21000");
642        }
643    }
644
645    /**
646     * Perform a simple spot check on the parsing going into an infinite loop for alternate rules.
647     */
648    public void TestMultiplePluralRules() {
649        // This is trying to model the feminine form, but don't worry about the details too much.
650        // We're trying to test the plural rules where there are different prefixes.
651        String ruRules = "%spellout-cardinal-feminine-genitive:"
652                + "-x: минус >>;"
653                + "x.x: << запятая >>;"
654                + "0: ноля;"
655                + "1: одной;"
656                + "2: двух;"
657                + "3: трех;"
658                + "4: четырех;"
659                + "5: пяти;"
660                + "6: шести;"
661                + "7: семи;"
662                + "8: восьми;"
663                + "9: девяти;"
664                + "10: десяти;"
665                + "11: одиннадцати;"
666                + "12: двенадцати;"
667                + "13: тринадцати;"
668                + "14: четырнадцати;"
669                + "15: пятнадцати;"
670                + "16: шестнадцати;"
671                + "17: семнадцати;"
672                + "18: восемнадцати;"
673                + "19: девятнадцати;"
674                + "20: двадцати[ >>];"
675                + "30: тридцати[ >>];"
676                + "40: сорока[ >>];"
677                + "50: пятидесяти[ >>];"
678                + "60: шестидесяти[ >>];"
679                + "70: семидесяти[ >>];"
680                + "80: восемидесяти[ >>];"
681                + "90: девяноста[ >>];"
682                + "100: ста[ >>];"
683                + "200: <<сот[ >>];"
684                + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})$[ >>];"
685                + "1000000: =#,##0=;"
686                + "%spellout-cardinal-feminine:"
687                + "-x: минус >>;"
688                + "x.x: << запятая >>;"
689                + "0: ноль;"
690                + "1: одна;"
691                + "2: две;"
692                + "3: три;"
693                + "4: четыре;"
694                + "5: пять;"
695                + "6: шесть;"
696                + "7: семь;"
697                + "8: восемь;"
698                + "9: девять;"
699                + "10: десять;"
700                + "11: одиннадцать;"
701                + "12: двенадцать;"
702                + "13: тринадцать;"
703                + "14: четырнадцать;"
704                + "15: пятнадцать;"
705                + "16: шестнадцать;"
706                + "17: семнадцать;"
707                + "18: восемнадцать;"
708                + "19: девятнадцать;"
709                + "20: двадцать[ >>];"
710                + "30: тридцать[ >>];"
711                + "40: сорок[ >>];"
712                + "50: пятьдесят[ >>];"
713                + "60: шестьдесят[ >>];"
714                + "70: семьдесят[ >>];"
715                + "80: восемьдесят[ >>];"
716                + "90: девяносто[ >>];"
717                + "100: сто[ >>];"
718                + "200: <<сти[ >>];"
719                + "300: <<ста[ >>];"
720                + "500: <<сот[ >>];"
721                + "1000: << $(cardinal,one{тысяча}few{тысячи}other{тысяч})$[ >>];"
722                + "1000000: =#,##0=;";
723        RuleBasedNumberFormat ruFormatter = new RuleBasedNumberFormat(ruRules, new ULocale("ru"));
724        try {
725            Number result;
726            if (1000 != (result = ruFormatter.parse(ruFormatter.format(1000))).doubleValue()) {
727                errln("RuleBasedNumberFormat did not return the correct value. Got: " + result);
728            }
729            if (1000 != (result = ruFormatter.parse(ruFormatter.format(1000, "%spellout-cardinal-feminine-genitive"))).doubleValue()) {
730                errln("RuleBasedNumberFormat did not return the correct value. Got: " + result);
731            }
732            if (1000 != (result = ruFormatter.parse(ruFormatter.format(1000, "%spellout-cardinal-feminine"))).doubleValue()) {
733                errln("RuleBasedNumberFormat did not return the correct value. Got: " + result);
734            }
735        }
736        catch (ParseException e) {
737            errln(e.toString());
738        }
739    }
740
741    public void TestFractionalRuleSet() {
742        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(fracRules,
743                Locale.ENGLISH);
744
745        String[][] testData = {
746                { "0", "0" },
747                { "1", "1" },
748                { "10", "10" },
749                { ".1", "1/10" },
750                { ".11", "1/9" },
751                { ".125", "1/8" },
752                { ".1428", "1/7" },
753                { ".1667", "1/6" },
754                { ".2", "1/5" },
755                { ".25", "1/4" },
756                { ".333", "1/3" },
757                { ".5", "1/2" },
758                { "1.1", "1 1/10" },
759                { "2.11", "2 1/9" },
760                { "3.125", "3 1/8" },
761                { "4.1428", "4 1/7" },
762                { "5.1667", "5 1/6" },
763                { "6.2", "6 1/5" },
764                { "7.25", "7 1/4" },
765                { "8.333", "8 1/3" },
766                { "9.5", "9 1/2" },
767                { ".2222", "2/9" },
768                { ".4444", "4/9" },
769                { ".5555", "5/9" },
770                { "1.2856", "1 2/7" }
771        };
772        doTest(formatter, testData, false); // exact values aren't parsable from fractions
773    }
774
775    public void TestSwedishSpellout()
776    {
777        Locale locale = new Locale("sv", "", "");
778        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(locale,
779                RuleBasedNumberFormat.SPELLOUT);
780
781        String[][] testDataDefault = {
782                { "101", "ett\u00ADhundra\u00ADett" },
783                { "123", "ett\u00ADhundra\u00ADtjugo\u00ADtre" },
784                { "1,001", "et\u00ADtusen ett" },
785                { "1,100", "et\u00ADtusen ett\u00ADhundra" },
786                { "1,101", "et\u00ADtusen ett\u00ADhundra\u00ADett" },
787                { "1,234", "et\u00ADtusen tv\u00e5\u00ADhundra\u00ADtrettio\u00ADfyra" },
788                { "10,001", "tio\u00ADtusen ett" },
789                { "11,000", "elva\u00ADtusen" },
790                { "12,000", "tolv\u00ADtusen" },
791                { "20,000", "tjugo\u00ADtusen" },
792                { "21,000", "tjugo\u00ADet\u00ADtusen" },
793                { "21,001", "tjugo\u00ADet\u00ADtusen ett" },
794                { "200,000", "tv\u00e5\u00ADhundra\u00ADtusen" },
795                { "201,000", "tv\u00e5\u00ADhundra\u00ADet\u00ADtusen" },
796                { "200,200", "tv\u00e5\u00ADhundra\u00ADtusen tv\u00e5\u00ADhundra" },
797                { "2,002,000", "tv\u00e5 miljoner tv\u00e5\u00ADtusen" },
798                { "12,345,678", "tolv miljoner tre\u00ADhundra\u00ADfyrtio\u00ADfem\u00ADtusen sex\u00ADhundra\u00ADsjuttio\u00AD\u00e5tta" },
799                { "123,456.789", "ett\u00ADhundra\u00ADtjugo\u00ADtre\u00ADtusen fyra\u00ADhundra\u00ADfemtio\u00ADsex komma sju \u00e5tta nio" },
800                { "-12,345.678", "minus tolv\u00ADtusen tre\u00ADhundra\u00ADfyrtio\u00ADfem komma sex sju \u00e5tta" },
801        };
802
803        logln("testing default rules");
804        doTest(formatter, testDataDefault, true);
805
806        String[][] testDataNeutrum = {
807                { "101", "ett\u00adhundra\u00adett" },
808                { "1,001", "et\u00adtusen ett" },
809                { "1,101", "et\u00adtusen ett\u00adhundra\u00adett" },
810                { "10,001", "tio\u00adtusen ett" },
811                { "21,001", "tjugo\u00adet\u00adtusen ett" }
812        };
813
814        formatter.setDefaultRuleSet("%spellout-cardinal-neuter");
815        logln("testing neutrum rules");
816        doTest(formatter, testDataNeutrum, true);
817
818        String[][] testDataYear = {
819                { "101", "ett\u00adhundra\u00adett" },
820                { "900", "nio\u00adhundra" },
821                { "1,001", "et\u00adtusen ett" },
822                { "1,100", "elva\u00adhundra" },
823                { "1,101", "elva\u00adhundra\u00adett" },
824                { "1,234", "tolv\u00adhundra\u00adtrettio\u00adfyra" },
825                { "2,001", "tjugo\u00adhundra\u00adett" },
826                { "10,001", "tio\u00adtusen ett" }
827        };
828
829        formatter.setDefaultRuleSet("%spellout-numbering-year");
830        logln("testing year rules");
831        doTest(formatter, testDataYear, true);
832    }
833
834    public void TestBigNumbers() {
835        BigInteger bigI = new BigInteger("1234567890", 10);
836        StringBuffer buf = new StringBuffer();
837        RuleBasedNumberFormat fmt = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
838        fmt.format(bigI, buf, null);
839        logln("big int: " + buf.toString());
840
841        buf.setLength(0);
842        java.math.BigDecimal bigD = new java.math.BigDecimal(bigI);
843        fmt.format(bigD, buf, null);
844        logln("big dec: " + buf.toString());
845    }
846
847    public void TestTrailingSemicolon() {
848        String thaiRules =
849            "%default:\n" +
850            "  -x: \u0e25\u0e1a>>;\n" +
851            "  x.x: <<\u0e08\u0e38\u0e14>>>;\n" +
852            "  \u0e28\u0e39\u0e19\u0e22\u0e4c; \u0e2b\u0e19\u0e36\u0e48\u0e07; \u0e2a\u0e2d\u0e07; \u0e2a\u0e32\u0e21;\n" +
853            "  \u0e2a\u0e35\u0e48; \u0e2b\u0e49\u0e32; \u0e2b\u0e01; \u0e40\u0e08\u0e47\u0e14; \u0e41\u0e1b\u0e14;\n" +
854            "  \u0e40\u0e01\u0e49\u0e32; \u0e2a\u0e34\u0e1a; \u0e2a\u0e34\u0e1a\u0e40\u0e2d\u0e47\u0e14;\n" +
855            "  \u0e2a\u0e34\u0e1a\u0e2a\u0e2d\u0e07; \u0e2a\u0e34\u0e1a\u0e2a\u0e32\u0e21;\n" +
856            "  \u0e2a\u0e34\u0e1a\u0e2a\u0e35\u0e48; \u0e2a\u0e34\u0e1a\u0e2b\u0e49\u0e32;\n" +
857            "  \u0e2a\u0e34\u0e1a\u0e2b\u0e01; \u0e2a\u0e34\u0e1a\u0e40\u0e08\u0e47\u0e14;\n" +
858            "  \u0e2a\u0e34\u0e1a\u0e41\u0e1b\u0e14; \u0e2a\u0e34\u0e1a\u0e40\u0e01\u0e49\u0e32;\n" +
859            "  20: \u0e22\u0e35\u0e48\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
860            "  30: \u0e2a\u0e32\u0e21\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
861            "  40: \u0e2a\u0e35\u0e48\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
862            "  50: \u0e2b\u0e49\u0e32\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
863            "  60: \u0e2b\u0e01\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
864            "  70: \u0e40\u0e08\u0e47\u0e14\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
865            "  80: \u0e41\u0e1b\u0e14\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
866            "  90: \u0e40\u0e01\u0e49\u0e32\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
867            "  100: <<\u0e23\u0e49\u0e2d\u0e22[>>];\n" +
868            "  1000: <<\u0e1e\u0e31\u0e19[>>];\n" +
869            "  10000: <<\u0e2b\u0e21\u0e37\u0e48\u0e19[>>];\n" +
870            "  100000: <<\u0e41\u0e2a\u0e19[>>];\n" +
871            "  1,000,000: <<\u0e25\u0e49\u0e32\u0e19[>>];\n" +
872            "  1,000,000,000: <<\u0e1e\u0e31\u0e19\u0e25\u0e49\u0e32\u0e19[>>];\n" +
873            "  1,000,000,000,000: <<\u0e25\u0e49\u0e32\u0e19\u0e25\u0e49\u0e32\u0e19[>>];\n" +
874            "  1,000,000,000,000,000: =#,##0=;\n" +
875            "%%alt-ones:\n" +
876            "  \u0e28\u0e39\u0e19\u0e22\u0e4c;\n" +
877            "  \u0e40\u0e2d\u0e47\u0e14;\n" +
878            "  =%default=;\n ; ;; ";
879
880        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(thaiRules, new Locale("th", "TH", ""));
881
882        String[][] testData = {
883                { "0", "\u0e28\u0e39\u0e19\u0e22\u0e4c" },
884                { "1", "\u0e2b\u0e19\u0e36\u0e48\u0e07" },
885                { "123.45", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u0e23\u0e49\u0e2d\u0e22\u0e22\u0e35\u0e48\u0e2a\u0e34\u0e1a\u0e2a\u0e32\u0e21\u0e08\u0e38\u0e14\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" }
886        };
887
888        doTest(formatter, testData, true);
889    }
890
891    public void TestSmallValues() {
892        String[][] testData = {
893                { "0.001", "zero point zero zero one" },
894                { "0.0001", "zero point zero zero zero one" },
895                { "0.00001", "zero point zero zero zero zero one" },
896                { "0.000001", "zero point zero zero zero zero zero one" },
897                { "0.0000001", "zero point zero zero zero zero zero zero one" },
898                { "0.00000001", "zero point zero zero zero zero zero zero zero one" },
899                { "0.000000001", "zero point zero zero zero zero zero zero zero zero one" },
900                { "0.0000000001", "zero point zero zero zero zero zero zero zero zero zero one" },
901                { "0.00000000001", "zero point zero zero zero zero zero zero zero zero zero zero one" },
902                { "0.000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero one" },
903                { "0.0000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero one" },
904                { "0.00000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero zero one" },
905                { "0.000000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero zero zero one" },
906                { "10,000,000.001", "ten million point zero zero one" },
907                { "10,000,000.0001", "ten million point zero zero zero one" },
908                { "10,000,000.00001", "ten million point zero zero zero zero one" },
909                { "10,000,000.000001", "ten million point zero zero zero zero zero one" },
910                { "10,000,000.0000001", "ten million point zero zero zero zero zero zero one" },
911                { "10,000,000.00000001", "ten million point zero zero zero zero zero zero zero one" },
912                { "10,000,000.000000002", "ten million point zero zero zero zero zero zero zero zero two" },
913                { "10,000,000", "ten million" },
914                { "1,234,567,890.0987654", "one billion two hundred thirty-four million five hundred sixty-seven thousand eight hundred ninety point zero nine eight seven six five four" },
915                { "123,456,789.9876543", "one hundred twenty-three million four hundred fifty-six thousand seven hundred eighty-nine point nine eight seven six five four three" },
916                { "12,345,678.87654321", "twelve million three hundred forty-five thousand six hundred seventy-eight point eight seven six five four three two one" },
917                { "1,234,567.7654321", "one million two hundred thirty-four thousand five hundred sixty-seven point seven six five four three two one" },
918                { "123,456.654321", "one hundred twenty-three thousand four hundred fifty-six point six five four three two one" },
919                { "12,345.54321", "twelve thousand three hundred forty-five point five four three two one" },
920                { "1,234.4321", "one thousand two hundred thirty-four point four three two one" },
921                { "123.321", "one hundred twenty-three point three two one" },
922                { "0.0000000011754944", "zero point zero zero zero zero zero zero zero zero one one seven five four nine four four" },
923                { "0.000001175494351", "zero point zero zero zero zero zero one one seven five four nine four three five one" },
924        };
925
926        RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(Locale.US, RuleBasedNumberFormat.SPELLOUT);
927        doTest(formatter, testData, true);
928    }
929
930    public void TestRuleSetDisplayName() {
931        /**
932         * Spellout rules for U.K. English.
933         * I borrow the rule sets for TestRuleSetDisplayName()
934         */
935        final String ukEnglish =
936                "%simplified:\n"
937                        + "    -x: minus >>;\n"
938                        + "    x.x: << point >>;\n"
939                        + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
940                        + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
941                        + "        seventeen; eighteen; nineteen;\n"
942                        + "    20: twenty[->>];\n"
943                        + "    30: thirty[->>];\n"
944                        + "    40: forty[->>];\n"
945                        + "    50: fifty[->>];\n"
946                        + "    60: sixty[->>];\n"
947                        + "    70: seventy[->>];\n"
948                        + "    80: eighty[->>];\n"
949                        + "    90: ninety[->>];\n"
950                        + "    100: << hundred[ >>];\n"
951                        + "    1000: << thousand[ >>];\n"
952                        + "    1,000,000: << million[ >>];\n"
953                        + "    1,000,000,000,000: << billion[ >>];\n"
954                        + "    1,000,000,000,000,000: =#,##0=;\n"
955                        + "%alt-teens:\n"
956                        + "    =%simplified=;\n"
957                        + "    1000>: <%%alt-hundreds<[ >>];\n"
958                        + "    10,000: =%simplified=;\n"
959                        + "    1,000,000: << million[ >%simplified>];\n"
960                        + "    1,000,000,000,000: << billion[ >%simplified>];\n"
961                        + "    1,000,000,000,000,000: =#,##0=;\n"
962                        + "%%alt-hundreds:\n"
963                        + "    0: SHOULD NEVER GET HERE!;\n"
964                        + "    10: <%simplified< thousand;\n"
965                        + "    11: =%simplified= hundred>%%empty>;\n"
966                        + "%%empty:\n"
967                        + "    0:;"
968                        + "%ordinal:\n"
969                        + "    zeroth; first; second; third; fourth; fifth; sixth; seventh;\n"
970                        + "        eighth; ninth;\n"
971                        + "    tenth; eleventh; twelfth; thirteenth; fourteenth;\n"
972                        + "        fifteenth; sixteenth; seventeenth; eighteenth;\n"
973                        + "        nineteenth;\n"
974                        + "    twentieth; twenty->>;\n"
975                        + "    30: thirtieth; thirty->>;\n"
976                        + "    40: fortieth; forty->>;\n"
977                        + "    50: fiftieth; fifty->>;\n"
978                        + "    60: sixtieth; sixty->>;\n"
979                        + "    70: seventieth; seventy->>;\n"
980                        + "    80: eightieth; eighty->>;\n"
981                        + "    90: ninetieth; ninety->>;\n"
982                        + "    100: <%simplified< hundredth; <%simplified< hundred >>;\n"
983                        + "    1000: <%simplified< thousandth; <%simplified< thousand >>;\n"
984                        + "    1,000,000: <%simplified< millionth; <%simplified< million >>;\n"
985                        + "    1,000,000,000,000: <%simplified< billionth;\n"
986                        + "        <%simplified< billion >>;\n"
987                        + "    1,000,000,000,000,000: =#,##0=;"
988                        + "%default:\n"
989                        + "    -x: minus >>;\n"
990                        + "    x.x: << point >>;\n"
991                        + "    =%simplified=;\n"
992                        + "    100: << hundred[ >%%and>];\n"
993                        + "    1000: << thousand[ >%%and>];\n"
994                        + "    100,000>>: << thousand[>%%commas>];\n"
995                        + "    1,000,000: << million[>%%commas>];\n"
996                        + "    1,000,000,000,000: << billion[>%%commas>];\n"
997                        + "    1,000,000,000,000,000: =#,##0=;\n"
998                        + "%%and:\n"
999                        + "    and =%default=;\n"
1000                        + "    100: =%default=;\n"
1001                        + "%%commas:\n"
1002                        + "    ' and =%default=;\n"
1003                        + "    100: , =%default=;\n"
1004                        + "    1000: , <%default< thousand, >%default>;\n"
1005                        + "    1,000,000: , =%default=;"
1006                        + "%%lenient-parse:\n"
1007                        + "    & ' ' , ',' ;\n";
1008        ULocale.setDefault(ULocale.US);
1009        String[][] localizations = new String[][] {
1010            /* public rule sets*/
1011                {"%simplified", "%default", "%ordinal"},
1012            /* display names in "en_US" locale*/
1013                {"en_US", "Simplified", "Default", "Ordinal"},
1014            /* display names in "zh_Hans" locale*/
1015                {"zh_Hans", "\u7B80\u5316", "\u7F3A\u7701",  "\u5E8F\u5217"},
1016            /* display names in a fake locale*/
1017                {"foo_Bar_BAZ", "Simplified", "Default", "Ordinal"}
1018        };
1019
1020        //Construct RuleBasedNumberFormat by rule sets and localizations list
1021        RuleBasedNumberFormat formatter
1022                = new RuleBasedNumberFormat(ukEnglish, localizations, ULocale.US);
1023        RuleBasedNumberFormat f2= new RuleBasedNumberFormat(ukEnglish, localizations);
1024        assertTrue("Check the two formatters' equality", formatter.equals(f2));
1025
1026        //get displayName by name
1027        String[] ruleSetNames = formatter.getRuleSetNames();
1028        for (int i=0; i<ruleSetNames.length; i++) {
1029            logln("Rule set name: " + ruleSetNames[i]);
1030            String RSName_defLoc = formatter.getRuleSetDisplayName(ruleSetNames[i]);
1031            assertEquals("Display name in default locale", localizations[1][i+1], RSName_defLoc);
1032            String RSName_loc = formatter.getRuleSetDisplayName(ruleSetNames[i], ULocale.CHINA);
1033            assertEquals("Display name in Chinese", localizations[2][i+1], RSName_loc);
1034        }
1035
1036        // getDefaultRuleSetName
1037        String defaultRS = formatter.getDefaultRuleSetName();
1038        //you know that the default rule set is %simplified according to rule sets string ukEnglish
1039        assertEquals("getDefaultRuleSetName", "%simplified", defaultRS);
1040
1041        //get locales of localizations
1042        ULocale[] locales = formatter.getRuleSetDisplayNameLocales();
1043        for (int i=0; i<locales.length; i++) {
1044            logln(locales[i].getName());
1045        }
1046
1047        //get displayNames
1048        String[] RSNames_defLoc = formatter.getRuleSetDisplayNames();
1049        for (int i=0; i<RSNames_defLoc.length; i++) {
1050            assertEquals("getRuleSetDisplayNames in default locale", localizations[1][i+1], RSNames_defLoc[i]);
1051        }
1052
1053        String[] RSNames_loc = formatter.getRuleSetDisplayNames(ULocale.UK);
1054        for (int i=0; i<RSNames_loc.length; i++) {
1055            assertEquals("getRuleSetDisplayNames in English", localizations[1][i+1], RSNames_loc[i]);
1056        }
1057
1058        RSNames_loc = formatter.getRuleSetDisplayNames(ULocale.CHINA);
1059        for (int i=0; i<RSNames_loc.length; i++) {
1060            assertEquals("getRuleSetDisplayNames in Chinese", localizations[2][i+1], RSNames_loc[i]);
1061        }
1062
1063        RSNames_loc = formatter.getRuleSetDisplayNames(new ULocale("foo_Bar_BAZ"));
1064        for (int i=0; i<RSNames_loc.length; i++) {
1065            assertEquals("getRuleSetDisplayNames in fake locale", localizations[3][i+1], RSNames_loc[i]);
1066        }
1067    }
1068
1069    public void TestAllLocales() {
1070        StringBuilder errors = new StringBuilder();
1071        String[] names = {
1072                " (spellout) ",
1073                " (ordinal) "
1074                //" (duration) " // English only
1075        };
1076        double[] numbers = {45.678, 1, 2, 10, 11, 100, 110, 200, 1000, 1111, -1111};
1077        int count = numbers.length;
1078        Random r = (count <= numbers.length ? null : createRandom());
1079
1080        for (ULocale loc : NumberFormat.getAvailableULocales()) {
1081            for (int j = 0; j < names.length; ++j) {
1082                RuleBasedNumberFormat fmt = new RuleBasedNumberFormat(loc, j+1);
1083                if (!loc.equals(fmt.getLocale(ULocale.ACTUAL_LOCALE))) {
1084                    // Skip the redundancy
1085                    break;
1086                }
1087
1088                for (int c = 0; c < count; c++) {
1089                    double n;
1090                    if (c < numbers.length) {
1091                        n = numbers[c];
1092                    } else {
1093                        n = (r.nextInt(10000) - 3000) / 16d;
1094                    }
1095
1096                    String s = fmt.format(n);
1097                    if (isVerbose()) {
1098                        logln(loc.getName() + names[j] + "success format: " + n + " -> " + s);
1099                    }
1100
1101                    try {
1102                        // RBNF parse is extremely slow when lenient option is enabled.
1103                        // non-lenient parse
1104                        fmt.setLenientParseMode(false);
1105                        Number num = fmt.parse(s);
1106                        if (isVerbose()) {
1107                            logln(loc.getName() + names[j] + "success parse: " + s + " -> " + num);
1108                        }
1109                        if (j != 0) {
1110                            // TODO: Fix the ordinal rules.
1111                            continue;
1112                        }
1113                        if (n != num.doubleValue()) {
1114                            errors.append("\n" + loc + names[j] + "got " + num + " expected " + n);
1115                        }
1116                    } catch (ParseException pe) {
1117                        String msg = loc.getName() + names[j] + "ERROR:" + pe.getMessage();
1118                        logln(msg);
1119                        errors.append("\n" + msg);
1120                    }
1121                }
1122            }
1123        }
1124        if (errors.length() > 0) {
1125            errln(errors.toString());
1126        }
1127    }
1128
1129    void doTest(RuleBasedNumberFormat formatter, String[][] testData,
1130                boolean testParsing) {
1131        //        NumberFormat decFmt = NumberFormat.getInstance(Locale.US);
1132        NumberFormat decFmt = new DecimalFormat("#,###.################");
1133        try {
1134            for (int i = 0; i < testData.length; i++) {
1135                String number = testData[i][0];
1136                String expectedWords = testData[i][1];
1137                if (isVerbose()) {
1138                    logln("test[" + i + "] number: " + number + " target: " + expectedWords);
1139                }
1140                Number num = decFmt.parse(number);
1141                String actualWords = formatter.format(num);
1142
1143                if (!actualWords.equals(expectedWords)) {
1144                    errln("Spot check format failed: for " + number + ", expected\n    "
1145                            + expectedWords + ", but got\n    " +
1146                            actualWords);
1147                }
1148                else if (testParsing) {
1149                    String actualNumber = decFmt.format(formatter
1150                            .parse(actualWords));
1151
1152                    if (!actualNumber.equals(number)) {
1153                        errln("Spot check parse failed: for " + actualWords +
1154                                ", expected " + number + ", but got " +
1155                                actualNumber);
1156                    }
1157                }
1158            }
1159        }
1160        catch (Throwable e) {
1161            e.printStackTrace();
1162            errln("Test failed with exception: " + e.toString());
1163        }
1164    }
1165
1166    /* Tests the method
1167     *      public boolean equals(Object that)
1168     */
1169    public void TestEquals(){
1170        // Tests when "if (!(that instanceof RuleBasedNumberFormat))" is true
1171        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1172        if (rbnf.equals("dummy") ||
1173                rbnf.equals(new Character('a')) ||
1174                rbnf.equals(new Object()) ||
1175                rbnf.equals(-1) ||
1176                rbnf.equals(0) ||
1177                rbnf.equals(1) ||
1178                rbnf.equals(-1.0) ||
1179                rbnf.equals(0.0) ||
1180                rbnf.equals(1.0))
1181        {
1182            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1183                    "be false for an invalid object.");
1184        }
1185
1186        // Tests when
1187        // "if (!locale.equals(that2.locale) || lenientParse != that2.lenientParse)"
1188        // is true
1189        RuleBasedNumberFormat rbnf1 = new RuleBasedNumberFormat("dummy", new Locale("en"));
1190        RuleBasedNumberFormat rbnf2 = new RuleBasedNumberFormat("dummy", new Locale("jp"));
1191        RuleBasedNumberFormat rbnf3 = new RuleBasedNumberFormat("dummy", new Locale("sp"));
1192        RuleBasedNumberFormat rbnf4 = new RuleBasedNumberFormat("dummy", new Locale("fr"));
1193
1194        if(rbnf1.equals(rbnf2) || rbnf1.equals(rbnf3) ||
1195                rbnf1.equals(rbnf4) || rbnf2.equals(rbnf3) ||
1196                rbnf2.equals(rbnf4) || rbnf3.equals(rbnf4)){
1197            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1198                    "be false for an invalid object.");
1199        }
1200
1201        if(!rbnf1.equals(rbnf1)){
1202            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1203                    "be false for an invalid object.");
1204        }
1205
1206        if(!rbnf2.equals(rbnf2)){
1207            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1208                    "be false for an invalid object.");
1209        }
1210
1211        if(!rbnf3.equals(rbnf3)){
1212            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1213                    "be false for an invalid object.");
1214        }
1215
1216        if(!rbnf4.equals(rbnf4)){
1217            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1218                    "be false for an invalid object.");
1219        }
1220
1221        RuleBasedNumberFormat rbnf5 = new RuleBasedNumberFormat("dummy", new Locale("en"));
1222        RuleBasedNumberFormat rbnf6 = new RuleBasedNumberFormat("dummy", new Locale("en"));
1223
1224        if(!rbnf5.equals(rbnf6)){
1225            errln("RuleBasedNumberFormat.equals(Object that) was not suppose to " +
1226                    "be false for an invalid object.");
1227        }
1228        rbnf6.setLenientParseMode(true);
1229
1230        if(rbnf5.equals(rbnf6)){
1231            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1232                    "be false for an invalid object.");
1233        }
1234
1235        // Tests when "if (!ruleSets[i].equals(that2.ruleSets[i]))" is true
1236        RuleBasedNumberFormat rbnf7 = new RuleBasedNumberFormat("not_dummy", new Locale("en"));
1237        if(rbnf5.equals(rbnf7)){
1238            errln("RuleBasedNumberFormat.equals(Object that) was suppose to " +
1239                    "be false for an invalid object.");
1240        }
1241    }
1242
1243    /* Tests the method
1244     *      public ULocale[] getRuleSetDisplayNameLocales()
1245     */
1246    public void TestGetRuleDisplayNameLocales(){
1247        // Tests when "if (ruleSetDisplayNames != null" is false
1248        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1249        rbnf.getRuleSetDisplayNameLocales();
1250        if(rbnf.getRuleSetDisplayNameLocales() != null){
1251            errln("RuleBasedNumberFormat.getRuleDisplayNameLocales() was suppose to " +
1252                    "return null.");
1253        }
1254    }
1255
1256    /* Tests the method
1257     *      private String[] getNameListForLocale(ULocale loc)
1258     *      public String[] getRuleSetDisplayNames(ULocale loc)
1259     */
1260    public void TestGetNameListForLocale(){
1261        // Tests when "if (names != null)" is false and
1262        //  "if (loc != null && ruleSetDisplayNames != null)" is false
1263        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1264        rbnf.getRuleSetDisplayNames(null);
1265        try{
1266            rbnf.getRuleSetDisplayNames(null);
1267        } catch(Exception e){
1268            errln("RuleBasedNumberFormat.getRuleSetDisplayNames(ULocale loc) " +
1269                    "was not suppose to have an exception.");
1270        }
1271    }
1272
1273    /* Tests the method
1274     *      public String getRuleSetDisplayName(String ruleSetName, ULocale loc)
1275     */
1276    public void TestGetRulesSetDisplayName(){
1277        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat("dummy");
1278        //rbnf.getRuleSetDisplayName("dummy", new ULocale("en_US"));
1279
1280        // Tests when "if (names != null) " is true
1281
1282        // Tests when the method throws an exception
1283        try{
1284            rbnf.getRuleSetDisplayName("", new ULocale("en_US"));
1285            errln("RuleBasedNumberFormat.getRuleSetDisplayName(String ruleSetName, ULocale loc) " +
1286                    "was suppose to have an exception.");
1287        } catch(Exception e){}
1288
1289        try{
1290            rbnf.getRuleSetDisplayName("dummy", new ULocale("en_US"));
1291            errln("RuleBasedNumberFormat.getRuleSetDisplayName(String ruleSetName, ULocale loc) " +
1292                    "was suppose to have an exception.");
1293        } catch(Exception e){}
1294    }
1295
1296    /* Test the method
1297     *      public void process(StringBuffer buf, NFRuleSet ruleSet)
1298     */
1299    public void TestChineseProcess(){
1300        String ruleWithChinese =
1301            "%simplified:\n"
1302            + "    -x: minus >>;\n"
1303            + "    x.x: << point >>;\n"
1304            + "    zero; one; two; three; four; five; six; seven; eight; nine;\n"
1305            + "    ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
1306            + "        seventeen; eighteen; nineteen;\n"
1307            + "    20: twenty[->>];\n"
1308            + "    30: thirty[->>];\n"
1309            + "    40: forty[->>];\n"
1310            + "    50: fifty[->>];\n"
1311            + "    60: sixty[->>];\n"
1312            + "    70: seventy[->>];\n"
1313            + "    80: eighty[->>];\n"
1314            + "    90: ninety[->>];\n"
1315            + "    100: << hundred[ >>];\n"
1316            + "    1000: << thousand[ >>];\n"
1317            + "    1,000,000: << million[ >>];\n"
1318            + "    1,000,000,000,000: << billion[ >>];\n"
1319            + "    1,000,000,000,000,000: =#,##0=;\n"
1320            + "%alt-teens:\n"
1321            + "    =%simplified=;\n"
1322            + "    1000>: <%%alt-hundreds<[ >>];\n"
1323            + "    10,000: =%simplified=;\n"
1324            + "    1,000,000: << million[ >%simplified>];\n"
1325            + "    1,000,000,000,000: << billion[ >%simplified>];\n"
1326            + "    1,000,000,000,000,000: =#,##0=;\n"
1327            + "%%alt-hundreds:\n"
1328            + "    0: SHOULD NEVER GET HERE!;\n"
1329            + "    10: <%simplified< thousand;\n"
1330            + "    11: =%simplified= hundred>%%empty>;\n"
1331            + "%%empty:\n"
1332            + "    0:;"
1333            + "%accounting:\n"
1334            + "    \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1335            + "        \u842c; \u842c;\n"
1336            + "    \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1337            + "        \u842c; \u842c; \u842c; \u842c;\n"
1338            + "        \u842c;\n"
1339            + "    twentieth; \u96f6|>>;\n"
1340            + "    30: \u96f6; \u96f6|>>;\n"
1341            + "    40: \u96f6; \u96f6|>>;\n"
1342            + "    50: \u96f6; \u96f6|>>;\n"
1343            + "    60: \u96f6; \u96f6|>>;\n"
1344            + "    70: \u96f6; \u96f6|>>;\n"
1345            + "    80: \u96f6; \u96f6|>>;\n"
1346            + "    90: \u96f6; \u96f6|>>;\n"
1347            + "    100: <%simplified< \u96f6; <%simplified< \u96f6 >>;\n"
1348            + "    1000: <%simplified< \u96f6; <%simplified< \u96f6 >>;\n"
1349            + "    1,000,000: <%simplified< \u96f6; <%simplified< \u96f6 >>;\n"
1350            + "    1,000,000,000,000: <%simplified< \u96f6;\n"
1351            + "        <%simplified< \u96f6 >>;\n"
1352            + "    1,000,000,000,000,000: =#,##0=;"
1353            + "%default:\n"
1354            + "    -x: minus >>;\n"
1355            + "    x.x: << point >>;\n"
1356            + "    =%simplified=;\n"
1357            + "    100: << hundred[ >%%and>];\n"
1358            + "    1000: << thousand[ >%%and>];\n"
1359            + "    100,000>>: << thousand[>%%commas>];\n"
1360            + "    1,000,000: << million[>%%commas>];\n"
1361            + "    1,000,000,000,000: << billion[>%%commas>];\n"
1362            + "    1,000,000,000,000,000: =#,##0=;\n"
1363            + "%%and:\n"
1364            + "    and =%default=;\n"
1365            + "    100: =%default=;\n"
1366            + "%%commas:\n"
1367            + "    ' and =%default=;\n"
1368            + "    100: , =%default=;\n"
1369            + "    1000: , <%default< thousand, >%default>;\n"
1370            + "    1,000,000: , =%default=;"
1371            + "%traditional:\n"
1372            + "    -x: \u3007| >>;\n"
1373            + "    x.x: << \u9ede >>;\n"
1374            + "    \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1375            + "    \u842c; \u842c; \u842c; \u842c; \u842c; \u842c; \u842c;\n"
1376            + "        \u842c; \u842c; \u842c;\n"
1377            + "    20: \u842c[->>];\n"
1378            + "    30: \u842c[->>];\n"
1379            + "    40: \u842c[->>];\n"
1380            + "    50: \u842c[->>];\n"
1381            + "    60: \u842c[->>];\n"
1382            + "    70: \u842c[->>];\n"
1383            + "    80: \u842c[->>];\n"
1384            + "    90: \u842c[->>];\n"
1385            + "    100: << \u842c[ >>];\n"
1386            + "    1000: << \u842c[ >>];\n"
1387            + "    1,000,000: << \u842c[ >>];\n"
1388            + "    1,000,000,000,000: << \u842c[ >>];\n"
1389            + "    1,000,000,000,000,000: =#,##0=;\n"
1390            + "%time:\n"
1391            + "    =0= sec.;\n"
1392            + "    60: =%%min-sec=;\n"
1393            + "    3600: =%%hr-min-sec=;\n"
1394            + "%%min-sec:\n"
1395            + "    0: *=00=;\n"
1396            + "    60/60: <0<>>;\n"
1397            + "%%hr-min-sec:\n"
1398            + "    0: *=00=;\n"
1399            + "    60/60: <00<>>;\n"
1400            + "    3600/60: <#,##0<:>>>;\n"
1401            + "%%post-process:com.ibm.icu.text.RBNFChinesePostProcessor\n";
1402
1403        RuleBasedNumberFormat rbnf =  new RuleBasedNumberFormat(ruleWithChinese, ULocale.CHINESE);
1404        String[] ruleNames = rbnf.getRuleSetNames();
1405        try{
1406            // Test with "null" rules
1407            rbnf.format(0.0,null);
1408            errln("This was suppose to return an exception for a null format");
1409        } catch(Exception e){}
1410        for(int i=0; i<ruleNames.length; i++){
1411            try{
1412                rbnf.format(-123450.6789,ruleNames[i]);
1413            } catch(Exception e){
1414                errln("RBNFChinesePostProcessor was not suppose to return an exception " +
1415                        "when being formatted with parameters 0.0 and " + ruleNames[i]);
1416            }
1417        }
1418    }
1419
1420    public void TestSetDecimalFormatSymbols() {
1421        RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(Locale.ENGLISH, RuleBasedNumberFormat.ORDINAL);
1422
1423        DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.ENGLISH);
1424
1425        double number = 1001;
1426
1427        String[] expected = { "1,001st", "1&001st" };
1428
1429        String result = rbnf.format(number);
1430        if (!result.equals(expected[0])) {
1431            errln("Format Error - Got: " + result + " Expected: " + expected[0]);
1432        }
1433
1434        /* Set new symbol for testing */
1435        dfs.setGroupingSeparator('&');
1436        rbnf.setDecimalFormatSymbols(dfs);
1437
1438        result = rbnf.format(number);
1439        if (!result.equals(expected[1])) {
1440            errln("Format Error - Got: " + result + " Expected: " + expected[1]);
1441        }
1442    }
1443
1444    public void TestContext() {
1445        class TextContextItem {
1446            public String locale;
1447            public int format;
1448            public DisplayContext context;
1449            public double value;
1450            public String expectedResult;
1451            // Simple constructor
1452            public TextContextItem(String loc, int fmt, DisplayContext ctxt, double val, String expRes) {
1453                locale = loc;
1454                format = fmt;
1455                context = ctxt;
1456                value = val;
1457                expectedResult = expRes;
1458            }
1459        }
1460        final TextContextItem[] items = {
1461                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,    123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1462                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 123.45, "Ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1463                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,       123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1464                new TextContextItem( "sv", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_STANDALONE,            123.45, "ett\u00ADhundra\u00ADtjugo\u00ADtre komma fyra fem" ),
1465                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,    123.45, "one hundred twenty-three point four five" ),
1466                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 123.45, "One hundred twenty-three point four five" ),
1467                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,       123.45, "One hundred twenty-three point four five" ),
1468                new TextContextItem( "en", RuleBasedNumberFormat.SPELLOUT, DisplayContext.CAPITALIZATION_FOR_STANDALONE,            123.45, "One hundred twenty-three point four five" ),
1469        };
1470        for (TextContextItem item: items) {
1471            ULocale locale = new ULocale(item.locale);
1472            RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(locale, item.format);
1473            rbnf.setContext(item.context);
1474            String result = rbnf.format(item.value, rbnf.getDefaultRuleSetName());
1475            if (!result.equals(item.expectedResult)) {
1476                errln("Error for locale " + item.locale + ", context " + item.context + ", expected " + item.expectedResult + ", got " + result);
1477            }
1478            RuleBasedNumberFormat rbnfClone = (RuleBasedNumberFormat)rbnf.clone();
1479            if (!rbnfClone.equals(rbnf)) {
1480                errln("Error for locale " + item.locale + ", context " + item.context + ", rbnf.clone() != rbnf");
1481            } else {
1482                result = rbnfClone.format(item.value, rbnfClone.getDefaultRuleSetName());
1483                if (!result.equals(item.expectedResult)) {
1484                    errln("Error with clone for locale " + item.locale + ", context " + item.context + ", expected " + item.expectedResult + ", got " + result);
1485                }
1486            }
1487        }
1488    }
1489
1490}
1491