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