/* ******************************************************************************* * Copyright (C) 2007-2014, International Business Machines Corporation and * others. All Rights Reserved. ******************************************************************************* */ package com.ibm.icu.dev.test.format; import java.text.ParsePosition; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import com.ibm.icu.dev.test.TestFmwk; import com.ibm.icu.text.DecimalFormat; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.MessageFormat; import com.ibm.icu.text.NumberFormat; import com.ibm.icu.text.PluralFormat; import com.ibm.icu.text.PluralRules; import com.ibm.icu.text.PluralRules.PluralType; import com.ibm.icu.text.PluralRules.SampleType; import com.ibm.icu.util.ULocale; /** * @author tschumann (Tim Schumann) * */ public class PluralFormatUnitTest extends TestFmwk { public static void main(String[] args) throws Exception { new PluralFormatUnitTest().run(args); } public void TestConstructor() { // Test correct formatting of numbers. PluralFormat plFmts[] = new PluralFormat[10]; plFmts[0] = new PluralFormat(); plFmts[0].applyPattern("other{#}"); plFmts[1] = new PluralFormat(PluralRules.DEFAULT); plFmts[1].applyPattern("other{#}"); plFmts[2] = new PluralFormat(PluralRules.DEFAULT, "other{#}"); plFmts[3] = new PluralFormat("other{#}"); plFmts[4] = new PluralFormat(ULocale.getDefault()); plFmts[4].applyPattern("other{#}"); plFmts[5] = new PluralFormat(ULocale.getDefault(), PluralRules.DEFAULT); plFmts[5].applyPattern("other{#}"); plFmts[6] = new PluralFormat(ULocale.getDefault(), PluralRules.DEFAULT, "other{#}"); plFmts[7] = new PluralFormat(ULocale.getDefault(), "other{#}"); // Constructors with Java Locale plFmts[8] = new PluralFormat(Locale.getDefault()); plFmts[8].applyPattern("other{#}"); plFmts[9] = new PluralFormat(Locale.getDefault(), PluralRules.DEFAULT); plFmts[9].applyPattern("other{#}"); // These plural formats should produce the same output as a // NumberFormat for the default locale. NumberFormat numberFmt = NumberFormat.getInstance(ULocale.getDefault()); for (int n = 1; n < 13; n++) { String result = numberFmt.format(n); for (int k = 0; k < plFmts.length; ++k) { this.assertEquals("PluralFormat's output is not as expected", result, plFmts[k].format(n)); } } // Test some bigger numbers. for (int n = 100; n < 113; n++) { String result = numberFmt.format(n*n); for (int k = 0; k < plFmts.length; ++k) { this.assertEquals("PluralFormat's output is not as expected", result, plFmts[k].format(n*n)); } } } public void TestApplyPatternAndFormat() { // Create rules for testing. PluralRules oddAndEven = PluralRules.createRules("odd: n mod 2 is 1"); { // Test full specified case for testing RuleSet PluralFormat plfOddAndEven = new PluralFormat(oddAndEven); plfOddAndEven.applyPattern("odd{# is odd.} other{# is even.}"); // Test fall back to other. PluralFormat plfOddOrEven = new PluralFormat(oddAndEven); plfOddOrEven.applyPattern("other{# is odd or even.}"); NumberFormat numberFormat = NumberFormat.getInstance(ULocale.getDefault()); for (int i = 0; i < 22; ++i) { assertEquals("Fallback to other gave wrong results", numberFormat.format(i) + " is odd or even.", plfOddOrEven.format(i)); assertEquals("Fully specified PluralFormat gave wrong results", numberFormat.format(i) + ((i%2 == 1) ? " is odd." : " is even."), plfOddAndEven.format(i)); } // ICU 4.8 does not check for duplicate keywords any more. PluralFormat pf = new PluralFormat(ULocale.ENGLISH, oddAndEven, "odd{foo} odd{bar} other{foobar}"); assertEquals("should use first occurrence of the 'odd' keyword", "foo", pf.format(1)); pf.applyPattern("odd{foo} other{bar} other{foobar}"); assertEquals("should use first occurrence of the 'other' keyword", "bar", pf.format(2)); // This sees the first "other" before calling the PluralSelector which then selects "other". pf.applyPattern("other{foo} odd{bar} other{foobar}"); assertEquals("should use first occurrence of the 'other' keyword", "foo", pf.format(2)); } // omit other keyword. try { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("odd{foo}"); errln("Not defining plural case other should result in an " + "exception but did not."); }catch (IllegalArgumentException e){} // ICU 4.8 does not check for unknown keywords any more. { PluralFormat pf = new PluralFormat(ULocale.ENGLISH, oddAndEven, "otto{foo} other{bar}"); assertEquals("should ignore unknown keywords", "bar", pf.format(1)); } // Test invalid keyword. try { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("*odd{foo} other{bar}"); errln("Defining a message for an invalid keyword should result in " + "an exception but did not."); }catch (IllegalArgumentException e){} // Test invalid syntax // -- comma between keyword{message} clauses // -- space in keywords // -- keyword{message1}{message2} try { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("odd{foo},other{bar}"); errln("Separating keyword{message} items with other characters " + "than space should provoke an exception but did not."); }catch (IllegalArgumentException e){} try { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("od d{foo} other{bar}"); errln("Spaces inside keywords should provoke an exception but " + "did not."); }catch (IllegalArgumentException e){} try { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("odd{foo}{foobar}other{foo}"); errln("Defining multiple messages after a keyword should provoke " + "an exception but did not."); }catch (IllegalArgumentException e){} // Check that nested format is preserved. { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("odd{The number {0, number, #.#0} is odd.}" + "other{The number {0, number, #.#0} is even.}"); for (int i = 1; i < 3; ++i) { assertEquals("format did not preserve a nested format string.", ((i % 2 == 1) ? "The number {0, number, #.#0} is odd." : "The number {0, number, #.#0} is even."), plFmt.format(i)); } } // Check that a pound sign in curly braces is preserved. { PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("odd{The number {1,number,#} is odd.}" + "other{The number {2,number,#} is even.}"); for (int i = 1; i < 3; ++i) { assertEquals("format did not preserve # inside curly braces.", ((i % 2 == 1) ? "The number {1,number,#} is odd." : "The number {2,number,#} is even."), plFmt.format(i)); } } } public void TestSamples() { Map> same = new LinkedHashMap(); for (ULocale locale : PluralRules.getAvailableULocales()) { ULocale otherLocale = PluralRules.getFunctionalEquivalent(locale, null); Set others = same.get(otherLocale); if (others == null) same.put(otherLocale, others = new LinkedHashSet()); others.add(locale); continue; } for (ULocale locale0 : same.keySet()) { PluralRules rules = PluralRules.forLocale(locale0); String localeName = locale0.toString().length() == 0 ? "root" : locale0.toString(); logln(localeName + "\t=\t" + same.get(locale0)); logln(localeName + "\ttoString\t" + rules.toString()); Set keywords = rules.getKeywords(); for (String keyword : keywords) { Collection list = rules.getSamples(keyword); if (list.size() == 0) { // if there aren't any integer samples, get the decimal ones. list = rules.getSamples(keyword, SampleType.DECIMAL); } if (list == null || list.size() == 0) { errln("Empty list for " + localeName + " : " + keyword); } else { logln("\t" + localeName + " : " + keyword + " ; " + list); } } } } public void TestSetLocale() { // Create rules for testing. PluralRules oddAndEven = PluralRules.createRules("odd__: n mod 2 is 1"); PluralFormat plFmt = new PluralFormat(oddAndEven); plFmt.applyPattern("odd__{odd} other{even}"); plFmt.setLocale(ULocale.ENGLISH); // Check that pattern gets deleted. NumberFormat nrFmt = NumberFormat.getInstance(ULocale.ENGLISH); assertEquals("pattern was not resetted by setLocale() call.", nrFmt.format(5), plFmt.format(5)); // Check that rules got updated. plFmt.applyPattern("odd__{odd} other{even}"); assertEquals("SetLocale should reset rules but did not.", "even", plFmt.format(1)); plFmt.applyPattern("one{one} other{not one}"); for (int i = 0; i < 20; ++i) { assertEquals("Wrong ruleset loaded by setLocale()", ((i==1) ? "one" : "not one"), plFmt.format(i)); } } public void TestParse() { PluralFormat plFmt = new PluralFormat("other{test}"); try { plFmt.parse("test", new ParsePosition(0)); errln("parse() should throw an UnsupportedOperationException but " + "did not"); } catch (UnsupportedOperationException e) { } plFmt = new PluralFormat("other{test}"); try { plFmt.parseObject("test", new ParsePosition(0)); errln("parse() should throw an UnsupportedOperationException but " + "did not"); } catch (UnsupportedOperationException e) { } } public void TestPattern() { Object[] args = { "acme", null }; { // ICU 4.8 PluralFormat does not trim() its pattern any more. // None of the other *Format classes do. String pat = " one {one ''widget} other {# widgets} "; PluralFormat pf = new PluralFormat(pat); assertEquals("should not trim() the pattern", pat, pf.toPattern()); } MessageFormat pfmt = new MessageFormat("The disk ''{0}'' contains {1, plural, one {one ''''{1, number, #.0}'''' widget} other {# widgets}}."); logln(""); for (int i = 0; i < 3; ++i) { args[1] = new Integer(i); logln(pfmt.format(args)); } /* ICU 4.8 returns null instead of a choice/plural/select Format object * (because it does not create an object for any "complex" argument). PluralFormat pf = (PluralFormat)pfmt.getFormatsByArgumentIndex()[1]; logln(pf.toPattern()); */ logln(pfmt.toPattern()); MessageFormat pfmt2 = new MessageFormat(pfmt.toPattern()); assertEquals("message formats are equal", pfmt, pfmt2); } public void TestExtendedPluralFormat() { String[] targets = { "There are no widgets.", "There is one widget.", "There is a bling widget and one other widget.", "There is a bling widget and 2 other widgets.", "There is a bling widget and 3 other widgets.", "Widgets, five (5-1=4) there be.", "There is a bling widget and 5 other widgets.", "There is a bling widget and 6 other widgets.", }; String pluralStyle = "offset:1.0 " + "=0 {There are no widgets.} " + "=1.0 {There is one widget.} " + "=5 {Widgets, five (5-1=#) there be.} " + "one {There is a bling widget and one other widget.} " + "other {There is a bling widget and # other widgets.}"; PluralFormat pf = new PluralFormat(ULocale.ENGLISH, pluralStyle); MessageFormat mf = new MessageFormat("{0,plural," + pluralStyle + "}", ULocale.ENGLISH); Integer args[] = new Integer[1]; for (int i = 0; i <= 7; ++i) { String result = pf.format(i); assertEquals("PluralFormat.format(value " + i + ")", targets[i], result); args[0] = i; result = mf.format(args); assertEquals("MessageFormat.format(value " + i + ")", targets[i], result); } // Try explicit values after keywords. pf.applyPattern("other{zz}other{yy}one{xx}one{ww}=1{vv}=1{uu}"); assertEquals("should find first matching *explicit* value", "vv", pf.format(1)); } public void TestExtendedPluralFormatParsing() { String[] failures = { "offset:1..0 =0 {Foo}", "offset:1.0 {Foo}", "=0= {Foo}", "=0 {Foo} =0.0 {Bar}", " = {Foo}", }; for (String fmt : failures) { try { new PluralFormat(fmt); fail("expected exception when parsing '" + fmt + "'"); } catch (IllegalArgumentException e) { // ok } } } public void TestOrdinalFormat() { String pattern = "one{#st file}two{#nd file}few{#rd file}other{#th file}"; PluralFormat pf = new PluralFormat(ULocale.ENGLISH, PluralType.ORDINAL, pattern); assertEquals("PluralFormat.format(321)", "321st file", pf.format(321)); assertEquals("PluralFormat.format(22)", "22nd file", pf.format(22)); assertEquals("PluralFormat.format(3)", "3rd file", pf.format(3)); // Code coverage: Use the other new-for-PluralType constructor as well. pf = new PluralFormat(ULocale.ENGLISH, PluralType.ORDINAL); pf.applyPattern(pattern); assertEquals("PluralFormat.format(456)", "456th file", pf.format(456)); assertEquals("PluralFormat.format(111)", "111th file", pf.format(111)); } public void TestDecimals() { // Simple number replacement. PluralFormat pf = new PluralFormat(ULocale.ENGLISH, "one{one meter}other{# meters}"); assertEquals("simple format(1)", "one meter", pf.format(1)); assertEquals("simple format(1.5)", "1.5 meters", pf.format(1.5)); PluralFormat pf2 = new PluralFormat(ULocale.ENGLISH, "offset:1 one{another meter}other{another # meters}"); pf2.setNumberFormat(new DecimalFormat("0.0", new DecimalFormatSymbols(ULocale.ENGLISH))); assertEquals("offset-decimals format(1)", "another 0.0 meters", pf2.format(1)); assertEquals("offset-decimals format(2)", "another 1.0 meters", pf2.format(2)); assertEquals("offset-decimals format(2.5)", "another 1.5 meters", pf2.format(2.5)); } }