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 */ 9package com.ibm.icu.dev.test.format; 10 11import java.math.BigInteger; 12import java.text.ParseException; 13import java.util.Locale; 14import java.util.Random; 15 16import org.junit.Test; 17import org.junit.runner.RunWith; 18import org.junit.runners.JUnit4; 19 20import com.ibm.icu.dev.test.TestFmwk; 21import com.ibm.icu.math.BigDecimal; 22import com.ibm.icu.text.DecimalFormat; 23import com.ibm.icu.text.DecimalFormatSymbols; 24import com.ibm.icu.text.DisplayContext; 25import com.ibm.icu.text.NumberFormat; 26import com.ibm.icu.text.RuleBasedNumberFormat; 27import 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) 34public 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