3 *******************************************************************************
4 * Copyright (C) 1996-2009, International Business Machines Corporation and *
5 * others. All Rights Reserved. *
6 *******************************************************************************
8 package com.ibm.icu.dev.test.format;
10 import java.math.BigInteger;
11 import java.text.DecimalFormat;
12 import java.text.NumberFormat;
13 import java.text.ParseException;
14 import java.util.Locale;
15 import java.util.Random;
17 import com.ibm.icu.dev.test.TestFmwk;
18 import com.ibm.icu.text.RuleBasedNumberFormat;
19 import com.ibm.icu.util.ULocale;
21 public class RbnfTest extends TestFmwk {
22 public static void main(String[] args) {
23 RbnfTest test = new RbnfTest();
29 System.out.println("Entire test failed because of exception: "
35 static String fracRules =
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
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
68 char[] fracRulesArr = fracRules.toCharArray();
69 int len = fracRulesArr.length;
71 for (int i = 0; i < len; ++i) {
72 char ch = fracRulesArr[i];
74 change = 2; // change ok
75 } else if (ch == ':') {
76 change = 1; // change, but once we hit a non-space char, don't change
77 } else if (ch == ' ') {
79 fracRulesArr[i] = (char)0x200e;
87 fracRules = new String(fracRulesArr);
90 static final String durationInSecondsRules =
91 // main rule set for formatting with words
93 // take care of singular and plural forms of "second"
94 + " 0 seconds; 1 second; =0= seconds;\n"
95 // use %%min to format values greater than 60 seconds
96 + " 60/60: <%%min<[, >>];\n"
97 // use %%hr to format values greater than 3,600 seconds
98 // (the ">>>" below causes us to see the number of minutes
99 // when when there are zero minutes)
100 + " 3600/60: <%%hr<[, >>>];\n"
101 // this rule set takes care of the singular and plural forms
104 + " 0 minutes; 1 minute; =0= minutes;\n"
105 // this rule set takes care of the singular and plural forms
108 + " 0 hours; 1 hour; =0= hours;\n"
110 // main rule set for formatting in numerals
112 // values below 60 seconds are shown with "sec."
114 // higher values are shown with colons: %%min-sec is used for
115 // values below 3,600 seconds...
116 + " 60: =%%min-sec=;\n"
117 // ...and %%hr-min-sec is used for values of 3,600 seconds
119 + " 3600: =%%hr-min-sec=;\n"
120 // this rule causes values of less than 10 minutes to show without
125 // this rule set is used for values of 3,600 or more. Minutes are always
126 // shown, and always shown with two digits
129 + " 60/60: <00<>>;\n"
130 + " 3600/60: <#,##0<:>>>;\n"
131 // the lenient-parse rules allow several different characters to be used
132 // as delimiters between hours, minutes, and seconds
133 + "%%lenient-parse:\n"
134 + " & : = . = ' ' = -;\n";
136 public void TestCoverage() {
137 // extra calls to boost coverage numbers
138 RuleBasedNumberFormat fmt0 = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
139 RuleBasedNumberFormat fmt1 = (RuleBasedNumberFormat)fmt0.clone();
140 RuleBasedNumberFormat fmt2 = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
141 if (!fmt0.equals(fmt0)) {
142 errln("self equality fails");
144 if (!fmt0.equals(fmt1)) {
145 errln("clone equality fails");
147 if (!fmt0.equals(fmt2)) {
148 errln("duplicate equality fails");
150 String str = fmt0.toString();
153 RuleBasedNumberFormat fmt3 = new RuleBasedNumberFormat(durationInSecondsRules);
155 if (fmt0.equals(fmt3)) {
156 errln("nonequal fails");
158 if (!fmt3.equals(fmt3)) {
159 errln("self equal 2 fails");
161 str = fmt3.toString();
164 String[] names = fmt3.getRuleSetNames();
167 fmt3.setDefaultRuleSet(null);
168 fmt3.setDefaultRuleSet("%%foo");
169 errln("sdrf %%foo didn't fail");
171 catch (Exception e) {
172 logln("Got the expected exception");
176 fmt3.setDefaultRuleSet("%bogus");
177 errln("sdrf %bogus didn't fail");
179 catch (Exception e) {
180 logln("Got the expected exception");
184 str = fmt3.format(2.3, names[0]);
186 str = fmt3.format(2.3, "%%foo");
187 errln("format double %%foo didn't fail");
189 catch (Exception e) {
190 logln("Got the expected exception");
194 str = fmt3.format(123L, names[0]);
196 str = fmt3.format(123L, "%%foo");
197 errln("format double %%foo didn't fail");
199 catch (Exception e) {
200 logln("Got the expected exception");
203 RuleBasedNumberFormat fmt4 = new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
204 RuleBasedNumberFormat fmt5 = new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
205 str = fmt4.toString();
207 if (!fmt4.equals(fmt5)) {
208 errln("duplicate 2 equality failed");
210 str = fmt4.format(123L);
213 Number num = fmt4.parse(str);
214 logln(num.toString());
216 catch (Exception e) {
217 errln("parse caught exception");
220 str = fmt4.format(.000123);
223 Number num = fmt4.parse(str);
224 logln(num.toString());
226 catch (Exception e) {
227 errln("parse caught exception");
230 str = fmt4.format(456.000123);
233 Number num = fmt4.parse(str);
234 logln(num.toString());
236 catch (Exception e) {
237 errln("parse caught exception");
241 public void TestUndefinedSpellout() {
242 Locale greek = new Locale("el", "", "");
243 RuleBasedNumberFormat[] formatters = {
244 new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.SPELLOUT),
245 new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.ORDINAL),
246 new RuleBasedNumberFormat(greek, RuleBasedNumberFormat.DURATION),
274 NumberFormat decFormat = NumberFormat.getInstance(Locale.US);
275 for (int j = 0; j < formatters.length; ++j) {
276 com.ibm.icu.text.NumberFormat formatter = formatters[j];
277 logln("formatter[" + j + "]");
278 for (int i = 0; i < data.length; ++i) {
280 String result = formatter.format(decFormat.parse(data[i]));
281 logln("[" + i + "] " + data[i] + " ==> " + result);
283 catch (Exception e) {
284 errln("formatter[" + j + "], data[" + i + "] " + data[i] + " threw exception " + e.getMessage());
291 * Perform a simple spot check on the English spellout rules
293 public void TestEnglishSpellout() {
294 RuleBasedNumberFormat formatter
295 = new RuleBasedNumberFormat(Locale.US,
296 RuleBasedNumberFormat.SPELLOUT);
297 String[][] testData = {
301 { "23", "twenty-three" },
302 { "73", "seventy-three" },
303 { "88", "eighty-eight" },
304 { "100", "one hundred" },
305 { "106", "one hundred six" },
306 { "127", "one hundred twenty-seven" },
307 { "200", "two hundred" },
308 { "579", "five hundred seventy-nine" },
309 { "1,000", "one thousand" },
310 { "2,000", "two thousand" },
311 { "3,004", "three thousand four" },
312 { "4,567", "four thousand five hundred sixty-seven" },
313 { "15,943", "fifteen thousand nine hundred forty-three" },
314 { "2,345,678", "two million three hundred forty-five "
315 + "thousand six hundred seventy-eight" },
316 { "-36", "minus thirty-six" },
317 { "234.567", "two hundred thirty-four point five six seven" }
320 doTest(formatter, testData, true);
322 formatter.setLenientParseMode(true);
323 String[][] lpTestData = {
324 { "FOurhundred thiRTY six", "436" },
325 // test spaces before fifty-7 causing lenient parse match of "fifty-" to " fifty"
326 // leaving "-7" for remaining parse, resulting in 2643 as the parse result.
328 { " fifty-7", "57" },
329 { " fifty-7", "57" },
330 { "2 thousand six HUNDRED fifty-7", "2,657" },
331 { "fifteen hundred and zero", "1,500" }
333 doLenientParseTest(formatter, lpTestData);
337 * Perform a simple spot check on the English ordinal-abbreviation rules
339 public void TestOrdinalAbbreviations() {
340 RuleBasedNumberFormat formatter
341 = new RuleBasedNumberFormat(Locale.US,
342 RuleBasedNumberFormat.ORDINAL);
343 String[][] testData = {
344 { "1", "1\u02e2\u1d57" },
345 { "2", "2\u207f\u1d48" },
346 { "3", "3\u02b3\u1d48" },
347 { "4", "4\u1d57\u02b0" },
348 { "7", "7\u1d57\u02b0" },
349 { "10", "10\u1d57\u02b0" },
350 { "11", "11\u1d57\u02b0" },
351 { "13", "13\u1d57\u02b0" },
352 { "20", "20\u1d57\u02b0" },
353 { "21", "21\u02e2\u1d57" },
354 { "22", "22\u207f\u1d48" },
355 { "23", "23\u02b3\u1d48" },
356 { "24", "24\u1d57\u02b0" },
357 { "33", "33\u02b3\u1d48" },
358 { "102", "102\u207f\u1d48" },
359 { "312", "312\u1d57\u02b0" },
360 { "12,345", "12,345\u1d57\u02b0" }
363 doTest(formatter, testData, false);
367 * Perform a simple spot check on the duration-formatting rules
369 public void TestDurations() {
370 RuleBasedNumberFormat formatter
371 = new RuleBasedNumberFormat(Locale.US,
372 RuleBasedNumberFormat.DURATION);
373 String[][] testData = {
374 { "3,600", "1:00:00" }, //move me and I fail
382 // { "3,600", "1:00:00" },
383 { "3,740", "1:02:20" },
384 { "10,293", "2:51:33" }
387 doTest(formatter, testData, true);
389 formatter.setLenientParseMode(true);
390 String[][] lpTestData = {
391 { "2-51-33", "10,293" }
393 doLenientParseTest(formatter, lpTestData);
397 * Perform a simple spot check on the Spanish spellout rules
399 public void TestSpanishSpellout() {
400 RuleBasedNumberFormat formatter
401 = new RuleBasedNumberFormat(new Locale("es", "es",
402 ""), RuleBasedNumberFormat.SPELLOUT);
403 String[][] testData = {
406 { "16", "diecis\u00e9is" },
408 { "24", "veinticuatro" },
409 { "26", "veintis\u00e9is" },
410 { "73", "setenta y tres" },
411 { "88", "ochenta y ocho" },
413 { "106", "ciento seis" },
414 { "127", "ciento veintisiete" },
415 { "200", "doscientos" },
416 { "579", "quinientos setenta y nueve" },
418 { "2,000", "dos mil" },
419 { "3,004", "tres mil cuatro" },
420 { "4,567", "cuatro mil quinientos sesenta y siete" },
421 { "15,943", "quince mil novecientos cuarenta y tres" },
422 { "2,345,678", "dos millones trescientos cuarenta y cinco mil "
423 + "seiscientos setenta y ocho"},
424 { "-36", "menos treinta y seis" },
425 { "234.567", "doscientos treinta y cuatro coma cinco seis siete" }
428 doTest(formatter, testData, true);
432 * Perform a simple spot check on the French spellout rules
434 public void TestFrenchSpellout() {
435 RuleBasedNumberFormat formatter
436 = new RuleBasedNumberFormat(Locale.FRANCE,
437 RuleBasedNumberFormat.SPELLOUT);
438 String[][] testData = {
442 { "21", "vingt-et-un" },
443 { "23", "vingt-trois" },
444 { "62", "soixante-deux" },
445 { "70", "soixante-dix" },
446 { "71", "soixante-et-onze" },
447 { "73", "soixante-treize" },
448 { "80", "quatre-vingts" },
449 { "88", "quatre-vingt-huit" },
451 { "106", "cent-six" },
452 { "127", "cent-vingt-sept" },
453 { "200", "deux-cents" },
454 { "579", "cinq-cent-soixante-dix-neuf" },
455 { "1,000", "mille" },
456 { "1,123", "mille-cent-vingt-trois" },
457 { "1,594", "mille-cinq-cent-quatre-vingt-quatorze" },
458 { "2,000", "deux-mille" },
459 { "3,004", "trois-mille-quatre" },
460 { "4,567", "quatre-mille-cinq-cent-soixante-sept" },
461 { "15,943", "quinze-mille-neuf-cent-quarante-trois" },
462 { "2,345,678", "deux millions trois-cent-quarante-cinq-mille-"
463 + "six-cent-soixante-dix-huit" },
464 { "-36", "moins trente-six" },
465 { "234.567", "deux-cent-trente-quatre virgule cinq six sept" }
468 doTest(formatter, testData, true);
470 formatter.setLenientParseMode(true);
471 String[][] lpTestData = {
472 { "trente-et-un", "31" },
473 { "un cent quatre vingt dix huit", "198" }
475 doLenientParseTest(formatter, lpTestData);
479 * Perform a simple spot check on the Swiss French spellout rules
481 public void TestSwissFrenchSpellout() {
482 RuleBasedNumberFormat formatter
483 = new RuleBasedNumberFormat(new Locale("fr", "CH",
484 ""), RuleBasedNumberFormat.SPELLOUT);
485 String[][] testData = {
489 { "21", "vingt-et-un" },
490 { "23", "vingt-trois" },
491 { "62", "soixante-deux" },
492 { "70", "septante" },
493 { "71", "septante-et-un" },
494 { "73", "septante-trois" },
495 { "80", "huitante" },
496 { "88", "huitante-huit" },
498 { "106", "cent-six" },
499 { "127", "cent-vingt-sept" },
500 { "200", "deux-cents" },
501 { "579", "cinq-cent-septante-neuf" },
502 { "1,000", "mille" },
503 { "1,123", "mille-cent-vingt-trois" },
504 { "1,594", "mille-cinq-cent-nonante-quatre" },
505 { "2,000", "deux-mille" },
506 { "3,004", "trois-mille-quatre" },
507 { "4,567", "quatre-mille-cinq-cent-soixante-sept" },
508 { "15,943", "quinze-mille-neuf-cent-quarante-trois" },
509 { "2,345,678", "deux millions trois-cent-quarante-cinq-mille-"
510 + "six-cent-septante-huit" },
511 { "-36", "moins trente-six" },
512 { "234.567", "deux-cent-trente-quatre virgule cinq six sept" }
515 doTest(formatter, testData, true);
519 * Perform a simple spot check on the Italian spellout rules
521 public void TestItalianSpellout() {
522 RuleBasedNumberFormat formatter
523 = new RuleBasedNumberFormat(Locale.ITALIAN,
524 RuleBasedNumberFormat.SPELLOUT);
525 String[][] testData = {
527 { "15", "quindici" },
529 { "23", "venti\u00ADtr\u00E9" },
530 { "73", "settanta\u00ADtr\u00E9" },
531 { "88", "ottant\u00ADotto" },
533 { "106", "cento\u00ADsei" },
534 { "108", "cent\u00ADotto" },
535 { "127", "cento\u00ADventi\u00ADsette" },
536 { "181", "cent\u00ADottant\u00ADuno" },
537 { "200", "due\u00ADcento" },
538 { "579", "cinque\u00ADcento\u00ADsettanta\u00ADnove" },
539 { "1,000", "mille" },
540 { "2,000", "due\u00ADmila" },
541 { "3,004", "tre\u00ADmila\u00ADquattro" },
542 { "4,567", "quattro\u00ADmila\u00ADcinque\u00ADcento\u00ADsessanta\u00ADsette" },
543 { "15,943", "quindici\u00ADmila\u00ADnove\u00ADcento\u00ADquaranta\u00ADtr\u00E9" },
544 { "-36", "meno trenta\u00ADsei" },
545 { "234.567", "due\u00ADcento\u00ADtrenta\u00ADquattro virgola cinque sei sette" }
548 doTest(formatter, testData, true);
552 * Perform a simple spot check on the German spellout rules
554 public void TestGermanSpellout() {
555 RuleBasedNumberFormat formatter
556 = new RuleBasedNumberFormat(Locale.GERMANY,
557 RuleBasedNumberFormat.SPELLOUT);
558 String[][] testData = {
560 { "15", "f\u00fcnfzehn" },
562 { "23", "drei\u00ADund\u00ADzwanzig" },
563 { "73", "drei\u00ADund\u00ADsiebzig" },
564 { "88", "acht\u00ADund\u00ADachtzig" },
565 { "100", "ein\u00ADhundert" },
566 { "106", "ein\u00ADhundert\u00ADsechs" },
567 { "127", "ein\u00ADhundert\u00ADsieben\u00ADund\u00ADzwanzig" },
568 { "200", "zwei\u00ADhundert" },
569 { "579", "f\u00fcnf\u00ADhundert\u00ADneun\u00ADund\u00ADsiebzig" },
570 { "1,000", "ein\u00ADtausend" },
571 { "2,000", "zwei\u00ADtausend" },
572 { "3,004", "drei\u00ADtausend\u00ADvier" },
573 { "4,567", "vier\u00ADtausend\u00ADf\u00fcnf\u00ADhundert\u00ADsieben\u00ADund\u00ADsechzig" },
574 { "15,943", "f\u00fcnfzehn\u00ADtausend\u00ADneun\u00ADhundert\u00ADdrei\u00ADund\u00ADvierzig" },
575 { "2,345,678", "zwei Millionen drei\u00ADhundert\u00ADf\u00fcnf\u00ADund\u00ADvierzig\u00ADtausend\u00AD"
576 + "sechs\u00ADhundert\u00ADacht\u00ADund\u00ADsiebzig" }
579 doTest(formatter, testData, true);
581 formatter.setLenientParseMode(true);
582 String[][] lpTestData = {
583 { "ein Tausend sechs Hundert fuenfunddreissig", "1,635" }
585 doLenientParseTest(formatter, lpTestData);
589 * Perform a simple spot check on the Thai spellout rules
591 public void TestThaiSpellout() {
592 RuleBasedNumberFormat formatter
593 = new RuleBasedNumberFormat(new Locale("th", "TH", ""),
594 RuleBasedNumberFormat.SPELLOUT);
595 String[][] testData = {
596 { "0", "\u0e28\u0e39\u0e19\u0e22\u0e4c" },
597 { "1", "\u0e2b\u0e19\u0e36\u0e48\u0e07" },
598 { "10", "\u0e2a\u0e34\u0e1a" },
599 { "11", "\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14" },
600 { "21", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e40\u0e2d\u0e47\u0e14" },
601 { "101", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e23\u0e49\u0e2d\u0e22\u200b\u0e2b\u0e19\u0e36\u0e48\u0e07" },
602 { "1.234", "\u0e2b\u0e19\u0e36\u0e48\u0e07\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e2d\u0e07\u0e2a\u0e32\u0e21\u0e2a\u0e35\u0e48" },
603 { "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" },
604 { "22.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e2d\u0e07\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
605 { "23.45", "\u0e22\u0e35\u0e48\u200b\u0e2a\u0e34\u0e1a\u200b\u0e2a\u0e32\u0e21\u200b\u0e08\u0e38\u0e14\u200b\u0e2a\u0e35\u0e48\u0e2b\u0e49\u0e32" },
606 { "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" },
607 { "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" },
610 doTest(formatter, testData, true);
613 formatter.setLenientParseMode(true);
614 String[][] lpTestData = {
615 { "ein Tausend sechs Hundert fuenfunddreissig", "1,635" }
617 doLenientParseTest(formatter, lpTestData);
621 public void TestFractionalRuleSet() {
624 RuleBasedNumberFormat formatter =
625 new RuleBasedNumberFormat(fracRules, Locale.ENGLISH);
627 String[][] testData = {
642 { "3.125", "3 1/8" },
643 { "4.1428", "4 1/7" },
644 { "5.1667", "5 1/6" },
647 { "8.333", "8 1/3" },
652 { "1.2856", "1 2/7" }
654 doTest(formatter, testData, false); // exact values aren't parsable from fractions
657 public void TestSwedishSpellout()
659 Locale locale = new Locale("sv", "", "");
660 RuleBasedNumberFormat formatter = new RuleBasedNumberFormat(locale, RuleBasedNumberFormat.SPELLOUT);
662 String[][] testDataDefault = {
663 { "101", "ett\u00ADhundra\u00ADett" },
664 { "123", "ett\u00ADhundra\u00ADtjugo\u00ADtre" },
665 { "1,001", "ettusen ett" },
666 { "1,100", "ettusen ett\u00ADhundra" },
667 { "1,101", "ettusen ett\u00ADhundra\u00ADett" },
668 { "1,234", "ettusen tv\u00e5\u00ADhundra\u00ADtrettio\u00ADfyra" },
669 { "10,001", "tio\u00ADtusen ett" },
670 { "11,000", "elva\u00ADtusen" },
671 { "12,000", "tolv\u00ADtusen" },
672 { "20,000", "tjugo-tusen" },
673 { "21,000", "tjugo\u00ADett-tusen" },
674 { "21,001", "tjugo\u00ADett-tusen ett" },
675 { "200,000", "tv\u00e5\u00ADhundra-tusen" },
676 { "201,000", "tv\u00e5\u00ADhundra\u00ADett-tusen" },
677 { "200,200", "tv\u00e5\u00ADhundra-tusen tv\u00e5\u00ADhundra" },
678 { "2,002,000", "tv\u00e5 miljoner tv\u00e5\u00ADtusen" },
679 { "12,345,678", "tolv miljoner tre\u00ADhundra\u00ADfyrtio\u00ADfem-tusen sex\u00ADhundra\u00ADsjuttio\u00AD\u00e5tta" },
680 { "123,456.789", "ett\u00ADhundra\u00ADtjugo\u00ADtre-tusen fyra\u00ADhundra\u00ADfemtio\u00ADsex komma sju \u00e5tta nio" },
681 { "-12,345.678", "minus tolv\u00ADtusen tre\u00ADhundra\u00ADfyrtio\u00ADfem komma sex sju \u00e5tta" }
684 logln("testing default rules");
685 doTest(formatter, testDataDefault, true);
687 String[][] testDataNeutrum = {
688 { "101", "ett\u00adhundra\u00aden" },
689 { "1,001", "ettusen en" },
690 { "1,101", "ettusen ett\u00adhundra\u00aden" },
691 { "10,001", "tio\u00adtusen en" },
692 { "21,001", "tjugo\u00aden\u00adtusen en" }
695 formatter.setDefaultRuleSet("%spellout-cardinal-neutre");
696 logln("testing neutrum rules");
697 doTest(formatter, testDataNeutrum, true);
699 String[][] testDataYear = {
700 { "101", "ett\u00adhundra\u00adett" },
701 { "900", "nio\u00adhundra" },
702 { "1,001", "ettusen ett" },
703 { "1,100", "elva\u00adhundra" },
704 { "1,101", "elva\u00adhundra\u00adett" },
705 { "1,234", "tolv\u00adhundra\u00adtrettio\u00adfyra" },
706 { "2,001", "tjugo\u00adhundra\u00adett" },
707 { "10,001", "tio\u00adtusen ett" }
710 formatter.setDefaultRuleSet("%spellout-numbering-year");
711 logln("testing year rules");
712 doTest(formatter, testDataYear, true);
715 public void TestBigNumbers() {
716 BigInteger bigI = new BigInteger("1234567890", 10);
717 StringBuffer buf = new StringBuffer();
718 RuleBasedNumberFormat fmt = new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
719 fmt.format(bigI, buf, null);
720 logln("big int: " + buf.toString());
722 //#if defined(FOUNDATION10)
725 java.math.BigDecimal bigD = new java.math.BigDecimal(bigI);
726 fmt.format(bigD, buf, null);
727 logln("big dec: " + buf.toString());
731 public void TestTrailingSemicolon() {
734 " -x: \u0e25\u0e1a>>;\n" +
735 " x.x: <<\u0e08\u0e38\u0e14>>>;\n" +
736 " \u0e28\u0e39\u0e19\u0e22\u0e4c; \u0e2b\u0e19\u0e36\u0e48\u0e07; \u0e2a\u0e2d\u0e07; \u0e2a\u0e32\u0e21;\n" +
737 " \u0e2a\u0e35\u0e48; \u0e2b\u0e49\u0e32; \u0e2b\u0e01; \u0e40\u0e08\u0e47\u0e14; \u0e41\u0e1b\u0e14;\n" +
738 " \u0e40\u0e01\u0e49\u0e32; \u0e2a\u0e34\u0e1a; \u0e2a\u0e34\u0e1a\u0e40\u0e2d\u0e47\u0e14;\n" +
739 " \u0e2a\u0e34\u0e1a\u0e2a\u0e2d\u0e07; \u0e2a\u0e34\u0e1a\u0e2a\u0e32\u0e21;\n" +
740 " \u0e2a\u0e34\u0e1a\u0e2a\u0e35\u0e48; \u0e2a\u0e34\u0e1a\u0e2b\u0e49\u0e32;\n" +
741 " \u0e2a\u0e34\u0e1a\u0e2b\u0e01; \u0e2a\u0e34\u0e1a\u0e40\u0e08\u0e47\u0e14;\n" +
742 " \u0e2a\u0e34\u0e1a\u0e41\u0e1b\u0e14; \u0e2a\u0e34\u0e1a\u0e40\u0e01\u0e49\u0e32;\n" +
743 " 20: \u0e22\u0e35\u0e48\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
744 " 30: \u0e2a\u0e32\u0e21\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
745 " 40: \u0e2a\u0e35\u0e48\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
746 " 50: \u0e2b\u0e49\u0e32\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
747 " 60: \u0e2b\u0e01\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
748 " 70: \u0e40\u0e08\u0e47\u0e14\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
749 " 80: \u0e41\u0e1b\u0e14\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
750 " 90: \u0e40\u0e01\u0e49\u0e32\u0e2a\u0e34\u0e1a[>%%alt-ones>];\n" +
751 " 100: <<\u0e23\u0e49\u0e2d\u0e22[>>];\n" +
752 " 1000: <<\u0e1e\u0e31\u0e19[>>];\n" +
753 " 10000: <<\u0e2b\u0e21\u0e37\u0e48\u0e19[>>];\n" +
754 " 100000: <<\u0e41\u0e2a\u0e19[>>];\n" +
755 " 1,000,000: <<\u0e25\u0e49\u0e32\u0e19[>>];\n" +
756 " 1,000,000,000: <<\u0e1e\u0e31\u0e19\u0e25\u0e49\u0e32\u0e19[>>];\n" +
757 " 1,000,000,000,000: <<\u0e25\u0e49\u0e32\u0e19\u0e25\u0e49\u0e32\u0e19[>>];\n" +
758 " 1,000,000,000,000,000: =#,##0=;\n" +
760 " \u0e28\u0e39\u0e19\u0e22\u0e4c;\n" +
761 " \u0e40\u0e2d\u0e47\u0e14;\n" +
762 " =%default=;\n ; ;; ";
764 RuleBasedNumberFormat formatter
765 = new RuleBasedNumberFormat(thaiRules, new Locale("th", "TH", ""));
767 String[][] testData = {
768 { "0", "\u0e28\u0e39\u0e19\u0e22\u0e4c" },
769 { "1", "\u0e2b\u0e19\u0e36\u0e48\u0e07" },
770 { "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" }
773 doTest(formatter, testData, true);
776 public void TestSmallValues() {
777 String[][] testData = {
778 { "0.001", "zero point zero zero one" },
779 { "0.0001", "zero point zero zero zero one" },
780 { "0.00001", "zero point zero zero zero zero one" },
781 { "0.000001", "zero point zero zero zero zero zero one" },
782 { "0.0000001", "zero point zero zero zero zero zero zero one" },
783 { "0.00000001", "zero point zero zero zero zero zero zero zero one" },
784 { "0.000000001", "zero point zero zero zero zero zero zero zero zero one" },
785 { "0.0000000001", "zero point zero zero zero zero zero zero zero zero zero one" },
786 { "0.00000000001", "zero point zero zero zero zero zero zero zero zero zero zero one" },
787 { "0.000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero one" },
788 { "0.0000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero one" },
789 { "0.00000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero zero one" },
790 { "0.000000000000001", "zero point zero zero zero zero zero zero zero zero zero zero zero zero zero zero one" },
791 { "10,000,000.001", "ten million point zero zero one" },
792 { "10,000,000.0001", "ten million point zero zero zero one" },
793 { "10,000,000.00001", "ten million point zero zero zero zero one" },
794 { "10,000,000.000001", "ten million point zero zero zero zero zero one" },
795 { "10,000,000.0000001", "ten million point zero zero zero zero zero zero one" },
796 { "10,000,000.00000001", "ten million point zero zero zero zero zero zero zero one" },
797 { "10,000,000.000000002", "ten million point zero zero zero zero zero zero zero zero two" },
798 { "10,000,000", "ten million" },
799 { "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" },
800 { "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" },
801 { "12,345,678.87654321", "twelve million three hundred forty-five thousand six hundred seventy-eight point eight seven six five four three two one" },
802 { "1,234,567.7654321", "one million two hundred thirty-four thousand five hundred sixty-seven point seven six five four three two one" },
803 { "123,456.654321", "one hundred twenty-three thousand four hundred fifty-six point six five four three two one" },
804 { "12,345.54321", "twelve thousand three hundred forty-five point five four three two one" },
805 { "1,234.4321", "one thousand two hundred thirty-four point four three two one" },
806 { "123.321", "one hundred twenty-three point three two one" },
807 { "0.0000000011754944", "zero point zero zero zero zero zero zero zero zero one one seven five four nine four four" },
808 { "0.000001175494351", "zero point zero zero zero zero zero one one seven five four nine four three five one" },
811 RuleBasedNumberFormat formatter
812 = new RuleBasedNumberFormat(Locale.US, RuleBasedNumberFormat.SPELLOUT);
813 doTest(formatter, testData, true);
816 public void TestRuleSetDisplayName() {
817 ULocale.setDefault(ULocale.US);
818 String[][] localizations = new String[][] {
819 /* public rule sets*/
820 {"%simplified", "%default", "%ordinal"},
821 /* display names in "en_US" locale*/
822 {"en_US", "Simplified", "Default", "Ordinal"},
823 /* display names in "zh_Hans" locale*/
824 {"zh_Hans", "\u7B80\u5316", "\u7F3A\u7701", "\u5E8F\u5217"},
825 /* display names in a fake locale*/
826 {"foo_Bar_BAZ", "Simplified", "Default", "Ordinal"}
829 //Construct RuleBasedNumberFormat by rule sets and localizations list
830 RuleBasedNumberFormat formatter
831 = new RuleBasedNumberFormat(ukEnglish, localizations, ULocale.US);
832 RuleBasedNumberFormat f2= new RuleBasedNumberFormat(ukEnglish, localizations);
833 assertTrue("Check the two formatters' equality", formatter.equals(f2));
835 //get displayName by name
836 String[] ruleSetNames = formatter.getRuleSetNames();
837 for (int i=0; i<ruleSetNames.length; i++) {
838 logln("Rule set name: " + ruleSetNames[i]);
839 String RSName_defLoc = formatter.getRuleSetDisplayName(ruleSetNames[i]);
840 assertEquals("Display name in default locale", localizations[1][i+1], RSName_defLoc);
841 String RSName_loc = formatter.getRuleSetDisplayName(ruleSetNames[i], ULocale.CHINA);
842 assertEquals("Display name in Chinese", localizations[2][i+1], RSName_loc);
845 // getDefaultRuleSetName
846 String defaultRS = formatter.getDefaultRuleSetName();
847 //you know that the default rule set is %simplified according to rule sets string ukEnglish
848 assertEquals("getDefaultRuleSetName", "%simplified", defaultRS);
850 //get locales of localizations
851 ULocale[] locales = formatter.getRuleSetDisplayNameLocales();
852 for (int i=0; i<locales.length; i++) {
853 logln(locales[i].getName());
857 String[] RSNames_defLoc = formatter.getRuleSetDisplayNames();
858 for (int i=0; i<RSNames_defLoc.length; i++) {
859 assertEquals("getRuleSetDisplayNames in default locale", localizations[1][i+1], RSNames_defLoc[i]);
862 String[] RSNames_loc = formatter.getRuleSetDisplayNames(ULocale.UK);
863 for (int i=0; i<RSNames_loc.length; i++) {
864 assertEquals("getRuleSetDisplayNames in English", localizations[1][i+1], RSNames_loc[i]);
867 RSNames_loc = formatter.getRuleSetDisplayNames(ULocale.CHINA);
868 for (int i=0; i<RSNames_loc.length; i++) {
869 assertEquals("getRuleSetDisplayNames in Chinese", localizations[2][i+1], RSNames_loc[i]);
872 RSNames_loc = formatter.getRuleSetDisplayNames(new ULocale("foo_Bar_BAZ"));
873 for (int i=0; i<RSNames_loc.length; i++) {
874 assertEquals("getRuleSetDisplayNames in fake locale", localizations[3][i+1], RSNames_loc[i]);
878 public void TestAllLocales() {
879 StringBuffer errors = null;
880 ULocale[] locales = ULocale.getAvailableLocales();
886 double[] numbers = {45.678, 1, 2, 10, 11, 100, 110, 200, 1000, 1111, -1111};
889 // RBNF parse is extremely slow when lenient option is enabled.
890 // For non-exhaustive mode, we only test a few locales.
891 // "nl_NL", "be" had crash problem reported by #6534
892 String[] parseLocales = {"en_US", "nl_NL", "be"};
894 for (int i = 0; i < locales.length; ++i) {
895 ULocale loc = locales[i];
896 int count = numbers.length;
897 boolean testParse = true;
898 if (getInclusion() <= 5) {
900 for (int k = 0; k < parseLocales.length; k++) {
901 if (loc.toString().equals(parseLocales[k])) {
907 //RBNF parse is too slow. Increase count only for debugging purpose for now.
911 for (int j = 0; j < 3; ++j) {
912 RuleBasedNumberFormat fmt = new RuleBasedNumberFormat(loc, j+1);
914 for (int c = 0; c < count; c++) {
916 if (c < numbers.length) {
922 n = ((int)(r.nextInt(10000) - 3000)) / 16d;
925 String s = fmt.format(n);
926 logln(loc.getName() + names[j] + "success format: " + n + " -> " + s);
929 // We do not validate the result in this test case,
930 // because there are cases which do not round trip by design.
933 fmt.setLenientParseMode(false);
934 Number num = fmt.parse(s);
935 logln(loc.getName() + names[j] + "success parse: " + s + " -> " + num);
938 fmt.setLenientParseMode(true);
940 logln(loc.getName() + names[j] + "success parse (lenient): " + s + " -> " + num);
941 } catch (ParseException pe) {
942 String msg = loc.getName() + names[j] + "ERROR:" + pe.getMessage();
944 if (errors == null) {
945 errors = new StringBuffer();
947 errors.append("\n" + msg);
953 if (errors != null) {
954 //TODO: We need to fix parse problems - see #6895 / #6896
955 //errln(errors.toString());
956 logln(errors.toString());
960 void doTest(RuleBasedNumberFormat formatter, String[][] testData,
961 boolean testParsing) {
962 // NumberFormat decFmt = NumberFormat.getInstance(Locale.US);
963 NumberFormat decFmt = new DecimalFormat("#,###.################");
965 for (int i = 0; i < testData.length; i++) {
966 String number = testData[i][0];
967 String expectedWords = testData[i][1];
968 logln("test[" + i + "] number: " + number + " target: " + expectedWords);
969 Number num = decFmt.parse(number);
970 String actualWords = formatter.format(num);
972 if (!actualWords.equals(expectedWords)) {
973 errln("Spot check format failed: for " + number + ", expected\n "
974 + expectedWords + ", but got\n " +
977 else if (testParsing) {
978 String actualNumber = decFmt.format(formatter
979 .parse(actualWords));
981 if (!actualNumber.equals(number)) {
982 errln("Spot check parse failed: for " + actualWords +
983 ", expected " + number + ", but got " +
989 catch (Throwable e) {
991 errln("Test failed with exception: " + e.toString());
995 void doLenientParseTest(RuleBasedNumberFormat formatter,
996 String[][] testData) {
997 NumberFormat decFmt = NumberFormat.getInstance(Locale.US);
1000 for (int i = 0; i < testData.length; i++) {
1001 String words = testData[i][0];
1002 String expectedNumber = testData[i][1];
1003 String actualNumber = decFmt.format(formatter.parse(words));
1005 if (!actualNumber.equals(expectedNumber)) {
1006 errln("Lenient-parse spot check failed: for "
1007 + words + ", expected " + expectedNumber
1008 + ", but got " + actualNumber);
1012 catch (Throwable e) {
1013 errln("Test failed with exception: " + e.toString());
1014 e.printStackTrace();
1020 * Spellout rules for U.K. English.
1021 * I borrow the rule sets for TestRuleSetDisplayName()
1023 public static final String ukEnglish =
1025 + " -x: minus >>;\n"
1026 + " x.x: << point >>;\n"
1027 + " zero; one; two; three; four; five; six; seven; eight; nine;\n"
1028 + " ten; eleven; twelve; thirteen; fourteen; fifteen; sixteen;\n"
1029 + " seventeen; eighteen; nineteen;\n"
1030 + " 20: twenty[->>];\n"
1031 + " 30: thirty[->>];\n"
1032 + " 40: forty[->>];\n"
1033 + " 50: fifty[->>];\n"
1034 + " 60: sixty[->>];\n"
1035 + " 70: seventy[->>];\n"
1036 + " 80: eighty[->>];\n"
1037 + " 90: ninety[->>];\n"
1038 + " 100: << hundred[ >>];\n"
1039 + " 1000: << thousand[ >>];\n"
1040 + " 1,000,000: << million[ >>];\n"
1041 + " 1,000,000,000,000: << billion[ >>];\n"
1042 + " 1,000,000,000,000,000: =#,##0=;\n"
1044 + " =%simplified=;\n"
1045 + " 1000>: <%%alt-hundreds<[ >>];\n"
1046 + " 10,000: =%simplified=;\n"
1047 + " 1,000,000: << million[ >%simplified>];\n"
1048 + " 1,000,000,000,000: << billion[ >%simplified>];\n"
1049 + " 1,000,000,000,000,000: =#,##0=;\n"
1050 + "%%alt-hundreds:\n"
1051 + " 0: SHOULD NEVER GET HERE!;\n"
1052 + " 10: <%simplified< thousand;\n"
1053 + " 11: =%simplified= hundred>%%empty>;\n"
1057 + " zeroth; first; second; third; fourth; fifth; sixth; seventh;\n"
1058 + " eighth; ninth;\n"
1059 + " tenth; eleventh; twelfth; thirteenth; fourteenth;\n"
1060 + " fifteenth; sixteenth; seventeenth; eighteenth;\n"
1062 + " twentieth; twenty->>;\n"
1063 + " 30: thirtieth; thirty->>;\n"
1064 + " 40: fortieth; forty->>;\n"
1065 + " 50: fiftieth; fifty->>;\n"
1066 + " 60: sixtieth; sixty->>;\n"
1067 + " 70: seventieth; seventy->>;\n"
1068 + " 80: eightieth; eighty->>;\n"
1069 + " 90: ninetieth; ninety->>;\n"
1070 + " 100: <%simplified< hundredth; <%simplified< hundred >>;\n"
1071 + " 1000: <%simplified< thousandth; <%simplified< thousand >>;\n"
1072 + " 1,000,000: <%simplified< millionth; <%simplified< million >>;\n"
1073 + " 1,000,000,000,000: <%simplified< billionth;\n"
1074 + " <%simplified< billion >>;\n"
1075 + " 1,000,000,000,000,000: =#,##0=;"
1077 + " -x: minus >>;\n"
1078 + " x.x: << point >>;\n"
1079 + " =%simplified=;\n"
1080 + " 100: << hundred[ >%%and>];\n"
1081 + " 1000: << thousand[ >%%and>];\n"
1082 + " 100,000>>: << thousand[>%%commas>];\n"
1083 + " 1,000,000: << million[>%%commas>];\n"
1084 + " 1,000,000,000,000: << billion[>%%commas>];\n"
1085 + " 1,000,000,000,000,000: =#,##0=;\n"
1087 + " and =%default=;\n"
1088 + " 100: =%default=;\n"
1090 + " ' and =%default=;\n"
1091 + " 100: , =%default=;\n"
1092 + " 1000: , <%default< thousand, >%default>;\n"
1093 + " 1,000,000: , =%default=;"
1094 + "%%lenient-parse:\n"
1095 + " & ' ' , ',' ;\n";