From eb858d8ff35567219d8292c1a85471476590661f Mon Sep 17 00:00:00 2001 From: Mark Davis Date: Tue, 11 Mar 2014 17:25:36 +0000 Subject: [PATCH] ICU-10374 integrate branch X-SVN-Rev: 35403 --- .../src/com/ibm/icu/text/PluralRules.java | 56 ++++++++++++++++--- .../src/com/ibm/icu/util/LocaleMatcher.java | 4 +- .../icu/dev/test/format/PluralRulesTest.java | 40 +++++++++++-- .../icu/dev/test/util/LocaleMatcherTest.java | 21 +++++-- 4 files changed, 103 insertions(+), 18 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java index 384ff40e2f5..c83c51a92a7 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/PluralRules.java @@ -547,6 +547,8 @@ public class PluralRules implements Serializable { return baseFactor; } + static final long MAX = (long)1E18; + /** * @internal * @deprecated This API is ICU internal only. @@ -561,7 +563,9 @@ public class PluralRules implements Serializable { source = isNegative ? -n : n; visibleDecimalDigitCount = v; decimalDigits = f; - integerValue = (long)n; + integerValue = n > MAX + ? MAX + : (long)n; hasIntegerValue = source == integerValue; // check values. TODO make into unit test. // @@ -605,6 +609,9 @@ public class PluralRules implements Serializable { if (v == 0) { return 0; } else { + if (n < 0) { + n = -n; + } int baseFactor = (int) Math.pow(10, v); long scaled = Math.round(n * baseFactor); return (int) (scaled % baseFactor); @@ -629,15 +636,49 @@ public class PluralRules implements Serializable { this(n,0); } + private static final long MAX_INTEGER_PART = 1000000000; /** + * Return a guess as to the number of decimals that would be displayed. This is only a guess; callers should + * always supply the decimals explicitly if possible. Currently, it is up to 6 decimals (without trailing zeros). + * Returns 0 for infinities and nans. * @internal * @deprecated This API is ICU internal only. + * */ @Deprecated public static int decimals(double n) { // Ugly... - String temp = String.valueOf(n); - return temp.endsWith(".0") ? 0 : temp.length() - temp.indexOf('.') - 1; + if (Double.isInfinite(n) || Double.isNaN(n)) { + return 0; + } + if (n < 0) { + n = -n; + } + if (n < MAX_INTEGER_PART) { + long temp = (long)(n * 1000000) % 1000000; // get 6 decimals + for (int mask = 10, digits = 6; digits > 0; mask *= 10, --digits) { + if ((temp % mask) != 0) { + return digits; + } + } + return 0; + } else { + String buf = String.format(Locale.ENGLISH, "%1.15e", n); + int ePos = buf.lastIndexOf('e'); + String exponentStr = buf.substring(ePos+1); + int exponent = Integer.parseInt(exponentStr); + int numFractionDigits = ePos - 2 - exponent; + if (numFractionDigits < 0) { + return 0; + } + for (int i=ePos-1; numFractionDigits > 0; --i) { + if (buf.charAt(i) != '0') { + break; + } + --numFractionDigits; + } + return numFractionDigits; + } } /** @@ -796,7 +837,7 @@ public class PluralRules implements Serializable { @Deprecated @Override public double doubleValue() { - return source; + return isNegative ? -source : source; } /** @@ -1643,11 +1684,10 @@ public class PluralRules implements Serializable { } public String select(FixedDecimal n) { + if (Double.isInfinite(n.source) || Double.isNaN(n.source)) { + return KEYWORD_OTHER; + } Rule r = selectRule(n); - // since we have explict 'other', we don't need this. - // if (r == null) { - // return KEYWORD_OTHER; - // } return r.getKeyword(); } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/LocaleMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/util/LocaleMatcher.java index 7eb375bb3ce..ef801e9ef19 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/LocaleMatcher.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/LocaleMatcher.java @@ -46,7 +46,7 @@ import com.ibm.icu.impl.Row.R3; */ public class LocaleMatcher { - private static boolean DEBUG = false; + private static boolean DEBUG = true; private static final ULocale UNKNOWN_LOCALE = new ULocale("und"); @@ -112,7 +112,7 @@ public class LocaleMatcher { */ @Deprecated public LocaleMatcher(LocalePriorityList languagePriorityList, LanguageMatcherData matcherData, double threshold) { - this.matcherData = matcherData; + this.matcherData = matcherData == null ? defaultWritten : matcherData; for (final ULocale language : languagePriorityList) { add(language, languagePriorityList.getWeight(language)); } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java index 2f3a8f3969a..ea94ad09ca8 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/PluralRulesTest.java @@ -58,6 +58,38 @@ public class PluralRulesTest extends TestFmwk { new PluralRulesTest().run(args); } + public void testOverUnderflow() { + logln(String.valueOf(Long.MAX_VALUE + 1d)); + for (double[] testDouble : new double[][]{ + {1E18, 0, 0, 1E18}, // check overflow + {10000000000000.1d, 1, 1, 10000000000000d}, + {-0.00001d, 1, 5, 0}, + {1d, 0, 0, 1}, + {1.1d, 1, 1, 1}, + {12345d, 0, 0, 12345}, + {12345.678912d, 678912, 6, 12345}, + {12345.6789123d, 678912, 6, 12345}, // we only go out 6 digits + {1E18, 0, 0, 1E18}, // check overflow + {1E19, 0, 0, 1E18}, // check overflow + }) { + FixedDecimal fd = new FixedDecimal(testDouble[0]); + assertEquals(testDouble[0] + "=doubleValue()", testDouble[0], fd.doubleValue()); + assertEquals(testDouble[0] + " decimalDigits", (int)testDouble[1], fd.decimalDigits); + assertEquals(testDouble[0] + " visibleDecimalDigitCount", (int)testDouble[2], fd.visibleDecimalDigitCount); + assertEquals(testDouble[0] + " decimalDigitsWithoutTrailingZeros", (int)testDouble[1], fd.decimalDigitsWithoutTrailingZeros); + assertEquals(testDouble[0] + " visibleDecimalDigitCountWithoutTrailingZeros", (int)testDouble[2], fd.visibleDecimalDigitCountWithoutTrailingZeros); + assertEquals(testDouble[0] + " integerValue", (long)testDouble[3], fd.integerValue); + } + + for (ULocale locale : new ULocale[]{ULocale.ENGLISH, new ULocale("cy"), new ULocale("ar")}) { + PluralRules rules = factory.forLocale(locale); + + assertEquals(locale + " NaN", "other", rules.select(Double.NaN)); + assertEquals(locale + " ∞", "other", rules.select(Double.POSITIVE_INFINITY)); + assertEquals(locale + " -∞", "other", rules.select(Double.NEGATIVE_INFINITY)); + } + } + public void testSyntaxRestrictions() { Object[][] shouldFail = { {"a:n in 3..10,13..19"}, @@ -81,7 +113,7 @@ public class PluralRulesTest extends TestFmwk { {"a: n = 1 .. 3"}, {"a: n != 1 .. 3"}, {"a: n ! = 1 .. 3"}, - + // more complicated {"a:n in 3 .. 10 , 13 .. 19"}, @@ -91,7 +123,7 @@ public class PluralRulesTest extends TestFmwk { {"a: n not is 1", ParseException.class}, // hacked to fail {"a: n in 1"}, {"a: n not in 1"}, - + // multiples also have special exceptions // TODO enable the following once there is an update to CLDR // {"a: n is 1,3", ParseException.class}, @@ -721,7 +753,7 @@ public class PluralRulesTest extends TestFmwk { nf.setMinimumFractionDigits(minFracDigits); String expectedFormat = row[2]; String expectedKeyword = row[3]; - + UFieldPosition pos = new UFieldPosition(); String formatted = nf.format(1.0, new StringBuffer(), pos).toString(); int countVisibleFractionDigits = pos.getCountVisibleFractionDigits(); @@ -1019,7 +1051,7 @@ public class PluralRulesTest extends TestFmwk { } logln("max \tsize:\t" + max); } - + public static class FixedDecimalHandler implements SerializableTest.Handler { public Object[] getTestObjects() diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleMatcherTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleMatcherTest.java index ae4dc8fed9c..a2d5038b6e9 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleMatcherTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/LocaleMatcherTest.java @@ -27,6 +27,20 @@ public class LocaleMatcherTest extends TestFmwk { new LocaleMatcherTest().run(args); } + public void testChinese() { + LocaleMatcher matcher = new LocaleMatcher("zh_CN, zh_TW, iw"); + ULocale taiwanChinese = new ULocale("zh_TW"); + ULocale chinaChinese = new ULocale("zh_CN"); +// assertEquals("zh_CN, zh_TW, iw;", taiwanChinese, matcher.getBestMatch("zh_Hant_TW")); +// assertEquals("zh_CN, zh_TW, iw;", taiwanChinese, matcher.getBestMatch("zh_Hant")); +// assertEquals("zh_CN, zh_TW, iw;", taiwanChinese, matcher.getBestMatch("zh_TW")); +// assertEquals("zh_CN, zh_TW, iw;", chinaChinese, matcher.getBestMatch("zh_Hans_CN")); +// assertEquals("zh_CN, zh_TW, iw;", chinaChinese, matcher.getBestMatch("zh_CN")); +// assertEquals("zh_CN, zh_TW, iw;", chinaChinese, matcher.getBestMatch("zh")); + + assertEquals("zh_CN, zh_TW, iw;", taiwanChinese, matcher.getBestMatch("zh_Hant_HK")); + } + public void testenGB() { final LocaleMatcher matcher = new LocaleMatcher("fr, en, en_GB, es_MX, es_419, es"); assertEquals("en_GB", matcher.getBestMatch("en_NZ").toString()); @@ -36,10 +50,9 @@ public class LocaleMatcherTest extends TestFmwk { } public void testFallbacks() { - final LocaleMatcher matcher = new LocaleMatcher("en, hi"); - if (!logKnownIssue("10705", "Need new data from CLDR for languageMatching")) { - assertEquals("hi", matcher.getBestMatch("sa").toString()); - } + LocalePriorityList lpl = LocalePriorityList.add("en, hi").build(); + final LocaleMatcher matcher = new LocaleMatcher(lpl, null, 0.09); + assertEquals("hi", matcher.getBestMatch("sa").toString()); } public void testOverrideData() { -- 2.40.0