| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package libcore.java.text; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.math.RoundingMode; |
| import java.text.DecimalFormat; |
| import java.text.DecimalFormatSymbols; |
| import java.text.FieldPosition; |
| import java.text.NumberFormat; |
| import java.text.ParsePosition; |
| import java.util.Currency; |
| import java.util.Locale; |
| |
| public class DecimalFormatTest extends junit.framework.TestCase { |
| public void test_exponentSeparator() throws Exception { |
| DecimalFormat df = new DecimalFormat("0E0"); |
| assertEquals("1E4", df.format(12345.)); |
| |
| DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); |
| dfs.setExponentSeparator("-useless-api-"); |
| df.setDecimalFormatSymbols(dfs); |
| assertEquals("1-useless-api-4", df.format(12345.)); |
| } |
| |
| public void test_setMaximumFractionDigitsAffectsRoundingMode() throws Exception { |
| DecimalFormat df = (DecimalFormat) DecimalFormat.getInstance(Locale.US); |
| df.setMaximumFractionDigits(0); |
| df.setRoundingMode(RoundingMode.HALF_UP); |
| assertEquals("-0", df.format(-0.2)); |
| df.setMaximumFractionDigits(1); |
| assertEquals("-0.2", df.format(-0.2)); |
| } |
| |
| // Android fails this test, truncating to 127 digits. |
| public void test_setMaximumIntegerDigits() throws Exception { |
| NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US); |
| numberFormat.setGroupingUsed(false); |
| numberFormat.setMinimumIntegerDigits(400); |
| // The RI's documentation suggests that the int should be formatted to 309 characters -- |
| // a magic number they don't explain -- but the BigInteger should be formatted to the 400 |
| // characters we asked for. In practice, the RI uses 309 in both cases. |
| assertEquals(309, numberFormat.format(123).length()); |
| assertEquals(309, numberFormat.format(BigInteger.valueOf(123)).length()); |
| } |
| |
| // Regression test for http://b/1897917: BigDecimal does not take into account multiplier. |
| public void testBigDecimalBug1897917() { |
| // For example. the BigDecimal 0.17 formatted in PercentInstance is 0% instead of 17%: |
| NumberFormat pf = NumberFormat.getPercentInstance(); |
| assertEquals("17%", pf.format(BigDecimal.valueOf(0.17))); |
| |
| // Test long decimal formatted in PercentInstance with various fractions. |
| String longDec = "11.2345678901234567890123456789012345678901234567890"; |
| BigDecimal bd = new BigDecimal(longDec); |
| assertBigDecimalWithFraction(bd, "1,123.46%", 2); |
| assertBigDecimalWithFraction(bd, "1,123.45678901%", 8); |
| assertBigDecimalWithFraction(bd, "1,123.4567890123%", 10); |
| assertBigDecimalWithFraction(bd, "1,123.45678901234567890123%", 20); |
| assertBigDecimalWithFraction(bd, "1,123.456789012345678901234567890123%", 30); |
| |
| // Test trailing zeros. |
| assertDecFmtWithMultiplierAndFraction("3333.33333333", 3, 4, "10,000"); |
| assertDecFmtWithMultiplierAndFraction("3333.33333333", -3, 4, "-10,000"); |
| assertDecFmtWithMultiplierAndFraction("0.00333333", 3, 4, "0.01"); |
| |
| assertDecFmtWithMultiplierAndFractionByLocale("3330000000000000000000000000000000", 3, 4, |
| Locale.US, "9,990,000,000,000,000,000,000,000,000,000,000"); |
| |
| Locale en_IN = new Locale("en", "IN"); |
| assertDecFmtWithMultiplierAndFractionByLocale("3330000000000000000000000000000000", 3, 4, |
| en_IN, "9,99,00,00,00,00,00,00,00,00,00,00,00,00,00,00,000"); |
| } |
| |
| public void testBigDecimalTestBigIntWithMultiplier() { |
| // Big integer tests. |
| assertDecFmtWithMultiplierAndFractionByLocale("123456789012345", 10, 0, |
| Locale.US, "1,234,567,890,123,450"); |
| assertDecFmtWithMultiplierAndFractionByLocale("12345678901234567890", 10, 0, |
| Locale.US, "123,456,789,012,345,678,900"); |
| assertDecFmtWithMultiplierAndFractionByLocale("98765432109876543210987654321", 10, 0, |
| Locale.US, "987,654,321,098,765,432,109,876,543,210"); |
| |
| assertDecFmtWithMultiplierAndFractionByLocale("123456789012345", -10, 0, |
| Locale.US, "-1,234,567,890,123,450"); |
| assertDecFmtWithMultiplierAndFractionByLocale("12345678901234567890", -10, 0, |
| Locale.US, "-123,456,789,012,345,678,900"); |
| assertDecFmtWithMultiplierAndFractionByLocale("98765432109876543210987654321", -10, 0, |
| Locale.US, "-987,654,321,098,765,432,109,876,543,210"); |
| |
| Locale en_IN = new Locale("en", "IN"); |
| assertDecFmtWithMultiplierAndFractionByLocale("123456789012345", 10, 0, |
| en_IN, "1,23,45,67,89,01,23,450"); |
| assertDecFmtWithMultiplierAndFractionByLocale("12345678901234567890", 10, 0, |
| en_IN, "12,34,56,78,90,12,34,56,78,900"); |
| assertDecFmtWithMultiplierAndFractionByLocale("98765432109876543210987654321", 10, 0, |
| en_IN, "9,87,65,43,21,09,87,65,43,21,09,87,65,43,210"); |
| |
| assertDecFmtWithMultiplierAndFractionByLocale("123456789012345", -10, 0, |
| en_IN, "-1,23,45,67,89,01,23,450"); |
| assertDecFmtWithMultiplierAndFractionByLocale("12345678901234567890", -10, 0, |
| en_IN, "-12,34,56,78,90,12,34,56,78,900"); |
| assertDecFmtWithMultiplierAndFractionByLocale("98765432109876543210987654321", -10, 0, |
| en_IN, "-9,87,65,43,21,09,87,65,43,21,09,87,65,43,210"); |
| } |
| |
| public void testBigDecimalICUConsistency() { |
| DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(); |
| df.setMaximumFractionDigits(2); |
| df.setMultiplier(2); |
| assertEquals(df.format(BigDecimal.valueOf(0.16)), |
| df.format(BigDecimal.valueOf(0.16).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(0.0293)), |
| df.format(BigDecimal.valueOf(0.0293).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(0.006)), |
| df.format(BigDecimal.valueOf(0.006).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(0.00283)), |
| df.format(BigDecimal.valueOf(0.00283).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(1.60)), |
| df.format(BigDecimal.valueOf(1.60).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(15)), |
| df.format(BigDecimal.valueOf(15).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(170)), |
| df.format(BigDecimal.valueOf(170).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(234.56)), |
| df.format(BigDecimal.valueOf(234.56).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(0)), |
| df.format(BigDecimal.valueOf(0).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(-1)), |
| df.format(BigDecimal.valueOf(-1).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(-10000)), |
| df.format(BigDecimal.valueOf(-10000).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(-0.001)), |
| df.format(BigDecimal.valueOf(-0.001).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(1234567890.1234567)), |
| df.format(BigDecimal.valueOf(1234567890.1234567).doubleValue())); |
| assertEquals(df.format(BigDecimal.valueOf(1.234567E100)), |
| df.format(BigDecimal.valueOf(1.234567E100).doubleValue())); |
| } |
| |
| private void assertBigDecimalWithFraction(BigDecimal bd, String expectedResult, int fraction) { |
| NumberFormat pf = NumberFormat.getPercentInstance(); |
| pf.setMaximumFractionDigits(fraction); |
| assertEquals(expectedResult, pf.format(bd)); |
| } |
| |
| private void assertDecFmtWithMultiplierAndFraction(String value, int multiplier, int fraction, String expectedResult) { |
| DecimalFormat df = (DecimalFormat)NumberFormat.getInstance(); |
| df.setMultiplier(multiplier); |
| df.setMaximumFractionDigits(fraction); |
| BigDecimal d = new BigDecimal(value); |
| assertEquals(expectedResult, df.format(d)); |
| } |
| |
| private void assertDecFmtWithMultiplierAndFractionByLocale(String value, int multiplier, int fraction, Locale locale, String expectedResult) { |
| DecimalFormat df = (DecimalFormat)NumberFormat.getIntegerInstance(locale); |
| df.setMultiplier(multiplier); |
| df.setMaximumFractionDigits(fraction); |
| BigDecimal d = new BigDecimal(value); |
| assertEquals(expectedResult, df.format(d)); |
| } |
| |
| public void testSetZeroDigitForPattern() { |
| DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(); |
| decimalFormatSymbols.setZeroDigit('a'); |
| DecimalFormat formatter = new DecimalFormat(); |
| formatter.setDecimalFormatSymbols(decimalFormatSymbols); |
| formatter.applyLocalizedPattern("#.aa"); |
| assertEquals("e.fa", formatter.format(4.50)); |
| } |
| |
| public void testSetZeroDigitForFormatting() { |
| DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(); |
| decimalFormatSymbols.setZeroDigit('a'); |
| DecimalFormat formatter = new DecimalFormat(); |
| formatter.setDecimalFormatSymbols(decimalFormatSymbols); |
| formatter.applyLocalizedPattern("#"); |
| assertEquals("eadacab", formatter.format(4030201)); |
| } |
| |
| public void testBug9087737() throws Exception { |
| DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.US); |
| // These shouldn't make valgrind unhappy. |
| df.setCurrency(Currency.getInstance("CHF")); |
| df.setCurrency(Currency.getInstance("GBP")); |
| } |
| |
| // Check we don't crash on null inputs. |
| public void testBug15081434() throws Exception { |
| DecimalFormat df = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.US); |
| try { |
| df.parse(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| try { |
| df.applyLocalizedPattern(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| try { |
| df.applyPattern(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| try { |
| df.applyPattern(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| try { |
| df.format(null, new StringBuffer(), new FieldPosition(0)); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| try { |
| df.parse(null, new ParsePosition(0)); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| // This just ignores null. |
| df.setDecimalFormatSymbols(null); |
| |
| try { |
| df.setCurrency(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| |
| // These just ignore null. |
| df.setNegativePrefix(null); |
| df.setNegativeSuffix(null); |
| df.setPositivePrefix(null); |
| df.setPositiveSuffix(null); |
| |
| try { |
| df.setRoundingMode(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| // Confirm the fraction digits do not change when the currency is changed. |
| public void testBug71369() { |
| final String nonBreakingSpace = "\u00A0"; |
| |
| NumberFormat numberFormat = NumberFormat.getCurrencyInstance(Locale.GERMAN); |
| numberFormat.setCurrency(Currency.getInstance("USD")); |
| |
| assertEquals("2,01" + nonBreakingSpace + "$", numberFormat.format(2.01)); |
| |
| numberFormat.setMinimumFractionDigits(0); |
| numberFormat.setMaximumFractionDigits(0); |
| |
| String expected = "2" + nonBreakingSpace + "$"; |
| assertEquals(expected, numberFormat.format(2.01)); |
| |
| // Changing the currency must not reset the digits. |
| numberFormat.setCurrency(Currency.getInstance("EUR")); |
| numberFormat.setCurrency(Currency.getInstance("USD")); |
| |
| assertEquals(expected, numberFormat.format(2.01)); |
| } |
| |
| /** |
| * Test no extra spacing between currency symbol and the numeric amount |
| */ |
| public void testCurrencySymbolSpacing() { |
| Currency currency = Currency.getInstance(Locale.US); |
| for (Locale locale : Locale.getAvailableLocales()) { |
| DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); |
| String formattedZero = new DecimalFormat("0", dfs).format(0); |
| |
| assertCurrencyFormat("USD" + formattedZero, "\u00a4\u00a40", dfs, currency, locale); |
| assertCurrencyFormat(formattedZero + "USD", "0\u00a4\u00a4", dfs, currency, locale); |
| assertCurrencyFormat(currency.getSymbol(locale) + formattedZero, "\u00a40", dfs, |
| currency, locale); |
| assertCurrencyFormat(formattedZero + currency.getSymbol(locale), "0\u00a4", dfs, |
| currency, locale); |
| } |
| } |
| |
| private static void assertCurrencyFormat(String expected, String pattern, |
| DecimalFormatSymbols dfs, |
| Currency currency, Locale locale) { |
| DecimalFormat df = new DecimalFormat(pattern, dfs); |
| df.setCurrency(currency); |
| df.setMaximumFractionDigits(0); |
| assertEquals("Not formatted as expected with pattern " + pattern + " in locale " + locale, |
| expected, df.format(0)); |
| } |
| |
| // http://b/27855939 |
| public void testBug27855939() { |
| DecimalFormat df = new DecimalFormat("00"); |
| assertEquals("01", df.format(BigDecimal.ONE)); |
| assertEquals("00", df.format(BigDecimal.ZERO)); |
| } |
| |
| // Confirm the currency symbol used by a format is determined by the locale of the format |
| // not the current default Locale. |
| public void testSetCurrency_symbolOrigin() { |
| Currency currency = Currency.getInstance("CNY"); |
| Locale locale1 = Locale.CHINA; |
| Locale locale2 = Locale.US; |
| String locale1Symbol = currency.getSymbol(locale1); |
| String locale2Symbol = currency.getSymbol(locale2); |
| // This test only works if we can tell where the symbol came from, which requires they are |
| // different across the two locales chosen. |
| assertFalse(locale1Symbol.equals(locale2Symbol)); |
| |
| Locale originalLocale = Locale.getDefault(); |
| try { |
| Locale.setDefault(locale1); |
| String amountDefaultLocale1 = |
| formatArbitraryCurrencyAmountInLocale(currency, locale2); |
| |
| Locale.setDefault(locale2); |
| String amountDefaultLocale2 = |
| formatArbitraryCurrencyAmountInLocale(currency, locale2); |
| |
| // This used to fail because Currency.getSymbol() was used without providing the |
| // format's locale. |
| assertEquals(amountDefaultLocale1, amountDefaultLocale2); |
| } finally { |
| Locale.setDefault(originalLocale); |
| } |
| } |
| |
| // Test that overriding the currency symbol survives a roundrip through the |
| // DecimalFormat constructor. |
| // http://b/28732330 |
| public void testSetCurrencySymbol() { |
| DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(Locale.US); |
| decimalFormatSymbols.setCurrencySymbol("¥"); |
| DecimalFormat decimalFormat = new DecimalFormat("¤#,##0.00", decimalFormatSymbols); |
| assertEquals("¥", decimalFormat.getDecimalFormatSymbols().getCurrencySymbol()); |
| } |
| |
| private String formatArbitraryCurrencyAmountInLocale(Currency currency, Locale locale) { |
| NumberFormat localeCurrencyFormat = NumberFormat.getCurrencyInstance(locale); |
| localeCurrencyFormat.setCurrency(currency); |
| return localeCurrencyFormat.format(1000); |
| } |
| |
| /** |
| * DecimalFormat doesn't support different group separator for currency and non-currency |
| * number formats. Ensure normal group separator is used, and ignore monetary group separator |
| * when formatting currency. http://b/37135768 |
| */ |
| public void testLocaleGroupingSeparator() { |
| // CLDR uses '.' for currency and U+00a0 for non-currency number formats in de_AT |
| // Assert ICU is using these characters |
| Locale locale = new Locale("de", "AT"); |
| android.icu.text.DecimalFormatSymbols icuDfs = |
| new android.icu.text.DecimalFormatSymbols(locale); |
| assertEquals(icuDfs.getGroupingSeparator(), '\u00a0'); |
| assertEquals(icuDfs.getMonetaryGroupingSeparator(), '.'); |
| |
| // In this class, only U+00a0 should be used for both cases. |
| DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); |
| // Assert CLDR uses U+00a0 as grouping separator |
| assertEquals(dfs.getGroupingSeparator(), '\u00a0'); |
| |
| // Test non-currency number formats |
| assertEquals("1\u00a0234,00", new DecimalFormat("#,##0.00", dfs).format(1234)); |
| // Test currency format |
| assertEquals("\u20ac1\u00a0234,00", new DecimalFormat("¤#,##0.00", dfs).format(1234)); |
| } |
| |
| /** |
| * Test {@link DecimalFormatSymbols#setGroupingSeparator(char)} for currency and non-currency |
| * number formats. http://b/37135768 |
| */ |
| public void testSetGroupingSeparator() { |
| DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); |
| dfs.setGroupingSeparator(' '); |
| // Test non-currency number formats |
| assertEquals("1 234.00", new DecimalFormat("#,##0.00", dfs).format(1234)); |
| // Test currency format |
| assertEquals("$1 234.00", new DecimalFormat("¤#,##0.00", dfs).format(1234)); |
| } |
| |
| /** |
| * PerMill should be truncated into one char. http://b/67034519 |
| */ |
| public void testPerMill() { |
| String pattern = "0\u2030"; |
| double number = 0.1; |
| Locale locale; |
| |
| // Test US locale behavior: java.text perMill char is expected to be \u2030. |
| locale = Locale.US; |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| assertEquals("100\u2030", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals("100\u2030", df.format(number)); |
| } |
| |
| // Test setPerMill() works |
| DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); |
| dfs.setPerMill(';'); |
| assertEquals("100;", new DecimalFormat(pattern, dfs).format(number)); |
| |
| // Confirm ICU and java.text agree. Test PerMill is localized. |
| locale = new Locale("ar"); |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| // Confirm the "correct" perMill sign uses a single non-ascii char. |
| // ICU's perMill string for ar is known to use single non-ascii char U+0609 |
| assertEquals("\u0609", df.getDecimalFormatSymbols().getPerMillString()); |
| assertEquals("\u0661\u0660\u0660\u0609", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals('\u0609', df.getDecimalFormatSymbols().getPerMill()); |
| assertEquals("\u0661\u0660\u0660\u0609", df.format(number)); |
| } |
| |
| // Confirm ICU and java.text disagree. |
| // java.text doesn't localize PerMill and fallback to default char U+2030 |
| // when PerMill in that locale has more than one visible characters. |
| locale = Locale.forLanguageTag("en-US-POSIX"); |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| // Confirm the "correct" perMill sign requires more than one char. |
| // ICU's perMill string for en_US_POSIX is known to have more than one visible chars. |
| assertEquals("0/00", df.getDecimalFormatSymbols().getPerMillString()); |
| assertEquals("1000/00", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals('\u2030', df.getDecimalFormatSymbols().getPerMill()); |
| assertEquals("100\u2030", df.format(number)); |
| } |
| } |
| |
| /** |
| * Percent should be truncated into one char. |
| */ |
| public void testPercent() { |
| String pattern = "0%"; |
| double number = 0.1; |
| Locale locale; |
| |
| // Test US locale behavior: java.text percent char is expected to be '%'. |
| locale = Locale.US; |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| assertEquals("10%", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals("10%", df.format(number)); |
| } |
| |
| // Test setPercent() works |
| DecimalFormatSymbols dfs = new DecimalFormatSymbols(locale); |
| dfs.setPercent(';'); |
| assertEquals("10;", new DecimalFormat(pattern, dfs).format(number)); |
| |
| // Confirm ICU and java.text disagree because java.text strips out bidi marker |
| locale = new Locale("ar"); |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| // Confirm the "correct" percent sign requires more than one char. |
| // ICU's percent string for ar is known to have a bidi marker. |
| assertEquals("\u066a\u061c", df.getDecimalFormatSymbols().getPercentString()); |
| assertEquals("\u0661\u0660\u066a\u061c", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| // Confirm that java.text.DecimalFormat strips bidi marker characters. |
| // so the ar percent can fit in a single char. |
| assertEquals('\u066a', df.getDecimalFormatSymbols().getPercent()); |
| assertEquals("\u0661\u0660\u066a", df.format(number)); |
| } |
| } |
| |
| /** |
| * Minus sign should be truncated into one char. |
| */ |
| public void testMinusSign() { |
| String pattern = "0;-0"; |
| double number = -123; |
| Locale locale; |
| |
| // Test US locale behavior: java.text percent char is expected to be '-'. |
| locale = Locale.US; |
| assertEquals("-123", new DecimalFormat(pattern, new DecimalFormatSymbols(locale)) |
| .format(number)); |
| assertEquals("-123", new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)).format(number)); |
| |
| // Confirm ICU and java.text agree. Minus sign is localized |
| locale = new Locale("lt"); |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| // Confirm the "correct" minus sign uses a single non-ascii char. |
| // ICU's minus string for ar is known to use single non-ascii char U+2212 |
| assertEquals("\u2212", df.getDecimalFormatSymbols().getMinusSignString()); |
| assertEquals("\u2212123", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals('\u2212', df.getDecimalFormatSymbols().getMinusSign()); |
| assertEquals("\u2212123", df.format(number)); |
| } |
| |
| // Confirm ICU and java.text disagree because java.text strips out bidi marker |
| locale = new Locale("ar"); |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| // Confirm the "correct" minus sign requires more than one char. |
| // ICU's minus string for ar is known to have a bidi marker. |
| assertEquals("\u061c\u002d", df.getDecimalFormatSymbols().getMinusSignString()); |
| assertEquals("\u061c\u002d\u0661\u0662\u0663", df.format(number)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals('\u002d', df.getDecimalFormatSymbols().getMinusSign()); |
| assertEquals("\u002d\u0661\u0662\u0663", df.format(number)); |
| } |
| } |
| |
| /** |
| * Plus sign should not be localized. http://b/67034519 |
| */ |
| public void testPlusSign() { |
| // Test US Locale |
| String pattern = "+0;-0"; |
| assertEquals("+123", new DecimalFormat(pattern, new DecimalFormatSymbols(Locale.US)) |
| .format(123)); |
| |
| // Confirm ICU and java.text disagree because java.text doesn't localize plus sign. |
| Locale locale = new Locale("ar"); |
| { |
| android.icu.text.DecimalFormat df = new android.icu.text.DecimalFormat(pattern, |
| new android.icu.text.DecimalFormatSymbols(locale)); |
| // Confirm the "correct" plus sign requires more than one char. |
| // ICU's plus string for ar is known to have a bidi marker. |
| assertEquals("\u061c\u002b", df.getDecimalFormatSymbols().getPlusSignString()); |
| assertEquals("\u061c\u002b\u0661\u0662\u0663", df.format(123)); |
| } |
| for (DecimalFormat df : createDecimalFormatInstances(locale, pattern)) { |
| assertEquals("\u002b\u0661\u0662\u0663", df.format(123)); |
| } |
| } |
| |
| /** |
| * Returns DecimalFormat instances created in different ways: |
| * <ol> |
| * <li>Using an implicit DecimalFormatSymbols created using the default locale.</li> |
| * <li>Using an explicit DecimalFormatSymbols object. |
| * </ol> |
| * This is to confirm the behavior is currently the same. In future we may choose to improve the |
| * behavior when the caller doesn't provide an explicit DecimalFormatSymbols: |
| * in that case we wouldn't have to pretend that some symbols fit into single char; because the |
| * caller hasn't explicitly specified the symbols they want us to use and we'd be under no |
| * obligation to use the limited char-based public API on DecimalFormatSymbols. |
| */ |
| private static DecimalFormat[] createDecimalFormatInstances(Locale locale, String pattern) { |
| Locale originalLocale = Locale.getDefault(); |
| Locale.setDefault(locale); |
| DecimalFormat[] instances = new DecimalFormat[] { |
| new DecimalFormat(pattern), |
| new DecimalFormat(pattern, new DecimalFormatSymbols(locale)) |
| }; |
| Locale.setDefault(originalLocale); |
| return instances; |
| } |
| |
| // http://b/68143370 |
| public void testWhitespaceTolerated() { |
| // Trailing space is tolerated, but not consumed. |
| assertParsed("0", "1 ", 1, 1); |
| // Digits after trailing space are ignored. |
| assertParsed("0", "1 2", 1, 1); |
| // Space after decimal point is treated as end of input. |
| assertParsed("0", "1. 1", 1, 2); |
| // Space before decimal point is treated as end of input. |
| assertParsed("0", "1 .1", 1, 1); |
| // Space after decimal digit is treated as end of input. |
| assertParsed("0", "1.2 3", 1.2d, 3); |
| // Leading space treated as part of negative prefix |
| assertParsed("0; 0", " 1 ", -1, 2); |
| // Leading space in prefix is accepted. |
| assertParsed(" 0", " 1 ", 1, 2); |
| // Extra space after prefix with space is accepted. |
| assertParsed(" 0", " 1 ", 1, 3); |
| } |
| |
| // http://b/68143370 |
| public void testWhitespaceError() { |
| // Space before currency symbol is not tolerated. |
| assertParseError("¤0", " $1"); |
| // Space after currency symbol is not tolerated. |
| assertParseError("¤0", "$ 1"); |
| // Space before positive prefix is not tolerated. |
| assertParseError("+0", " +1"); |
| // Space after positive prefix is not tolerated. |
| assertParseError("+0", "+ 1"); |
| // Leading space is not tolerated. |
| assertParseError("0", " 1"); |
| // Space in prefix is expected to be present. |
| assertParseError(" 0", "1"); |
| } |
| |
| // Test that getMaximumIntegerDigits should return value >= 309 by default, even though a |
| // leading optional digit # is provided in the input pattern. 309 is chosen because |
| // it is the upper limit of integer digits when formatting numbers other than BigInteger |
| // and BigDecimal. |
| public void testDefaultGetMaximumIntegerDigits() { |
| Locale originalLocale = Locale.getDefault(); |
| try { |
| Locale.setDefault(Locale.US); |
| DecimalFormat df = new DecimalFormat(); |
| int maxIntegerDigits = df.getMaximumIntegerDigits(); |
| assertTrue("getMaximumIntegerDigits should be >= 309, but returns " + maxIntegerDigits, |
| maxIntegerDigits >= 309); |
| |
| String[] patterns = new String[] { "0", "#0", "#.", "#", ".#", "#.#", "#,##0.00", |
| "#,##0.00%", "#,##0.00%", "¤#,##0.00%", "#00.00", "#,#00.00" |
| }; |
| for (String pattern : patterns) { |
| df = new DecimalFormat(pattern); |
| maxIntegerDigits = df.getMaximumIntegerDigits(); |
| assertTrue("getMaximumIntegerDigits should be >= 309, but returns " |
| + maxIntegerDigits, maxIntegerDigits >= 309); |
| } |
| } finally { |
| Locale.setDefault(originalLocale); |
| } |
| } |
| |
| |
| private void assertParseError(String pattern, String input) { |
| ParsePosition pos = new ParsePosition(0); |
| DecimalFormat df = new DecimalFormat(pattern, DecimalFormatSymbols.getInstance(Locale.US)); |
| Number result = df.parse(input, pos); |
| if (result != null) { |
| fail(String.format("Parsed <%s> using <%s>, should have failed: %s", |
| input, pattern, describeParseResult(result, pos))); |
| } |
| } |
| |
| private static void assertParsed(String pattern, String input, Number expected, |
| int expectedIndex) { |
| ParsePosition expectedPos = new ParsePosition(expectedIndex); |
| ParsePosition pos = new ParsePosition(0); |
| DecimalFormat df = new DecimalFormat(pattern, DecimalFormatSymbols.getInstance(Locale.US)); |
| Number result = df.parse(input, pos); |
| assertEquals("Parse <" + input + "> using <" + pattern + ">.", |
| describeParseResult(expected, expectedPos), describeParseResult(result, pos)); |
| } |
| |
| private static String describeParseResult(Number result, ParsePosition pos) { |
| return String.format("%s, index=%d, errorIndex=%d", |
| result, pos.getIndex(), pos.getErrorIndex()); |
| } |
| } |