From afab3f992c9c0eef665c6d8a0deb8134728a8f60 Mon Sep 17 00:00:00 2001 From: Shane Carr Date: Thu, 8 Aug 2019 18:24:10 -0700 Subject: [PATCH] ICU-13780 Removing DecimalFormat_ICU58 (finally). --- .../numberformattestspecification.txt | 426 +- .../data/numberformattestspecification.txt | 426 +- .../format/NumberFormatDataDrivenTest.java | 190 - .../icu/dev/test/format/NumberFormatTest.java | 21 - .../ibm/icu/dev/text/DecimalFormat_ICU58.java | 6286 ----------------- .../src/com/ibm/icu/dev/text/DigitList.java | 842 --- .../com/ibm/icu/dev/text/DigitListTest.java | 47 - 7 files changed, 404 insertions(+), 7834 deletions(-) delete mode 100644 icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java delete mode 100644 icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java delete mode 100644 icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitListTest.java diff --git a/icu4c/source/test/testdata/numberformattestspecification.txt b/icu4c/source/test/testdata/numberformattestspecification.txt index e4e644fa87e..aa4fa3c3f7a 100644 --- a/icu4c/source/test/testdata/numberformattestspecification.txt +++ b/icu4c/source/test/testdata/numberformattestspecification.txt @@ -10,7 +10,6 @@ // per line. // // Field names: -// H = ICU4J 58 (archive) // J = ICU4J (current) // K = JDK (ignored if not OpenJDK 1.8) // C = ICU4C (current) @@ -25,7 +24,7 @@ set locale ar-EG set pattern +0;-# begin format output breaks -6 \u061C+\u0666 HK +6 \u061C+\u0666 K -6 \u061C-\u0666 K test basic patterns @@ -188,7 +187,7 @@ pattern format output breaks // JDK gives 12,3001E3 ##0.000#E0 12300.1 12,30E3 K ##0.000#E0 12301 12,301E3 -0.05E0 12301.2 1,25E4 HK +0.05E0 12301.2 1,25E4 K ##0.000#E0 0.17 170,0E-3 // JDK doesn't support significant digits in exponents @@@E0 6.235 6,24E0 K @@ -270,17 +269,16 @@ $**####,##0 1234 $***1\u202f234 K ####,##0$*x;n#'*' -1234 n1\u202f234*xx K *y%4.2###### 4.33 yyyy%432,6 K // In J ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4) -\u00a4\u00a4 **####0.00 433.0 EUR *433,00 HK -// In H ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4) +\u00a4\u00a4 **####0.00 433.0 EUR *433,00 K // P fails this one because the test code bypasses CurrencyUsage -\u00a4\u00a4 **#######0 433.0 EUR *433,00 HKP +\u00a4\u00a4 **#######0 433.0 EUR *433,00 KP test padding and currencies begin locale currency pattern format output breaks // In J, JPY is considered 2 char (2 * 0xa4) even though padding is done // after prefix. In C this test works. -fr JPY \u00a4\u00a4 **#######0 433.22 JPY ****433 HK +fr JPY \u00a4\u00a4 **#######0 433.22 JPY ****433 K // JDK doesn't correct rounding for currency, shows USD (433 en USD \u00a4\u00a4 **#######0;\u00a4\u00a4 (#) -433.22 USD (433.22) K @@ -310,9 +308,7 @@ set pattern #,##0 set minGroupingDigits 2 begin format output breaks -// min grouping digits not supported in any existing implementation -// but could be easily added to the new DecimalFormat C code. -1000 1000 HK +1000 1000 K 10000 10,000 100000 100,000 1000000 1,000,000 @@ -351,7 +347,7 @@ minIntegerDigits maxIntegerDigits minFractionDigits maxFractionDigits output bre // JDK gives E0 instead of allowing for unlimited precision 0 0 0 0 2.99792458E8 K // J gives 2.9979E8 -0 1 0 5 2.99792E8 HK +0 1 0 5 2.99792E8 K // JDK gives 300E6 0 3 0 0 299.792458E6 K // JDK gives 299.8E6 (maybe maxInt + maxFrac instead of minInt + maxFrac)? @@ -368,7 +364,7 @@ minIntegerDigits maxIntegerDigits minFractionDigits maxFractionDigits output bre // JDK gives E0 0 0 1 0 2.99792458E8 K // J gives 2.998E8 -0 0 0 4 .29979E9 HK +0 0 0 4 .29979E9 K // According to the spec, if maxInt>minInt and minInt>1, then set // Context: #13289 2 8 1 6 2.9979246E8 K @@ -423,7 +419,7 @@ begin format maxIntegerDigits output breaks 123 1 3 // C obeys maxIntegerDigits and prints after the decimal place -0 0 .0 HKP +0 0 .0 KP // CJP ignore max integer if it is less than zero and prints "123" 123 -2147483648 0 CJP 12345 1 5 @@ -516,7 +512,7 @@ begin output grouping grouping2 minGroupingDigits breaks 1,23,45,6789 4 2 2 K 123,456789 6 6 3 -123456789 6 6 4 HK +123456789 6 6 4 K test multiplier setters set locale en_US @@ -524,10 +520,10 @@ begin format multiplier output breaks 23 -12 -276 23 -1 -23 -// H (ICU4J 58) and J (current ICU4J) throw exception on zero multiplier. +// J (current ICU4J) throws exception on zero multiplier. // ICU4C prints 23. // Q multiplies by zero and prints 0. -23 0 0 CHJ +23 0 0 CJ 23 1 23 23 12 276 -23 12 -276 @@ -599,12 +595,12 @@ begin currency currencyUsage toPattern breaks // These work in J, but it prepends an extra hash sign to the pattern. // K does not support this feature. -USD standard 0.00 HK -CHF standard 0.00 HK -CZK standard 0.00 HK -USD cash 0.00 HK -CHF cash 0.05 HK -CZK cash 0 HK +USD standard 0.00 K +CHF standard 0.00 K +CZK standard 0.00 K +USD cash 0.00 K +CHF cash 0.05 K +CZK cash 0 K test currency rounding set locale en @@ -688,7 +684,7 @@ Inf [\u221e] -Inf (\u221e) K // J does not print the affixes // K prints \uFFFD -NaN [NaN] HK +NaN [NaN] K test nan and infinity with multiplication set locale en @@ -711,10 +707,10 @@ Inf beforeSuffix $$$\u221e $ K Inf afterSuffix $$$\u221e$ K // J does not print the affixes // K prints \uFFFD -NaN beforePrefix $$$NaN$ HK -NaN afterPrefix $$$ NaN$ HK -NaN beforeSuffix $$$NaN $ HK -NaN afterSuffix $$$NaN$ HK +NaN beforePrefix $$$NaN$ K +NaN afterPrefix $$$ NaN$ K +NaN beforeSuffix $$$NaN $ K +NaN afterSuffix $$$NaN$ K test apply formerly localized patterns begin @@ -742,8 +738,7 @@ begin pattern toPattern breaks // All of the C and S failures in this section are because of functionally equivalent patterns // JDK doesn't support any patterns with padding or both negative prefix and suffix -// Breaks ICU4J See ticket 11671 -**0,000 **0,000 HK +**0,000 **0,000 K **##0,000 **##0,000 K **###0,000 **###0,000 K **####0,000 **#,##0,000 CJKP @@ -783,25 +778,24 @@ parse output breaks +5347,,, 5347 +5347,,,d8 5347 (5,347.25) -5347.25 -// H requires prefix and suffix for lenient parsing, but C doesn't -5,347.25 5347.25 HK -(5,347.25 -5347.25 H +5,347.25 5347.25 K +(5,347.25 -5347.25 // S is successful at parsing this as -5347.25 in lenient mode --5,347.25 -5347.25 HK +-5,347.25 -5347.25 K +3.52E4 35200 (34.8E-3) -0.0348 // JDK stops parsing at the spaces. JDK doesn't see space as a grouping separator (34 25E-1) -342.5 K (34,,25E-1) -342.5 // Trailing grouping separators are not OK. -// H fails; C/J/P stop at the offending separator. +// C/J/P stop at the offending separator. (34,,25,E-1) fail CJKP -(34,,25,E-1) -3425 HK -(34 25 E-1) -342.5 HK -(34,,25 E-1) -342.5 HK +(34,,25,E-1) -3425 K +(34 25 E-1) -342.5 K +(34,,25 E-1) -342.5 K // Spaces are not allowed after exponent symbol -// C parses up to the E but H bails -(34 25E -1) -3425 HK +// C parses up to the E +(34 25E -1) -3425 K +3.52EE4 3.52 +1,234,567.8901 1234567.8901 +1,23,4567.8901 1234567.8901 @@ -818,29 +812,21 @@ parse output breaks + 79 79 K +,79,,20,33 792033 +7920d3 7920 -// Whitespace immediately after prefix doesn't count as digit separator -// in C but is does in H -+ ,79,,20,33 792033 HK +// Whitespace immediately after prefix doesn't count as digit separator in C ++ ,79,,20,33 792033 K ( 19 45) -1945 K // C allows trailing separators when there is a prefix and suffix. -// H allows trailing separators only when there is just a prefix. -// In this case, H just bails -( 19 45 ) -1945 HK +( 19 45 ) -1945 K (,,19,45) -1945 -// C parses to the space, but H bails -(,,19 45) -19 H -// H bails b/c comma different separator than space. C doesn't treat leading spaces -// as a separator. -( 19,45) -1945 HK -// H bails. Doesn't allow trailing separators when there is prefix and suffix. -(,,19,45,) -1945 H -// H bails on next 4 because H doesn't allow letters inside prefix and suffix. +// C parses to the spaces +(,,19 45) -19 +( 19,45) -1945 K +(,,19,45,) -1945 // C will parse up to the letter. -(,,19,45,d1) -1945 H -(,,19,45d1) -1945 H -( 19 45 d1) -1945 HK -( 19 45d1) -1945 HK -// H does allow trailing separator before a decimal point +(,,19,45,d1) -1945 +(,,19,45d1) -1945 +( 19 45 d1) -1945 K +( 19 45d1) -1945 K (19,45,.25) -1945.25 // 2nd decimal points are ignored +4.12.926 4.12 @@ -851,18 +837,16 @@ set pattern #,##0.0###+;#- begin parse output breaks // J and K just bail. -3426 3426 HK +3426 3426 K 3426+ 3426 // J bails; K sees -34 -34 d1+ 34 HK +34 d1+ 34 K // JDK sees this as -1234 for some reason -// H bails b/c of trailing separators -1,234,,,+ 1234 HK +1,234,,,+ 1234 K 1,234- -1234 -// H, C, and P bail because of trailing separators -1,234,- -1234 CHJP -// J bails here too -1234 - -1234 H +// C and P bail because of trailing separators +1,234,- -1234 CJP +1234 - -1234 @@ -888,8 +872,7 @@ parse output breaks (3425E-1) -342.5 // Strict doesn't allow separators in sci notation. (63,425) -63425 -// H does not allow grouping separators in scientific notation. -(63,425E-1) -6342.5 H +(63,425E-1) -6342.5 // Both prefix and suffix needed for strict. // JDK accepts this and parses as -342.5 (3425E-1 fail K @@ -913,8 +896,7 @@ parse output breaks +1,234.5 1234.5 // Comma after decimal means parse to a comma +1,23,456.78,9 123456.78 -// H fails upon seeing the second decimal point -+1,23,456.78.9 123456.78 H ++1,23,456.78.9 123456.78 +79 79 +79 79 + 79 fail @@ -952,8 +934,7 @@ set parseIntegerOnly 1 begin parse output breaks 35 35 -// S accepts leading plus signs -+35 35 HK ++35 35 K -35 -35 2.63 2 -39.99 -39 @@ -967,8 +948,8 @@ parseNoExponent parse output breaks 0 5e2 500 K 0 5.3E2 530 // See ticket 11725 -1 5e2 5 H -1 5.3E2 5.3 HK +1 5e2 5 +1 5.3E2 5.3 K test parse currency fail set pattern 0 @@ -1006,8 +987,8 @@ begin parse output breaks // A non-greedy parse is required to pass these cases. // All of the implementations being tested are greedy. -6549K 654 CHJKP -6549N -654 CHJKP +6549K 654 CJKP +6549N -654 CJKP test really strange prefix set locale en @@ -1076,41 +1057,41 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD (7.92) USD -7.92 USD (7.92) GBP -7.92 GBP (7.926) USD -7.926 USD -(7.926 USD) -7.926 USD H -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD H -USD (7.92) -7.92 USD H -(7.92)USD -7.92 USD H -USD(7.92) -7.92 USD H +(7.926 USD) -7.926 USD +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD +USD (7.92) -7.92 USD +(7.92)USD -7.92 USD +USD(7.92) -7.92 USD (8) USD -8 USD -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H -US Dollars (53.45) -53.45 USD H +53.45US Dollar 53.45 USD +US Dollars (53.45) -53.45 USD (53.45) US Dollars -53.45 USD (53.45) Euros -53.45 EUR -US Dollar (53.45) -53.45 USD H +US Dollar (53.45) -53.45 USD (53.45) US Dollar -53.45 USD -US Dollars(53.45) -53.45 USD H -(53.45)US Dollars -53.45 USD H -US Dollar(53.45) -53.45 USD H +US Dollars(53.45) -53.45 USD +(53.45)US Dollars -53.45 USD +US Dollar(53.45) -53.45 USD US Dollat(53.45) fail USD -(53.45)US Dollar -53.45 USD H +(53.45)US Dollar -53.45 USD test parse currency ISO negative @@ -1123,27 +1104,27 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD -7.92 USD -7.92 USD -7.92 GBP -7.92 GBP -7.926 USD -7.926 USD -USD -7.926 -7.926 USD H --7.92USD -7.92 USD H -USD-7.92 -7.92 USD H +USD -7.926 -7.926 USD +-7.92USD -7.92 USD +USD-7.92 -7.92 USD -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test parse currency long @@ -1151,38 +1132,36 @@ set pattern 0.00 \u00a4\u00a4\u00a4;(#) \u00a4\u00a4\u00a4 set locale en_GB begin parse output outputCurrency breaks -// H throws a NullPointerException on the first case 53.45 fail GBP £53.45 53.45 GBP $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD (7.92) USD -7.92 USD (7.92) GBP -7.92 GBP (7.926) USD -7.926 USD -(7.926 USD) -7.926 USD H -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD H -USD (7.92) -7.92 USD H -(7.92)USD -7.92 USD H -USD(7.92) -7.92 USD H +(7.926 USD) -7.926 USD +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD +USD (7.92) -7.92 USD +(7.92)USD -7.92 USD +USD(7.92) -7.92 USD (8) USD -8 USD -8 USD -8 USD 67 USD 67 USD -// H throws a NullPointerException on the next case -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test parse currency short @@ -1195,31 +1174,31 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD (7.92) USD -7.92 USD (7.92) GBP -7.92 GBP (7.926) USD -7.926 USD -(7.926 USD) -7.926 USD H -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD H -USD (7.92) -7.92 USD H -(7.92)USD -7.92 USD H -USD(7.92) -7.92 USD H +(7.926 USD) -7.926 USD +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD +USD (7.92) -7.92 USD +(7.92)USD -7.92 USD +USD(7.92) -7.92 USD (8) USD -8 USD -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test parse currency short prefix @@ -1232,31 +1211,31 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD // C and P fail these because '(' is an incomplete prefix. -(7.92) USD -7.92 USD CHJP -(7.92) GBP -7.92 GBP CHJP -(7.926) USD -7.926 USD CHJP -(7.926 USD) -7.926 USD CHJP -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD CHJP -USD (7.92) -7.92 USD CHJP -(7.92)USD -7.92 USD CHJP -USD(7.92) -7.92 USD CHJP -(8) USD -8 USD CHJP +(7.92) USD -7.92 USD CJP +(7.92) GBP -7.92 GBP CJP +(7.926) USD -7.926 USD CJP +(7.926 USD) -7.926 USD CJP +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD CJP +USD (7.92) -7.92 USD CJP +(7.92)USD -7.92 USD CJP +USD(7.92) -7.92 USD CJP +(8) USD -8 USD CJP -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test format foreign currency set locale fa_IR @@ -1371,7 +1350,7 @@ USD (7.92) fail USD (7.92)USD fail USD USD(7.92) fail USD (8) USD -8 USD --8 USD fail USD H +-8 USD fail USD 67 USD 67 USD 53.45$ fail USD US Dollars 53.45 fail USD @@ -1400,7 +1379,7 @@ set minFractionDigits 0 set maxFractionDigits 0 begin format output breaks --0.99 -0 HK +-0.99 -0 K test parse decimalPatternMatchRequired set locale en @@ -1409,11 +1388,11 @@ begin pattern parse output breaks // K doesn't support this feature. 0 123 123 -0 123. fail HK -0 1.23 fail HK +0 123. fail K +0 1.23 fail K 0 -513 -513 -0 -513. fail HK -0 -5.13 fail HK +0 -513. fail K +0 -5.13 fail K 0.0 123 fail K 0.0 123. 123 0.0 1.23 1.23 @@ -1427,13 +1406,13 @@ set pattern # begin pattern parse output breaks # -123 -123 -# - 123 -123 HK -# -123 -123 HK -# - 123 -123 HK +# - 123 -123 K +# -123 -123 K +# - 123 -123 K # 123- 123 # 123 - 123 #;#- 123- -123 -#;#- 123 - -123 HK +#;#- 123 - -123 K test parse case sensitive set locale en @@ -1444,19 +1423,18 @@ parse parseCaseSensitive output breaks Aa1.23 1 1.23 Aa1.23 0 1.23 AA1.23 1 fail -// H and K do not support case-insensitive parsing for prefix/suffix. -// H supports it for the exponent separator, but not K. -AA1.23 0 1.23 HK +// K does not support case-insensitive parsing for prefix/suffix. +AA1.23 0 1.23 K aa1.23 1 fail -aa1.23 0 1.23 HK +aa1.23 0 1.23 K Aa1.23E3 1 1230 Aa1.23E3 0 1230 -Aa1.23e3 1 1.23 H +Aa1.23e3 1 1.23 Aa1.23e3 0 1230 K NaN 1 NaN K NaN 0 NaN K nan 1 fail -nan 0 NaN HK +nan 0 NaN K test parse infinity and scientific notation overflow set locale en @@ -1472,13 +1450,13 @@ NaN NaN K -1E-99999999999999 -0.0 1E2147483648 Inf K 1E2147483647 Inf K -// H, J and K get Infinity -1E2147483646 1E+2147483646 HJK +// J and K get Infinity +1E2147483646 1E+2147483646 JK 1E-2147483649 0 1E-2147483648 0 -// H and K return zero here -1E-2147483647 1E-2147483647 HJK -1E-2147483646 1E-2147483646 HJK +// K returns zero here +1E-2147483647 1E-2147483647 JK +1E-2147483646 1E-2147483646 JK test format push limits set locale en @@ -1506,13 +1484,13 @@ begin pattern lenient parse output breaks // Groups after the first group need 2 digits to be accepted. // JDK does not see space as grouping and parses most of these as 9. -#,##0 1 9 9 9 H +#,##0 1 9 9 9 #,##0 1 9 99 999 K #,##0 1 9 999 9999 K -#,##0 1 9 9 9 9 H +#,##0 1 9 9 9 9 #,##0 1 ,9 9 #,##0 1 99,.0 99 -#,##0 1 9 9. 9 H +#,##0 1 9 9. 9 #,##0 1 9 99. 999 K 0 1 9 9 9 0 1 9 99 9 @@ -1559,36 +1537,36 @@ parse output breaks x a‎b56c df 56 x a‎b56c df 56 K x ab56c df 56 K -x ab56c df 56 HK +x ab56c df 56 K x ab56c df 56 K -x ab56 56 HK -x a b56 56 HK -56cdf 56 HK -56c df 56 HK -56cd f 56 HK -56c‎d‎f 56 HK -56cdf 56 HK -56c d‎f 56 HK -56‎c df 56 HK +x ab56 56 K +x a b56 56 K +56cdf 56 K +56c df 56 K +56cd f 56 K +56c‎d‎f 56 K +56cdf 56 K +56c d‎f 56 K +56‎c df 56 K y g‎h56i jk -56 y g‎h56i jk -56 K y gh56i jk -56 K -y gh56i jk -56 HK +y gh56i jk -56 K y gh56i jk -56 K -y gh56 -56 HK -y g h56 -56 HK +y gh56 -56 K +y g h56 -56 K // S stops parsing after the 'i' for these and returns -56 // C stops before the 'i' and gets 56 -56ijk -56 HK -56i jk -56 HK -56ij k -56 HK -56i‎j‎k -56 HK -56ijk -56 HK -56i j‎k -56 HK -56‎i jk -56 HK -// S and C get 56 (accepts ' ' gs grouping); H and K get null +56ijk -56 K +56i jk -56 K +56ij k -56 K +56i‎j‎k -56 K +56ijk -56 K +56i j‎k -56 K +56‎i jk -56 K +// S and C get 56 (accepts ' ' gs grouping); K gets null 5 6 fail CJP -5‎6 5 HK +5‎6 5 K test parse spaces in grouping // This test gives the ideal behavior of these cases, which @@ -1598,7 +1576,7 @@ set locale en set pattern #,##0 begin parse output breaks -1 2 1 H +1 2 1 1 23 123 K // K gets 1 here; doesn't pick up the grouping separator 1 234 1234 K @@ -1614,7 +1592,7 @@ format output breaks // C and J get "1" // P gets "1.0" // K gets "1.1" (??) -0.975 0.98 CHJKP +0.975 0.98 CJKP test lenient parse currency match // This test is for #13112 @@ -1622,8 +1600,8 @@ set locale en set pattern ¤#,##0.00 begin parse output breaks -// H and K get null -1.23!@#$%^ 1.23 HK +// K gets null +1.23!@#$%^ 1.23 K test percentage parsing multiplier // This test is for #13114 @@ -1632,9 +1610,9 @@ set pattern 0% begin parse output breaks 55% 0.55 -// H and K get null +// K gets null // C and P scale by 100 even if the percent sign is not present -55 0.55 HK +55 0.55 K test trailing grouping separators in pattern // This test is for #13115 @@ -1653,8 +1631,8 @@ begin pattern format output breaks 0 -15 -15 0; -15 -15 -// H and K still prepend a '-' even though the pattern says otherwise -0;0 -15 15 HK +// K still prepends a '-' even though the pattern says otherwise +0;0 -15 15 K test percentage multiplier parsing // This test is for #13129 @@ -1670,9 +1648,9 @@ set pattern 0 set signAlwaysShown 1 begin format output breaks -// H and K do not support this feature -42 +42 HK -0 +0 HK +// K does not support this feature +42 +42 K +0 +0 K -42 -42 test parse strict with plus sign @@ -1683,14 +1661,14 @@ begin lenient parse output breaks 1 42 42 1 -42 -42 -1 +42 42 HK +1 +42 42 K 1 0 0 -1 +0 0 HK -0 42 fail HK +1 +0 0 K +0 42 fail K 0 -42 -42 -0 +42 42 HK -0 0 fail HK -0 +0 0 HK +0 +42 42 K +0 0 fail K +0 +0 0 K test parse with scientific-separator-affix overlap set locale en @@ -1708,13 +1686,13 @@ set currency USD begin parse output breaks USD123 123 -USD 123 123 HK +USD 123 123 K usd123 123 K -usd 123 123 HK +usd 123 123 K Usd123 123 K -Usd 123 123 HK +Usd 123 123 K // US$ is not used for US dollars in en-US -US$123 fail H +US$123 fail us$123 fail Us$123 fail 123 US dollars 123 K @@ -1733,11 +1711,11 @@ set currency USD begin parse output outputCurrency breaks USD123 123 USD -USD 123 123 USD H +USD 123 123 USD usd123 123 USD -usd 123 123 USD H +usd 123 123 USD Usd123 123 USD -Usd 123 123 USD H +Usd 123 123 USD US$123 123 USD C us$123 fail fail Us$123 fail fail @@ -1746,16 +1724,16 @@ Us$123 fail fail 123 us dollars 123 USD GBP123 123 GBP gbp123 123 GBP C -British pounds 123 123 GBP H -british POUNDS 123 123 GBP H +British pounds 123 123 GBP +british POUNDS 123 123 GBP test parse scientific with bidi marks begin locale parse output breaks -en 4E\u200E+02 400 HK +en 4E\u200E+02 400 K en 4E+02 400 K he 4E\u200E+02 400 K -he 4E+02 400 HK +he 4E+02 400 K diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt index e4e644fa87e..aa4fa3c3f7a 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt @@ -10,7 +10,6 @@ // per line. // // Field names: -// H = ICU4J 58 (archive) // J = ICU4J (current) // K = JDK (ignored if not OpenJDK 1.8) // C = ICU4C (current) @@ -25,7 +24,7 @@ set locale ar-EG set pattern +0;-# begin format output breaks -6 \u061C+\u0666 HK +6 \u061C+\u0666 K -6 \u061C-\u0666 K test basic patterns @@ -188,7 +187,7 @@ pattern format output breaks // JDK gives 12,3001E3 ##0.000#E0 12300.1 12,30E3 K ##0.000#E0 12301 12,301E3 -0.05E0 12301.2 1,25E4 HK +0.05E0 12301.2 1,25E4 K ##0.000#E0 0.17 170,0E-3 // JDK doesn't support significant digits in exponents @@@E0 6.235 6,24E0 K @@ -270,17 +269,16 @@ $**####,##0 1234 $***1\u202f234 K ####,##0$*x;n#'*' -1234 n1\u202f234*xx K *y%4.2###### 4.33 yyyy%432,6 K // In J ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4) -\u00a4\u00a4 **####0.00 433.0 EUR *433,00 HK -// In H ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4) +\u00a4\u00a4 **####0.00 433.0 EUR *433,00 K // P fails this one because the test code bypasses CurrencyUsage -\u00a4\u00a4 **#######0 433.0 EUR *433,00 HKP +\u00a4\u00a4 **#######0 433.0 EUR *433,00 KP test padding and currencies begin locale currency pattern format output breaks // In J, JPY is considered 2 char (2 * 0xa4) even though padding is done // after prefix. In C this test works. -fr JPY \u00a4\u00a4 **#######0 433.22 JPY ****433 HK +fr JPY \u00a4\u00a4 **#######0 433.22 JPY ****433 K // JDK doesn't correct rounding for currency, shows USD (433 en USD \u00a4\u00a4 **#######0;\u00a4\u00a4 (#) -433.22 USD (433.22) K @@ -310,9 +308,7 @@ set pattern #,##0 set minGroupingDigits 2 begin format output breaks -// min grouping digits not supported in any existing implementation -// but could be easily added to the new DecimalFormat C code. -1000 1000 HK +1000 1000 K 10000 10,000 100000 100,000 1000000 1,000,000 @@ -351,7 +347,7 @@ minIntegerDigits maxIntegerDigits minFractionDigits maxFractionDigits output bre // JDK gives E0 instead of allowing for unlimited precision 0 0 0 0 2.99792458E8 K // J gives 2.9979E8 -0 1 0 5 2.99792E8 HK +0 1 0 5 2.99792E8 K // JDK gives 300E6 0 3 0 0 299.792458E6 K // JDK gives 299.8E6 (maybe maxInt + maxFrac instead of minInt + maxFrac)? @@ -368,7 +364,7 @@ minIntegerDigits maxIntegerDigits minFractionDigits maxFractionDigits output bre // JDK gives E0 0 0 1 0 2.99792458E8 K // J gives 2.998E8 -0 0 0 4 .29979E9 HK +0 0 0 4 .29979E9 K // According to the spec, if maxInt>minInt and minInt>1, then set // Context: #13289 2 8 1 6 2.9979246E8 K @@ -423,7 +419,7 @@ begin format maxIntegerDigits output breaks 123 1 3 // C obeys maxIntegerDigits and prints after the decimal place -0 0 .0 HKP +0 0 .0 KP // CJP ignore max integer if it is less than zero and prints "123" 123 -2147483648 0 CJP 12345 1 5 @@ -516,7 +512,7 @@ begin output grouping grouping2 minGroupingDigits breaks 1,23,45,6789 4 2 2 K 123,456789 6 6 3 -123456789 6 6 4 HK +123456789 6 6 4 K test multiplier setters set locale en_US @@ -524,10 +520,10 @@ begin format multiplier output breaks 23 -12 -276 23 -1 -23 -// H (ICU4J 58) and J (current ICU4J) throw exception on zero multiplier. +// J (current ICU4J) throws exception on zero multiplier. // ICU4C prints 23. // Q multiplies by zero and prints 0. -23 0 0 CHJ +23 0 0 CJ 23 1 23 23 12 276 -23 12 -276 @@ -599,12 +595,12 @@ begin currency currencyUsage toPattern breaks // These work in J, but it prepends an extra hash sign to the pattern. // K does not support this feature. -USD standard 0.00 HK -CHF standard 0.00 HK -CZK standard 0.00 HK -USD cash 0.00 HK -CHF cash 0.05 HK -CZK cash 0 HK +USD standard 0.00 K +CHF standard 0.00 K +CZK standard 0.00 K +USD cash 0.00 K +CHF cash 0.05 K +CZK cash 0 K test currency rounding set locale en @@ -688,7 +684,7 @@ Inf [\u221e] -Inf (\u221e) K // J does not print the affixes // K prints \uFFFD -NaN [NaN] HK +NaN [NaN] K test nan and infinity with multiplication set locale en @@ -711,10 +707,10 @@ Inf beforeSuffix $$$\u221e $ K Inf afterSuffix $$$\u221e$ K // J does not print the affixes // K prints \uFFFD -NaN beforePrefix $$$NaN$ HK -NaN afterPrefix $$$ NaN$ HK -NaN beforeSuffix $$$NaN $ HK -NaN afterSuffix $$$NaN$ HK +NaN beforePrefix $$$NaN$ K +NaN afterPrefix $$$ NaN$ K +NaN beforeSuffix $$$NaN $ K +NaN afterSuffix $$$NaN$ K test apply formerly localized patterns begin @@ -742,8 +738,7 @@ begin pattern toPattern breaks // All of the C and S failures in this section are because of functionally equivalent patterns // JDK doesn't support any patterns with padding or both negative prefix and suffix -// Breaks ICU4J See ticket 11671 -**0,000 **0,000 HK +**0,000 **0,000 K **##0,000 **##0,000 K **###0,000 **###0,000 K **####0,000 **#,##0,000 CJKP @@ -783,25 +778,24 @@ parse output breaks +5347,,, 5347 +5347,,,d8 5347 (5,347.25) -5347.25 -// H requires prefix and suffix for lenient parsing, but C doesn't -5,347.25 5347.25 HK -(5,347.25 -5347.25 H +5,347.25 5347.25 K +(5,347.25 -5347.25 // S is successful at parsing this as -5347.25 in lenient mode --5,347.25 -5347.25 HK +-5,347.25 -5347.25 K +3.52E4 35200 (34.8E-3) -0.0348 // JDK stops parsing at the spaces. JDK doesn't see space as a grouping separator (34 25E-1) -342.5 K (34,,25E-1) -342.5 // Trailing grouping separators are not OK. -// H fails; C/J/P stop at the offending separator. +// C/J/P stop at the offending separator. (34,,25,E-1) fail CJKP -(34,,25,E-1) -3425 HK -(34 25 E-1) -342.5 HK -(34,,25 E-1) -342.5 HK +(34,,25,E-1) -3425 K +(34 25 E-1) -342.5 K +(34,,25 E-1) -342.5 K // Spaces are not allowed after exponent symbol -// C parses up to the E but H bails -(34 25E -1) -3425 HK +// C parses up to the E +(34 25E -1) -3425 K +3.52EE4 3.52 +1,234,567.8901 1234567.8901 +1,23,4567.8901 1234567.8901 @@ -818,29 +812,21 @@ parse output breaks + 79 79 K +,79,,20,33 792033 +7920d3 7920 -// Whitespace immediately after prefix doesn't count as digit separator -// in C but is does in H -+ ,79,,20,33 792033 HK +// Whitespace immediately after prefix doesn't count as digit separator in C ++ ,79,,20,33 792033 K ( 19 45) -1945 K // C allows trailing separators when there is a prefix and suffix. -// H allows trailing separators only when there is just a prefix. -// In this case, H just bails -( 19 45 ) -1945 HK +( 19 45 ) -1945 K (,,19,45) -1945 -// C parses to the space, but H bails -(,,19 45) -19 H -// H bails b/c comma different separator than space. C doesn't treat leading spaces -// as a separator. -( 19,45) -1945 HK -// H bails. Doesn't allow trailing separators when there is prefix and suffix. -(,,19,45,) -1945 H -// H bails on next 4 because H doesn't allow letters inside prefix and suffix. +// C parses to the spaces +(,,19 45) -19 +( 19,45) -1945 K +(,,19,45,) -1945 // C will parse up to the letter. -(,,19,45,d1) -1945 H -(,,19,45d1) -1945 H -( 19 45 d1) -1945 HK -( 19 45d1) -1945 HK -// H does allow trailing separator before a decimal point +(,,19,45,d1) -1945 +(,,19,45d1) -1945 +( 19 45 d1) -1945 K +( 19 45d1) -1945 K (19,45,.25) -1945.25 // 2nd decimal points are ignored +4.12.926 4.12 @@ -851,18 +837,16 @@ set pattern #,##0.0###+;#- begin parse output breaks // J and K just bail. -3426 3426 HK +3426 3426 K 3426+ 3426 // J bails; K sees -34 -34 d1+ 34 HK +34 d1+ 34 K // JDK sees this as -1234 for some reason -// H bails b/c of trailing separators -1,234,,,+ 1234 HK +1,234,,,+ 1234 K 1,234- -1234 -// H, C, and P bail because of trailing separators -1,234,- -1234 CHJP -// J bails here too -1234 - -1234 H +// C and P bail because of trailing separators +1,234,- -1234 CJP +1234 - -1234 @@ -888,8 +872,7 @@ parse output breaks (3425E-1) -342.5 // Strict doesn't allow separators in sci notation. (63,425) -63425 -// H does not allow grouping separators in scientific notation. -(63,425E-1) -6342.5 H +(63,425E-1) -6342.5 // Both prefix and suffix needed for strict. // JDK accepts this and parses as -342.5 (3425E-1 fail K @@ -913,8 +896,7 @@ parse output breaks +1,234.5 1234.5 // Comma after decimal means parse to a comma +1,23,456.78,9 123456.78 -// H fails upon seeing the second decimal point -+1,23,456.78.9 123456.78 H ++1,23,456.78.9 123456.78 +79 79 +79 79 + 79 fail @@ -952,8 +934,7 @@ set parseIntegerOnly 1 begin parse output breaks 35 35 -// S accepts leading plus signs -+35 35 HK ++35 35 K -35 -35 2.63 2 -39.99 -39 @@ -967,8 +948,8 @@ parseNoExponent parse output breaks 0 5e2 500 K 0 5.3E2 530 // See ticket 11725 -1 5e2 5 H -1 5.3E2 5.3 HK +1 5e2 5 +1 5.3E2 5.3 K test parse currency fail set pattern 0 @@ -1006,8 +987,8 @@ begin parse output breaks // A non-greedy parse is required to pass these cases. // All of the implementations being tested are greedy. -6549K 654 CHJKP -6549N -654 CHJKP +6549K 654 CJKP +6549N -654 CJKP test really strange prefix set locale en @@ -1076,41 +1057,41 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD (7.92) USD -7.92 USD (7.92) GBP -7.92 GBP (7.926) USD -7.926 USD -(7.926 USD) -7.926 USD H -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD H -USD (7.92) -7.92 USD H -(7.92)USD -7.92 USD H -USD(7.92) -7.92 USD H +(7.926 USD) -7.926 USD +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD +USD (7.92) -7.92 USD +(7.92)USD -7.92 USD +USD(7.92) -7.92 USD (8) USD -8 USD -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H -US Dollars (53.45) -53.45 USD H +53.45US Dollar 53.45 USD +US Dollars (53.45) -53.45 USD (53.45) US Dollars -53.45 USD (53.45) Euros -53.45 EUR -US Dollar (53.45) -53.45 USD H +US Dollar (53.45) -53.45 USD (53.45) US Dollar -53.45 USD -US Dollars(53.45) -53.45 USD H -(53.45)US Dollars -53.45 USD H -US Dollar(53.45) -53.45 USD H +US Dollars(53.45) -53.45 USD +(53.45)US Dollars -53.45 USD +US Dollar(53.45) -53.45 USD US Dollat(53.45) fail USD -(53.45)US Dollar -53.45 USD H +(53.45)US Dollar -53.45 USD test parse currency ISO negative @@ -1123,27 +1104,27 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD -7.92 USD -7.92 USD -7.92 GBP -7.92 GBP -7.926 USD -7.926 USD -USD -7.926 -7.926 USD H --7.92USD -7.92 USD H -USD-7.92 -7.92 USD H +USD -7.926 -7.926 USD +-7.92USD -7.92 USD +USD-7.92 -7.92 USD -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test parse currency long @@ -1151,38 +1132,36 @@ set pattern 0.00 \u00a4\u00a4\u00a4;(#) \u00a4\u00a4\u00a4 set locale en_GB begin parse output outputCurrency breaks -// H throws a NullPointerException on the first case 53.45 fail GBP £53.45 53.45 GBP $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD (7.92) USD -7.92 USD (7.92) GBP -7.92 GBP (7.926) USD -7.926 USD -(7.926 USD) -7.926 USD H -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD H -USD (7.92) -7.92 USD H -(7.92)USD -7.92 USD H -USD(7.92) -7.92 USD H +(7.926 USD) -7.926 USD +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD +USD (7.92) -7.92 USD +(7.92)USD -7.92 USD +USD(7.92) -7.92 USD (8) USD -8 USD -8 USD -8 USD 67 USD 67 USD -// H throws a NullPointerException on the next case -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test parse currency short @@ -1195,31 +1174,31 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD (7.92) USD -7.92 USD (7.92) GBP -7.92 GBP (7.926) USD -7.926 USD -(7.926 USD) -7.926 USD H -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD H -USD (7.92) -7.92 USD H -(7.92)USD -7.92 USD H -USD(7.92) -7.92 USD H +(7.926 USD) -7.926 USD +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD +USD (7.92) -7.92 USD +(7.92)USD -7.92 USD +USD(7.92) -7.92 USD (8) USD -8 USD -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD US Dollat53.45 fail USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test parse currency short prefix @@ -1232,31 +1211,31 @@ parse output outputCurrency breaks $53.45 53.45 USD C 53.45 USD 53.45 USD 53.45 GBP 53.45 GBP -USD 53.45 53.45 USD H -53.45USD 53.45 USD H +USD 53.45 53.45 USD +53.45USD 53.45 USD USD53.45 53.45 USD // C and P fail these because '(' is an incomplete prefix. -(7.92) USD -7.92 USD CHJP -(7.92) GBP -7.92 GBP CHJP -(7.926) USD -7.926 USD CHJP -(7.926 USD) -7.926 USD CHJP -(USD 7.926) -7.926 USD H -USD (7.926) -7.926 USD CHJP -USD (7.92) -7.92 USD CHJP -(7.92)USD -7.92 USD CHJP -USD(7.92) -7.92 USD CHJP -(8) USD -8 USD CHJP +(7.92) USD -7.92 USD CJP +(7.92) GBP -7.92 GBP CJP +(7.926) USD -7.926 USD CJP +(7.926 USD) -7.926 USD CJP +(USD 7.926) -7.926 USD +USD (7.926) -7.926 USD CJP +USD (7.92) -7.92 USD CJP +(7.92)USD -7.92 USD CJP +USD(7.92) -7.92 USD CJP +(8) USD -8 USD CJP -8 USD -8 USD 67 USD 67 USD -53.45$ 53.45 USD CH -US Dollars 53.45 53.45 USD H +53.45$ 53.45 USD C +US Dollars 53.45 53.45 USD 53.45 US Dollars 53.45 USD -US Dollar 53.45 53.45 USD H +US Dollar 53.45 53.45 USD 53.45 US Dollar 53.45 USD US Dollars53.45 53.45 USD -53.45US Dollars 53.45 USD H +53.45US Dollars 53.45 USD US Dollar53.45 53.45 USD -53.45US Dollar 53.45 USD H +53.45US Dollar 53.45 USD test format foreign currency set locale fa_IR @@ -1371,7 +1350,7 @@ USD (7.92) fail USD (7.92)USD fail USD USD(7.92) fail USD (8) USD -8 USD --8 USD fail USD H +-8 USD fail USD 67 USD 67 USD 53.45$ fail USD US Dollars 53.45 fail USD @@ -1400,7 +1379,7 @@ set minFractionDigits 0 set maxFractionDigits 0 begin format output breaks --0.99 -0 HK +-0.99 -0 K test parse decimalPatternMatchRequired set locale en @@ -1409,11 +1388,11 @@ begin pattern parse output breaks // K doesn't support this feature. 0 123 123 -0 123. fail HK -0 1.23 fail HK +0 123. fail K +0 1.23 fail K 0 -513 -513 -0 -513. fail HK -0 -5.13 fail HK +0 -513. fail K +0 -5.13 fail K 0.0 123 fail K 0.0 123. 123 0.0 1.23 1.23 @@ -1427,13 +1406,13 @@ set pattern # begin pattern parse output breaks # -123 -123 -# - 123 -123 HK -# -123 -123 HK -# - 123 -123 HK +# - 123 -123 K +# -123 -123 K +# - 123 -123 K # 123- 123 # 123 - 123 #;#- 123- -123 -#;#- 123 - -123 HK +#;#- 123 - -123 K test parse case sensitive set locale en @@ -1444,19 +1423,18 @@ parse parseCaseSensitive output breaks Aa1.23 1 1.23 Aa1.23 0 1.23 AA1.23 1 fail -// H and K do not support case-insensitive parsing for prefix/suffix. -// H supports it for the exponent separator, but not K. -AA1.23 0 1.23 HK +// K does not support case-insensitive parsing for prefix/suffix. +AA1.23 0 1.23 K aa1.23 1 fail -aa1.23 0 1.23 HK +aa1.23 0 1.23 K Aa1.23E3 1 1230 Aa1.23E3 0 1230 -Aa1.23e3 1 1.23 H +Aa1.23e3 1 1.23 Aa1.23e3 0 1230 K NaN 1 NaN K NaN 0 NaN K nan 1 fail -nan 0 NaN HK +nan 0 NaN K test parse infinity and scientific notation overflow set locale en @@ -1472,13 +1450,13 @@ NaN NaN K -1E-99999999999999 -0.0 1E2147483648 Inf K 1E2147483647 Inf K -// H, J and K get Infinity -1E2147483646 1E+2147483646 HJK +// J and K get Infinity +1E2147483646 1E+2147483646 JK 1E-2147483649 0 1E-2147483648 0 -// H and K return zero here -1E-2147483647 1E-2147483647 HJK -1E-2147483646 1E-2147483646 HJK +// K returns zero here +1E-2147483647 1E-2147483647 JK +1E-2147483646 1E-2147483646 JK test format push limits set locale en @@ -1506,13 +1484,13 @@ begin pattern lenient parse output breaks // Groups after the first group need 2 digits to be accepted. // JDK does not see space as grouping and parses most of these as 9. -#,##0 1 9 9 9 H +#,##0 1 9 9 9 #,##0 1 9 99 999 K #,##0 1 9 999 9999 K -#,##0 1 9 9 9 9 H +#,##0 1 9 9 9 9 #,##0 1 ,9 9 #,##0 1 99,.0 99 -#,##0 1 9 9. 9 H +#,##0 1 9 9. 9 #,##0 1 9 99. 999 K 0 1 9 9 9 0 1 9 99 9 @@ -1559,36 +1537,36 @@ parse output breaks x a‎b56c df 56 x a‎b56c df 56 K x ab56c df 56 K -x ab56c df 56 HK +x ab56c df 56 K x ab56c df 56 K -x ab56 56 HK -x a b56 56 HK -56cdf 56 HK -56c df 56 HK -56cd f 56 HK -56c‎d‎f 56 HK -56cdf 56 HK -56c d‎f 56 HK -56‎c df 56 HK +x ab56 56 K +x a b56 56 K +56cdf 56 K +56c df 56 K +56cd f 56 K +56c‎d‎f 56 K +56cdf 56 K +56c d‎f 56 K +56‎c df 56 K y g‎h56i jk -56 y g‎h56i jk -56 K y gh56i jk -56 K -y gh56i jk -56 HK +y gh56i jk -56 K y gh56i jk -56 K -y gh56 -56 HK -y g h56 -56 HK +y gh56 -56 K +y g h56 -56 K // S stops parsing after the 'i' for these and returns -56 // C stops before the 'i' and gets 56 -56ijk -56 HK -56i jk -56 HK -56ij k -56 HK -56i‎j‎k -56 HK -56ijk -56 HK -56i j‎k -56 HK -56‎i jk -56 HK -// S and C get 56 (accepts ' ' gs grouping); H and K get null +56ijk -56 K +56i jk -56 K +56ij k -56 K +56i‎j‎k -56 K +56ijk -56 K +56i j‎k -56 K +56‎i jk -56 K +// S and C get 56 (accepts ' ' gs grouping); K gets null 5 6 fail CJP -5‎6 5 HK +5‎6 5 K test parse spaces in grouping // This test gives the ideal behavior of these cases, which @@ -1598,7 +1576,7 @@ set locale en set pattern #,##0 begin parse output breaks -1 2 1 H +1 2 1 1 23 123 K // K gets 1 here; doesn't pick up the grouping separator 1 234 1234 K @@ -1614,7 +1592,7 @@ format output breaks // C and J get "1" // P gets "1.0" // K gets "1.1" (??) -0.975 0.98 CHJKP +0.975 0.98 CJKP test lenient parse currency match // This test is for #13112 @@ -1622,8 +1600,8 @@ set locale en set pattern ¤#,##0.00 begin parse output breaks -// H and K get null -1.23!@#$%^ 1.23 HK +// K gets null +1.23!@#$%^ 1.23 K test percentage parsing multiplier // This test is for #13114 @@ -1632,9 +1610,9 @@ set pattern 0% begin parse output breaks 55% 0.55 -// H and K get null +// K gets null // C and P scale by 100 even if the percent sign is not present -55 0.55 HK +55 0.55 K test trailing grouping separators in pattern // This test is for #13115 @@ -1653,8 +1631,8 @@ begin pattern format output breaks 0 -15 -15 0; -15 -15 -// H and K still prepend a '-' even though the pattern says otherwise -0;0 -15 15 HK +// K still prepends a '-' even though the pattern says otherwise +0;0 -15 15 K test percentage multiplier parsing // This test is for #13129 @@ -1670,9 +1648,9 @@ set pattern 0 set signAlwaysShown 1 begin format output breaks -// H and K do not support this feature -42 +42 HK -0 +0 HK +// K does not support this feature +42 +42 K +0 +0 K -42 -42 test parse strict with plus sign @@ -1683,14 +1661,14 @@ begin lenient parse output breaks 1 42 42 1 -42 -42 -1 +42 42 HK +1 +42 42 K 1 0 0 -1 +0 0 HK -0 42 fail HK +1 +0 0 K +0 42 fail K 0 -42 -42 -0 +42 42 HK -0 0 fail HK -0 +0 0 HK +0 +42 42 K +0 0 fail K +0 +0 0 K test parse with scientific-separator-affix overlap set locale en @@ -1708,13 +1686,13 @@ set currency USD begin parse output breaks USD123 123 -USD 123 123 HK +USD 123 123 K usd123 123 K -usd 123 123 HK +usd 123 123 K Usd123 123 K -Usd 123 123 HK +Usd 123 123 K // US$ is not used for US dollars in en-US -US$123 fail H +US$123 fail us$123 fail Us$123 fail 123 US dollars 123 K @@ -1733,11 +1711,11 @@ set currency USD begin parse output outputCurrency breaks USD123 123 USD -USD 123 123 USD H +USD 123 123 USD usd123 123 USD -usd 123 123 USD H +usd 123 123 USD Usd123 123 USD -Usd 123 123 USD H +Usd 123 123 USD US$123 123 USD C us$123 fail fail Us$123 fail fail @@ -1746,16 +1724,16 @@ Us$123 fail fail 123 us dollars 123 USD GBP123 123 GBP gbp123 123 GBP C -British pounds 123 123 GBP H -british POUNDS 123 123 GBP H +British pounds 123 123 GBP +british POUNDS 123 123 GBP test parse scientific with bidi marks begin locale parse output breaks -en 4E\u200E+02 400 HK +en 4E\u200E+02 400 K en 4E+02 400 K he 4E\u200E+02 400 K -he 4E+02 400 HK +he 4E+02 400 K diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java index f3cab4f3370..4674e2c19b4 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java @@ -9,7 +9,6 @@ import java.text.ParsePosition; import org.junit.Test; import com.ibm.icu.dev.test.TestUtil; -import com.ibm.icu.dev.text.DecimalFormat_ICU58; import com.ibm.icu.impl.number.DecimalFormatProperties; import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode; import com.ibm.icu.impl.number.Padder.PadPosition; @@ -290,185 +289,6 @@ public class NumberFormatDataDrivenTest { } }; - /** - * Backwards-compatibility test: snapshot of DecimalFormat from ICU 58. - */ - private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU58 = new DataDrivenNumberFormatTestUtility.CodeUnderTest() { - @Override - public Character Id() { - return 'H'; - } - - @Override - public String format(DataDrivenNumberFormatTestData tuple) { - DecimalFormat_ICU58 fmt = createDecimalFormat(tuple); - String actual = fmt.format(toNumber(tuple.format)); - String expected = tuple.output; - if (!expected.equals(actual)) { - return "Expected " + expected + ", got " + actual; - } - return null; - } - - @Override - public String toPattern(DataDrivenNumberFormatTestData tuple) { - DecimalFormat_ICU58 fmt = createDecimalFormat(tuple); - StringBuilder result = new StringBuilder(); - if (tuple.toPattern != null) { - String expected = tuple.toPattern; - String actual = fmt.toPattern(); - if (!expected.equals(actual)) { - result.append("Expected toPattern=" + expected + ", got " + actual); - } - } - if (tuple.toLocalizedPattern != null) { - String expected = tuple.toLocalizedPattern; - String actual = fmt.toLocalizedPattern(); - if (!expected.equals(actual)) { - result.append("Expected toLocalizedPattern=" + expected + ", got " + actual); - } - } - return result.length() == 0 ? null : result.toString(); - } - - @Override - public String parse(DataDrivenNumberFormatTestData tuple) { - DecimalFormat_ICU58 fmt = createDecimalFormat(tuple); - ParsePosition ppos = new ParsePosition(0); - Number actual = fmt.parse(tuple.parse, ppos); - return compareParseResult(tuple.output, actual, ppos); - } - - @Override - public String parseCurrency(DataDrivenNumberFormatTestData tuple) { - DecimalFormat_ICU58 fmt = createDecimalFormat(tuple); - ParsePosition ppos = new ParsePosition(0); - CurrencyAmount actual = fmt.parseCurrency(tuple.parse, ppos); - return compareParseCurrencyResult(tuple.output, tuple.outputCurrency, actual, ppos); - } - - /** - * @param tuple - * @return - */ - private DecimalFormat_ICU58 createDecimalFormat(DataDrivenNumberFormatTestData tuple) { - - DecimalFormat_ICU58 fmt = new DecimalFormat_ICU58( - tuple.pattern == null ? "0" : tuple.pattern, - new DecimalFormatSymbols(tuple.locale == null ? EN : tuple.locale)); - adjustDecimalFormat(tuple, fmt); - return fmt; - } - - /** - * @param tuple - * @param fmt - */ - private void adjustDecimalFormat(DataDrivenNumberFormatTestData tuple, DecimalFormat_ICU58 fmt) { - if (tuple.minIntegerDigits != null) { - fmt.setMinimumIntegerDigits(tuple.minIntegerDigits); - } - if (tuple.maxIntegerDigits != null) { - fmt.setMaximumIntegerDigits(tuple.maxIntegerDigits); - } - if (tuple.minFractionDigits != null) { - fmt.setMinimumFractionDigits(tuple.minFractionDigits); - } - if (tuple.maxFractionDigits != null) { - fmt.setMaximumFractionDigits(tuple.maxFractionDigits); - } - if (tuple.currency != null) { - fmt.setCurrency(tuple.currency); - } - if (tuple.minGroupingDigits != null) { - // Oops we don't support this. - } - if (tuple.useSigDigits != null) { - fmt.setSignificantDigitsUsed(tuple.useSigDigits != 0); - } - if (tuple.minSigDigits != null) { - fmt.setMinimumSignificantDigits(tuple.minSigDigits); - } - if (tuple.maxSigDigits != null) { - fmt.setMaximumSignificantDigits(tuple.maxSigDigits); - } - if (tuple.useGrouping != null) { - fmt.setGroupingUsed(tuple.useGrouping != 0); - } - if (tuple.multiplier != null) { - fmt.setMultiplier(tuple.multiplier); - } - if (tuple.roundingIncrement != null) { - fmt.setRoundingIncrement(tuple.roundingIncrement.doubleValue()); - } - if (tuple.formatWidth != null) { - fmt.setFormatWidth(tuple.formatWidth); - } - if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) { - fmt.setPadCharacter(tuple.padCharacter.charAt(0)); - } - if (tuple.useScientific != null) { - fmt.setScientificNotation(tuple.useScientific != 0); - } - if (tuple.grouping != null) { - fmt.setGroupingSize(tuple.grouping); - } - if (tuple.grouping2 != null) { - fmt.setSecondaryGroupingSize(tuple.grouping2); - } - if (tuple.roundingMode != null) { - fmt.setRoundingMode(tuple.roundingMode); - } - if (tuple.currencyUsage != null) { - fmt.setCurrencyUsage(tuple.currencyUsage); - } - if (tuple.minimumExponentDigits != null) { - fmt.setMinimumExponentDigits(tuple.minimumExponentDigits.byteValue()); - } - if (tuple.exponentSignAlwaysShown != null) { - fmt.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0); - } - if (tuple.decimalSeparatorAlwaysShown != null) { - fmt.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0); - } - if (tuple.padPosition != null) { - fmt.setPadPosition(tuple.padPosition); - } - if (tuple.positivePrefix != null) { - fmt.setPositivePrefix(tuple.positivePrefix); - } - if (tuple.positiveSuffix != null) { - fmt.setPositiveSuffix(tuple.positiveSuffix); - } - if (tuple.negativePrefix != null) { - fmt.setNegativePrefix(tuple.negativePrefix); - } - if (tuple.negativeSuffix != null) { - fmt.setNegativeSuffix(tuple.negativeSuffix); - } - if (tuple.signAlwaysShown != null) { - // Not supported. - } - if (tuple.localizedPattern != null) { - fmt.applyLocalizedPattern(tuple.localizedPattern); - } - int lenient = tuple.lenient == null ? 1 : tuple.lenient.intValue(); - fmt.setParseStrict(lenient == 0); - if (tuple.parseIntegerOnly != null) { - fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0); - } - if (tuple.parseCaseSensitive != null) { - // Not supported. - } - if (tuple.decimalPatternMatchRequired != null) { - fmt.setDecimalPatternMatchRequired(tuple.decimalPatternMatchRequired != 0); - } - if (tuple.parseNoExponent != null) { - // Oops, not supported for now - } - } - }; - /** * Test of available JDK APIs. */ @@ -893,16 +713,6 @@ public class NumberFormatDataDrivenTest { .runFormatSuiteIncludingKnownFailures("numberformattestspecification.txt", ICU4J); } - @Test - public void TestDataDrivenICU58() { - // Android can't access DecimalFormat_ICU58 for testing (ticket #13283). - if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android) - return; - - DataDrivenNumberFormatTestUtility - .runFormatSuiteIncludingKnownFailures("numberformattestspecification.txt", ICU58); - } - @Test public void TestDataDrivenJDK() { // #13373: Since not all JDK implementations are the same, test only whitelisted JDKs diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java index dcbf19d8129..1bc00e2c9a2 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java @@ -43,7 +43,6 @@ import org.junit.runners.JUnit4; import com.ibm.icu.dev.test.TestFmwk; import com.ibm.icu.dev.test.TestUtil; import com.ibm.icu.dev.test.format.IntlTestDecimalFormatAPIC.FieldContainer; -import com.ibm.icu.dev.text.DecimalFormat_ICU58; import com.ibm.icu.impl.ICUConfig; import com.ibm.icu.impl.LocaleUtility; import com.ibm.icu.impl.data.ResourceReader; @@ -1701,9 +1700,7 @@ public class NumberFormatTest extends TestFmwk { @Test public void TestLocalizedPatternSymbolCoverage() { String[] standardPatterns = { "#,##0.05+%;#,##0.05-%", "* @@@E0‰" }; - String[] standardPatterns58 = { "#,##0.05+%;#,##0.05-%", "* @@@E0‰;* -@@@E0‰" }; String[] localizedPatterns = { "▰⁖▰▰໐⁘໐໕†⁜⁙▰⁖▰▰໐⁘໐໕‡⁜", "⁂ ⁕⁕⁕⁑⁑໐‱" }; - String[] localizedPatterns58 = { "▰⁖▰▰໐⁘໐໕+⁜⁙▰⁖▰▰໐⁘໐໕‡⁜", "⁂ ⁕⁕⁕⁑⁑໐‱⁙⁂ ‡⁕⁕⁕⁑⁑໐‱" }; DecimalFormatSymbols dfs = new DecimalFormatSymbols(); dfs.setGroupingSeparator('⁖'); @@ -1721,9 +1718,7 @@ public class NumberFormatTest extends TestFmwk { for (int i=0; i<2; i++) { String standardPattern = standardPatterns[i]; - String standardPattern58 = standardPatterns58[i]; String localizedPattern = localizedPatterns[i]; - String localizedPattern58 = localizedPatterns58[i]; DecimalFormat df1 = new DecimalFormat("#", dfs); df1.applyPattern(standardPattern); @@ -1735,22 +1730,6 @@ public class NumberFormatTest extends TestFmwk { standardPattern, df2.toPattern()); assertEquals("toLocalizedPattern should match on standardPattern instance", localizedPattern, df1.toLocalizedPattern()); - - // Android can't access DecimalFormat_ICU58 for testing (ticket #13283). - if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android) continue; - - // Note: ICU 58 does not support plus signs in patterns - // Note: ICU 58 always prints the negative part of scientific notation patterns, - // even when the negative part is not necessary - DecimalFormat_ICU58 df3 = new DecimalFormat_ICU58("#", dfs); - df3.applyPattern(standardPattern); // Reading standardPattern is OK - DecimalFormat_ICU58 df4 = new DecimalFormat_ICU58("#", dfs); - df4.applyLocalizedPattern(localizedPattern58); - // Note: DecimalFormat#equals() is broken on ICU 58 - assertEquals("toPattern should match on ICU58 localizedPattern instance", - standardPattern58, df4.toPattern()); - assertEquals("toLocalizedPattern should match on ICU58 standardPattern instance", - localizedPattern58, df3.toLocalizedPattern()); } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java deleted file mode 100644 index 4d81883d8ad..00000000000 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DecimalFormat_ICU58.java +++ /dev/null @@ -1,6286 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -/* - ******************************************************************************* - * Copyright (C) 1996-2016, International Business Machines Corporation and - * others. All Rights Reserved. - ******************************************************************************* - */ -package com.ibm.icu.dev.text; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.math.BigInteger; -import java.text.AttributedCharacterIterator; -import java.text.AttributedString; -import java.text.ChoiceFormat; -import java.text.FieldPosition; -import java.text.Format; -import java.text.ParsePosition; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Objects; -import java.util.Set; - -import com.ibm.icu.impl.ICUConfig; -import com.ibm.icu.impl.PatternProps; -import com.ibm.icu.lang.UCharacter; -import com.ibm.icu.math.BigDecimal; -import com.ibm.icu.math.MathContext; -import com.ibm.icu.text.CurrencyPluralInfo; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.NumberFormat; -import com.ibm.icu.text.PluralRules.FixedDecimal; -import com.ibm.icu.text.UFieldPosition; -import com.ibm.icu.text.UTF16; -import com.ibm.icu.text.UnicodeSet; -import com.ibm.icu.util.Currency; -import com.ibm.icu.util.Currency.CurrencyUsage; -import com.ibm.icu.util.CurrencyAmount; -import com.ibm.icu.util.ULocale; -import com.ibm.icu.util.ULocale.Category; - -/** - * {@icuenhanced java.text.DecimalFormat}.{@icu _usage_} - * - * DecimalFormat is a concrete subclass of {@link NumberFormat} that formats - * decimal numbers. It has a variety of features designed to make it possible to parse and - * format numbers in any locale, including support for Western, Arabic, or Indic digits. - * It also supports different flavors of numbers, including integers ("123"), fixed-point - * numbers ("123.4"), scientific notation ("1.23E4"), percentages ("12%"), and currency - * amounts ("$123.00", "USD123.00", "123.00 US dollars"). All of these flavors can be - * easily localized. - * - *

To obtain a {@link NumberFormat} for a specific locale (including the default - * locale) call one of NumberFormat's factory methods such as {@link - * NumberFormat#getInstance}. Do not call the DecimalFormat constructors - * directly, unless you know what you are doing, since the {@link NumberFormat} factory - * methods may return subclasses other than DecimalFormat. If you need to - * customize the format object, do something like this: - * - *

- * NumberFormat f = NumberFormat.getInstance(loc);
- * if (f instanceof DecimalFormat) {
- *     ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true);
- * }
- * - *

Example Usage - * - * Print out a number using the localized number, currency, and percent - * format for each locale. - * - *

- * Locale[] locales = NumberFormat.getAvailableLocales();
- * double myNumber = -1234.56;
- * NumberFormat format;
- * for (int j=0; j<3; ++j) {
- *     System.out.println("FORMAT");
- *     for (int i = 0; i < locales.length; ++i) {
- *         if (locales[i].getCountry().length() == 0) {
- *            // Skip language-only locales
- *            continue;
- *         }
- *         System.out.print(locales[i].getDisplayName());
- *         switch (j) {
- *         case 0:
- *             format = NumberFormat.getInstance(locales[i]); break;
- *         case 1:
- *             format = NumberFormat.getCurrencyInstance(locales[i]); break;
- *         default:
- *             format = NumberFormat.getPercentInstance(locales[i]); break;
- *         }
- *         try {
- *             // Assume format is a DecimalFormat
- *             System.out.print(": " + ((DecimalFormat) format).toPattern()
- *                              + " -> " + form.format(myNumber));
- *         } catch (Exception e) {}
- *         try {
- *             System.out.println(" -> " + format.parse(form.format(myNumber)));
- *         } catch (ParseException e) {}
- *     }
- * }
- * - *

Another example use getInstance(style).
- * Print out a number using the localized number, currency, percent, - * scientific, integer, iso currency, and plural currency format for each locale. - * - *

- * ULocale locale = new ULocale("en_US");
- * double myNumber = 1234.56;
- * for (int j=NumberFormat.NUMBERSTYLE; j<=NumberFormat.PLURALCURRENCYSTYLE; ++j) {
- *     NumberFormat format = NumberFormat.getInstance(locale, j);
- *     try {
- *         // Assume format is a DecimalFormat
- *         System.out.print(": " + ((DecimalFormat) format).toPattern()
- *                          + " -> " + form.format(myNumber));
- *     } catch (Exception e) {}
- *     try {
- *         System.out.println(" -> " + format.parse(form.format(myNumber)));
- *     } catch (ParseException e) {}
- * }
- * - *

Patterns

- * - *

A DecimalFormat consists of a pattern and a set of - * symbols. The pattern may be set directly using {@link #applyPattern}, or - * indirectly using other API methods which manipulate aspects of the pattern, such as the - * minimum number of integer digits. The symbols are stored in a {@link - * DecimalFormatSymbols} object. When using the {@link NumberFormat} factory methods, the - * pattern and symbols are read from ICU's locale data. - * - *

Special Pattern Characters

- * - *

Many characters in a pattern are taken literally; they are matched during parsing - * and output unchanged during formatting. Special characters, on the other hand, stand - * for other characters, strings, or classes of characters. For example, the '#' - * character is replaced by a localized digit. Often the replacement character is the - * same as the pattern character; in the U.S. locale, the ',' grouping character is - * replaced by ','. However, the replacement is still happening, and if the symbols are - * modified, the grouping character changes. Some special characters affect the behavior - * of the formatter by their presence; for example, if the percent character is seen, then - * the value is multiplied by 100 before being displayed. - * - *

To insert a special character in a pattern as a literal, that is, without any - * special meaning, the character must be quoted. There are some exceptions to this which - * are noted below. - * - *

The characters listed here are used in non-localized patterns. Localized patterns - * use the corresponding characters taken from this formatter's {@link - * DecimalFormatSymbols} object instead, and these characters lose their special status. - * Two exceptions are the currency sign and quote, which are not localized. - * - *

- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Symbol - * Location - * Localized? - * Meaning - *
0 - * Number - * Yes - * Digit - *
1-9 - * Number - * Yes - * '1' through '9' indicate rounding. - *
@ - * Number - * No - * Significant digit - *
# - * Number - * Yes - * Digit, zero shows as absent - *
. - * Number - * Yes - * Decimal separator or monetary decimal separator - *
- - * Number - * Yes - * Minus sign - *
, - * Number - * Yes - * Grouping separator - *
E - * Number - * Yes - * Separates mantissa and exponent in scientific notation. - * Need not be quoted in prefix or suffix. - *
+ - * Exponent - * Yes - * Prefix positive exponents with localized plus sign. - * Need not be quoted in prefix or suffix. - *
; - * Subpattern boundary - * Yes - * Separates positive and negative subpatterns - *
% - * Prefix or suffix - * Yes - * Multiply by 100 and show as percentage - *
\u2030 - * Prefix or suffix - * Yes - * Multiply by 1000 and show as per mille - *
¤ (\u00A4) - * Prefix or suffix - * No - * Currency sign, replaced by currency symbol. If - * doubled, replaced by international currency symbol. - * If tripled, replaced by currency plural names, for example, - * "US dollar" or "US dollars" for America. - * If present in a pattern, the monetary decimal separator - * is used instead of the decimal separator. - *
' - * Prefix or suffix - * No - * Used to quote special characters in a prefix or suffix, - * for example, "'#'#" formats 123 to - * "#123". To create a single quote - * itself, use two in a row: "# o''clock". - *
* - * Prefix or suffix boundary - * Yes - * Pad escape, precedes pad character - *
- *
- * - *

A DecimalFormat pattern contains a postive and negative subpattern, for - * example, "#,##0.00;(#,##0.00)". Each subpattern has a prefix, a numeric part, and a - * suffix. If there is no explicit negative subpattern, the negative subpattern is the - * localized minus sign prefixed to the positive subpattern. That is, "0.00" alone is - * equivalent to "0.00;-0.00". If there is an explicit negative subpattern, it serves - * only to specify the negative prefix and suffix; the number of digits, minimal digits, - * and other characteristics are ignored in the negative subpattern. That means that - * "#,##0.0#;(#)" has precisely the same result as "#,##0.0#;(#,##0.0#)". - * - *

The prefixes, suffixes, and various symbols used for infinity, digits, thousands - * separators, decimal separators, etc. may be set to arbitrary values, and they will - * appear properly during formatting. However, care must be taken that the symbols and - * strings do not conflict, or parsing will be unreliable. For example, either the - * positive and negative prefixes or the suffixes must be distinct for {@link #parse} to - * be able to distinguish positive from negative values. Another example is that the - * decimal separator and thousands separator should be distinct characters, or parsing - * will be impossible. - * - *

The grouping separator is a character that separates clusters of integer - * digits to make large numbers more legible. It commonly used for thousands, but in some - * locales it separates ten-thousands. The grouping size is the number of digits - * between the grouping separators, such as 3 for "100,000,000" or 4 for "1 0000 - * 0000". There are actually two different grouping sizes: One used for the least - * significant integer digits, the primary grouping size, and one used for all - * others, the secondary grouping size. In most locales these are the same, but - * sometimes they are different. For example, if the primary grouping interval is 3, and - * the secondary is 2, then this corresponds to the pattern "#,##,##0", and the number - * 123456789 is formatted as "12,34,56,789". If a pattern contains multiple grouping - * separators, the interval between the last one and the end of the integer defines the - * primary grouping size, and the interval between the last two defines the secondary - * grouping size. All others are ignored, so "#,##,###,####" == "###,###,####" == - * "##,#,###,####". - * - *

Illegal patterns, such as "#.#.#" or "#.###,###", will cause - * DecimalFormat to throw an {@link IllegalArgumentException} with a message - * that describes the problem. - * - *

Pattern BNF

- * - *
- * pattern    := subpattern (';' subpattern)?
- * subpattern := prefix? number exponent? suffix?
- * number     := (integer ('.' fraction)?) | sigDigits
- * prefix     := '\u0000'..'\uFFFD' - specialCharacters
- * suffix     := '\u0000'..'\uFFFD' - specialCharacters
- * integer    := '#'* '0'* '0'
- * fraction   := '0'* '#'*
- * sigDigits  := '#'* '@' '@'* '#'*
- * exponent   := 'E' '+'? '0'* '0'
- * padSpec    := '*' padChar
- * padChar    := '\u0000'..'\uFFFD' - quote
- *  
- * Notation:
- *   X*       0 or more instances of X
- *   X?       0 or 1 instances of X
- *   X|Y      either X or Y
- *   C..D     any character from C up to D, inclusive
- *   S-T      characters in S, except those in T
- * 
- * The first subpattern is for positive numbers. The second (optional) - * subpattern is for negative numbers. - * - *

Not indicated in the BNF syntax above: - * - *

- * - *

Parsing

- * - *

DecimalFormat parses all Unicode characters that represent decimal - * digits, as defined by {@link UCharacter#digit}. In addition, - * DecimalFormat also recognizes as digits the ten consecutive characters - * starting with the localized zero digit defined in the {@link DecimalFormatSymbols} - * object. During formatting, the {@link DecimalFormatSymbols}-based digits are output. - * - *

During parsing, grouping separators are ignored. - * - *

For currency parsing, the formatter is able to parse every currency style formats no - * matter which style the formatter is constructed with. For example, a formatter - * instance gotten from NumberFormat.getInstance(ULocale, NumberFormat.CURRENCYSTYLE) can - * parse formats such as "USD1.00" and "3.00 US dollars". - * - *

If {@link #parse(String, ParsePosition)} fails to parse a string, it returns - * null and leaves the parse position unchanged. The convenience method - * {@link #parse(String)} indicates parse failure by throwing a {@link - * java.text.ParseException}. - * - *

Parsing an extremely large or small absolute value (such as 1.0E10000 or 1.0E-10000) - * requires huge memory allocation for representing the parsed number. Such input may expose - * a risk of DoS attacks. To prevent huge memory allocation triggered by such inputs, - * DecimalFormat internally limits of maximum decimal digits to be 1000. Thus, - * an input string resulting more than 1000 digits in plain decimal representation (non-exponent) - * will be treated as either overflow (positive/negative infinite) or underflow (+0.0/-0.0). - * - *

Formatting

- * - *

Formatting is guided by several parameters, all of which can be specified either - * using a pattern or using the API. The following description applies to formats that do - * not use scientific notation or significant - * digits. - * - *

- * - *

Special Values - * - *

NaN is represented as a single character, typically - * \uFFFD. This character is determined by the {@link - * DecimalFormatSymbols} object. This is the only value for which the prefixes and - * suffixes are not used. - * - *

Infinity is represented as a single character, typically \u221E, - * with the positive or negative prefixes and suffixes applied. The infinity character is - * determined by the {@link DecimalFormatSymbols} object. - * - *

Scientific Notation

- * - *

Numbers in scientific notation are expressed as the product of a mantissa and a - * power of ten, for example, 1234 can be expressed as 1.234 x 103. The - * mantissa is typically in the half-open interval [1.0, 10.0) or sometimes [0.0, 1.0), - * but it need not be. DecimalFormat supports arbitrary mantissas. - * DecimalFormat can be instructed to use scientific notation through the API - * or through the pattern. In a pattern, the exponent character immediately followed by - * one or more digit characters indicates scientific notation. Example: "0.###E0" formats - * the number 1234 as "1.234E3". - * - *

- * - *

Significant Digits

- * - * DecimalFormat has two ways of controlling how many digits are shows: (a) - * significant digits counts, or (b) integer and fraction digit counts. Integer and - * fraction digit counts are described above. When a formatter is using significant - * digits counts, the number of integer and fraction digits is not specified directly, and - * the formatter settings for these counts are ignored. Instead, the formatter uses - * however many integer and fraction digits are required to display the specified number - * of significant digits. Examples: - * - *
- * - * - * - * - * - * - *
Pattern - * Minimum significant digits - * Maximum significant digits - * Number - * Output of format() - *
@@@ - * 3 - * 3 - * 12345 - * 12300 - *
@@@ - * 3 - * 3 - * 0.12345 - * 0.123 - *
@@## - * 2 - * 4 - * 3.14159 - * 3.142 - *
@@## - * 2 - * 4 - * 1.23004 - * 1.23 - *
- *
- * - * - * - *

Padding

- * - *

DecimalFormat supports padding the result of {@link #format} to a - * specific width. Padding may be specified either through the API or through the pattern - * syntax. In a pattern the pad escape character, followed by a single pad character, - * causes padding to be parsed and formatted. The pad escape character is '*' in - * unlocalized patterns, and can be localized using {@link - * DecimalFormatSymbols#setPadEscape}. For example, "$*x#,##0.00" formats - * 123 to "$xx123.00", and 1234 to "$1,234.00". - * - *

- * - *

- * Rounding - * - *

DecimalFormat supports rounding to a specific increment. For example, - * 1230 rounded to the nearest 50 is 1250. 1.234 rounded to the nearest 0.65 is 1.3. The - * rounding increment may be specified through the API or in a pattern. To specify a - * rounding increment in a pattern, include the increment in the pattern itself. "#,#50" - * specifies a rounding increment of 50. "#,##0.05" specifies a rounding increment of - * 0.05. - * - *

- * - *

Synchronization

- * - *

DecimalFormat objects are not synchronized. Multiple threads should - * not access one formatter concurrently. - * - * @see java.text.Format - * @see NumberFormat - * @author Mark Davis - * @author Alan Liu - * @deprecated DecimalFormat was overhauled in ICU 59. This is the old implementation, provided - * temporarily to ease the transition. This class will be removed from ICU 60. - */ -@Deprecated -public class DecimalFormat_ICU58 extends NumberFormat { - - /** - * Creates a DecimalFormat using the default pattern and symbols for the default - * FORMAT locale. This is a convenient way to obtain a DecimalFormat when - * internationalization is not the main concern. - * - *

To obtain standard formats for a given locale, use the factory methods on - * NumberFormat such as getNumberInstance. These factories will return the most - * appropriate sub-class of NumberFormat for a given locale. - * - * @see NumberFormat#getInstance - * @see NumberFormat#getNumberInstance - * @see NumberFormat#getCurrencyInstance - * @see NumberFormat#getPercentInstance - * @see Category#FORMAT - * @stable ICU 2.0 - */ - public DecimalFormat_ICU58() { - ULocale def = ULocale.getDefault(Category.FORMAT); - String pattern = getPattern(def, 0); - // Always applyPattern after the symbols are set - this.symbols = new DecimalFormatSymbols(def); - setCurrency(Currency.getInstance(def)); - applyPatternWithoutExpandAffix(pattern, false); - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - currencyPluralInfo = new CurrencyPluralInfo(def); - // the exact pattern is not known until the plural count is known. - // so, no need to expand affix now. - } else { - expandAffixAdjustWidth(null); - } - } - - /** - * Creates a DecimalFormat from the given pattern and the symbols for the default - * FORMAT locale. This is a convenient way to obtain a DecimalFormat when - * internationalization is not the main concern. - * - *

To obtain standard formats for a given locale, use the factory methods on - * NumberFormat such as getNumberInstance. These factories will return the most - * appropriate sub-class of NumberFormat for a given locale. - * - * @param pattern A non-localized pattern string. - * @throws IllegalArgumentException if the given pattern is invalid. - * @see NumberFormat#getInstance - * @see NumberFormat#getNumberInstance - * @see NumberFormat#getCurrencyInstance - * @see NumberFormat#getPercentInstance - * @see Category#FORMAT - * @stable ICU 2.0 - */ - public DecimalFormat_ICU58(String pattern) { - // Always applyPattern after the symbols are set - ULocale def = ULocale.getDefault(Category.FORMAT); - this.symbols = new DecimalFormatSymbols(def); - setCurrency(Currency.getInstance(def)); - applyPatternWithoutExpandAffix(pattern, false); - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - currencyPluralInfo = new CurrencyPluralInfo(def); - } else { - expandAffixAdjustWidth(null); - } - } - - /** - * Creates a DecimalFormat from the given pattern and symbols. Use this constructor - * when you need to completely customize the behavior of the format. - * - *

To obtain standard formats for a given locale, use the factory methods on - * NumberFormat such as getInstance or getCurrencyInstance. If you need only minor - * adjustments to a standard format, you can modify the format returned by a - * NumberFormat factory method. - * - * @param pattern a non-localized pattern string - * @param symbols the set of symbols to be used - * @exception IllegalArgumentException if the given pattern is invalid - * @see NumberFormat#getInstance - * @see NumberFormat#getNumberInstance - * @see NumberFormat#getCurrencyInstance - * @see NumberFormat#getPercentInstance - * @see DecimalFormatSymbols - * @stable ICU 2.0 - */ - public DecimalFormat_ICU58(String pattern, DecimalFormatSymbols symbols) { - createFromPatternAndSymbols(pattern, symbols); - } - - private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols inputSymbols) { - // Always applyPattern after the symbols are set - symbols = (DecimalFormatSymbols) inputSymbols.clone(); - if (pattern.indexOf(CURRENCY_SIGN) >= 0) { - // Only spend time with currency symbols when we're going to display it. - // Also set some defaults before the apply pattern. - setCurrencyForSymbols(); - } - applyPatternWithoutExpandAffix(pattern, false); - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale()); - } else { - expandAffixAdjustWidth(null); - } - } - - /** - * Creates a DecimalFormat from the given pattern, symbols, information used for - * currency plural format, and format style. Use this constructor when you need to - * completely customize the behavior of the format. - * - *

To obtain standard formats for a given locale, use the factory methods on - * NumberFormat such as getInstance or getCurrencyInstance. - * - *

If you need only minor adjustments to a standard format, you can modify the - * format returned by a NumberFormat factory method using the setters. - * - *

If you want to completely customize a decimal format, using your own - * DecimalFormatSymbols (such as group separators) and your own information for - * currency plural formatting (such as plural rule and currency plural patterns), you - * can use this constructor. - * - * @param pattern a non-localized pattern string - * @param symbols the set of symbols to be used - * @param infoInput the information used for currency plural format, including - * currency plural patterns and plural rules. - * @param style the decimal formatting style, it is one of the following values: - * NumberFormat.NUMBERSTYLE; NumberFormat.CURRENCYSTYLE; NumberFormat.PERCENTSTYLE; - * NumberFormat.SCIENTIFICSTYLE; NumberFormat.INTEGERSTYLE; - * NumberFormat.ISOCURRENCYSTYLE; NumberFormat.PLURALCURRENCYSTYLE; - * @stable ICU 4.2 - */ - public DecimalFormat_ICU58(String pattern, DecimalFormatSymbols symbols, CurrencyPluralInfo infoInput, - int style) { - CurrencyPluralInfo info = infoInput; - if (style == NumberFormat.PLURALCURRENCYSTYLE) { - info = (CurrencyPluralInfo) infoInput.clone(); - } - create(pattern, symbols, info, style); - } - - private void create(String pattern, DecimalFormatSymbols inputSymbols, CurrencyPluralInfo info, - int inputStyle) { - if (inputStyle != NumberFormat.PLURALCURRENCYSTYLE) { - createFromPatternAndSymbols(pattern, inputSymbols); - } else { - // Always applyPattern after the symbols are set - symbols = (DecimalFormatSymbols) inputSymbols.clone(); - currencyPluralInfo = info; - // the pattern used in format is not fixed until formatting, in which, the - // number is known and will be used to pick the right pattern based on plural - // count. Here, set the pattern as the pattern of plural count == "other". - // For most locale, the patterns are probably the same for all plural - // count. If not, the right pattern need to be re-applied during format. - String currencyPluralPatternForOther = - currencyPluralInfo.getCurrencyPluralPattern("other"); - applyPatternWithoutExpandAffix(currencyPluralPatternForOther, false); - setCurrencyForSymbols(); - } - style = inputStyle; - } - - /** - * Creates a DecimalFormat for currency plural format from the given pattern, symbols, - * and style. - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - public DecimalFormat_ICU58(String pattern, DecimalFormatSymbols inputSymbols, int style) { - CurrencyPluralInfo info = null; - if (style == NumberFormat.PLURALCURRENCYSTYLE) { - info = new CurrencyPluralInfo(inputSymbols.getULocale()); - } - create(pattern, inputSymbols, info, style); - } - - /** - * {@inheritDoc} - * @stable ICU 2.0 - */ - @Override - public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) { - return format(number, result, fieldPosition, false); - } - - // See if number is negative. - // usage: isNegative(multiply(numberToBeFormatted)); - private boolean isNegative(double number) { - // Detecting whether a double is negative is easy with the exception of the value - // -0.0. This is a double which has a zero mantissa (and exponent), but a negative - // sign bit. It is semantically distinct from a zero with a positive sign bit, and - // this distinction is important to certain kinds of computations. However, it's a - // little tricky to detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you - // may ask, does it behave distinctly from +0.0? Well, 1/(-0.0) == - // -Infinity. Proper detection of -0.0 is needed to deal with the issues raised by - // bugs 4106658, 4106667, and 4147706. Liu 7/6/98. - return (number < 0.0) || (number == 0.0 && 1 / number < 0.0); - } - - // Rounds the number and strips of the negative sign. - // usage: round(multiply(numberToBeFormatted)) - private double round(double number) { - boolean isNegative = isNegative(number); - if (isNegative) - number = -number; - - // Apply rounding after multiplier - if (roundingDouble > 0.0) { - // number = roundingDouble - // * round(number / roundingDouble, roundingMode, isNegative); - return round( - number, roundingDouble, roundingDoubleReciprocal, roundingMode, - isNegative); - } - return number; - } - - // Multiplies given number by multipler (if there is one) returning the new - // number. If there is no multiplier, returns the number passed in unchanged. - private double multiply(double number) { - if (multiplier != 1) { - return number * multiplier; - } - return number; - } - - // [Spark/CDL] The actual method to format number. If boolean value - // parseAttr == true, then attribute information will be recorded. - private StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition, - boolean parseAttr) { - fieldPosition.setBeginIndex(0); - fieldPosition.setEndIndex(0); - - if (Double.isNaN(number)) { - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setBeginIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setBeginIndex(result.length()); - } - - result.append(symbols.getNaN()); - // TODO: Combine setting a single FieldPosition or adding to an AttributedCharacterIterator - // into a function like recordAttribute(FieldAttribute, begin, end). - - // [Spark/CDL] Add attribute for NaN here. - // result.append(symbols.getNaN()); - if (parseAttr) { - addAttribute(Field.INTEGER, result.length() - symbols.getNaN().length(), - result.length()); - } - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setEndIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setEndIndex(result.length()); - } - - addPadding(result, fieldPosition, 0, 0); - return result; - } - - // Do this BEFORE checking to see if value is negative or infinite and - // before rounding. - number = multiply(number); - boolean isNegative = isNegative(number); - number = round(number); - - if (Double.isInfinite(number)) { - int prefixLen = appendAffix(result, isNegative, true, fieldPosition, parseAttr); - - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setBeginIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setBeginIndex(result.length()); - } - - // [Spark/CDL] Add attribute for infinity here. - result.append(symbols.getInfinity()); - if (parseAttr) { - addAttribute(Field.INTEGER, result.length() - symbols.getInfinity().length(), - result.length()); - } - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setEndIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setEndIndex(result.length()); - } - - int suffixLen = appendAffix(result, isNegative, false, fieldPosition, parseAttr); - - addPadding(result, fieldPosition, prefixLen, suffixLen); - return result; - } - - int precision = precision(false); - - // This is to fix rounding for scientific notation. See ticket:10542. - // This code should go away when a permanent fix is done for ticket:9931. - // - // This block of code only executes for scientific notation so it will not interfere with the - // previous fix in {@link #resetActualRounding} for fixed decimal numbers. - // Moreover this code only runs when there is rounding to be done (precision > 0) and when the - // rounding mode is something other than ROUND_HALF_EVEN. - // This block of code does the correct rounding of number in advance so that it will fit into - // the number of digits indicated by precision. In this way, we avoid using the default - // ROUND_HALF_EVEN behavior of DigitList. For example, if number = 0.003016 and roundingMode = - // ROUND_DOWN and precision = 3 then after this code executes, number = 0.00301 (3 significant digits) - if (useExponentialNotation && precision > 0 && number != 0.0 && roundingMode != BigDecimal.ROUND_HALF_EVEN) { - int log10RoundingIncr = 1 - precision + (int) Math.floor(Math.log10(Math.abs(number))); - double roundingIncReciprocal = 0.0; - double roundingInc = 0.0; - if (log10RoundingIncr < 0) { - roundingIncReciprocal = - BigDecimal.ONE.movePointRight(-log10RoundingIncr).doubleValue(); - } else { - roundingInc = - BigDecimal.ONE.movePointRight(log10RoundingIncr).doubleValue(); - } - number = DecimalFormat_ICU58.round(number, roundingInc, roundingIncReciprocal, roundingMode, isNegative); - } - // End fix for ticket:10542 - - // At this point we are guaranteed a nonnegative finite - // number. - synchronized (digitList) { - digitList.set(number, precision, !useExponentialNotation && - !areSignificantDigitsUsed()); - return subformat(number, result, fieldPosition, isNegative, false, parseAttr); - } - } - - /** - * This is a special function used by the CompactDecimalFormat subclass. - * It completes only the rounding portion of the formatting and returns - * the resulting double. CompactDecimalFormat uses the result to compute - * the plural form to use. - * - * @param number The number to format. - * @return The number rounded to the correct number of significant digits - * with negative sign stripped off. - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - double adjustNumberAsInFormatting(double number) { - if (Double.isNaN(number)) { - return number; - } - number = round(multiply(number)); - if (Double.isInfinite(number)) { - return number; - } - return toDigitList(number).getDouble(); - } - - @Deprecated - DigitList toDigitList(double number) { - DigitList result = new DigitList(); - result.set(number, precision(false), false); - return result; - } - - /** - * This is a special function used by the CompactDecimalFormat subclass - * to determine if the number to be formatted is negative. - * - * @param number The number to format. - * @return True if number is negative. - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - boolean isNumberNegative(double number) { - if (Double.isNaN(number)) { - return false; - } - return isNegative(multiply(number)); - } - - /** - * Round a double value to the nearest multiple of the given rounding increment, - * according to the given mode. This is equivalent to rounding value/roundingInc to - * the nearest integer, according to the given mode, and returning that integer * - * roundingInc. Note this is changed from the version in 2.4, since division of - * doubles have inaccuracies. jitterbug 1871. - * - * @param number - * the absolute value of the number to be rounded - * @param roundingInc - * the rounding increment - * @param roundingIncReciprocal - * if non-zero, is the reciprocal of rounding inc. - * @param mode - * a BigDecimal rounding mode - * @param isNegative - * true if the number to be rounded is negative - * @return the absolute value of the rounded result - */ - private static double round(double number, double roundingInc, double roundingIncReciprocal, - int mode, boolean isNegative) { - - double div = roundingIncReciprocal == 0.0 ? number / roundingInc : number * - roundingIncReciprocal; - - // do the absolute cases first - - switch (mode) { - case BigDecimal.ROUND_CEILING: - div = (isNegative ? Math.floor(div + epsilon) : Math.ceil(div - epsilon)); - break; - case BigDecimal.ROUND_FLOOR: - div = (isNegative ? Math.ceil(div - epsilon) : Math.floor(div + epsilon)); - break; - case BigDecimal.ROUND_DOWN: - div = (Math.floor(div + epsilon)); - break; - case BigDecimal.ROUND_UP: - div = (Math.ceil(div - epsilon)); - break; - case BigDecimal.ROUND_UNNECESSARY: - if (div != Math.floor(div)) { - throw new ArithmeticException("Rounding necessary"); - } - return number; - default: - - // Handle complex cases, where the choice depends on the closer value. - - // We figure out the distances to the two possible values, ceiling and floor. - // We then go for the diff that is smaller. Only if they are equal does the - // mode matter. - - double ceil = Math.ceil(div); - double ceildiff = ceil - div; // (ceil * roundingInc) - number; - double floor = Math.floor(div); - double floordiff = div - floor; // number - (floor * roundingInc); - - // Note that the diff values were those mapped back to the "normal" space by - // using the roundingInc. I don't have access to the original author of the - // code but suspect that that was to produce better result in edge cases - // because of machine precision, rather than simply using the difference - // between, say, ceil and div. However, it didn't work in all cases. Am - // trying instead using an epsilon value. - - switch (mode) { - case BigDecimal.ROUND_HALF_EVEN: - // We should be able to just return Math.rint(a), but this - // doesn't work in some VMs. - // if one is smaller than the other, take the corresponding side - if (floordiff + epsilon < ceildiff) { - div = floor; - } else if (ceildiff + epsilon < floordiff) { - div = ceil; - } else { // they are equal, so we want to round to whichever is even - double testFloor = floor / 2; - div = (testFloor == Math.floor(testFloor)) ? floor : ceil; - } - break; - case BigDecimal.ROUND_HALF_DOWN: - div = ((floordiff <= ceildiff + epsilon) ? floor : ceil); - break; - case BigDecimal.ROUND_HALF_UP: - div = ((ceildiff <= floordiff + epsilon) ? ceil : floor); - break; - default: - throw new IllegalArgumentException("Invalid rounding mode: " + mode); - } - } - number = roundingIncReciprocal == 0.0 ? div * roundingInc : div / roundingIncReciprocal; - return number; - } - - private static double epsilon = 0.00000000001; - - /** - * @stable ICU 2.0 - */ - // [Spark/CDL] Delegate to format_long_StringBuffer_FieldPosition_boolean - @Override - public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) { - return format(number, result, fieldPosition, false); - } - - private StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition, - boolean parseAttr) { - fieldPosition.setBeginIndex(0); - fieldPosition.setEndIndex(0); - - // If we are to do rounding, we need to move into the BigDecimal - // domain in order to do divide/multiply correctly. - if (actualRoundingIncrementICU != null) { - return format(BigDecimal.valueOf(number), result, fieldPosition); - } - - boolean isNegative = (number < 0); - if (isNegative) - number = -number; - - // In general, long values always represent real finite numbers, so we don't have - // to check for +/- Infinity or NaN. However, there is one case we have to be - // careful of: The multiplier can push a number near MIN_VALUE or MAX_VALUE - // outside the legal range. We check for this before multiplying, and if it - // happens we use BigInteger instead. - if (multiplier != 1) { - boolean tooBig = false; - if (number < 0) { // This can only happen if number == Long.MIN_VALUE - long cutoff = Long.MIN_VALUE / multiplier; - tooBig = (number <= cutoff); // number == cutoff can only happen if multiplier == -1 - } else { - long cutoff = Long.MAX_VALUE / multiplier; - tooBig = (number > cutoff); - } - if (tooBig) { - // [Spark/CDL] Use - // format_BigInteger_StringBuffer_FieldPosition_boolean instead - // parseAttr is used to judge whether to synthesize attributes. - return format(BigInteger.valueOf(isNegative ? -number : number), result, - fieldPosition, parseAttr); - } - } - - number *= multiplier; - synchronized (digitList) { - digitList.set(number, precision(true)); - // Issue 11808 - if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) { - throw new ArithmeticException("Rounding necessary"); - } - return subformat(number, result, fieldPosition, isNegative, true, parseAttr); - } - } - - /** - * Formats a BigInteger number. - * - * @stable ICU 2.0 - */ - @Override - public StringBuffer format(BigInteger number, StringBuffer result, - FieldPosition fieldPosition) { - return format(number, result, fieldPosition, false); - } - - private StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition, - boolean parseAttr) { - // If we are to do rounding, we need to move into the BigDecimal - // domain in order to do divide/multiply correctly. - if (actualRoundingIncrementICU != null) { - return format(new BigDecimal(number), result, fieldPosition); - } - - if (multiplier != 1) { - number = number.multiply(BigInteger.valueOf(multiplier)); - } - - // At this point we are guaranteed a nonnegative finite - // number. - synchronized (digitList) { - digitList.set(number, precision(true)); - // For issue 11808. - if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) { - throw new ArithmeticException("Rounding necessary"); - } - return subformat(number.intValue(), result, fieldPosition, number.signum() < 0, true, - parseAttr); - } - } - - /** - * Formats a BigDecimal number. - * - * @stable ICU 2.0 - */ - @Override - public StringBuffer format(java.math.BigDecimal number, StringBuffer result, - FieldPosition fieldPosition) { - return format(number, result, fieldPosition, false); - } - - private StringBuffer format(java.math.BigDecimal number, StringBuffer result, - FieldPosition fieldPosition, - boolean parseAttr) { - if (multiplier != 1) { - number = number.multiply(java.math.BigDecimal.valueOf(multiplier)); - } - - if (actualRoundingIncrement != null) { - number = number.divide(actualRoundingIncrement, 0, roundingMode).multiply(actualRoundingIncrement); - } - - synchronized (digitList) { - digitList.set(number, precision(false), !useExponentialNotation && - !areSignificantDigitsUsed()); - // For issue 11808. - if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) { - throw new ArithmeticException("Rounding necessary"); - } - return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, - false, parseAttr); - } - } - - /** - * Formats a BigDecimal number. - * - * @stable ICU 2.0 - */ - @Override - public StringBuffer format(BigDecimal number, StringBuffer result, - FieldPosition fieldPosition) { - // This method is just a copy of the corresponding java.math.BigDecimal method - // for now. It isn't very efficient since it must create a conversion object to - // do math on the rounding increment. In the future we may try to clean this up, - // or even better, limit our support to just one flavor of BigDecimal. - if (multiplier != 1) { - number = number.multiply(BigDecimal.valueOf(multiplier), mathContext); - } - - if (actualRoundingIncrementICU != null) { - number = number.divide(actualRoundingIncrementICU, 0, roundingMode) - .multiply(actualRoundingIncrementICU, mathContext); - } - - synchronized (digitList) { - digitList.set(number, precision(false), !useExponentialNotation && - !areSignificantDigitsUsed()); - // For issue 11808. - if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) { - throw new ArithmeticException("Rounding necessary"); - } - return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, - false, false); - } - } - - /** - * Returns true if a grouping separator belongs at the given position, based on whether - * grouping is in use and the values of the primary and secondary grouping interval. - * - * @param pos the number of integer digits to the right of the current position. Zero - * indicates the position after the rightmost integer digit. - * @return true if a grouping character belongs at the current position. - */ - private boolean isGroupingPosition(int pos) { - boolean result = false; - if (isGroupingUsed() && (pos > 0) && (groupingSize > 0)) { - if ((groupingSize2 > 0) && (pos > groupingSize)) { - result = ((pos - groupingSize) % groupingSize2) == 0; - } else { - result = pos % groupingSize == 0; - } - } - return result; - } - - /** - * Return the number of fraction digits to display, or the total - * number of digits for significant digit formats and exponential - * formats. - */ - private int precision(boolean isIntegral) { - if (areSignificantDigitsUsed()) { - return getMaximumSignificantDigits(); - } else if (useExponentialNotation) { - return getMinimumIntegerDigits() + getMaximumFractionDigits(); - } else { - return isIntegral ? 0 : getMaximumFractionDigits(); - } - } - - private StringBuffer subformat(int number, StringBuffer result, FieldPosition fieldPosition, - boolean isNegative, boolean isInteger, boolean parseAttr) { - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - // compute the plural category from the digitList plus other settings - return subformat(currencyPluralInfo.select(getFixedDecimal(number)), - result, fieldPosition, isNegative, - isInteger, parseAttr); - } else { - return subformat(result, fieldPosition, isNegative, isInteger, parseAttr); - } - } - - /** - * This is ugly, but don't see a better way to do it without major restructuring of the code. - */ - /*package*/ FixedDecimal getFixedDecimal(double number) { - // get the visible fractions and the number of fraction digits. - return getFixedDecimal(number, digitList); - } - - FixedDecimal getFixedDecimal(double number, DigitList dl) { - int fractionalDigitsInDigitList = dl.count - dl.decimalAt; - int v; - long f; - int maxFractionalDigits; - int minFractionalDigits; - if (useSignificantDigits) { - maxFractionalDigits = maxSignificantDigits - dl.decimalAt; - minFractionalDigits = minSignificantDigits - dl.decimalAt; - if (minFractionalDigits < 0) { - minFractionalDigits = 0; - } - if (maxFractionalDigits < 0) { - maxFractionalDigits = 0; - } - } else { - maxFractionalDigits = getMaximumFractionDigits(); - minFractionalDigits = getMinimumFractionDigits(); - } - v = fractionalDigitsInDigitList; - if (v < minFractionalDigits) { - v = minFractionalDigits; - } else if (v > maxFractionalDigits) { - v = maxFractionalDigits; - } - f = 0; - if (v > 0) { - for (int i = Math.max(0, dl.decimalAt); i < dl.count; ++i) { - f *= 10; - f += (dl.digits[i] - '0'); - } - for (int i = v; i < fractionalDigitsInDigitList; ++i) { - f *= 10; - } - } - return new FixedDecimal(number, v, f); - } - - private StringBuffer subformat(double number, StringBuffer result, FieldPosition fieldPosition, - boolean isNegative, - boolean isInteger, boolean parseAttr) { - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - // compute the plural category from the digitList plus other settings - return subformat(currencyPluralInfo.select(getFixedDecimal(number)), - result, fieldPosition, isNegative, - isInteger, parseAttr); - } else { - return subformat(result, fieldPosition, isNegative, isInteger, parseAttr); - } - } - - private StringBuffer subformat(String pluralCount, StringBuffer result, FieldPosition fieldPosition, - boolean isNegative, boolean isInteger, boolean parseAttr) { - // There are 2 ways to activate currency plural format: by applying a pattern with - // 3 currency sign directly, or by instantiate a decimal formatter using - // PLURALCURRENCYSTYLE. For both cases, the number of currency sign in the - // pattern is 3. Even if the number of currency sign in the pattern is 3, it does - // not mean we need to reset the pattern. For 1st case, we do not need to reset - // pattern. For 2nd case, we might need to reset pattern, if the default pattern - // (corresponding to plural count 'other') we use is different from the pattern - // based on 'pluralCount'. - // - // style is only valid when decimal formatter is constructed through - // DecimalFormat(pattern, symbol, style) - if (style == NumberFormat.PLURALCURRENCYSTYLE) { - // May need to reset pattern if the style is PLURALCURRENCYSTYLE. - String currencyPluralPattern = currencyPluralInfo.getCurrencyPluralPattern(pluralCount); - if (formatPattern.equals(currencyPluralPattern) == false) { - applyPatternWithoutExpandAffix(currencyPluralPattern, false); - } - } - // Expand the affix to the right name according to the plural rule. This is only - // used for currency plural formatting. Currency plural name is not a fixed - // static one, it is a dynamic name based on the currency plural count. So, the - // affixes need to be expanded here. For other cases, the affix is a static one - // based on pattern alone, and it is already expanded during applying pattern, or - // setDecimalFormatSymbols, or setCurrency. - expandAffixAdjustWidth(pluralCount); - return subformat(result, fieldPosition, isNegative, isInteger, parseAttr); - } - - /** - * Complete the formatting of a finite number. On entry, the - * digitList must be filled in with the correct digits. - */ - private StringBuffer subformat(StringBuffer result, FieldPosition fieldPosition, - boolean isNegative, boolean isInteger, boolean parseAttr) { - // NOTE: This isn't required anymore because DigitList takes care of this. - // - // // The negative of the exponent represents the number of leading // zeros - // between the decimal and the first non-zero digit, for // a value < 0.1 (e.g., - // for 0.00123, -fExponent == 2). If this // is more than the maximum fraction - // digits, then we have an underflow // for the printed representation. We - // recognize this here and set // the DigitList representation to zero in this - // situation. - // - // if (-digitList.decimalAt >= getMaximumFractionDigits()) - // { - // digitList.count = 0; - // } - - - - // Per bug 4147706, DecimalFormat must respect the sign of numbers which format as - // zero. This allows sensible computations and preserves relations such as - // signum(1/x) = signum(x), where x is +Infinity or -Infinity. Prior to this fix, - // we always formatted zero values as if they were positive. Liu 7/6/98. - if (digitList.isZero()) { - digitList.decimalAt = 0; // Normalize - } - - int prefixLen = appendAffix(result, isNegative, true, fieldPosition, parseAttr); - - if (useExponentialNotation) { - subformatExponential(result, fieldPosition, parseAttr); - } else { - subformatFixed(result, fieldPosition, isInteger, parseAttr); - } - - int suffixLen = appendAffix(result, isNegative, false, fieldPosition, parseAttr); - addPadding(result, fieldPosition, prefixLen, suffixLen); - return result; - } - - private void subformatFixed(StringBuffer result, - FieldPosition fieldPosition, - boolean isInteger, - boolean parseAttr) { - String[] digits = symbols.getDigitStrings(); - - String grouping = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ? - symbols.getGroupingSeparatorString(): symbols.getMonetaryGroupingSeparatorString(); - String decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ? - symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString(); - boolean useSigDig = areSignificantDigitsUsed(); - int maxIntDig = getMaximumIntegerDigits(); - int minIntDig = getMinimumIntegerDigits(); - int i; - // [Spark/CDL] Record the integer start index. - int intBegin = result.length(); - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD || - fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setBeginIndex(intBegin); - } - long fractionalDigits = 0; - int fractionalDigitsCount = 0; - boolean recordFractionDigits = false; - - int sigCount = 0; - int minSigDig = getMinimumSignificantDigits(); - int maxSigDig = getMaximumSignificantDigits(); - if (!useSigDig) { - minSigDig = 0; - maxSigDig = Integer.MAX_VALUE; - } - - // Output the integer portion. Here 'count' is the total number of integer - // digits we will display, including both leading zeros required to satisfy - // getMinimumIntegerDigits, and actual digits present in the number. - int count = useSigDig ? Math.max(1, digitList.decimalAt) : minIntDig; - if (digitList.decimalAt > 0 && count < digitList.decimalAt) { - count = digitList.decimalAt; - } - - // Handle the case where getMaximumIntegerDigits() is smaller than the real - // number of integer digits. If this is so, we output the least significant - // max integer digits. For example, the value 1997 printed with 2 max integer - // digits is just "97". - - int digitIndex = 0; // Index into digitList.fDigits[] - if (count > maxIntDig && maxIntDig >= 0) { - count = maxIntDig; - digitIndex = digitList.decimalAt - count; - } - - int sizeBeforeIntegerPart = result.length(); - for (i = count - 1; i >= 0; --i) { - if (i < digitList.decimalAt && digitIndex < digitList.count - && sigCount < maxSigDig) { - // Output a real digit - result.append(digits[digitList.getDigitValue(digitIndex++)]); - ++sigCount; - } else { - // Output a zero (leading or trailing) - result.append(digits[0]); - if (sigCount > 0) { - ++sigCount; - } - } - - // Output grouping separator if necessary. - if (isGroupingPosition(i)) { - result.append(grouping); - // [Spark/CDL] Add grouping separator attribute here. - // Set only for the first instance. - // Length of grouping separator is 1. - if (fieldPosition.getFieldAttribute() == Field.GROUPING_SEPARATOR && - fieldPosition.getBeginIndex() == 0 && fieldPosition.getEndIndex() == 0) { - fieldPosition.setBeginIndex(result.length()-1); - fieldPosition.setEndIndex(result.length()); - } - if (parseAttr) { - addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length()); - } - } - } - - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD || - fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setEndIndex(result.length()); - } - - // This handles the special case of formatting 0. For zero only, we count the - // zero to the left of the decimal point as one signficant digit. Ordinarily we - // do not count any leading 0's as significant. If the number we are formatting - // is not zero, then either sigCount or digits.getCount() will be non-zero. - if (sigCount == 0 && digitList.count == 0) { - sigCount = 1; - } - - // Determine whether or not there are any printable fractional digits. If - // we've used up the digits we know there aren't. - boolean fractionPresent = (!isInteger && digitIndex < digitList.count) - || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0)); - - // If there is no fraction present, and we haven't printed any integer digits, - // then print a zero. Otherwise we won't print _any_ digits, and we won't be - // able to parse this string. - if (!fractionPresent && result.length() == sizeBeforeIntegerPart) - result.append(digits[0]); - // [Spark/CDL] Add attribute for integer part. - if (parseAttr) { - addAttribute(Field.INTEGER, intBegin, result.length()); - } - // Output the decimal separator if we always do so. - if (decimalSeparatorAlwaysShown || fractionPresent) { - if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { - fieldPosition.setBeginIndex(result.length()); - } - result.append(decimal); - if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { - fieldPosition.setEndIndex(result.length()); - } - // [Spark/CDL] Add attribute for decimal separator - if (parseAttr) { - addAttribute(Field.DECIMAL_SEPARATOR, result.length() - 1, result.length()); - } - } - - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) { - fieldPosition.setBeginIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) { - fieldPosition.setBeginIndex(result.length()); - } - - // [Spark/CDL] Record the begin index of fraction part. - int fracBegin = result.length(); - recordFractionDigits = fieldPosition instanceof UFieldPosition; - - count = useSigDig ? Integer.MAX_VALUE : getMaximumFractionDigits(); - if (useSigDig && (sigCount == maxSigDig || - (sigCount >= minSigDig && digitIndex == digitList.count))) { - count = 0; - } - for (i = 0; i < count; ++i) { - // Here is where we escape from the loop. We escape if we've output the - // maximum fraction digits (specified in the for expression above). We - // also stop when we've output the minimum digits and either: we have an - // integer, so there is no fractional stuff to display, or we're out of - // significant digits. - if (!useSigDig && i >= getMinimumFractionDigits() && - (isInteger || digitIndex >= digitList.count)) { - break; - } - - // Output leading fractional zeros. These are zeros that come after the - // decimal but before any significant digits. These are only output if - // abs(number being formatted) < 1.0. - if (-1 - i > (digitList.decimalAt - 1)) { - result.append(digits[0]); - if (recordFractionDigits) { - ++fractionalDigitsCount; - fractionalDigits *= 10; - } - continue; - } - - // Output a digit, if we have any precision left, or a zero if we - // don't. We don't want to output noise digits. - if (!isInteger && digitIndex < digitList.count) { - byte digit = digitList.getDigitValue(digitIndex++); - result.append(digits[digit]); - if (recordFractionDigits) { - ++fractionalDigitsCount; - fractionalDigits *= 10; - fractionalDigits += digit; - } - } else { - result.append(digits[0]); - if (recordFractionDigits) { - ++fractionalDigitsCount; - fractionalDigits *= 10; - } - } - - // If we reach the maximum number of significant digits, or if we output - // all the real digits and reach the minimum, then we are done. - ++sigCount; - if (useSigDig && (sigCount == maxSigDig || - (digitIndex == digitList.count && sigCount >= minSigDig))) { - break; - } - } - - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) { - fieldPosition.setEndIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) { - fieldPosition.setEndIndex(result.length()); - } - if (recordFractionDigits) { - ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits); - } - - // [Spark/CDL] Add attribute information if necessary. - if (parseAttr && (decimalSeparatorAlwaysShown || fractionPresent)) { - addAttribute(Field.FRACTION, fracBegin, result.length()); - } - } - - private void subformatExponential(StringBuffer result, - FieldPosition fieldPosition, - boolean parseAttr) { - String[] digits = symbols.getDigitStringsLocal(); - String decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ? - symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString(); - boolean useSigDig = areSignificantDigitsUsed(); - int maxIntDig = getMaximumIntegerDigits(); - int minIntDig = getMinimumIntegerDigits(); - int i; - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setBeginIndex(result.length()); - fieldPosition.setEndIndex(-1); - } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) { - fieldPosition.setBeginIndex(-1); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setBeginIndex(result.length()); - fieldPosition.setEndIndex(-1); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) { - fieldPosition.setBeginIndex(-1); - } - - // [Spark/CDL] - // the begin index of integer part - // the end index of integer part - // the begin index of fractional part - int intBegin = result.length(); - int intEnd = -1; - int fracBegin = -1; - int minFracDig = 0; - if (useSigDig) { - maxIntDig = minIntDig = 1; - minFracDig = getMinimumSignificantDigits() - 1; - } else { - minFracDig = getMinimumFractionDigits(); - if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) { - maxIntDig = 1; - if (maxIntDig < minIntDig) { - maxIntDig = minIntDig; - } - } - if (maxIntDig > minIntDig) { - minIntDig = 1; - } - } - long fractionalDigits = 0; - int fractionalDigitsCount = 0; - boolean recordFractionDigits = false; - - // Minimum integer digits are handled in exponential format by adjusting the - // exponent. For example, 0.01234 with 3 minimum integer digits is "123.4E-4". - - // Maximum integer digits are interpreted as indicating the repeating - // range. This is useful for engineering notation, in which the exponent is - // restricted to a multiple of 3. For example, 0.01234 with 3 maximum integer - // digits is "12.34e-3". If maximum integer digits are defined and are larger - // than minimum integer digits, then minimum integer digits are ignored. - - int exponent = digitList.decimalAt; - if (maxIntDig > 1 && maxIntDig != minIntDig) { - // A exponent increment is defined; adjust to it. - exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1; - exponent *= maxIntDig; - } else { - // No exponent increment is defined; use minimum integer digits. - // If none is specified, as in "#E0", generate 1 integer digit. - exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1; - } - - // We now output a minimum number of digits, and more if there are more - // digits, up to the maximum number of digits. We place the decimal point - // after the "integer" digits, which are the first (decimalAt - exponent) - // digits. - int minimumDigits = minIntDig + minFracDig; - // The number of integer digits is handled specially if the number - // is zero, since then there may be no digits. - int integerDigits = digitList.isZero() ? minIntDig : digitList.decimalAt - exponent; - int totalDigits = digitList.count; - if (minimumDigits > totalDigits) - totalDigits = minimumDigits; - if (integerDigits > totalDigits) - totalDigits = integerDigits; - - for (i = 0; i < totalDigits; ++i) { - if (i == integerDigits) { - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setEndIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setEndIndex(result.length()); - } - - // [Spark/CDL] Add attribute for integer part - if (parseAttr) { - intEnd = result.length(); - addAttribute(Field.INTEGER, intBegin, result.length()); - } - if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { - fieldPosition.setBeginIndex(result.length()); - } - result.append(decimal); - if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { - fieldPosition.setEndIndex(result.length()); - } - // [Spark/CDL] Add attribute for decimal separator - fracBegin = result.length(); - if (parseAttr) { - // Length of decimal separator is 1. - int decimalSeparatorBegin = result.length() - 1; - addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin, - result.length()); - } - // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) { - fieldPosition.setBeginIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) { - fieldPosition.setBeginIndex(result.length()); - } - recordFractionDigits = fieldPosition instanceof UFieldPosition; - - } - byte digit = (i < digitList.count) ? digitList.getDigitValue(i) : (byte)0; - result.append(digits[digit]); - if (recordFractionDigits) { - ++fractionalDigitsCount; - fractionalDigits *= 10; - fractionalDigits += digit; - } - } - - // For ICU compatibility and format 0 to 0E0 with pattern "#E0" [Richard/GCL] - if (digitList.isZero() && (totalDigits == 0)) { - result.append(digits[0]); - } - - // add the decimal separator if it is to be always shown AND there are no decimal digits - if ((fracBegin == -1) && this.decimalSeparatorAlwaysShown) { - if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { - fieldPosition.setBeginIndex(result.length()); - } - result.append(decimal); - if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { - fieldPosition.setEndIndex(result.length()); - } - if (parseAttr) { - // Length of decimal separator is 1. - int decimalSeparatorBegin = result.length() - 1; - addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin, result.length()); - } - } - - // Record field information - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - if (fieldPosition.getEndIndex() < 0) { - fieldPosition.setEndIndex(result.length()); - } - } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) { - if (fieldPosition.getBeginIndex() < 0) { - fieldPosition.setBeginIndex(result.length()); - } - fieldPosition.setEndIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - if (fieldPosition.getEndIndex() < 0) { - fieldPosition.setEndIndex(result.length()); - } - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) { - if (fieldPosition.getBeginIndex() < 0) { - fieldPosition.setBeginIndex(result.length()); - } - fieldPosition.setEndIndex(result.length()); - } - if (recordFractionDigits) { - ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits); - } - - // [Spark/CDL] Calculate the end index of integer part and fractional - // part if they are not properly processed yet. - if (parseAttr) { - if (intEnd < 0) { - addAttribute(Field.INTEGER, intBegin, result.length()); - } - if (fracBegin > 0) { - addAttribute(Field.FRACTION, fracBegin, result.length()); - } - } - - // The exponent is output using the pattern-specified minimum exponent - // digits. There is no maximum limit to the exponent digits, since truncating - // the exponent would result in an unacceptable inaccuracy. - if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SYMBOL) { - fieldPosition.setBeginIndex(result.length()); - } - - result.append(symbols.getExponentSeparator()); - if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SYMBOL) { - fieldPosition.setEndIndex(result.length()); - } - // [Spark/CDL] For exponent symbol, add an attribute. - if (parseAttr) { - addAttribute(Field.EXPONENT_SYMBOL, result.length() - - symbols.getExponentSeparator().length(), result.length()); - } - // For zero values, we force the exponent to zero. We must do this here, and - // not earlier, because the value is used to determine integer digit count - // above. - if (digitList.isZero()) - exponent = 0; - - boolean negativeExponent = exponent < 0; - if (negativeExponent) { - exponent = -exponent; - if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { - fieldPosition.setBeginIndex(result.length()); - } - result.append(symbols.getMinusSignString()); - if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { - fieldPosition.setEndIndex(result.length()); - } - // [Spark/CDL] If exponent has sign, then add an exponent sign - // attribute. - if (parseAttr) { - // Length of exponent sign is 1. - addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length()); - } - } else if (exponentSignAlwaysShown) { - if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { - fieldPosition.setBeginIndex(result.length()); - } - result.append(symbols.getPlusSignString()); - if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { - fieldPosition.setEndIndex(result.length()); - } - // [Spark/CDL] Add an plus sign attribute. - if (parseAttr) { - // Length of exponent sign is 1. - int expSignBegin = result.length() - 1; - addAttribute(Field.EXPONENT_SIGN, expSignBegin, result.length()); - } - } - int expBegin = result.length(); - digitList.set(exponent); - { - int expDig = minExponentDigits; - if (useExponentialNotation && expDig < 1) { - expDig = 1; - } - for (i = digitList.decimalAt; i < expDig; ++i) - result.append(digits[0]); - } - for (i = 0; i < digitList.decimalAt; ++i) { - result.append((i < digitList.count) ? digits[digitList.getDigitValue(i)] - : digits[0]); - } - // [Spark/CDL] Add attribute for exponent part. - if (fieldPosition.getFieldAttribute() == Field.EXPONENT) { - fieldPosition.setBeginIndex(expBegin); - fieldPosition.setEndIndex(result.length()); - } - if (parseAttr) { - addAttribute(Field.EXPONENT, expBegin, result.length()); - } - } - - private final void addPadding(StringBuffer result, FieldPosition fieldPosition, int prefixLen, - int suffixLen) { - if (formatWidth > 0) { - int len = formatWidth - result.length(); - if (len > 0) { - char[] padding = new char[len]; - for (int i = 0; i < len; ++i) { - padding[i] = pad; - } - switch (padPosition) { - case PAD_AFTER_PREFIX: - result.insert(prefixLen, padding); - break; - case PAD_BEFORE_PREFIX: - result.insert(0, padding); - break; - case PAD_BEFORE_SUFFIX: - result.insert(result.length() - suffixLen, padding); - break; - case PAD_AFTER_SUFFIX: - result.append(padding); - break; - } - if (padPosition == PAD_BEFORE_PREFIX || padPosition == PAD_AFTER_PREFIX) { - fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + len); - fieldPosition.setEndIndex(fieldPosition.getEndIndex() + len); - } - } - } - } - - /** - * Parses the given string, returning a Number object to represent the - * parsed value. Double objects are returned to represent non-integral - * values which cannot be stored in a BigDecimal. These are - * NaN, infinity, -infinity, and -0.0. If {@link #isParseBigDecimal()} is - * false (the default), all other values are returned as Long, - * BigInteger, or BigDecimal values, in that order of - * preference. If {@link #isParseBigDecimal()} is true, all other values are returned - * as BigDecimal valuse. If the parse fails, null is returned. - * - * @param text the string to be parsed - * @param parsePosition defines the position where parsing is to begin, and upon - * return, the position where parsing left off. If the position has not changed upon - * return, then parsing failed. - * @return a Number object with the parsed value or - * null if the parse failed - * @stable ICU 2.0 - */ - @Override - public Number parse(String text, ParsePosition parsePosition) { - return (Number) parse(text, parsePosition, null); - } - - /** - * Parses text from the given string as a CurrencyAmount. Unlike the parse() method, - * this method will attempt to parse a generic currency name, searching for a match of - * this object's locale's currency display names, or for a 3-letter ISO currency - * code. This method will fail if this format is not a currency format, that is, if it - * does not contain the currency pattern symbol (U+00A4) in its prefix or suffix. - * - * @param text the text to parse - * @param pos input-output position; on input, the position within text to match; must - * have 0 <= pos.getIndex() < text.length(); on output, the position after the last - * matched character. If the parse fails, the position in unchanged upon output. - * @return a CurrencyAmount, or null upon failure - * @stable ICU 49 - */ - @Override - public CurrencyAmount parseCurrency(CharSequence text, ParsePosition pos) { - Currency[] currency = new Currency[1]; - return (CurrencyAmount) parse(text.toString(), pos, currency); - } - - /** - * Parses the given text as either a Number or a CurrencyAmount. - * - * @param text the string to parse - * @param parsePosition input-output position; on input, the position within text to - * match; must have 0 <= pos.getIndex() < text.length(); on output, the position after - * the last matched character. If the parse fails, the position in unchanged upon - * output. - * @param currency if non-null, a CurrencyAmount is parsed and returned; otherwise a - * Number is parsed and returned - * @return a Number or CurrencyAmount or null - */ - private Object parse(String text, ParsePosition parsePosition, Currency[] currency) { - int backup; - int i = backup = parsePosition.getIndex(); - - // Handle NaN as a special case: - - // Skip padding characters, if around prefix - if (formatWidth > 0 && - (padPosition == PAD_BEFORE_PREFIX || padPosition == PAD_AFTER_PREFIX)) { - i = skipPadding(text, i); - } - if (text.regionMatches(i, symbols.getNaN(), 0, symbols.getNaN().length())) { - i += symbols.getNaN().length(); - // Skip padding characters, if around suffix - if (formatWidth > 0 && (padPosition == PAD_BEFORE_SUFFIX || - padPosition == PAD_AFTER_SUFFIX)) { - i = skipPadding(text, i); - } - parsePosition.setIndex(i); - return new Double(Double.NaN); - } - - // NaN parse failed; start over - i = backup; - - boolean[] status = new boolean[STATUS_LENGTH]; - if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) { - if (!parseForCurrency(text, parsePosition, currency, status)) { - return null; - } - } else if (currency != null) { - return null; - } else { - if (!subparse(text, parsePosition, digitList, status, currency, negPrefixPattern, - negSuffixPattern, posPrefixPattern, posSuffixPattern, - false, Currency.SYMBOL_NAME)) { - parsePosition.setIndex(backup); - return null; - } - } - - Number n = null; - - // Handle infinity - if (status[STATUS_INFINITE]) { - n = new Double(status[STATUS_POSITIVE] ? Double.POSITIVE_INFINITY : - Double.NEGATIVE_INFINITY); - } - - // Handle underflow - else if (status[STATUS_UNDERFLOW]) { - n = status[STATUS_POSITIVE] ? new Double("0.0") : new Double("-0.0"); - } - - // Handle -0.0 - else if (!status[STATUS_POSITIVE] && digitList.isZero()) { - n = new Double("-0.0"); - } - - else { - // Do as much of the multiplier conversion as possible without - // losing accuracy. - int mult = multiplier; // Don't modify this.multiplier - while (mult % 10 == 0) { - --digitList.decimalAt; - mult /= 10; - } - - // Handle integral values - if (!parseBigDecimal && mult == 1 && digitList.isIntegral()) { - // hack quick long - if (digitList.decimalAt < 12) { // quick check for long - long l = 0; - if (digitList.count > 0) { - int nx = 0; - while (nx < digitList.count) { - l = l * 10 + (char) digitList.digits[nx++] - '0'; - } - while (nx++ < digitList.decimalAt) { - l *= 10; - } - if (!status[STATUS_POSITIVE]) { - l = -l; - } - } - n = Long.valueOf(l); - } else { - BigInteger big = digitList.getBigInteger(status[STATUS_POSITIVE]); - n = (big.bitLength() < 64) ? (Number) Long.valueOf(big.longValue()) : (Number) big; - } - } - // Handle non-integral values or the case where parseBigDecimal is set - else { - BigDecimal big = digitList.getBigDecimalICU(status[STATUS_POSITIVE]); - n = big; - if (mult != 1) { - n = big.divide(BigDecimal.valueOf(mult), mathContext); - } - } - } - - // Assemble into CurrencyAmount if necessary - return (currency != null) ? (Object) new CurrencyAmount(n, currency[0]) : (Object) n; - } - - private boolean parseForCurrency(String text, ParsePosition parsePosition, - Currency[] currency, boolean[] status) { - int origPos = parsePosition.getIndex(); - if (!isReadyForParsing) { - int savedCurrencySignCount = currencySignCount; - setupCurrencyAffixForAllPatterns(); - // reset pattern back - if (savedCurrencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - applyPatternWithoutExpandAffix(formatPattern, false); - } else { - applyPattern(formatPattern, false); - } - isReadyForParsing = true; - } - int maxPosIndex = origPos; - int maxErrorPos = -1; - boolean[] savedStatus = null; - // First, parse against current pattern. - // Since current pattern could be set by applyPattern(), - // it could be an arbitrary pattern, and it may not be the one - // defined in current locale. - boolean[] tmpStatus = new boolean[STATUS_LENGTH]; - ParsePosition tmpPos = new ParsePosition(origPos); - DigitList tmpDigitList = new DigitList(); - boolean found; - if (style == NumberFormat.PLURALCURRENCYSTYLE) { - found = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency, - negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern, - true, Currency.LONG_NAME); - } else { - found = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency, - negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern, - true, Currency.SYMBOL_NAME); - } - if (found) { - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - savedStatus = tmpStatus; - digitList = tmpDigitList; - } - } else { - maxErrorPos = tmpPos.getErrorIndex(); - } - // Then, parse against affix patterns. Those are currency patterns and currency - // plural patterns defined in the locale. - for (AffixForCurrency affix : affixPatternsForCurrency) { - tmpStatus = new boolean[STATUS_LENGTH]; - tmpPos = new ParsePosition(origPos); - tmpDigitList = new DigitList(); - boolean result = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency, - affix.getNegPrefix(), affix.getNegSuffix(), - affix.getPosPrefix(), affix.getPosSuffix(), - true, affix.getPatternType()); - if (result) { - found = true; - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - savedStatus = tmpStatus; - digitList = tmpDigitList; - } - } else { - maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? tmpPos.getErrorIndex() - : maxErrorPos; - } - } - // Finally, parse against simple affix to find the match. For example, in - // TestMonster suite, if the to-be-parsed text is "-\u00A40,00". - // complexAffixCompare will not find match, since there is no ISO code matches - // "\u00A4", and the parse stops at "\u00A4". We will just use simple affix - // comparison (look for exact match) to pass it. - // - // TODO: We should parse against simple affix first when - // output currency is not requested. After the complex currency - // parsing implementation was introduced, the default currency - // instance parsing slowed down because of the new code flow. - // I filed #10312 - Yoshito - tmpStatus = new boolean[STATUS_LENGTH]; - tmpPos = new ParsePosition(origPos); - tmpDigitList = new DigitList(); - - // Disable complex currency parsing and try it again. - boolean result = subparse(text, tmpPos, tmpDigitList, tmpStatus, currency, - negativePrefix, negativeSuffix, positivePrefix, positiveSuffix, - false /* disable complex currency parsing */, Currency.SYMBOL_NAME); - if (result) { - if (tmpPos.getIndex() > maxPosIndex) { - maxPosIndex = tmpPos.getIndex(); - savedStatus = tmpStatus; - digitList = tmpDigitList; - } - found = true; - } else { - maxErrorPos = (tmpPos.getErrorIndex() > maxErrorPos) ? tmpPos.getErrorIndex() : - maxErrorPos; - } - - if (!found) { - // parsePosition.setIndex(origPos); - parsePosition.setErrorIndex(maxErrorPos); - } else { - parsePosition.setIndex(maxPosIndex); - parsePosition.setErrorIndex(-1); - for (int index = 0; index < STATUS_LENGTH; ++index) { - status[index] = savedStatus[index]; - } - } - return found; - } - - // Get affix patterns used in locale's currency pattern (NumberPatterns[1]) and - // currency plural pattern (CurrencyUnitPatterns). - private void setupCurrencyAffixForAllPatterns() { - if (currencyPluralInfo == null) { - currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale()); - } - affixPatternsForCurrency = new HashSet<>(); - - // save the current pattern, since it will be changed by - // applyPatternWithoutExpandAffix - String savedFormatPattern = formatPattern; - - // CURRENCYSTYLE and ISOCURRENCYSTYLE should have the same prefix and suffix, so, - // only need to save one of them. Here, chose onlyApplyPatternWithoutExpandAffix - // without saving the actualy pattern in 'pattern' data member. TODO: is it uloc? - applyPatternWithoutExpandAffix(getPattern(symbols.getULocale(), NumberFormat.CURRENCYSTYLE), - false); - AffixForCurrency affixes = new AffixForCurrency( - negPrefixPattern, negSuffixPattern, posPrefixPattern, posSuffixPattern, - Currency.SYMBOL_NAME); - affixPatternsForCurrency.add(affixes); - - // add plural pattern - Iterator iter = currencyPluralInfo.pluralPatternIterator(); - Set currencyUnitPatternSet = new HashSet<>(); - while (iter.hasNext()) { - String pluralCount = iter.next(); - String currencyPattern = currencyPluralInfo.getCurrencyPluralPattern(pluralCount); - if (currencyPattern != null && - currencyUnitPatternSet.contains(currencyPattern) == false) { - currencyUnitPatternSet.add(currencyPattern); - applyPatternWithoutExpandAffix(currencyPattern, false); - affixes = new AffixForCurrency(negPrefixPattern, negSuffixPattern, posPrefixPattern, - posSuffixPattern, Currency.LONG_NAME); - affixPatternsForCurrency.add(affixes); - } - } - // reset pattern back - formatPattern = savedFormatPattern; - } - - // currency formatting style options - private static final int CURRENCY_SIGN_COUNT_ZERO = 0; - private static final int CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT = 1; - private static final int CURRENCY_SIGN_COUNT_IN_ISO_FORMAT = 2; - private static final int CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT = 3; - - private static final int STATUS_INFINITE = 0; - private static final int STATUS_POSITIVE = 1; - private static final int STATUS_UNDERFLOW = 2; - private static final int STATUS_LENGTH = 3; - - private static final UnicodeSet dotEquivalents = new UnicodeSet( - //"[.\u2024\u3002\uFE12\uFE52\uFF0E\uFF61]" - 0x002E, 0x002E, - 0x2024, 0x2024, - 0x3002, 0x3002, - 0xFE12, 0xFE12, - 0xFE52, 0xFE52, - 0xFF0E, 0xFF0E, - 0xFF61, 0xFF61).freeze(); - - private static final UnicodeSet commaEquivalents = new UnicodeSet( - //"[,\u060C\u066B\u3001\uFE10\uFE11\uFE50\uFE51\uFF0C\uFF64]" - 0x002C, 0x002C, - 0x060C, 0x060C, - 0x066B, 0x066B, - 0x3001, 0x3001, - 0xFE10, 0xFE11, - 0xFE50, 0xFE51, - 0xFF0C, 0xFF0C, - 0xFF64, 0xFF64).freeze(); - -// private static final UnicodeSet otherGroupingSeparators = new UnicodeSet( -// //"[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]" -// 0x0020, 0x0020, -// 0x0027, 0x0027, -// 0x00A0, 0x00A0, -// 0x066C, 0x066C, -// 0x2000, 0x200A, -// 0x2018, 0x2019, -// 0x202F, 0x202F, -// 0x205F, 0x205F, -// 0x3000, 0x3000, -// 0xFF07, 0xFF07).freeze(); - - private static final UnicodeSet strictDotEquivalents = new UnicodeSet( - //"[.\u2024\uFE52\uFF0E\uFF61]" - 0x002E, 0x002E, - 0x2024, 0x2024, - 0xFE52, 0xFE52, - 0xFF0E, 0xFF0E, - 0xFF61, 0xFF61).freeze(); - - private static final UnicodeSet strictCommaEquivalents = new UnicodeSet( - //"[,\u066B\uFE10\uFE50\uFF0C]" - 0x002C, 0x002C, - 0x066B, 0x066B, - 0xFE10, 0xFE10, - 0xFE50, 0xFE50, - 0xFF0C, 0xFF0C).freeze(); - -// private static final UnicodeSet strictOtherGroupingSeparators = new UnicodeSet( -// //"[\\ '\u00A0\u066C\u2000-\u200A\u2018\u2019\u202F\u205F\u3000\uFF07]" -// 0x0020, 0x0020, -// 0x0027, 0x0027, -// 0x00A0, 0x00A0, -// 0x066C, 0x066C, -// 0x2000, 0x200A, -// 0x2018, 0x2019, -// 0x202F, 0x202F, -// 0x205F, 0x205F, -// 0x3000, 0x3000, -// 0xFF07, 0xFF07).freeze(); - - private static final UnicodeSet defaultGroupingSeparators = - // new UnicodeSet(dotEquivalents).addAll(commaEquivalents) - // .addAll(otherGroupingSeparators).freeze(); - new UnicodeSet( - 0x0020, 0x0020, - 0x0027, 0x0027, - 0x002C, 0x002C, - 0x002E, 0x002E, - 0x00A0, 0x00A0, - 0x060C, 0x060C, - 0x066B, 0x066C, - 0x2000, 0x200A, - 0x2018, 0x2019, - 0x2024, 0x2024, - 0x202F, 0x202F, - 0x205F, 0x205F, - 0x3000, 0x3002, - 0xFE10, 0xFE12, - 0xFE50, 0xFE52, - 0xFF07, 0xFF07, - 0xFF0C, 0xFF0C, - 0xFF0E, 0xFF0E, - 0xFF61, 0xFF61, - 0xFF64, 0xFF64).freeze(); - - private static final UnicodeSet strictDefaultGroupingSeparators = - // new UnicodeSet(strictDotEquivalents).addAll(strictCommaEquivalents) - // .addAll(strictOtherGroupingSeparators).freeze(); - new UnicodeSet( - 0x0020, 0x0020, - 0x0027, 0x0027, - 0x002C, 0x002C, - 0x002E, 0x002E, - 0x00A0, 0x00A0, - 0x066B, 0x066C, - 0x2000, 0x200A, - 0x2018, 0x2019, - 0x2024, 0x2024, - 0x202F, 0x202F, - 0x205F, 0x205F, - 0x3000, 0x3000, - 0xFE10, 0xFE10, - 0xFE50, 0xFE50, - 0xFE52, 0xFE52, - 0xFF07, 0xFF07, - 0xFF0C, 0xFF0C, - 0xFF0E, 0xFF0E, - 0xFF61, 0xFF61).freeze(); - - static final UnicodeSet minusSigns = - new UnicodeSet( - 0x002D, 0x002D, - 0x207B, 0x207B, - 0x208B, 0x208B, - 0x2212, 0x2212, - 0x2796, 0x2796, - 0xFE63, 0xFE63, - 0xFF0D, 0xFF0D).freeze(); - - static final UnicodeSet plusSigns = - new UnicodeSet( - 0x002B, 0x002B, - 0x207A, 0x207A, - 0x208A, 0x208A, - 0x2795, 0x2795, - 0xFB29, 0xFB29, - 0xFE62, 0xFE62, - 0xFF0B, 0xFF0B).freeze(); - - // equivalent grouping and decimal support - static final boolean skipExtendedSeparatorParsing = ICUConfig.get( - "com.ibm.icu.text.DecimalFormat.SkipExtendedSeparatorParsing", "false") - .equals("true"); - - // allow control of requiring a matching decimal point when parsing - boolean parseRequireDecimalPoint = false; - - // When parsing a number with big exponential value, it requires to transform the - // value into a string representation to construct BigInteger instance. We want to - // set the maximum size because it can easily trigger OutOfMemoryException. - // PARSE_MAX_EXPONENT is currently set to 1000 (See getParseMaxDigits()), - // which is much bigger than MAX_VALUE of Double ( See the problem reported by ticket#5698 - private int PARSE_MAX_EXPONENT = 1000; - - /** - * Parses the given text into a number. The text is parsed beginning at parsePosition, - * until an unparseable character is seen. - * - * @param text the string to parse. - * @param parsePosition the position at which to being parsing. Upon return, the first - * unparseable character. - * @param digits the DigitList to set to the parsed value. - * @param status Upon return contains boolean status flags indicating whether the - * value was infinite and whether it was positive. - * @param currency return value for parsed currency, for generic currency parsing - * mode, or null for normal parsing. In generic currency parsing mode, any currency is - * parsed, not just the currency that this formatter is set to. - * @param negPrefix negative prefix pattern - * @param negSuffix negative suffix pattern - * @param posPrefix positive prefix pattern - * @param negSuffix negative suffix pattern - * @param parseComplexCurrency whether it is complex currency parsing or not. - * @param type type of currency to parse against, LONG_NAME only or not. - */ - private final boolean subparse( - String text, ParsePosition parsePosition, DigitList digits, - boolean status[], Currency currency[], String negPrefix, String negSuffix, String posPrefix, - String posSuffix, boolean parseComplexCurrency, int type) { - - int position = parsePosition.getIndex(); - int oldStart = parsePosition.getIndex(); - - // Match padding before prefix - if (formatWidth > 0 && padPosition == PAD_BEFORE_PREFIX) { - position = skipPadding(text, position); - } - - // Match positive and negative prefixes; prefer longest match. - int posMatch = compareAffix(text, position, false, true, posPrefix, parseComplexCurrency, type, currency); - int negMatch = compareAffix(text, position, true, true, negPrefix, parseComplexCurrency, type, currency); - if (posMatch >= 0 && negMatch >= 0) { - if (posMatch > negMatch) { - negMatch = -1; - } else if (negMatch > posMatch) { - posMatch = -1; - } - } - if (posMatch >= 0) { - position += posMatch; - } else if (negMatch >= 0) { - position += negMatch; - } else { - parsePosition.setErrorIndex(position); - return false; - } - - // Match padding after prefix - if (formatWidth > 0 && padPosition == PAD_AFTER_PREFIX) { - position = skipPadding(text, position); - } - - // process digits or Inf, find decimal position - status[STATUS_INFINITE] = false; - if (text.regionMatches(position, symbols.getInfinity(), 0, - symbols.getInfinity().length())) { - position += symbols.getInfinity().length(); - status[STATUS_INFINITE] = true; - } else { - // We now have a string of digits, possibly with grouping symbols, and decimal - // points. We want to process these into a DigitList. We don't want to put a - // bunch of leading zeros into the DigitList though, so we keep track of the - // location of the decimal point, put only significant digits into the - // DigitList, and adjust the exponent as needed. - - digits.decimalAt = digits.count = 0; - String decimal = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ? - symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString(); - String grouping = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ? - symbols.getGroupingSeparatorString() : symbols.getMonetaryGroupingSeparatorString(); - - String exponentSep = symbols.getExponentSeparator(); - boolean sawDecimal = false; - boolean sawGrouping = false; - boolean sawDigit = false; - long exponent = 0; // Set to the exponent value, if any - - // strict parsing - boolean strictParse = isParseStrict(); - boolean strictFail = false; // did we exit with a strict parse failure? - int lastGroup = -1; // where did we last see a grouping separator? - int groupedDigitCount = 0; // tracking count of digits delimited by grouping separator - int gs2 = groupingSize2 == 0 ? groupingSize : groupingSize2; - - UnicodeSet decimalEquiv = skipExtendedSeparatorParsing ? UnicodeSet.EMPTY : - getEquivalentDecimals(decimal, strictParse); - UnicodeSet groupEquiv = skipExtendedSeparatorParsing ? UnicodeSet.EMPTY : - (strictParse ? strictDefaultGroupingSeparators : defaultGroupingSeparators); - - // We have to track digitCount ourselves, because digits.count will pin when - // the maximum allowable digits is reached. - int digitCount = 0; - - int backup = -1; // used for preserving the last confirmed position - int[] parsedDigit = {-1}; // allocates int[1] for parsing a single digit - - while (position < text.length()) { - // Check if the sequence at the current position matches a decimal digit - int matchLen = matchesDigit(text, position, parsedDigit); - if (matchLen > 0) { - // matched a digit - // Cancel out backup setting (see grouping handler below) - if (backup != -1) { - if (strictParse) { - // comma followed by digit, so group before comma is a secondary - // group. If there was a group separator before that, the group - // must == the secondary group length, else it can be <= the the - // secondary group length. - if ((lastGroup != -1 && groupedDigitCount != gs2) - || (lastGroup == -1 && groupedDigitCount > gs2)) { - strictFail = true; - break; - } - } - lastGroup = backup; - groupedDigitCount = 0; - } - - groupedDigitCount++; - position += matchLen; - backup = -1; - sawDigit = true; - if (parsedDigit[0] == 0 && digits.count == 0) { - // Handle leading zeros - if (!sawDecimal) { - // Ignore leading zeros in integer part of number. - continue; - } - // If we have seen the decimal, but no significant digits yet, - // then we account for leading zeros by decrementing the - // digits.decimalAt into negative values. - --digits.decimalAt; - } else { - ++digitCount; - digits.append((char) (parsedDigit[0] + '0')); - } - continue; - } - - // Check if the sequence at the current position matches locale's decimal separator - int decimalStrLen = decimal.length(); - if (text.regionMatches(position, decimal, 0, decimalStrLen)) { - // matched a decimal separator - if (strictParse) { - if (backup != -1 || - (lastGroup != -1 && groupedDigitCount != groupingSize)) { - strictFail = true; - break; - } - } - - // If we're only parsing integers, or if we ALREADY saw the decimal, - // then don't parse this one. - if (isParseIntegerOnly() || sawDecimal) { - break; - } - - digits.decimalAt = digitCount; // Not digits.count! - sawDecimal = true; - position += decimalStrLen; - continue; - } - - if (isGroupingUsed()) { - // Check if the sequence at the current position matches locale's grouping separator - int groupingStrLen = grouping.length(); - if (text.regionMatches(position, grouping, 0, groupingStrLen)) { - if (sawDecimal) { - break; - } - - if (strictParse) { - if ((!sawDigit || backup != -1)) { - // leading group, or two group separators in a row - strictFail = true; - break; - } - } - - // Ignore grouping characters, if we are using them, but require that - // they be followed by a digit. Otherwise we backup and reprocess - // them. - backup = position; - position += groupingStrLen; - sawGrouping = true; - continue; - } - } - - // Check if the code point at the current position matches one of decimal/grouping equivalent group chars - int cp = text.codePointAt(position); - if (!sawDecimal && decimalEquiv.contains(cp)) { - // matched a decimal separator - if (strictParse) { - if (backup != -1 || - (lastGroup != -1 && groupedDigitCount != groupingSize)) { - strictFail = true; - break; - } - } - - // If we're only parsing integers, or if we ALREADY saw the decimal, - // then don't parse this one. - if (isParseIntegerOnly()) { - break; - } - - digits.decimalAt = digitCount; // Not digits.count! - - // Once we see a decimal separator character, we only accept that - // decimal separator character from then on. - decimal = String.valueOf(Character.toChars(cp)); - - sawDecimal = true; - position += Character.charCount(cp); - continue; - } - - if (isGroupingUsed() && !sawGrouping && groupEquiv.contains(cp)) { - // matched a grouping separator - if (sawDecimal) { - break; - } - - if (strictParse) { - if ((!sawDigit || backup != -1)) { - // leading group, or two group separators in a row - strictFail = true; - break; - } - } - - // Once we see a grouping character, we only accept that grouping - // character from then on. - grouping = String.valueOf(Character.toChars(cp)); - - // Ignore grouping characters, if we are using them, but require that - // they be followed by a digit. Otherwise we backup and reprocess - // them. - backup = position; - position += Character.charCount(cp); - sawGrouping = true; - continue; - } - - // Check if the sequence at the current position matches locale's exponent separator - int exponentSepStrLen = exponentSep.length(); - if (text.regionMatches(true, position, exponentSep, 0, exponentSepStrLen)) { - // parse sign, if present - boolean negExp = false; - int pos = position + exponentSep.length(); - if (pos < text.length()) { - String plusSign = symbols.getPlusSignString(); - String minusSign = symbols.getMinusSignString(); - if (text.regionMatches(pos, plusSign, 0, plusSign.length())) { - pos += plusSign.length(); - } else if (text.regionMatches(pos, minusSign, 0, minusSign.length())) { - pos += minusSign.length(); - negExp = true; - } - } - - DigitList exponentDigits = new DigitList(); - exponentDigits.count = 0; - while (pos < text.length()) { - int digitMatchLen = matchesDigit(text, pos, parsedDigit); - if (digitMatchLen > 0) { - exponentDigits.append((char) (parsedDigit[0] + '0')); - pos += digitMatchLen; - } else { - break; - } - } - - if (exponentDigits.count > 0) { - // defer strict parse until we know we have a bona-fide exponent - if (strictParse && sawGrouping) { - strictFail = true; - break; - } - - // Quick overflow check for exponential part. Actual limit check - // will be done later in this code. - if (exponentDigits.count > 10 /* maximum decimal digits for int */) { - if (negExp) { - // set underflow flag - status[STATUS_UNDERFLOW] = true; - } else { - // set infinite flag - status[STATUS_INFINITE] = true; - } - } else { - exponentDigits.decimalAt = exponentDigits.count; - exponent = exponentDigits.getLong(); - if (negExp) { - exponent = -exponent; - } - } - position = pos; // Advance past the exponent - } - - break; // Whether we fail or succeed, we exit this loop - } - - // All other cases, stop parsing - break; - } - - if (digits.decimalAt == 0 && isDecimalPatternMatchRequired()) { - if (this.formatPattern.indexOf(decimal) != -1) { - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - return false; - } - } - - if (backup != -1) - position = backup; - - // If there was no decimal point we have an integer - if (!sawDecimal) { - digits.decimalAt = digitCount; // Not digits.count! - } - - // check for strict parse errors - if (strictParse && !sawDecimal) { - if (lastGroup != -1 && groupedDigitCount != groupingSize) { - strictFail = true; - } - } - if (strictFail) { - // only set with strictParse and a leading zero error leading zeros are an - // error with strict parsing except immediately before nondigit (except - // group separator followed by digit), or end of text. - - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(position); - return false; - } - - // Adjust for exponent, if any - exponent += digits.decimalAt; - if (exponent < -getParseMaxDigits()) { - status[STATUS_UNDERFLOW] = true; - } else if (exponent > getParseMaxDigits()) { - status[STATUS_INFINITE] = true; - } else { - digits.decimalAt = (int) exponent; - } - - // If none of the text string was recognized. For example, parse "x" with - // pattern "#0.00" (return index and error index both 0) parse "$" with - // pattern "$#0.00". (return index 0 and error index 1). - if (!sawDigit && digitCount == 0) { - parsePosition.setIndex(oldStart); - parsePosition.setErrorIndex(oldStart); - return false; - } - } - - // Match padding before suffix - if (formatWidth > 0 && padPosition == PAD_BEFORE_SUFFIX) { - position = skipPadding(text, position); - } - - // Match positive and negative suffixes; prefer longest match. - if (posMatch >= 0) { - posMatch = compareAffix(text, position, false, false, posSuffix, parseComplexCurrency, type, currency); - } - if (negMatch >= 0) { - negMatch = compareAffix(text, position, true, false, negSuffix, parseComplexCurrency, type, currency); - } - if (posMatch >= 0 && negMatch >= 0) { - if (posMatch > negMatch) { - negMatch = -1; - } else if (negMatch > posMatch) { - posMatch = -1; - } - } - - // Fail if neither or both - if ((posMatch >= 0) == (negMatch >= 0)) { - parsePosition.setErrorIndex(position); - return false; - } - - position += (posMatch >= 0 ? posMatch : negMatch); - - // Match padding after suffix - if (formatWidth > 0 && padPosition == PAD_AFTER_SUFFIX) { - position = skipPadding(text, position); - } - - parsePosition.setIndex(position); - - status[STATUS_POSITIVE] = (posMatch >= 0); - - if (parsePosition.getIndex() == oldStart) { - parsePosition.setErrorIndex(position); - return false; - } - return true; - } - - /** - * Check if the substring at the specified position matches a decimal digit. - * If matched, this method sets the decimal value to decVal and - * returns matched length. - * - * @param str The input string - * @param start The start index - * @param decVal Receives decimal value - * @return Length of match, or 0 if the sequence at the position is not - * a decimal digit. - */ - private int matchesDigit(String str, int start, int[] decVal) { - String[] localeDigits = symbols.getDigitStringsLocal(); - - // Check if the sequence at the current position matches locale digits. - for (int i = 0; i < 10; i++) { - int digitStrLen = localeDigits[i].length(); - if (str.regionMatches(start, localeDigits[i], 0, digitStrLen)) { - decVal[0] = i; - return digitStrLen; - } - } - - // If no locale digit match, then check if this is a Unicode digit - int cp = str.codePointAt(start); - decVal[0] = UCharacter.digit(cp, 10); - if (decVal[0] >= 0) { - return Character.charCount(cp); - } - - return 0; - } - - /** - * Returns a set of characters equivalent to the given desimal separator used for - * parsing number. This method may return an empty set. - */ - private UnicodeSet getEquivalentDecimals(String decimal, boolean strictParse) { - UnicodeSet equivSet = UnicodeSet.EMPTY; - if (strictParse) { - if (strictDotEquivalents.contains(decimal)) { - equivSet = strictDotEquivalents; - } else if (strictCommaEquivalents.contains(decimal)) { - equivSet = strictCommaEquivalents; - } - } else { - if (dotEquivalents.contains(decimal)) { - equivSet = dotEquivalents; - } else if (commaEquivalents.contains(decimal)) { - equivSet = commaEquivalents; - } - } - return equivSet; - } - - /** - * Starting at position, advance past a run of pad characters, if any. Return the - * index of the first character after position that is not a pad character. Result is - * >= position. - */ - private final int skipPadding(String text, int position) { - while (position < text.length() && text.charAt(position) == pad) { - ++position; - } - return position; - } - - /** - * Returns the length matched by the given affix, or -1 if none. Runs of white space - * in the affix, match runs of white space in the input. Pattern white space and input - * white space are determined differently; see code. - * - * @param text input text - * @param pos offset into input at which to begin matching - * @param isNegative - * @param isPrefix - * @param affixPat affix pattern used for currency affix comparison - * @param complexCurrencyParsing whether it is currency parsing or not - * @param type compare against currency type, LONG_NAME only or not. - * @param currency return value for parsed currency, for generic currency parsing - * mode, or null for normal parsing. In generic currency parsing mode, any currency - * is parsed, not just the currency that this formatter is set to. - * @return length of input that matches, or -1 if match failure - */ - private int compareAffix(String text, int pos, boolean isNegative, boolean isPrefix, - String affixPat, boolean complexCurrencyParsing, int type, Currency[] currency) { - if (currency != null || currencyChoice != null || (currencySignCount != CURRENCY_SIGN_COUNT_ZERO && complexCurrencyParsing)) { - return compareComplexAffix(affixPat, text, pos, type, currency); - } - if (isPrefix) { - return compareSimpleAffix(isNegative ? negativePrefix : positivePrefix, text, pos); - } else { - return compareSimpleAffix(isNegative ? negativeSuffix : positiveSuffix, text, pos); - } - - } - - /** - * Check for bidi marks: LRM, RLM, ALM - */ - private static boolean isBidiMark(int c) { - return (c==0x200E || c==0x200F || c==0x061C); - } - - /** - * Remove bidi marks from affix - */ - private static String trimMarksFromAffix(String affix) { - boolean hasBidiMark = false; - int idx = 0; - for (; idx < affix.length(); idx++) { - if (isBidiMark(affix.charAt(idx))) { - hasBidiMark = true; - break; - } - } - if (!hasBidiMark) { - return affix; - } - - StringBuilder buf = new StringBuilder(); - buf.append(affix, 0, idx); - idx++; // skip the first Bidi mark - for (; idx < affix.length(); idx++) { - char c = affix.charAt(idx); - if (!isBidiMark(c)) { - buf.append(c); - } - } - - return buf.toString(); - } - - /** - * Return the length matched by the given affix, or -1 if none. Runs of white space in - * the affix, match runs of white space in the input. Pattern white space and input - * white space are determined differently; see code. - * - * @param affix pattern string, taken as a literal - * @param input input text - * @param pos offset into input at which to begin matching - * @return length of input that matches, or -1 if match failure - */ - private static int compareSimpleAffix(String affix, String input, int pos) { - int start = pos; - // Affixes here might consist of sign, currency symbol and related spacing, etc. - // For more efficiency we should keep lazily-created trimmed affixes around in - // instance variables instead of trimming each time they are used (the next step). - String trimmedAffix = (affix.length() > 1)? trimMarksFromAffix(affix): affix; - for (int i = 0; i < trimmedAffix.length();) { - int c = UTF16.charAt(trimmedAffix, i); - int len = UTF16.getCharCount(c); - if (PatternProps.isWhiteSpace(c)) { - // We may have a pattern like: \u200F and input text like: \u200F Note - // that U+200F and U+0020 are Pattern_White_Space but only U+0020 is - // UWhiteSpace. So we have to first do a direct match of the run of RULE - // whitespace in the pattern, then match any extra characters. - boolean literalMatch = false; - while (pos < input.length()) { - int ic = UTF16.charAt(input, pos); - if (ic == c) { - literalMatch = true; - i += len; - pos += len; - if (i == trimmedAffix.length()) { - break; - } - c = UTF16.charAt(trimmedAffix, i); - len = UTF16.getCharCount(c); - if (!PatternProps.isWhiteSpace(c)) { - break; - } - } else if (isBidiMark(ic)) { - pos++; // just skip over this input text - } else { - break; - } - } - - // Advance over run in trimmedAffix - i = skipPatternWhiteSpace(trimmedAffix, i); - - // Advance over run in input text. Must see at least one white space char - // in input, unless we've already matched some characters literally. - int s = pos; - pos = skipUWhiteSpace(input, pos); - if (pos == s && !literalMatch) { - return -1; - } - // If we skip UWhiteSpace in the input text, we need to skip it in the - // pattern. Otherwise, the previous lines may have skipped over text - // (such as U+00A0) that is also in the trimmedAffix. - i = skipUWhiteSpace(trimmedAffix, i); - } else { - boolean match = false; - while (pos < input.length()) { - int ic = UTF16.charAt(input, pos); - if (!match && equalWithSignCompatibility(ic, c)) { - i += len; - pos += len; - match = true; - } else if (isBidiMark(ic)) { - pos++; // just skip over this input text - } else { - break; - } - } - if (!match) { - return -1; - } - } - } - return pos - start; - } - - private static boolean equalWithSignCompatibility(int lhs, int rhs) { - return lhs == rhs - || (minusSigns.contains(lhs) && minusSigns.contains(rhs)) - || (plusSigns.contains(lhs) && plusSigns.contains(rhs)); - } - - /** - * Skips over a run of zero or more Pattern_White_Space characters at pos in text. - */ - private static int skipPatternWhiteSpace(String text, int pos) { - while (pos < text.length()) { - int c = UTF16.charAt(text, pos); - if (!PatternProps.isWhiteSpace(c)) { - break; - } - pos += UTF16.getCharCount(c); - } - return pos; - } - - /** - * Skips over a run of zero or more isUWhiteSpace() characters at pos in text. - */ - private static int skipUWhiteSpace(String text, int pos) { - while (pos < text.length()) { - int c = UTF16.charAt(text, pos); - if (!UCharacter.isUWhiteSpace(c)) { - break; - } - pos += UTF16.getCharCount(c); - } - return pos; - } - - /** - * Skips over a run of zero or more bidi marks at pos in text. - */ - private static int skipBidiMarks(String text, int pos) { - while (pos < text.length()) { - int c = UTF16.charAt(text, pos); - if (!isBidiMark(c)) { - break; - } - pos += UTF16.getCharCount(c); - } - return pos; - } - - /** - * Returns the length matched by the given affix, or -1 if none. - * - * @param affixPat pattern string - * @param text input text - * @param pos offset into input at which to begin matching - * @param type parse against currency type, LONG_NAME only or not. - * @param currency return value for parsed currency, for generic - * currency parsing mode, or null for normal parsing. In generic - * currency parsing mode, any currency is parsed, not just the - * currency that this formatter is set to. - * @return position after the matched text, or -1 if match failure - */ - private int compareComplexAffix(String affixPat, String text, int pos, int type, - Currency[] currency) { - int start = pos; - for (int i = 0; i < affixPat.length() && pos >= 0;) { - char c = affixPat.charAt(i++); - if (c == QUOTE) { - for (;;) { - int j = affixPat.indexOf(QUOTE, i); - if (j == i) { - pos = match(text, pos, QUOTE); - i = j + 1; - break; - } else if (j > i) { - pos = match(text, pos, affixPat.substring(i, j)); - i = j + 1; - if (i < affixPat.length() && affixPat.charAt(i) == QUOTE) { - pos = match(text, pos, QUOTE); - ++i; - // loop again - } else { - break; - } - } else { - // Unterminated quote; should be caught by apply - // pattern. - throw new RuntimeException(); - } - } - continue; - } - - String affix = null; - - switch (c) { - case CURRENCY_SIGN: - // since the currency names in choice format is saved the same way as - // other currency names, do not need to do currency choice parsing here. - // the general currency parsing parse against all names, including names - // in choice format. assert(currency != null || (getCurrency() != null && - // currencyChoice != null)); - boolean intl = i < affixPat.length() && affixPat.charAt(i) == CURRENCY_SIGN; - if (intl) { - ++i; - } - boolean plural = i < affixPat.length() && affixPat.charAt(i) == CURRENCY_SIGN; - if (plural) { - ++i; - intl = false; - } - // Parse generic currency -- anything for which we have a display name, or - // any 3-letter ISO code. Try to parse display name for our locale; first - // determine our locale. TODO: use locale in CurrencyPluralInfo - ULocale uloc = getLocale(ULocale.VALID_LOCALE); - if (uloc == null) { - // applyPattern has been called; use the symbols - uloc = symbols.getLocale(ULocale.VALID_LOCALE); - } - // Delegate parse of display name => ISO code to Currency - ParsePosition ppos = new ParsePosition(pos); - // using Currency.parse to handle mixed style parsing. - String iso = Currency.parse(uloc, text, type, ppos); - - // If parse succeeds, populate currency[0] - if (iso != null) { - if (currency != null) { - currency[0] = Currency.getInstance(iso); - } else { - // The formatter is currency-style but the client has not requested - // the value of the parsed currency. In this case, if that value does - // not match the formatter's current value, then the parse fails. - Currency effectiveCurr = getEffectiveCurrency(); - if (iso.compareTo(effectiveCurr.getCurrencyCode()) != 0) { - pos = -1; - continue; - } - } - pos = ppos.getIndex(); - } else { - pos = -1; - } - continue; - case PATTERN_PERCENT: - affix = symbols.getPercentString(); - break; - case PATTERN_PER_MILLE: - affix = symbols.getPerMillString(); - break; - case PATTERN_PLUS_SIGN: - affix = symbols.getPlusSignString(); - break; - case PATTERN_MINUS_SIGN: - affix = symbols.getMinusSignString(); - break; - default: - // fall through to affix != null test, which will fail - break; - } - - if (affix != null) { - pos = match(text, pos, affix); - continue; - } - - pos = match(text, pos, c); - if (PatternProps.isWhiteSpace(c)) { - i = skipPatternWhiteSpace(affixPat, i); - } - } - - return pos - start; - } - - /** - * Matches a single character at text[pos] and return the index of the next character - * upon success. Return -1 on failure. If ch is a Pattern_White_Space then match a run of - * white space in text. - */ - static final int match(String text, int pos, int ch) { - if (pos < 0 || pos >= text.length()) { - return -1; - } - pos = skipBidiMarks(text, pos); - if (PatternProps.isWhiteSpace(ch)) { - // Advance over run of white space in input text - // Must see at least one white space char in input - int s = pos; - pos = skipPatternWhiteSpace(text, pos); - if (pos == s) { - return -1; - } - return pos; - } - if (pos >= text.length() || UTF16.charAt(text, pos) != ch) { - return -1; - } - pos = skipBidiMarks(text, pos + UTF16.getCharCount(ch)); - return pos; - } - - /** - * Matches a string at text[pos] and return the index of the next character upon - * success. Return -1 on failure. Match a run of white space in str with a run of - * white space in text. - */ - static final int match(String text, int pos, String str) { - for (int i = 0; i < str.length() && pos >= 0;) { - int ch = UTF16.charAt(str, i); - i += UTF16.getCharCount(ch); - if (isBidiMark(ch)) { - continue; - } - pos = match(text, pos, ch); - if (PatternProps.isWhiteSpace(ch)) { - i = skipPatternWhiteSpace(str, i); - } - } - return pos; - } - - /** - * Returns a copy of the decimal format symbols used by this format. - * - * @return desired DecimalFormatSymbols - * @see DecimalFormatSymbols - * @stable ICU 2.0 - */ - public DecimalFormatSymbols getDecimalFormatSymbols() { - try { - // don't allow multiple references - return (DecimalFormatSymbols) symbols.clone(); - } catch (Exception foo) { - return null; // should never happen - } - } - - /** - * Sets the decimal format symbols used by this format. The format uses a copy of the - * provided symbols. - * - * @param newSymbols desired DecimalFormatSymbols - * @see DecimalFormatSymbols - * @stable ICU 2.0 - */ - public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) { - symbols = (DecimalFormatSymbols) newSymbols.clone(); - setCurrencyForSymbols(); - expandAffixes(null); - } - - /** - * Update the currency object to match the symbols. This method is used only when the - * caller has passed in a symbols object that may not be the default object for its - * locale. - */ - private void setCurrencyForSymbols() { - - // Bug 4212072 Update the affix strings according to symbols in order to keep the - // affix strings up to date. [Richard/GCL] - - // With the introduction of the Currency object, the currency symbols in the DFS - // object are ignored. For backward compatibility, we check any explicitly set DFS - // object. If it is a default symbols object for its locale, we change the - // currency object to one for that locale. If it is custom, we set the currency to - // null. - DecimalFormatSymbols def = new DecimalFormatSymbols(symbols.getULocale()); - - if (symbols.getCurrencySymbol().equals(def.getCurrencySymbol()) - && symbols.getInternationalCurrencySymbol() - .equals(def.getInternationalCurrencySymbol())) { - setCurrency(Currency.getInstance(symbols.getULocale())); - } else { - setCurrency(null); - } - } - - /** - * Returns the positive prefix. - * - *

Examples: +123, $123, sFr123 - * @return the prefix - * @stable ICU 2.0 - */ - public String getPositivePrefix() { - return positivePrefix; - } - - /** - * Sets the positive prefix. - * - *

Examples: +123, $123, sFr123 - * @param newValue the prefix - * @stable ICU 2.0 - */ - public void setPositivePrefix(String newValue) { - positivePrefix = newValue; - posPrefixPattern = null; - } - - /** - * Returns the negative prefix. - * - *

Examples: -123, ($123) (with negative suffix), sFr-123 - * - * @return the prefix - * @stable ICU 2.0 - */ - public String getNegativePrefix() { - return negativePrefix; - } - - /** - * Sets the negative prefix. - * - *

Examples: -123, ($123) (with negative suffix), sFr-123 - * @param newValue the prefix - * @stable ICU 2.0 - */ - public void setNegativePrefix(String newValue) { - negativePrefix = newValue; - negPrefixPattern = null; - } - - /** - * Returns the positive suffix. - * - *

Example: 123% - * - * @return the suffix - * @stable ICU 2.0 - */ - public String getPositiveSuffix() { - return positiveSuffix; - } - - /** - * Sets the positive suffix. - * - *

Example: 123% - * @param newValue the suffix - * @stable ICU 2.0 - */ - public void setPositiveSuffix(String newValue) { - positiveSuffix = newValue; - posSuffixPattern = null; - } - - /** - * Returns the negative suffix. - * - *

Examples: -123%, ($123) (with positive suffixes) - * - * @return the suffix - * @stable ICU 2.0 - */ - public String getNegativeSuffix() { - return negativeSuffix; - } - - /** - * Sets the positive suffix. - * - *

Examples: 123% - * @param newValue the suffix - * @stable ICU 2.0 - */ - public void setNegativeSuffix(String newValue) { - negativeSuffix = newValue; - negSuffixPattern = null; - } - - /** - * Returns the multiplier for use in percent, permill, etc. For a percentage, set the - * suffixes to have "%" and the multiplier to be 100. (For Arabic, use arabic percent - * symbol). For a permill, set the suffixes to have "\u2031" and the multiplier to be - * 1000. - * - *

Examples: with 100, 1.23 -> "123", and "123" -> 1.23 - * - * @return the multiplier - * @stable ICU 2.0 - */ - public int getMultiplier() { - return multiplier; - } - - /** - * Sets the multiplier for use in percent, permill, etc. For a percentage, set the - * suffixes to have "%" and the multiplier to be 100. (For Arabic, use arabic percent - * symbol). For a permill, set the suffixes to have "\u2031" and the multiplier to be - * 1000. - * - *

Examples: with 100, 1.23 -> "123", and "123" -> 1.23 - * - * @param newValue the multiplier - * @stable ICU 2.0 - */ - public void setMultiplier(int newValue) { - if (newValue == 0) { - throw new IllegalArgumentException("Bad multiplier: " + newValue); - } - multiplier = newValue; - } - - /** - * {@icu} Returns the rounding increment. - * - * @return A positive rounding increment, or null if a custom rounding - * increment is not in effect. - * @see #setRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - * @stable ICU 2.0 - */ - public java.math.BigDecimal getRoundingIncrement() { - if (roundingIncrementICU == null) - return null; - return roundingIncrementICU.toBigDecimal(); - } - - /** - * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers - * will be rounded to the number of digits displayed. - * - * @param newValue A positive rounding increment, or null or - * BigDecimal(0.0) to use the default rounding increment. - * @throws IllegalArgumentException if newValue is < 0.0 - * @see #getRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - * @stable ICU 2.0 - */ - public void setRoundingIncrement(java.math.BigDecimal newValue) { - if (newValue == null) { - setRoundingIncrement((BigDecimal) null); - } else { - setRoundingIncrement(new BigDecimal(newValue)); - } - } - - /** - * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers - * will be rounded to the number of digits displayed. - * - * @param newValue A positive rounding increment, or null or - * BigDecimal(0.0) to use the default rounding increment. - * @throws IllegalArgumentException if newValue is < 0.0 - * @see #getRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - * @stable ICU 3.6 - */ - public void setRoundingIncrement(BigDecimal newValue) { - int i = newValue == null ? 0 : newValue.compareTo(BigDecimal.ZERO); - if (i < 0) { - throw new IllegalArgumentException("Illegal rounding increment"); - } - if (i == 0) { - setInternalRoundingIncrement(null); - } else { - setInternalRoundingIncrement(newValue); - } - resetActualRounding(); - } - - /** - * {@icu} Sets the rounding increment. In the absence of a rounding increment, numbers - * will be rounded to the number of digits displayed. - * - * @param newValue A positive rounding increment, or 0.0 to use the default - * rounding increment. - * @throws IllegalArgumentException if newValue is < 0.0 - * @see #getRoundingIncrement - * @see #getRoundingMode - * @see #setRoundingMode - * @stable ICU 2.0 - */ - public void setRoundingIncrement(double newValue) { - if (newValue < 0.0) { - throw new IllegalArgumentException("Illegal rounding increment"); - } - if (newValue == 0.0d) { - setInternalRoundingIncrement((BigDecimal) null); - } else { - // Should use BigDecimal#valueOf(double) instead of constructor - // to avoid the double precision problem. - setInternalRoundingIncrement(BigDecimal.valueOf(newValue)); - } - resetActualRounding(); - } - - /** - * Returns the rounding mode. - * - * @return A rounding mode, between BigDecimal.ROUND_UP and - * BigDecimal.ROUND_UNNECESSARY. - * @see #setRoundingIncrement - * @see #getRoundingIncrement - * @see #setRoundingMode - * @see java.math.BigDecimal - * @stable ICU 2.0 - */ - @Override - public int getRoundingMode() { - return roundingMode; - } - - /** - * Sets the rounding mode. This has no effect unless the rounding increment is greater - * than zero. - * - * @param roundingMode A rounding mode, between BigDecimal.ROUND_UP and - * BigDecimal.ROUND_UNNECESSARY. - * @exception IllegalArgumentException if roundingMode is unrecognized. - * @see #setRoundingIncrement - * @see #getRoundingIncrement - * @see #getRoundingMode - * @see java.math.BigDecimal - * @stable ICU 2.0 - */ - @Override - public void setRoundingMode(int roundingMode) { - if (roundingMode < BigDecimal.ROUND_UP || roundingMode > BigDecimal.ROUND_UNNECESSARY) { - throw new IllegalArgumentException("Invalid rounding mode: " + roundingMode); - } - - this.roundingMode = roundingMode; - resetActualRounding(); - } - - /** - * Returns the width to which the output of format() is padded. The width is - * counted in 16-bit code units. - * - * @return the format width, or zero if no padding is in effect - * @see #setFormatWidth - * @see #getPadCharacter - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - * @stable ICU 2.0 - */ - public int getFormatWidth() { - return formatWidth; - } - - /** - * Sets the width to which the output of format() is - * padded. The width is counted in 16-bit code units. This method - * also controls whether padding is enabled. - * - * @param width the width to which to pad the result of - * format(), or zero to disable padding - * @exception IllegalArgumentException if width is < 0 - * @see #getFormatWidth - * @see #getPadCharacter - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - * @stable ICU 2.0 - */ - public void setFormatWidth(int width) { - if (width < 0) { - throw new IllegalArgumentException("Illegal format width"); - } - formatWidth = width; - } - - /** - * {@icu} Returns the character used to pad to the format width. The default is ' '. - * - * @return the pad character - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadPosition - * @see #setPadPosition - * @stable ICU 2.0 - */ - public char getPadCharacter() { - return pad; - } - - /** - * {@icu} Sets the character used to pad to the format width. If padding is not - * enabled, then this will take effect if padding is later enabled. - * - * @param padChar the pad character - * @see #setFormatWidth - * @see #getFormatWidth - * @see #getPadCharacter - * @see #getPadPosition - * @see #setPadPosition - * @stable ICU 2.0 - */ - public void setPadCharacter(char padChar) { - pad = padChar; - } - - /** - * {@icu} Returns the position at which padding will take place. This is the location at - * which padding will be inserted if the result of format() is shorter - * than the format width. - * - * @return the pad position, one of PAD_BEFORE_PREFIX, - * PAD_AFTER_PREFIX, PAD_BEFORE_SUFFIX, or - * PAD_AFTER_SUFFIX. - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadCharacter - * @see #setPadPosition - * @see #PAD_BEFORE_PREFIX - * @see #PAD_AFTER_PREFIX - * @see #PAD_BEFORE_SUFFIX - * @see #PAD_AFTER_SUFFIX - * @stable ICU 2.0 - */ - public int getPadPosition() { - return padPosition; - } - - /** - * {@icu} Sets the position at which padding will take place. This is the location at - * which padding will be inserted if the result of format() is shorter - * than the format width. This has no effect unless padding is enabled. - * - * @param padPos the pad position, one of PAD_BEFORE_PREFIX, - * PAD_AFTER_PREFIX, PAD_BEFORE_SUFFIX, or - * PAD_AFTER_SUFFIX. - * @exception IllegalArgumentException if the pad position in unrecognized - * @see #setFormatWidth - * @see #getFormatWidth - * @see #setPadCharacter - * @see #getPadCharacter - * @see #getPadPosition - * @see #PAD_BEFORE_PREFIX - * @see #PAD_AFTER_PREFIX - * @see #PAD_BEFORE_SUFFIX - * @see #PAD_AFTER_SUFFIX - * @stable ICU 2.0 - */ - public void setPadPosition(int padPos) { - if (padPos < PAD_BEFORE_PREFIX || padPos > PAD_AFTER_SUFFIX) { - throw new IllegalArgumentException("Illegal pad position"); - } - padPosition = padPos; - } - - /** - * {@icu} Returns whether or not scientific notation is used. - * - * @return true if this object formats and parses scientific notation - * @see #setScientificNotation - * @see #getMinimumExponentDigits - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - * @stable ICU 2.0 - */ - public boolean isScientificNotation() { - return useExponentialNotation; - } - - /** - * {@icu} Sets whether or not scientific notation is used. When scientific notation is - * used, the effective maximum number of integer digits is <= 8. If the maximum number - * of integer digits is set to more than 8, the effective maximum will be 1. This - * allows this call to generate a 'default' scientific number format without - * additional changes. - * - * @param useScientific true if this object formats and parses scientific notation - * @see #isScientificNotation - * @see #getMinimumExponentDigits - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - * @stable ICU 2.0 - */ - public void setScientificNotation(boolean useScientific) { - useExponentialNotation = useScientific; - } - - /** - * {@icu} Returns the minimum exponent digits that will be shown. - * - * @return the minimum exponent digits that will be shown - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - * @stable ICU 2.0 - */ - public byte getMinimumExponentDigits() { - return minExponentDigits; - } - - /** - * {@icu} Sets the minimum exponent digits that will be shown. This has no effect - * unless scientific notation is in use. - * - * @param minExpDig a value >= 1 indicating the fewest exponent - * digits that will be shown - * @exception IllegalArgumentException if minExpDig < 1 - * @see #setScientificNotation - * @see #isScientificNotation - * @see #getMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @see #setExponentSignAlwaysShown - * @stable ICU 2.0 - */ - public void setMinimumExponentDigits(byte minExpDig) { - if (minExpDig < 1) { - throw new IllegalArgumentException("Exponent digits must be >= 1"); - } - minExponentDigits = minExpDig; - } - - /** - * {@icu} Returns whether the exponent sign is always shown. - * - * @return true if the exponent is always prefixed with either the localized minus - * sign or the localized plus sign, false if only negative exponents are prefixed with - * the localized minus sign. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #getMinimumExponentDigits - * @see #setExponentSignAlwaysShown - * @stable ICU 2.0 - */ - public boolean isExponentSignAlwaysShown() { - return exponentSignAlwaysShown; - } - - /** - * {@icu} Sets whether the exponent sign is always shown. This has no effect unless - * scientific notation is in use. - * - * @param expSignAlways true if the exponent is always prefixed with either the - * localized minus sign or the localized plus sign, false if only negative exponents - * are prefixed with the localized minus sign. - * @see #setScientificNotation - * @see #isScientificNotation - * @see #setMinimumExponentDigits - * @see #getMinimumExponentDigits - * @see #isExponentSignAlwaysShown - * @stable ICU 2.0 - */ - public void setExponentSignAlwaysShown(boolean expSignAlways) { - exponentSignAlwaysShown = expSignAlways; - } - - /** - * Returns the grouping size. Grouping size is the number of digits between grouping - * separators in the integer portion of a number. For example, in the number - * "123,456.78", the grouping size is 3. - * - * @see #setGroupingSize - * @see NumberFormat#isGroupingUsed - * @see DecimalFormatSymbols#getGroupingSeparator - * @stable ICU 2.0 - */ - public int getGroupingSize() { - return groupingSize; - } - - /** - * Sets the grouping size. Grouping size is the number of digits between grouping - * separators in the integer portion of a number. For example, in the number - * "123,456.78", the grouping size is 3. - * - * @see #getGroupingSize - * @see NumberFormat#setGroupingUsed - * @see DecimalFormatSymbols#setGroupingSeparator - * @stable ICU 2.0 - */ - public void setGroupingSize(int newValue) { - groupingSize = (byte) newValue; - } - - /** - * {@icu} Returns the secondary grouping size. In some locales one grouping interval - * is used for the least significant integer digits (the primary grouping size), and - * another is used for all others (the secondary grouping size). A formatter - * supporting a secondary grouping size will return a positive integer unequal to the - * primary grouping size returned by getGroupingSize(). For example, if - * the primary grouping size is 4, and the secondary grouping size is 2, then the - * number 123456789 formats as "1,23,45,6789", and the pattern appears as "#,##,###0". - * - * @return the secondary grouping size, or a value less than one if there is none - * @see #setSecondaryGroupingSize - * @see NumberFormat#isGroupingUsed - * @see DecimalFormatSymbols#getGroupingSeparator - * @stable ICU 2.0 - */ - public int getSecondaryGroupingSize() { - return groupingSize2; - } - - /** - * {@icu} Sets the secondary grouping size. If set to a value less than 1, then - * secondary grouping is turned off, and the primary grouping size is used for all - * intervals, not just the least significant. - * - * @see #getSecondaryGroupingSize - * @see NumberFormat#setGroupingUsed - * @see DecimalFormatSymbols#setGroupingSeparator - * @stable ICU 2.0 - */ - public void setSecondaryGroupingSize(int newValue) { - groupingSize2 = (byte) newValue; - } - - /** - * {@icu} Returns the MathContext used by this format. - * - * @return desired MathContext - * @see #getMathContext - * @stable ICU 4.2 - */ - public MathContext getMathContextICU() { - return mathContext; - } - - /** - * {@icu} Returns the MathContext used by this format. - * - * @return desired MathContext - * @see #getMathContext - * @stable ICU 4.2 - */ - public java.math.MathContext getMathContext() { - try { - // don't allow multiple references - return mathContext == null ? null : new java.math.MathContext(mathContext.getDigits(), - java.math.RoundingMode.valueOf(mathContext.getRoundingMode())); - } catch (Exception foo) { - return null; // should never happen - } - } - - /** - * {@icu} Sets the MathContext used by this format. - * - * @param newValue desired MathContext - * @see #getMathContext - * @stable ICU 4.2 - */ - public void setMathContextICU(MathContext newValue) { - mathContext = newValue; - } - - /** - * {@icu} Sets the MathContext used by this format. - * - * @param newValue desired MathContext - * @see #getMathContext - * @stable ICU 4.2 - */ - public void setMathContext(java.math.MathContext newValue) { - mathContext = new MathContext(newValue.getPrecision(), MathContext.SCIENTIFIC, false, - (newValue.getRoundingMode()).ordinal()); - } - - /** - * Returns the behavior of the decimal separator with integers. (The decimal - * separator will always appear with decimals.)

Example: Decimal ON: 12345 -> - * 12345.; OFF: 12345 -> 12345 - * - * @stable ICU 2.0 - */ - public boolean isDecimalSeparatorAlwaysShown() { - return decimalSeparatorAlwaysShown; - } - - /** - * When decimal match is not required, the input does not have to - * contain a decimal mark when there is a decimal mark specified in the - * pattern. - * @param value true if input must contain a match to decimal mark in pattern - * Default is false. - * @stable ICU 54 - */ - public void setDecimalPatternMatchRequired(boolean value) { - parseRequireDecimalPoint = value; - } - - /** - * {@icu} Returns whether the input to parsing must contain a decimal mark if there - * is a decimal mark in the pattern. - * @return true if input must contain a match to decimal mark in pattern - * @stable ICU 54 - */ - public boolean isDecimalPatternMatchRequired() { - return parseRequireDecimalPoint; - } - - - /** - * Sets the behavior of the decimal separator with integers. (The decimal separator - * will always appear with decimals.) - * - *

This only affects formatting, and only where there might be no digits after the - * decimal point, e.g., if true, 3456.00 -> "3,456." if false, 3456.00 -> "3456" This - * is independent of parsing. If you want parsing to stop at the decimal point, use - * setParseIntegerOnly. - * - *

- * Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345 - * - * @stable ICU 2.0 - */ - public void setDecimalSeparatorAlwaysShown(boolean newValue) { - decimalSeparatorAlwaysShown = newValue; - } - - /** - * {@icu} Returns a copy of the CurrencyPluralInfo used by this format. It might - * return null if the decimal format is not a plural type currency decimal - * format. Plural type currency decimal format means either the pattern in the decimal - * format contains 3 currency signs, or the decimal format is initialized with - * PLURALCURRENCYSTYLE. - * - * @return desired CurrencyPluralInfo - * @see CurrencyPluralInfo - * @stable ICU 4.2 - */ - public CurrencyPluralInfo getCurrencyPluralInfo() { - try { - // don't allow multiple references - return currencyPluralInfo == null ? null : - (CurrencyPluralInfo) currencyPluralInfo.clone(); - } catch (Exception foo) { - return null; // should never happen - } - } - - /** - * {@icu} Sets the CurrencyPluralInfo used by this format. The format uses a copy of - * the provided information. - * - * @param newInfo desired CurrencyPluralInfo - * @see CurrencyPluralInfo - * @stable ICU 4.2 - */ - public void setCurrencyPluralInfo(CurrencyPluralInfo newInfo) { - currencyPluralInfo = (CurrencyPluralInfo) newInfo.clone(); - isReadyForParsing = false; - } - - /** - * Overrides clone. - * @stable ICU 2.0 - */ - @Override - public Object clone() { - try { - DecimalFormat_ICU58 other = (DecimalFormat_ICU58) super.clone(); - other.symbols = (DecimalFormatSymbols) symbols.clone(); - other.digitList = new DigitList(); // fix for JB#5358 - if (currencyPluralInfo != null) { - other.currencyPluralInfo = (CurrencyPluralInfo) currencyPluralInfo.clone(); - } - other.attributes = new ArrayList<>(); // #9240 - other.currencyUsage = currencyUsage; - - // TODO: We need to figure out whether we share a single copy of DigitList by - // multiple cloned copies. format/subformat are designed to use a single - // instance, but parse/subparse implementation is not. - return other; - } catch (Exception e) { - throw new IllegalStateException(); - } - } - - /** - * Overrides equals. - * @stable ICU 2.0 - */ - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (!super.equals(obj)) - return false; // super does class check - - DecimalFormat_ICU58 other = (DecimalFormat_ICU58) obj; - // Add the comparison of the four new added fields ,they are posPrefixPattern, - // posSuffixPattern, negPrefixPattern, negSuffixPattern. [Richard/GCL] - // following are added to accomodate changes for currency plural format. - return currencySignCount == other.currencySignCount - && (style != NumberFormat.PLURALCURRENCYSTYLE || - equals(posPrefixPattern, other.posPrefixPattern) - && equals(posSuffixPattern, other.posSuffixPattern) - && equals(negPrefixPattern, other.negPrefixPattern) - && equals(negSuffixPattern, other.negSuffixPattern)) - && multiplier == other.multiplier - && groupingSize == other.groupingSize - && groupingSize2 == other.groupingSize2 - && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown - && useExponentialNotation == other.useExponentialNotation - && (!useExponentialNotation || minExponentDigits == other.minExponentDigits) - && useSignificantDigits == other.useSignificantDigits - && (!useSignificantDigits || minSignificantDigits == other.minSignificantDigits - && maxSignificantDigits == other.maxSignificantDigits) - && symbols.equals(other.symbols) - && Objects.equals(currencyPluralInfo, other.currencyPluralInfo) - && currencyUsage.equals(other.currencyUsage); - } - - // method to unquote the strings and compare - private boolean equals(String pat1, String pat2) { - if (pat1 == null || pat2 == null) { - return (pat1 == null && pat2 == null); - } - // fast path - if (pat1.equals(pat2)) { - return true; - } - return unquote(pat1).equals(unquote(pat2)); - } - - private String unquote(String pat) { - StringBuilder buf = new StringBuilder(pat.length()); - int i = 0; - while (i < pat.length()) { - char ch = pat.charAt(i++); - if (ch != QUOTE) { - buf.append(ch); - } - } - return buf.toString(); - } - - // protected void handleToString(StringBuffer buf) { - // buf.append("\nposPrefixPattern: '" + posPrefixPattern + "'\n"); - // buf.append("positivePrefix: '" + positivePrefix + "'\n"); - // buf.append("posSuffixPattern: '" + posSuffixPattern + "'\n"); - // buf.append("positiveSuffix: '" + positiveSuffix + "'\n"); - // buf.append("negPrefixPattern: '" + - // com.ibm.icu.impl.Utility.format1ForSource(negPrefixPattern) + "'\n"); - // buf.append("negativePrefix: '" + - // com.ibm.icu.impl.Utility.format1ForSource(negativePrefix) + "'\n"); - // buf.append("negSuffixPattern: '" + negSuffixPattern + "'\n"); - // buf.append("negativeSuffix: '" + negativeSuffix + "'\n"); - // buf.append("multiplier: '" + multiplier + "'\n"); - // buf.append("groupingSize: '" + groupingSize + "'\n"); - // buf.append("groupingSize2: '" + groupingSize2 + "'\n"); - // buf.append("decimalSeparatorAlwaysShown: '" + decimalSeparatorAlwaysShown + "'\n"); - // buf.append("useExponentialNotation: '" + useExponentialNotation + "'\n"); - // buf.append("minExponentDigits: '" + minExponentDigits + "'\n"); - // buf.append("useSignificantDigits: '" + useSignificantDigits + "'\n"); - // buf.append("minSignificantDigits: '" + minSignificantDigits + "'\n"); - // buf.append("maxSignificantDigits: '" + maxSignificantDigits + "'\n"); - // buf.append("symbols: '" + symbols + "'"); - // } - - /** - * Overrides hashCode. - * @stable ICU 2.0 - */ - @Override - public int hashCode() { - return super.hashCode() * 37 + positivePrefix.hashCode(); - // just enough fields for a reasonable distribution - } - - /** - * Synthesizes a pattern string that represents the current state of this Format - * object. - * - * @see #applyPattern - * @stable ICU 2.0 - */ - public String toPattern() { - if (style == NumberFormat.PLURALCURRENCYSTYLE) { - // the prefix or suffix pattern might not be defined yet, so they can not be - // synthesized, instead, get them directly. but it might not be the actual - // pattern used in formatting. the actual pattern used in formatting depends - // on the formatted number's plural count. - return formatPattern; - } - return toPattern(false); - } - - /** - * Synthesizes a localized pattern string that represents the current state of this - * Format object. - * - * @see #applyPattern - * @stable ICU 2.0 - */ - public String toLocalizedPattern() { - if (style == NumberFormat.PLURALCURRENCYSTYLE) { - return formatPattern; - } - return toPattern(true); - } - - /** - * Expands the affix pattern strings into the expanded affix strings. If any affix - * pattern string is null, do not expand it. This method should be called any time the - * symbols or the affix patterns change in order to keep the expanded affix strings up - * to date. This method also will be called before formatting if format currency - * plural names, since the plural name is not a static one, it is based on the - * currency plural count, the affix will be known only after the currency plural count - * is know. In which case, the parameter 'pluralCount' will be a non-null currency - * plural count. In all other cases, the 'pluralCount' is null, which means it is not - * needed. - */ - // Bug 4212072 [Richard/GCL] - private void expandAffixes(String pluralCount) { - // expandAffix() will set currencyChoice to a non-null value if - // appropriate AND if it is null. - currencyChoice = null; - - // Reuse one StringBuffer for better performance - StringBuffer buffer = new StringBuffer(); - if (posPrefixPattern != null) { - expandAffix(posPrefixPattern, pluralCount, buffer); - positivePrefix = buffer.toString(); - } - if (posSuffixPattern != null) { - expandAffix(posSuffixPattern, pluralCount, buffer); - positiveSuffix = buffer.toString(); - } - if (negPrefixPattern != null) { - expandAffix(negPrefixPattern, pluralCount, buffer); - negativePrefix = buffer.toString(); - } - if (negSuffixPattern != null) { - expandAffix(negSuffixPattern, pluralCount, buffer); - negativeSuffix = buffer.toString(); - } - } - - /** - * Expands an affix pattern into an affix string. All characters in the pattern are - * literal unless bracketed by QUOTEs. The following characters outside QUOTE are - * recognized: PATTERN_PERCENT, PATTERN_PER_MILLE, PATTERN_MINUS, and - * CURRENCY_SIGN. If CURRENCY_SIGN is doubled, it is interpreted as an international - * currency sign. If CURRENCY_SIGN is tripled, it is interpreted as currency plural - * long names, such as "US Dollars". Any other character outside QUOTE represents - * itself. Quoted text must be well-formed. - * - * This method is used in two distinct ways. First, it is used to expand the stored - * affix patterns into actual affixes. For this usage, doFormat must be false. Second, - * it is used to expand the stored affix patterns given a specific number (doFormat == - * true), for those rare cases in which a currency format references a ChoiceFormat - * (e.g., en_IN display name for INR). The number itself is taken from digitList. - * TODO: There are no currency ChoiceFormat patterns, figure out what is still relevant here. - * - * When used in the first way, this method has a side effect: It sets currencyChoice - * to a ChoiceFormat object, if the currency's display name in this locale is a - * ChoiceFormat pattern (very rare). It only does this if currencyChoice is null to - * start with. - * - * @param pattern the non-null, possibly empty pattern - * @param pluralCount the plural count. It is only used for currency plural format. In - * which case, it is the plural count of the currency amount. For example, in en_US, - * it is the singular "one", or the plural "other". For all other cases, it is null, - * and is not being used. - * @param buffer a scratch StringBuffer; its contents will be lost - */ - // Bug 4212072 [Richard/GCL] - private void expandAffix(String pattern, String pluralCount, StringBuffer buffer) { - buffer.setLength(0); - for (int i = 0; i < pattern.length();) { - char c = pattern.charAt(i++); - if (c == QUOTE) { - for (;;) { - int j = pattern.indexOf(QUOTE, i); - if (j == i) { - buffer.append(QUOTE); - i = j + 1; - break; - } else if (j > i) { - buffer.append(pattern.substring(i, j)); - i = j + 1; - if (i < pattern.length() && pattern.charAt(i) == QUOTE) { - buffer.append(QUOTE); - ++i; - // loop again - } else { - break; - } - } else { - // Unterminated quote; should be caught by apply - // pattern. - throw new RuntimeException(); - } - } - continue; - } - - switch (c) { - case CURRENCY_SIGN: - // As of ICU 2.2 we use the currency object, and ignore the currency - // symbols in the DFS, unless we have a null currency object. This occurs - // if resurrecting a pre-2.2 object or if the user sets a custom DFS. - boolean intl = i < pattern.length() && pattern.charAt(i) == CURRENCY_SIGN; - boolean plural = false; - if (intl) { - ++i; - if (i < pattern.length() && pattern.charAt(i) == CURRENCY_SIGN) { - plural = true; - intl = false; - ++i; - } - } - String s = null; - Currency currency = getCurrency(); - if (currency != null) { - // plural name is only needed when pluralCount != null, which means - // when formatting currency plural names. For other cases, - // pluralCount == null, and plural names are not needed. - if (plural && pluralCount != null) { - s = currency.getName(symbols.getULocale(), Currency.PLURAL_LONG_NAME, - pluralCount, null); - } else if (!intl) { - s = currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null); - } else { - s = currency.getCurrencyCode(); - } - } else { - s = intl ? symbols.getInternationalCurrencySymbol() : - symbols.getCurrencySymbol(); - } - // Here is where FieldPosition could be set for CURRENCY PLURAL. - buffer.append(s); - break; - case PATTERN_PERCENT: - buffer.append(symbols.getPercentString()); - break; - case PATTERN_PER_MILLE: - buffer.append(symbols.getPerMillString()); - break; - case PATTERN_MINUS_SIGN: - buffer.append(symbols.getMinusSignString()); - break; - default: - buffer.append(c); - break; - } - } - } - - /** - * Append an affix to the given StringBuffer. - * - * @param buf - * buffer to append to - * @param isNegative - * @param isPrefix - * @param fieldPosition - * @param parseAttr - */ - private int appendAffix(StringBuffer buf, boolean isNegative, boolean isPrefix, - FieldPosition fieldPosition, - boolean parseAttr) { - if (currencyChoice != null) { - String affixPat = null; - if (isPrefix) { - affixPat = isNegative ? negPrefixPattern : posPrefixPattern; - } else { - affixPat = isNegative ? negSuffixPattern : posSuffixPattern; - } - StringBuffer affixBuf = new StringBuffer(); - expandAffix(affixPat, null, affixBuf); - buf.append(affixBuf); - return affixBuf.length(); - } - - String affix = null; - String pattern; - if (isPrefix) { - affix = isNegative ? negativePrefix : positivePrefix; - pattern = isNegative ? negPrefixPattern : posPrefixPattern; - } else { - affix = isNegative ? negativeSuffix : positiveSuffix; - pattern = isNegative ? negSuffixPattern : posSuffixPattern; - } - // [Spark/CDL] Invoke formatAffix2Attribute to add attributes for affix - if (parseAttr) { - // Updates for Ticket 11805. - int offset = affix.indexOf(symbols.getCurrencySymbol()); - if (offset > -1) { - formatAffix2Attribute(isPrefix, Field.CURRENCY, buf, offset, - symbols.getCurrencySymbol().length()); - } - offset = affix.indexOf(symbols.getMinusSignString()); - if (offset > -1) { - formatAffix2Attribute(isPrefix, Field.SIGN, buf, offset, - symbols.getMinusSignString().length()); - } - offset = affix.indexOf(symbols.getPercentString()); - if (offset > -1) { - formatAffix2Attribute(isPrefix, Field.PERCENT, buf, offset, - symbols.getPercentString().length()); - } - offset = affix.indexOf(symbols.getPerMillString()); - if (offset > -1) { - formatAffix2Attribute(isPrefix, Field.PERMILLE, buf, offset, - symbols.getPerMillString().length()); - } - offset = pattern.indexOf("¤¤¤"); - if (offset > -1) { - formatAffix2Attribute(isPrefix, Field.CURRENCY, buf, offset, - affix.length() - offset); - } - } - - // Look for SIGN, PERCENT, PERMILLE in the formatted affix. - if (fieldPosition.getFieldAttribute() == NumberFormat.Field.SIGN) { - String sign = isNegative ? symbols.getMinusSignString() : symbols.getPlusSignString(); - int firstPos = affix.indexOf(sign); - if (firstPos > -1) { - int startPos = buf.length() + firstPos; - fieldPosition.setBeginIndex(startPos); - fieldPosition.setEndIndex(startPos + sign.length()); - } - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERCENT) { - int firstPos = affix.indexOf(symbols.getPercentString()); - if (firstPos > -1) { - int startPos = buf.length() + firstPos; - fieldPosition.setBeginIndex(startPos); - fieldPosition.setEndIndex(startPos + symbols.getPercentString().length()); - } - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERMILLE) { - int firstPos = affix.indexOf(symbols.getPerMillString()); - if (firstPos > -1) { - int startPos = buf.length() + firstPos; - fieldPosition.setBeginIndex(startPos); - fieldPosition.setEndIndex(startPos + symbols.getPerMillString().length()); - } - } else - // If CurrencySymbol or InternationalCurrencySymbol is in the affix, check for currency symbol. - // Get spelled out name if "¤¤¤" is in the pattern. - if (fieldPosition.getFieldAttribute() == NumberFormat.Field.CURRENCY) { - if (affix.indexOf(symbols.getCurrencySymbol()) > -1) { - String aff = symbols.getCurrencySymbol(); - int firstPos = affix.indexOf(aff); - int start = buf.length() + firstPos; - int end = start + aff.length(); - fieldPosition.setBeginIndex(start); - fieldPosition.setEndIndex(end); - } else if (affix.indexOf(symbols.getInternationalCurrencySymbol()) > -1) { - String aff = symbols.getInternationalCurrencySymbol(); - int firstPos = affix.indexOf(aff); - int start = buf.length() + firstPos; - int end = start + aff.length(); - fieldPosition.setBeginIndex(start); - fieldPosition.setEndIndex(end); - } else if (pattern.indexOf("¤¤¤") > -1) { - // It's a plural, and we know where it is in the pattern. - int firstPos = pattern.indexOf("¤¤¤"); - int start = buf.length() + firstPos; - int end = buf.length() + affix.length(); // This seems clunky and wrong. - fieldPosition.setBeginIndex(start); - fieldPosition.setEndIndex(end); - } - } - - buf.append(affix); - return affix.length(); - } - - // Fix for prefix and suffix in Ticket 11805. - private void formatAffix2Attribute(boolean isPrefix, Field fieldType, - StringBuffer buf, int offset, int symbolSize) { - int begin; - begin = offset; - if (!isPrefix) { - begin += buf.length(); - } - - addAttribute(fieldType, begin, begin + symbolSize); - } - - /** - * [Spark/CDL] Use this method to add attribute. - */ - private void addAttribute(Field field, int begin, int end) { - FieldPosition pos = new FieldPosition(field); - pos.setBeginIndex(begin); - pos.setEndIndex(end); - attributes.add(pos); - } - - /** - * Formats the object to an attributed string, and return the corresponding iterator. - * - * @stable ICU 3.6 - */ - @Override - public AttributedCharacterIterator formatToCharacterIterator(Object obj) { - return formatToCharacterIterator(obj, NULL_UNIT); - } - - AttributedCharacterIterator formatToCharacterIterator(Object obj, Unit unit) { - if (!(obj instanceof Number)) - throw new IllegalArgumentException(); - Number number = (Number) obj; - StringBuffer text = new StringBuffer(); - unit.writePrefix(text); - attributes.clear(); - if (obj instanceof BigInteger) { - format((BigInteger) number, text, new FieldPosition(0), true); - } else if (obj instanceof java.math.BigDecimal) { - format((java.math.BigDecimal) number, text, new FieldPosition(0) - , true); - } else if (obj instanceof Double) { - format(number.doubleValue(), text, new FieldPosition(0), true); - } else if (obj instanceof Integer || obj instanceof Long) { - format(number.longValue(), text, new FieldPosition(0), true); - } else { - throw new IllegalArgumentException(); - } - unit.writeSuffix(text); - AttributedString as = new AttributedString(text.toString()); - - // add NumberFormat field attributes to the AttributedString - for (int i = 0; i < attributes.size(); i++) { - FieldPosition pos = attributes.get(i); - Format.Field attribute = pos.getFieldAttribute(); - as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos.getEndIndex()); - } - - // return the CharacterIterator from AttributedString - return as.getIterator(); - } - - /** - * Appends an affix pattern to the given StringBuffer. Localize unquoted specials. - *

- * Note: This implementation does not support new String localized symbols. - */ - private void appendAffixPattern(StringBuffer buffer, boolean isNegative, boolean isPrefix, - boolean localized) { - String affixPat = null; - if (isPrefix) { - affixPat = isNegative ? negPrefixPattern : posPrefixPattern; - } else { - affixPat = isNegative ? negSuffixPattern : posSuffixPattern; - } - - // When there is a null affix pattern, we use the affix itself. - if (affixPat == null) { - String affix = null; - if (isPrefix) { - affix = isNegative ? negativePrefix : positivePrefix; - } else { - affix = isNegative ? negativeSuffix : positiveSuffix; - } - // Do this crudely for now: Wrap everything in quotes. - buffer.append(QUOTE); - for (int i = 0; i < affix.length(); ++i) { - char ch = affix.charAt(i); - if (ch == QUOTE) { - buffer.append(ch); - } - buffer.append(ch); - } - buffer.append(QUOTE); - return; - } - - if (!localized) { - buffer.append(affixPat); - } else { - int i, j; - for (i = 0; i < affixPat.length(); ++i) { - char ch = affixPat.charAt(i); - switch (ch) { - case QUOTE: - j = affixPat.indexOf(QUOTE, i + 1); - if (j < 0) { - throw new IllegalArgumentException("Malformed affix pattern: " + affixPat); - } - buffer.append(affixPat.substring(i, j + 1)); - i = j; - continue; - case PATTERN_PER_MILLE: - ch = symbols.getPerMill(); - break; - case PATTERN_PERCENT: - ch = symbols.getPercent(); - break; - case PATTERN_MINUS_SIGN: - ch = symbols.getMinusSign(); - break; - } - // check if char is same as any other symbol - if (ch == symbols.getDecimalSeparator() || ch == symbols.getGroupingSeparator()) { - buffer.append(QUOTE); - buffer.append(ch); - buffer.append(QUOTE); - } else { - buffer.append(ch); - } - } - } - } - - /** - * Does the real work of generating a pattern. - *

- * Note: This implementation does not support new String localized symbols. - */ - private String toPattern(boolean localized) { - StringBuffer result = new StringBuffer(); - char zero = localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT; - char digit = localized ? symbols.getDigit() : PATTERN_DIGIT; - char sigDigit = 0; - boolean useSigDig = areSignificantDigitsUsed(); - if (useSigDig) { - sigDigit = localized ? symbols.getSignificantDigit() : PATTERN_SIGNIFICANT_DIGIT; - } - char group = localized ? symbols.getGroupingSeparator() : PATTERN_GROUPING_SEPARATOR; - int i; - int roundingDecimalPos = 0; // Pos of decimal in roundingDigits - String roundingDigits = null; - int padPos = (formatWidth > 0) ? padPosition : -1; - String padSpec = (formatWidth > 0) - ? new StringBuffer(2).append(localized - ? symbols.getPadEscape() - : PATTERN_PAD_ESCAPE).append(pad).toString() - : null; - if (roundingIncrementICU != null) { - i = roundingIncrementICU.scale(); - roundingDigits = roundingIncrementICU.movePointRight(i).toString(); - roundingDecimalPos = roundingDigits.length() - i; - } - for (int part = 0; part < 2; ++part) { - // variable not used int partStart = result.length(); - if (padPos == PAD_BEFORE_PREFIX) { - result.append(padSpec); - } - - // Use original symbols read from resources in pattern eg. use "\u00A4" - // instead of "$" in Locale.US [Richard/GCL] - appendAffixPattern(result, part != 0, true, localized); - if (padPos == PAD_AFTER_PREFIX) { - result.append(padSpec); - } - int sub0Start = result.length(); - int g = isGroupingUsed() ? Math.max(0, groupingSize) : 0; - if (g > 0 && groupingSize2 > 0 && groupingSize2 != groupingSize) { - g += groupingSize2; - } - int maxDig = 0, minDig = 0, maxSigDig = 0; - if (useSigDig) { - minDig = getMinimumSignificantDigits(); - maxDig = maxSigDig = getMaximumSignificantDigits(); - } else { - minDig = getMinimumIntegerDigits(); - maxDig = getMaximumIntegerDigits(); - } - if (useExponentialNotation) { - if (maxDig > MAX_SCIENTIFIC_INTEGER_DIGITS) { - maxDig = 1; - } - } else if (useSigDig) { - maxDig = Math.max(maxDig, g + 1); - } else { - maxDig = Math.max(Math.max(g, getMinimumIntegerDigits()), roundingDecimalPos) + 1; - } - for (i = maxDig; i > 0; --i) { - if (!useExponentialNotation && i < maxDig && isGroupingPosition(i)) { - result.append(group); - } - if (useSigDig) { - // #@,@### (maxSigDig == 5, minSigDig == 2) 65 4321 (1-based pos, - // count from the right) Use # if pos > maxSigDig or 1 <= pos <= - // (maxSigDig - minSigDig) Use @ if (maxSigDig - minSigDig) < pos <= - // maxSigDig - result.append((maxSigDig >= i && i > (maxSigDig - minDig)) ? sigDigit : digit); - } else { - if (roundingDigits != null) { - int pos = roundingDecimalPos - i; - if (pos >= 0 && pos < roundingDigits.length()) { - result.append((char) (roundingDigits.charAt(pos) - '0' + zero)); - continue; - } - } - result.append(i <= minDig ? zero : digit); - } - } - if (!useSigDig) { - if (getMaximumFractionDigits() > 0 || decimalSeparatorAlwaysShown) { - result.append(localized ? symbols.getDecimalSeparator() : - PATTERN_DECIMAL_SEPARATOR); - } - int pos = roundingDecimalPos; - for (i = 0; i < getMaximumFractionDigits(); ++i) { - if (roundingDigits != null && pos < roundingDigits.length()) { - result.append(pos < 0 ? zero : - (char) (roundingDigits.charAt(pos) - '0' + zero)); - ++pos; - continue; - } - result.append(i < getMinimumFractionDigits() ? zero : digit); - } - } - if (useExponentialNotation) { - if (localized) { - result.append(symbols.getExponentSeparator()); - } else { - result.append(PATTERN_EXPONENT); - } - if (exponentSignAlwaysShown) { - result.append(localized ? symbols.getPlusSign() : PATTERN_PLUS_SIGN); - } - for (i = 0; i < minExponentDigits; ++i) { - result.append(zero); - } - } - if (padSpec != null && !useExponentialNotation) { - int add = formatWidth - - result.length() - + sub0Start - - ((part == 0) - ? positivePrefix.length() + positiveSuffix.length() - : negativePrefix.length() + negativeSuffix.length()); - while (add > 0) { - result.insert(sub0Start, digit); - ++maxDig; - --add; - // Only add a grouping separator if we have at least 2 additional - // characters to be added, so we don't end up with ",###". - if (add > 1 && isGroupingPosition(maxDig)) { - result.insert(sub0Start, group); - --add; - } - } - } - if (padPos == PAD_BEFORE_SUFFIX) { - result.append(padSpec); - } - // Use original symbols read from resources in pattern eg. use "\u00A4" - // instead of "$" in Locale.US [Richard/GCL] - appendAffixPattern(result, part != 0, false, localized); - if (padPos == PAD_AFTER_SUFFIX) { - result.append(padSpec); - } - if (part == 0) { - if (negativeSuffix.equals(positiveSuffix) && - negativePrefix.equals(PATTERN_MINUS_SIGN + positivePrefix)) { - break; - } else { - result.append(localized ? symbols.getPatternSeparator() : PATTERN_SEPARATOR); - } - } - } - return result.toString(); - } - - /** - * Applies the given pattern to this Format object. A pattern is a short-hand - * specification for the various formatting properties. These properties can also be - * changed individually through the various setter methods. - * - *

There is no limit to integer digits are set by this routine, since that is the - * typical end-user desire; use setMaximumInteger if you want to set a real value. For - * negative numbers, use a second pattern, separated by a semicolon - * - *

Example "#,#00.0#" -> 1,234.56 - * - *

This means a minimum of 2 integer digits, 1 fraction digit, and a maximum of 2 - * fraction digits. - * - *

Example: "#,#00.0#;(#,#00.0#)" for negatives in parentheses. - * - *

In negative patterns, the minimum and maximum counts are ignored; these are - * presumed to be set in the positive pattern. - * - * @stable ICU 2.0 - */ - public void applyPattern(String pattern) { - applyPattern(pattern, false); - } - - /** - * Applies the given pattern to this Format object. The pattern is assumed to be in a - * localized notation. A pattern is a short-hand specification for the various - * formatting properties. These properties can also be changed individually through - * the various setter methods. - * - *

There is no limit to integer digits are set by this routine, since that is the - * typical end-user desire; use setMaximumInteger if you want to set a real value. For - * negative numbers, use a second pattern, separated by a semicolon - * - *

Example "#,#00.0#" -> 1,234.56 - * - *

This means a minimum of 2 integer digits, 1 fraction digit, and a maximum of 2 - * fraction digits. - * - *

Example: "#,#00.0#;(#,#00.0#)" for negatives in parantheses. - * - *

In negative patterns, the minimum and maximum counts are ignored; these are - * presumed to be set in the positive pattern. - * - * @stable ICU 2.0 - */ - public void applyLocalizedPattern(String pattern) { - applyPattern(pattern, true); - } - - /** - * Does the real work of applying a pattern. - */ - private void applyPattern(String pattern, boolean localized) { - applyPatternWithoutExpandAffix(pattern, localized); - expandAffixAdjustWidth(null); - } - - private void expandAffixAdjustWidth(String pluralCount) { - // Bug 4212072 Update the affix strings according to symbols in order to keep the - // affix strings up to date. [Richard/GCL] - expandAffixes(pluralCount); - - // Now that we have the actual prefix and suffix, fix up formatWidth - if (formatWidth > 0) { - formatWidth += positivePrefix.length() + positiveSuffix.length(); - } - } - - private void applyPatternWithoutExpandAffix(String pattern, boolean localized) { - char zeroDigit = PATTERN_ZERO_DIGIT; // '0' - char sigDigit = PATTERN_SIGNIFICANT_DIGIT; // '@' - char groupingSeparator = PATTERN_GROUPING_SEPARATOR; - char decimalSeparator = PATTERN_DECIMAL_SEPARATOR; - char percent = PATTERN_PERCENT; - char perMill = PATTERN_PER_MILLE; - char digit = PATTERN_DIGIT; // '#' - char separator = PATTERN_SEPARATOR; - String exponent = String.valueOf(PATTERN_EXPONENT); - char plus = PATTERN_PLUS_SIGN; - char padEscape = PATTERN_PAD_ESCAPE; - char minus = PATTERN_MINUS_SIGN; // Bug 4212072 [Richard/GCL] - if (localized) { - zeroDigit = symbols.getZeroDigit(); - sigDigit = symbols.getSignificantDigit(); - groupingSeparator = symbols.getGroupingSeparator(); - decimalSeparator = symbols.getDecimalSeparator(); - percent = symbols.getPercent(); - perMill = symbols.getPerMill(); - digit = symbols.getDigit(); - separator = symbols.getPatternSeparator(); - exponent = symbols.getExponentSeparator(); - plus = symbols.getPlusSign(); - padEscape = symbols.getPadEscape(); - minus = symbols.getMinusSign(); // Bug 4212072 [Richard/GCL] - } - char nineDigit = (char) (zeroDigit + 9); - - boolean gotNegative = false; - - int pos = 0; - // Part 0 is the positive pattern. Part 1, if present, is the negative - // pattern. - for (int part = 0; part < 2 && pos < pattern.length(); ++part) { - // The subpart ranges from 0 to 4: 0=pattern proper, 1=prefix, 2=suffix, - // 3=prefix in quote, 4=suffix in quote. Subpart 0 is between the prefix and - // suffix, and consists of pattern characters. In the prefix and suffix, - // percent, permille, and currency symbols are recognized and translated. - int subpart = 1, sub0Start = 0, sub0Limit = 0, sub2Limit = 0; - - // It's important that we don't change any fields of this object - // prematurely. We set the following variables for the multiplier, grouping, - // etc., and then only change the actual object fields if everything parses - // correctly. This also lets us register the data from part 0 and ignore the - // part 1, except for the prefix and suffix. - StringBuilder prefix = new StringBuilder(); - StringBuilder suffix = new StringBuilder(); - int decimalPos = -1; - int multpl = 1; - int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0, sigDigitCount = 0; - byte groupingCount = -1; - byte groupingCount2 = -1; - int padPos = -1; - char padChar = 0; - int incrementPos = -1; - long incrementVal = 0; - byte expDigits = -1; - boolean expSignAlways = false; - int currencySignCnt = 0; - - // The affix is either the prefix or the suffix. - StringBuilder affix = prefix; - - int start = pos; - - PARTLOOP: for (; pos < pattern.length(); ++pos) { - char ch = pattern.charAt(pos); - switch (subpart) { - case 0: // Pattern proper subpart (between prefix & suffix) - // Process the digits, decimal, and grouping characters. We record - // five pieces of information. We expect the digits to occur in the - // pattern ####00.00####, and we record the number of left digits, - // zero (central) digits, and right digits. The position of the last - // grouping character is recorded (should be somewhere within the - // first two blocks of characters), as is the position of the decimal - // point, if any (should be in the zero digits). If there is no - // decimal point, then there should be no right digits. - if (ch == digit) { - if (zeroDigitCount > 0 || sigDigitCount > 0) { - ++digitRightCount; - } else { - ++digitLeftCount; - } - if (groupingCount >= 0 && decimalPos < 0) { - ++groupingCount; - } - } else if ((ch >= zeroDigit && ch <= nineDigit) || ch == sigDigit) { - if (digitRightCount > 0) { - patternError("Unexpected '" + ch + '\'', pattern); - } - if (ch == sigDigit) { - ++sigDigitCount; - } else { - ++zeroDigitCount; - if (ch != zeroDigit) { - int p = digitLeftCount + zeroDigitCount + digitRightCount; - if (incrementPos >= 0) { - while (incrementPos < p) { - incrementVal *= 10; - ++incrementPos; - } - } else { - incrementPos = p; - } - incrementVal += ch - zeroDigit; - } - } - if (groupingCount >= 0 && decimalPos < 0) { - ++groupingCount; - } - } else if (ch == groupingSeparator) { - // Bug 4212072 process the Localized pattern like - // "'Fr. '#'##0.05;'Fr.-'#'##0.05" (Locale="CH", groupingSeparator - // == QUOTE) [Richard/GCL] - if (ch == QUOTE && (pos + 1) < pattern.length()) { - char after = pattern.charAt(pos + 1); - if (!(after == digit || (after >= zeroDigit && after <= nineDigit))) { - // A quote outside quotes indicates either the opening - // quote or two quotes, which is a quote literal. That is, - // we have the first quote in 'do' or o''clock. - if (after == QUOTE) { - ++pos; - // Fall through to append(ch) - } else { - if (groupingCount < 0) { - subpart = 3; // quoted prefix subpart - } else { - // Transition to suffix subpart - subpart = 2; // suffix subpart - affix = suffix; - sub0Limit = pos--; - } - continue; - } - } - } - - if (decimalPos >= 0) { - patternError("Grouping separator after decimal", pattern); - } - groupingCount2 = groupingCount; - groupingCount = 0; - } else if (ch == decimalSeparator) { - if (decimalPos >= 0) { - patternError("Multiple decimal separators", pattern); - } - // Intentionally incorporate the digitRightCount, even though it - // is illegal for this to be > 0 at this point. We check pattern - // syntax below. - decimalPos = digitLeftCount + zeroDigitCount + digitRightCount; - } else { - if (pattern.regionMatches(pos, exponent, 0, exponent.length())) { - if (expDigits >= 0) { - patternError("Multiple exponential symbols", pattern); - } - if (groupingCount >= 0) { - patternError("Grouping separator in exponential", pattern); - } - pos += exponent.length(); - // Check for positive prefix - if (pos < pattern.length() && pattern.charAt(pos) == plus) { - expSignAlways = true; - ++pos; - } - // Use lookahead to parse out the exponential part of the - // pattern, then jump into suffix subpart. - expDigits = 0; - while (pos < pattern.length() && pattern.charAt(pos) == zeroDigit) { - ++expDigits; - ++pos; - } - - // 1. Require at least one mantissa pattern digit - // 2. Disallow "#+ @" in mantissa - // 3. Require at least one exponent pattern digit - if (((digitLeftCount + zeroDigitCount) < 1 && - (sigDigitCount + digitRightCount) < 1) - || (sigDigitCount > 0 && digitLeftCount > 0) || expDigits < 1) { - patternError("Malformed exponential", pattern); - } - } - // Transition to suffix subpart - subpart = 2; // suffix subpart - affix = suffix; - sub0Limit = pos--; // backup: for() will increment - continue; - } - break; - case 1: // Prefix subpart - case 2: // Suffix subpart - // Process the prefix / suffix characters Process unquoted characters - // seen in prefix or suffix subpart. - - // Several syntax characters implicitly begins the next subpart if we - // are in the prefix; otherwise they are illegal if unquoted. - if (ch == digit || ch == groupingSeparator || ch == decimalSeparator - || (ch >= zeroDigit && ch <= nineDigit) || ch == sigDigit) { - // Any of these characters implicitly begins the - // next subpart if we are in the prefix - if (subpart == 1) { // prefix subpart - subpart = 0; // pattern proper subpart - sub0Start = pos--; // Reprocess this character - continue; - } else if (ch == QUOTE) { - // Bug 4212072 process the Localized pattern like - // "'Fr. '#'##0.05;'Fr.-'#'##0.05" (Locale="CH", - // groupingSeparator == QUOTE) [Richard/GCL] - - // A quote outside quotes indicates either the opening quote - // or two quotes, which is a quote literal. That is, we have - // the first quote in 'do' or o''clock. - if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) { - ++pos; - affix.append(ch); - } else { - subpart += 2; // open quote - } - continue; - } - patternError("Unquoted special character '" + ch + '\'', pattern); - } else if (ch == CURRENCY_SIGN) { - // Use lookahead to determine if the currency sign is - // doubled or not. - boolean doubled = (pos + 1) < pattern.length() && - pattern.charAt(pos + 1) == CURRENCY_SIGN; - - // Bug 4212072 To meet the need of expandAffix(String, - // StirngBuffer) [Richard/GCL] - if (doubled) { - ++pos; // Skip over the doubled character - affix.append(ch); // append two: one here, one below - if ((pos + 1) < pattern.length() && - pattern.charAt(pos + 1) == CURRENCY_SIGN) { - ++pos; // Skip over the tripled character - affix.append(ch); // append again - currencySignCnt = CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT; - } else { - currencySignCnt = CURRENCY_SIGN_COUNT_IN_ISO_FORMAT; - } - } else { - currencySignCnt = CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT; - } - // Fall through to append(ch) - } else if (ch == QUOTE) { - // A quote outside quotes indicates either the opening quote or - // two quotes, which is a quote literal. That is, we have the - // first quote in 'do' or o''clock. - if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) { - ++pos; - affix.append(ch); // append two: one here, one below - } else { - subpart += 2; // open quote - } - // Fall through to append(ch) - } else if (ch == separator) { - // Don't allow separators in the prefix, and don't allow - // separators in the second pattern (part == 1). - if (subpart == 1 || part == 1) { - patternError("Unquoted special character '" + ch + '\'', pattern); - } - sub2Limit = pos++; - break PARTLOOP; // Go to next part - } else if (ch == percent || ch == perMill) { - // Next handle characters which are appended directly. - if (multpl != 1) { - patternError("Too many percent/permille characters", pattern); - } - multpl = (ch == percent) ? 100 : 1000; - // Convert to non-localized pattern - ch = (ch == percent) ? PATTERN_PERCENT : PATTERN_PER_MILLE; - // Fall through to append(ch) - } else if (ch == minus) { - // Convert to non-localized pattern - ch = PATTERN_MINUS_SIGN; - // Fall through to append(ch) - } else if (ch == padEscape) { - if (padPos >= 0) { - patternError("Multiple pad specifiers", pattern); - } - if ((pos + 1) == pattern.length()) { - patternError("Invalid pad specifier", pattern); - } - padPos = pos++; // Advance past pad char - padChar = pattern.charAt(pos); - continue; - } - affix.append(ch); - break; - case 3: // Prefix subpart, in quote - case 4: // Suffix subpart, in quote - // A quote within quotes indicates either the closing quote or two - // quotes, which is a quote literal. That is, we have the second quote - // in 'do' or 'don''t'. - if (ch == QUOTE) { - if ((pos + 1) < pattern.length() && pattern.charAt(pos + 1) == QUOTE) { - ++pos; - affix.append(ch); - } else { - subpart -= 2; // close quote - } - // Fall through to append(ch) - } - // NOTE: In ICU 2.2 there was code here to parse quoted percent and - // permille characters _within quotes_ and give them special - // meaning. This is incorrect, since quoted characters are literals - // without special meaning. - affix.append(ch); - break; - } - } - - if (subpart == 3 || subpart == 4) { - patternError("Unterminated quote", pattern); - } - - if (sub0Limit == 0) { - sub0Limit = pattern.length(); - } - - if (sub2Limit == 0) { - sub2Limit = pattern.length(); - } - - // Handle patterns with no '0' pattern character. These patterns are legal, - // but must be recodified to make sense. "##.###" -> "#0.###". ".###" -> - // ".0##". - // - // We allow patterns of the form "####" to produce a zeroDigitCount of zero - // (got that?); although this seems like it might make it possible for - // format() to produce empty strings, format() checks for this condition and - // outputs a zero digit in this situation. Having a zeroDigitCount of zero - // yields a minimum integer digits of zero, which allows proper round-trip - // patterns. We don't want "#" to become "#0" when toPattern() is called (even - // though that's what it really is, semantically). - if (zeroDigitCount == 0 && sigDigitCount == 0 && - digitLeftCount > 0 && decimalPos >= 0) { - // Handle "###.###" and "###." and ".###" - int n = decimalPos; - if (n == 0) - ++n; // Handle ".###" - digitRightCount = digitLeftCount - n; - digitLeftCount = n - 1; - zeroDigitCount = 1; - } - - // Do syntax checking on the digits, decimal points, and quotes. - if ((decimalPos < 0 && digitRightCount > 0 && sigDigitCount == 0) - || (decimalPos >= 0 - && (sigDigitCount > 0 - || decimalPos < digitLeftCount - || decimalPos > (digitLeftCount + zeroDigitCount))) - || groupingCount == 0 - || groupingCount2 == 0 - || (sigDigitCount > 0 && zeroDigitCount > 0) - || subpart > 2) { // subpart > 2 == unmatched quote - patternError("Malformed pattern", pattern); - } - - // Make sure pad is at legal position before or after affix. - if (padPos >= 0) { - if (padPos == start) { - padPos = PAD_BEFORE_PREFIX; - } else if (padPos + 2 == sub0Start) { - padPos = PAD_AFTER_PREFIX; - } else if (padPos == sub0Limit) { - padPos = PAD_BEFORE_SUFFIX; - } else if (padPos + 2 == sub2Limit) { - padPos = PAD_AFTER_SUFFIX; - } else { - patternError("Illegal pad position", pattern); - } - } - - if (part == 0) { - // Set negative affixes temporarily to match the positive - // affixes. Fix this up later after processing both parts. - - // Bug 4212072 To meet the need of expandAffix(String, StirngBuffer) - // [Richard/GCL] - posPrefixPattern = negPrefixPattern = prefix.toString(); - posSuffixPattern = negSuffixPattern = suffix.toString(); - - useExponentialNotation = (expDigits >= 0); - if (useExponentialNotation) { - minExponentDigits = expDigits; - exponentSignAlwaysShown = expSignAlways; - } - int digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount; - // The effectiveDecimalPos is the position the decimal is at or would be - // at if there is no decimal. Note that if decimalPos<0, then - // digitTotalCount == digitLeftCount + zeroDigitCount. - int effectiveDecimalPos = decimalPos >= 0 ? decimalPos : digitTotalCount; - boolean useSigDig = (sigDigitCount > 0); - setSignificantDigitsUsed(useSigDig); - if (useSigDig) { - setMinimumSignificantDigits(sigDigitCount); - setMaximumSignificantDigits(sigDigitCount + digitRightCount); - } else { - int minInt = effectiveDecimalPos - digitLeftCount; - setMinimumIntegerDigits(minInt); - - // Upper limit on integer and fraction digits for a Java double - // [Richard/GCL] - setMaximumIntegerDigits(useExponentialNotation ? digitLeftCount + minInt : - DOUBLE_INTEGER_DIGITS); - _setMaximumFractionDigits(decimalPos >= 0 ? - (digitTotalCount - decimalPos) : 0); - setMinimumFractionDigits(decimalPos >= 0 ? - (digitLeftCount + zeroDigitCount - decimalPos) : 0); - } - setGroupingUsed(groupingCount > 0); - this.groupingSize = (groupingCount > 0) ? groupingCount : 0; - this.groupingSize2 = (groupingCount2 > 0 && groupingCount2 != groupingCount) - ? groupingCount2 : 0; - this.multiplier = multpl; - setDecimalSeparatorAlwaysShown(decimalPos == 0 || decimalPos == digitTotalCount); - if (padPos >= 0) { - padPosition = padPos; - formatWidth = sub0Limit - sub0Start; // to be fixed up below - pad = padChar; - } else { - formatWidth = 0; - } - if (incrementVal != 0) { - // BigDecimal scale cannot be negative (even though this makes perfect - // sense), so we need to handle this. - int scale = incrementPos - effectiveDecimalPos; - roundingIncrementICU = BigDecimal.valueOf(incrementVal, scale > 0 ? scale : 0); - if (scale < 0) { - roundingIncrementICU = roundingIncrementICU.movePointRight(-scale); - } - roundingMode = BigDecimal.ROUND_HALF_EVEN; - } else { - setRoundingIncrement((BigDecimal) null); - } - - // Update currency sign count for the new pattern - currencySignCount = currencySignCnt; - } else { - // Bug 4212072 To meet the need of expandAffix(String, StirngBuffer) - // [Richard/GCL] - negPrefixPattern = prefix.toString(); - negSuffixPattern = suffix.toString(); - gotNegative = true; - } - } - - - // Bug 4140009 Process the empty pattern [Richard/GCL] - if (pattern.length() == 0) { - posPrefixPattern = posSuffixPattern = ""; - setMinimumIntegerDigits(0); - setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS); - setMinimumFractionDigits(0); - _setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); - } - - // If there was no negative pattern, or if the negative pattern is identical to - // the positive pattern, then prepend the minus sign to the positive pattern to - // form the negative pattern. - - // Bug 4212072 To meet the need of expandAffix(String, StirngBuffer) [Richard/GCL] - - if (!gotNegative || - (negPrefixPattern.equals(posPrefixPattern) - && negSuffixPattern.equals(posSuffixPattern))) { - negSuffixPattern = posSuffixPattern; - negPrefixPattern = PATTERN_MINUS_SIGN + posPrefixPattern; - } - - // Can't call setLocale when not in the right package: - //setLocale(null, null); - - // save the pattern - formatPattern = pattern; - - // special handlings for currency instance - if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) { - // reset rounding increment and max/min fractional digits - // by the currency - Currency theCurrency = getCurrency(); - if (theCurrency != null) { - setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage)); - int d = theCurrency.getDefaultFractionDigits(currencyUsage); - setMinimumFractionDigits(d); - _setMaximumFractionDigits(d); - } - - // initialize currencyPluralInfo if needed - if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT - && currencyPluralInfo == null) { - currencyPluralInfo = new CurrencyPluralInfo(symbols.getULocale()); - } - } - resetActualRounding(); - } - - - private void patternError(String msg, String pattern) { - throw new IllegalArgumentException(msg + " in pattern \"" + pattern + '"'); - } - - - // Rewrite the following 4 "set" methods Upper limit on integer and fraction digits - // for a Java double [Richard/GCL] - - /** - * Sets the maximum number of digits allowed in the integer portion of a number. This - * override limits the integer digit count to 309. - * - * @see NumberFormat#setMaximumIntegerDigits - * @stable ICU 2.0 - */ - @Override - public void setMaximumIntegerDigits(int newValue) { - super.setMaximumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS)); - } - - /** - * Sets the minimum number of digits allowed in the integer portion of a number. This - * override limits the integer digit count to 309. - * - * @see NumberFormat#setMinimumIntegerDigits - * @stable ICU 2.0 - */ - @Override - public void setMinimumIntegerDigits(int newValue) { - super.setMinimumIntegerDigits(Math.min(newValue, DOUBLE_INTEGER_DIGITS)); - } - - /** - * {@icu} Returns the minimum number of significant digits that will be - * displayed. This value has no effect unless {@link #areSignificantDigitsUsed()} - * returns true. - * - * @return the fewest significant digits that will be shown - * @stable ICU 3.0 - */ - public int getMinimumSignificantDigits() { - return minSignificantDigits; - } - - /** - * {@icu} Returns the maximum number of significant digits that will be - * displayed. This value has no effect unless {@link #areSignificantDigitsUsed()} - * returns true. - * - * @return the most significant digits that will be shown - * @stable ICU 3.0 - */ - public int getMaximumSignificantDigits() { - return maxSignificantDigits; - } - - /** - * {@icu} Sets the minimum number of significant digits that will be displayed. If - * min is less than one then it is set to one. If the maximum significant - * digits count is less than min, then it is set to min. - * This function also enables the use of significant digits by this formatter - - * {@link #areSignificantDigitsUsed()} will return true. - * - * @param min the fewest significant digits to be shown - * @stable ICU 3.0 - */ - public void setMinimumSignificantDigits(int min) { - if (min < 1) { - min = 1; - } - // pin max sig dig to >= min - int max = Math.max(maxSignificantDigits, min); - minSignificantDigits = min; - maxSignificantDigits = max; - setSignificantDigitsUsed(true); - } - - /** - * {@icu} Sets the maximum number of significant digits that will be displayed. If - * max is less than one then it is set to one. If the minimum significant - * digits count is greater than max, then it is set to max. - * This function also enables the use of significant digits by this formatter - - * {@link #areSignificantDigitsUsed()} will return true. - * - * @param max the most significant digits to be shown - * @stable ICU 3.0 - */ - public void setMaximumSignificantDigits(int max) { - if (max < 1) { - max = 1; - } - // pin min sig dig to 1..max - int min = Math.min(minSignificantDigits, max); - minSignificantDigits = min; - maxSignificantDigits = max; - setSignificantDigitsUsed(true); - } - - /** - * {@icu} Returns true if significant digits are in use or false if integer and - * fraction digit counts are in use. - * - * @return true if significant digits are in use - * @stable ICU 3.0 - */ - public boolean areSignificantDigitsUsed() { - return useSignificantDigits; - } - - /** - * {@icu} Sets whether significant digits are in use, or integer and fraction digit - * counts are in use. - * - * @param useSignificantDigits true to use significant digits, or false to use integer - * and fraction digit counts - * @stable ICU 3.0 - */ - public void setSignificantDigitsUsed(boolean useSignificantDigits) { - this.useSignificantDigits = useSignificantDigits; - } - - /** - * Sets the Currency object used to display currency amounts. This takes - * effect immediately, if this format is a currency format. If this format is not a - * currency format, then the currency object is used if and when this object becomes a - * currency format through the application of a new pattern. - * - * @param theCurrency new currency object to use. Must not be null. - * @stable ICU 2.2 - */ - @Override - public void setCurrency(Currency theCurrency) { - // If we are a currency format, then modify our affixes to - // encode the currency symbol for the given currency in our - // locale, and adjust the decimal digits and rounding for the - // given currency. - - super.setCurrency(theCurrency); - if (theCurrency != null) { - String s = theCurrency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null); - symbols.setCurrency(theCurrency); - symbols.setCurrencySymbol(s); - } - - if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) { - if (theCurrency != null) { - setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage)); - int d = theCurrency.getDefaultFractionDigits(currencyUsage); - setMinimumFractionDigits(d); - setMaximumFractionDigits(d); - } - if (currencySignCount != CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) { - // This is not necessary for plural format type - // because affixes will be resolved in subformat - expandAffixes(null); - } - } - } - - /** - * Sets the Currency Usage object used to display currency. - * This takes effect immediately, if this format is a - * currency format. - * @param newUsage new currency context object to use. - * @stable ICU 54 - */ - public void setCurrencyUsage(CurrencyUsage newUsage) { - if (newUsage == null) { - throw new NullPointerException("return value is null at method AAA"); - } - currencyUsage = newUsage; - Currency theCurrency = this.getCurrency(); - - // We set rounding/digit based on currency context - if (theCurrency != null) { - setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage)); - int d = theCurrency.getDefaultFractionDigits(currencyUsage); - setMinimumFractionDigits(d); - _setMaximumFractionDigits(d); - } - } - - /** - * Returns the Currency Usage object used to display currency - * @stable ICU 54 - */ - public CurrencyUsage getCurrencyUsage() { - return currencyUsage; - } - - /** - * Returns the currency in effect for this formatter. Subclasses should override this - * method as needed. Unlike getCurrency(), this method should never return null. - * - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - @Override - protected Currency getEffectiveCurrency() { - Currency c = getCurrency(); - if (c == null) { - c = Currency.getInstance(symbols.getInternationalCurrencySymbol()); - } - return c; - } - - /** - * Sets the maximum number of digits allowed in the fraction portion of a number. This - * override limits the fraction digit count to 340. - * - * @see NumberFormat#setMaximumFractionDigits - * @stable ICU 2.0 - */ - @Override - public void setMaximumFractionDigits(int newValue) { - _setMaximumFractionDigits(newValue); - resetActualRounding(); - } - - /* - * Internal method for DecimalFormat, setting maximum fractional digits - * without triggering actual rounding recalculated. - */ - private void _setMaximumFractionDigits(int newValue) { - super.setMaximumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS)); - } - - /** - * Sets the minimum number of digits allowed in the fraction portion of a number. This - * override limits the fraction digit count to 340. - * - * @see NumberFormat#setMinimumFractionDigits - * @stable ICU 2.0 - */ - @Override - public void setMinimumFractionDigits(int newValue) { - super.setMinimumFractionDigits(Math.min(newValue, DOUBLE_FRACTION_DIGITS)); - } - - /** - * Sets whether {@link #parse(String, ParsePosition)} returns BigDecimal. The - * default value is false. - * - * @param value true if {@link #parse(String, ParsePosition)} - * returns BigDecimal. - * @stable ICU 3.6 - */ - public void setParseBigDecimal(boolean value) { - parseBigDecimal = value; - } - - /** - * Returns whether {@link #parse(String, ParsePosition)} returns BigDecimal. - * - * @return true if {@link #parse(String, ParsePosition)} returns BigDecimal. - * @stable ICU 3.6 - */ - public boolean isParseBigDecimal() { - return parseBigDecimal; - } - - /** - * Set the maximum number of exponent digits when parsing a number. - * If the limit is set too high, an OutOfMemoryException may be triggered. - * The default value is 1000. - * @param newValue the new limit - * @stable ICU 51 - */ - public void setParseMaxDigits(int newValue) { - if (newValue > 0) { - PARSE_MAX_EXPONENT = newValue; - } - } - - /** - * Get the current maximum number of exponent digits when parsing a - * number. - * @return the maximum number of exponent digits for parsing - * @stable ICU 51 - */ - public int getParseMaxDigits() { - return PARSE_MAX_EXPONENT; - } - - private void writeObject(ObjectOutputStream stream) throws IOException { - // Ticket#6449 Format.Field instances are not serializable. When - // formatToCharacterIterator is called, attributes (ArrayList) stores - // FieldPosition instances with NumberFormat.Field. Because NumberFormat.Field is - // not serializable, we need to clear the contents of the list when writeObject is - // called. We could remove the field or make it transient, but it will break - // serialization compatibility. - attributes.clear(); - - stream.defaultWriteObject(); - } - - /** - * First, read the default serializable fields from the stream. Then if - * serialVersionOnStream is less than 1, indicating that the stream was - * written by JDK 1.1, initialize useExponentialNotation to false, since - * it was not present in JDK 1.1. Finally, set serialVersionOnStream back to the - * maximum allowed value so that default serialization will work properly if this - * object is streamed out again. - */ - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - - // Bug 4185761 validate fields [Richard/GCL] - - // We only need to check the maximum counts because NumberFormat .readObject has - // already ensured that the maximum is greater than the minimum count. - - // Commented for compatibility with previous version, and reserved for further use - // if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS || - // getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) { throw new - // InvalidObjectException("Digit count out of range"); } - - - // Truncate the maximumIntegerDigits to DOUBLE_INTEGER_DIGITS and - // maximumFractionDigits to DOUBLE_FRACTION_DIGITS - - if (getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS) { - setMaximumIntegerDigits(DOUBLE_INTEGER_DIGITS); - } - if (getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) { - _setMaximumFractionDigits(DOUBLE_FRACTION_DIGITS); - } - if (serialVersionOnStream < 2) { - exponentSignAlwaysShown = false; - setInternalRoundingIncrement(null); - roundingMode = BigDecimal.ROUND_HALF_EVEN; - formatWidth = 0; - pad = ' '; - padPosition = PAD_BEFORE_PREFIX; - if (serialVersionOnStream < 1) { - // Didn't have exponential fields - useExponentialNotation = false; - } - } - if (serialVersionOnStream < 3) { - // Versions prior to 3 do not store a currency object. Create one to match - // the DecimalFormatSymbols object. - setCurrencyForSymbols(); - } - if (serialVersionOnStream < 4) { - currencyUsage = CurrencyUsage.STANDARD; - } - serialVersionOnStream = currentSerialVersion; - digitList = new DigitList(); - - if (roundingIncrement != null) { - setInternalRoundingIncrement(new BigDecimal(roundingIncrement)); - } - resetActualRounding(); - } - - private void setInternalRoundingIncrement(BigDecimal value) { - roundingIncrementICU = value; - roundingIncrement = value == null ? null : value.toBigDecimal(); - } - - // ---------------------------------------------------------------------- - // INSTANCE VARIABLES - // ---------------------------------------------------------------------- - - private transient DigitList digitList = new DigitList(); - - /** - * The symbol used as a prefix when formatting positive numbers, e.g. "+". - * - * @serial - * @see #getPositivePrefix - */ - private String positivePrefix = ""; - - /** - * The symbol used as a suffix when formatting positive numbers. This is often an - * empty string. - * - * @serial - * @see #getPositiveSuffix - */ - private String positiveSuffix = ""; - - /** - * The symbol used as a prefix when formatting negative numbers, e.g. "-". - * - * @serial - * @see #getNegativePrefix - */ - private String negativePrefix = "-"; - - /** - * The symbol used as a suffix when formatting negative numbers. This is often an - * empty string. - * - * @serial - * @see #getNegativeSuffix - */ - private String negativeSuffix = ""; - - /** - * The prefix pattern for non-negative numbers. This variable corresponds to - * positivePrefix. - * - *

This pattern is expanded by the method expandAffix() to - * positivePrefix to update the latter to reflect changes in - * symbols. If this variable is null then - * positivePrefix is taken as a literal value that does not change when - * symbols changes. This variable is always null for - * DecimalFormat objects older than stream version 2 restored from - * stream. - * - * @serial - */ - // [Richard/GCL] - private String posPrefixPattern; - - /** - * The suffix pattern for non-negative numbers. This variable corresponds to - * positiveSuffix. This variable is analogous to - * posPrefixPattern; see that variable for further documentation. - * - * @serial - */ - // [Richard/GCL] - private String posSuffixPattern; - - /** - * The prefix pattern for negative numbers. This variable corresponds to - * negativePrefix. This variable is analogous to - * posPrefixPattern; see that variable for further documentation. - * - * @serial - */ - // [Richard/GCL] - private String negPrefixPattern; - - /** - * The suffix pattern for negative numbers. This variable corresponds to - * negativeSuffix. This variable is analogous to - * posPrefixPattern; see that variable for further documentation. - * - * @serial - */ - // [Richard/GCL] - private String negSuffixPattern; - - /** - * Formatter for ChoiceFormat-based currency names. If this field is not null, then - * delegate to it to format currency symbols. - * TODO: This is obsolete: Remove, and design extensible serialization. ICU ticket #12090. - * - * @since ICU 2.6 - */ - private ChoiceFormat currencyChoice; - - /** - * The multiplier for use in percent, permill, etc. - * - * @serial - * @see #getMultiplier - */ - private int multiplier = 1; - - /** - * The number of digits between grouping separators in the integer portion of a - * number. Must be greater than 0 if NumberFormat.groupingUsed is true. - * - * @serial - * @see #getGroupingSize - * @see NumberFormat#isGroupingUsed - */ - private byte groupingSize = 3; // invariant, > 0 if useThousands - - /** - * The secondary grouping size. This is only used for Hindi numerals, which use a - * primary grouping of 3 and a secondary grouping of 2, e.g., "12,34,567". If this - * value is less than 1, then secondary grouping is equal to the primary grouping. - * - */ - private byte groupingSize2 = 0; - - /** - * If true, forces the decimal separator to always appear in a formatted number, even - * if the fractional part of the number is zero. - * - * @serial - * @see #isDecimalSeparatorAlwaysShown - */ - private boolean decimalSeparatorAlwaysShown = false; - - /** - * The DecimalFormatSymbols object used by this format. It contains the - * symbols used to format numbers, e.g. the grouping separator, decimal separator, and - * so on. - * - * @serial - * @see #setDecimalFormatSymbols - * @see DecimalFormatSymbols - */ - private DecimalFormatSymbols symbols = null; // LIU new DecimalFormatSymbols(); - - /** - * True to use significant digits rather than integer and fraction digit counts. - * - * @serial - * @since ICU 3.0 - */ - private boolean useSignificantDigits = false; - - /** - * The minimum number of significant digits to show. Must be >= 1 and <= - * maxSignificantDigits. Ignored unless useSignificantDigits == true. - * - * @serial - * @since ICU 3.0 - */ - private int minSignificantDigits = 1; - - /** - * The maximum number of significant digits to show. Must be >= - * minSignficantDigits. Ignored unless useSignificantDigits == true. - * - * @serial - * @since ICU 3.0 - */ - private int maxSignificantDigits = 6; - - /** - * True to force the use of exponential (i.e. scientific) notation - * when formatting numbers. - * - *

Note that the JDK 1.2 public API provides no way to set this - * field, even though it is supported by the implementation and - * the stream format. The intent is that this will be added to the - * API in the future. - * - * @serial - */ - private boolean useExponentialNotation; // Newly persistent in JDK 1.2 - - /** - * The minimum number of digits used to display the exponent when a number is - * formatted in exponential notation. This field is ignored if - * useExponentialNotation is not true. - * - *

Note that the JDK 1.2 public API provides no way to set this field, even though - * it is supported by the implementation and the stream format. The intent is that - * this will be added to the API in the future. - * - * @serial - */ - private byte minExponentDigits; // Newly persistent in JDK 1.2 - - /** - * If true, the exponent is always prefixed with either the plus sign or the minus - * sign. Otherwise, only negative exponents are prefixed with the minus sign. This has - * no effect unless useExponentialNotation is true. - * - * @serial - * @since AlphaWorks NumberFormat - */ - private boolean exponentSignAlwaysShown = false; - - /** - * The value to which numbers are rounded during formatting. For example, if the - * rounding increment is 0.05, then 13.371 would be formatted as 13.350, assuming 3 - * fraction digits. Has the value null if rounding is not in effect, or a - * positive value if rounding is in effect. Default value null. - * - * @serial - * @since AlphaWorks NumberFormat - */ - // Note: this is kept in sync with roundingIncrementICU. - // it is only kept around to avoid a conversion when formatting a java.math.BigDecimal - private java.math.BigDecimal roundingIncrement = null; - - /** - * The value to which numbers are rounded during formatting. For example, if the - * rounding increment is 0.05, then 13.371 would be formatted as 13.350, assuming 3 - * fraction digits. Has the value null if rounding is not in effect, or a - * positive value if rounding is in effect. Default value null. WARNING: - * the roundingIncrement value is the one serialized. - * - * @serial - * @since AlphaWorks NumberFormat - */ - private transient BigDecimal roundingIncrementICU = null; - - /** - * The rounding mode. This value controls any rounding operations which occur when - * applying a rounding increment or when reducing the number of fraction digits to - * satisfy a maximum fraction digits limit. The value may assume any of the - * BigDecimal rounding mode values. Default value - * BigDecimal.ROUND_HALF_EVEN. - * - * @serial - * @since AlphaWorks NumberFormat - */ - private int roundingMode = BigDecimal.ROUND_HALF_EVEN; - - /** - * Operations on BigDecimal numbers are controlled by a {@link - * MathContext} object, which provides the context (precision and other information) - * for the operation. The default MathContext settings are - * digits=0, form=PLAIN, lostDigits=false, roundingMode=ROUND_HALF_UP; - * these settings perform fixed point arithmetic with unlimited precision, as defined - * for the original BigDecimal class in Java 1.1 and Java 1.2 - */ - // context for plain unlimited math - private MathContext mathContext = new MathContext(0, MathContext.PLAIN); - - /** - * The padded format width, or zero if there is no padding. Must be >= 0. Default - * value zero. - * - * @serial - * @since AlphaWorks NumberFormat - */ - private int formatWidth = 0; - - /** - * The character used to pad the result of format to formatWidth, if - * padding is in effect. Default value ' '. - * - * @serial - * @since AlphaWorks NumberFormat - */ - private char pad = ' '; - - /** - * The position in the string at which the pad character will be - * inserted, if padding is in effect. Must have a value from - * PAD_BEFORE_PREFIX to PAD_AFTER_SUFFIX. Default value - * PAD_BEFORE_PREFIX. - * - * @serial - * @since AlphaWorks NumberFormat - */ - private int padPosition = PAD_BEFORE_PREFIX; - - /** - * True if {@link #parse(String, ParsePosition)} to return BigDecimal rather than - * Long, Double or BigDecimal except special values. This property is introduced for - * J2SE 5 compatibility support. - * - * @serial - * @since ICU 3.6 - * @see #setParseBigDecimal(boolean) - * @see #isParseBigDecimal() - */ - private boolean parseBigDecimal = false; - - /** - * The currency usage for the NumberFormat(standard or cash usage). - * It is used as STANDARD by default - * @since ICU 54 - */ - private CurrencyUsage currencyUsage = CurrencyUsage.STANDARD; - - // ---------------------------------------------------------------------- - - static final int currentSerialVersion = 4; - - /** - * The internal serial version which says which version was written Possible values - * are: - * - *

- * - * @serial - */ - private int serialVersionOnStream = currentSerialVersion; - - // ---------------------------------------------------------------------- - // CONSTANTS - // ---------------------------------------------------------------------- - - /** - * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to - * specify pad characters inserted before the prefix. - * - * @see #setPadPosition - * @see #getPadPosition - * @see #PAD_AFTER_PREFIX - * @see #PAD_BEFORE_SUFFIX - * @see #PAD_AFTER_SUFFIX - * @stable ICU 2.0 - */ - public static final int PAD_BEFORE_PREFIX = 0; - - /** - * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to - * specify pad characters inserted after the prefix. - * - * @see #setPadPosition - * @see #getPadPosition - * @see #PAD_BEFORE_PREFIX - * @see #PAD_BEFORE_SUFFIX - * @see #PAD_AFTER_SUFFIX - * @stable ICU 2.0 - */ - public static final int PAD_AFTER_PREFIX = 1; - - /** - * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to - * specify pad characters inserted before the suffix. - * - * @see #setPadPosition - * @see #getPadPosition - * @see #PAD_BEFORE_PREFIX - * @see #PAD_AFTER_PREFIX - * @see #PAD_AFTER_SUFFIX - * @stable ICU 2.0 - */ - public static final int PAD_BEFORE_SUFFIX = 2; - - /** - * {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to - * specify pad characters inserted after the suffix. - * - * @see #setPadPosition - * @see #getPadPosition - * @see #PAD_BEFORE_PREFIX - * @see #PAD_AFTER_PREFIX - * @see #PAD_BEFORE_SUFFIX - * @stable ICU 2.0 - */ - public static final int PAD_AFTER_SUFFIX = 3; - - // Constants for characters used in programmatic (unlocalized) patterns. - static final char PATTERN_ZERO_DIGIT = '0'; - static final char PATTERN_ONE_DIGIT = '1'; - static final char PATTERN_TWO_DIGIT = '2'; - static final char PATTERN_THREE_DIGIT = '3'; - static final char PATTERN_FOUR_DIGIT = '4'; - static final char PATTERN_FIVE_DIGIT = '5'; - static final char PATTERN_SIX_DIGIT = '6'; - static final char PATTERN_SEVEN_DIGIT = '7'; - static final char PATTERN_EIGHT_DIGIT = '8'; - static final char PATTERN_NINE_DIGIT = '9'; - static final char PATTERN_GROUPING_SEPARATOR = ','; - static final char PATTERN_DECIMAL_SEPARATOR = '.'; - static final char PATTERN_DIGIT = '#'; - static final char PATTERN_SIGNIFICANT_DIGIT = '@'; - static final char PATTERN_EXPONENT = 'E'; - static final char PATTERN_PLUS_SIGN = '+'; - static final char PATTERN_MINUS_SIGN = '-'; - - // Affix - private static final char PATTERN_PER_MILLE = '\u2030'; - private static final char PATTERN_PERCENT = '%'; - static final char PATTERN_PAD_ESCAPE = '*'; - - // Other - private static final char PATTERN_SEPARATOR = ';'; - - // Pad escape is package private to allow access by DecimalFormatSymbols. - // Also plus sign. Also exponent. - - /** - * The CURRENCY_SIGN is the standard Unicode symbol for currency. It is used in - * patterns and substitued with either the currency symbol, or if it is doubled, with - * the international currency symbol. If the CURRENCY_SIGN is seen in a pattern, then - * the decimal separator is replaced with the monetary decimal separator. - * - * The CURRENCY_SIGN is not localized. - */ - private static final char CURRENCY_SIGN = '\u00A4'; - - private static final char QUOTE = '\''; - - /** - * Upper limit on integer and fraction digits for a Java double [Richard/GCL] - */ - static final int DOUBLE_INTEGER_DIGITS = 309; - static final int DOUBLE_FRACTION_DIGITS = 340; - - /** - * When someone turns on scientific mode, we assume that more than this number of - * digits is due to flipping from some other mode that didn't restrict the maximum, - * and so we force 1 integer digit. We don't bother to track and see if someone is - * using exponential notation with more than this number, it wouldn't make sense - * anyway, and this is just to make sure that someone turning on scientific mode with - * default settings doesn't end up with lots of zeroes. - */ - static final int MAX_SCIENTIFIC_INTEGER_DIGITS = 8; - - // Proclaim JDK 1.1 serial compatibility. - private static final long serialVersionUID = 864413376551465018L; - - private ArrayList attributes = new ArrayList<>(); - - // The following are used in currency format - - // -- triple currency sign char array - // private static final char[] tripleCurrencySign = {0xA4, 0xA4, 0xA4}; - // -- triple currency sign string - // private static final String tripleCurrencyStr = new String(tripleCurrencySign); - // - // -- default currency plural pattern char array - // private static final char[] defaultCurrencyPluralPatternChar = - // {0, '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4}; - // -- default currency plural pattern string - // private static final String defaultCurrencyPluralPattern = - // new String(defaultCurrencyPluralPatternChar); - - // pattern used in this formatter - private String formatPattern = ""; - // style is only valid when decimal formatter is constructed by - // DecimalFormat(pattern, decimalFormatSymbol, style) - private int style = NumberFormat.NUMBERSTYLE; - /** - * Represents whether this is a currency format, and which currency format style. 0: - * not currency format type; 1: currency style -- symbol name, such as "$" for US - * dollar. 2: currency style -- ISO name, such as USD for US dollar. 3: currency style - * -- plural long name, such as "US Dollar" for "1.00 US Dollar", or "US Dollars" for - * "3.00 US Dollars". - */ - private int currencySignCount = CURRENCY_SIGN_COUNT_ZERO; - - /** - * For parsing purposes, we need to remember all prefix patterns and suffix patterns - * of every currency format pattern, including the pattern of the default currency - * style, ISO currency style, and plural currency style. The patterns are set through - * applyPattern. The following are used to represent the affix patterns in currency - * plural formats. - */ - private static final class AffixForCurrency { - // negative prefix pattern - private String negPrefixPatternForCurrency = null; - // negative suffix pattern - private String negSuffixPatternForCurrency = null; - // positive prefix pattern - private String posPrefixPatternForCurrency = null; - // positive suffix pattern - private String posSuffixPatternForCurrency = null; - private final int patternType; - - public AffixForCurrency(String negPrefix, String negSuffix, String posPrefix, - String posSuffix, int type) { - negPrefixPatternForCurrency = negPrefix; - negSuffixPatternForCurrency = negSuffix; - posPrefixPatternForCurrency = posPrefix; - posSuffixPatternForCurrency = posSuffix; - patternType = type; - } - - public String getNegPrefix() { - return negPrefixPatternForCurrency; - } - - public String getNegSuffix() { - return negSuffixPatternForCurrency; - } - - public String getPosPrefix() { - return posPrefixPatternForCurrency; - } - - public String getPosSuffix() { - return posSuffixPatternForCurrency; - } - - public int getPatternType() { - return patternType; - } - } - - // Affix pattern set for currency. It is a set of AffixForCurrency, each element of - // the set saves the negative prefix, negative suffix, positive prefix, and positive - // suffix of a pattern. - private transient Set affixPatternsForCurrency = null; - - // For currency parsing. Since currency parsing needs to parse against all currency - // patterns, before the parsing, we need to set up the affix patterns for all currencies. - private transient boolean isReadyForParsing = false; - - // Information needed for DecimalFormat to format/parse currency plural. - private CurrencyPluralInfo currencyPluralInfo = null; - - /** - * Unit is an immutable class for the textual representation of a unit, in - * particular its prefix and suffix. - * - * @author rocketman - * - */ - static class Unit { - private final String prefix; - private final String suffix; - - public Unit(String prefix, String suffix) { - this.prefix = prefix; - this.suffix = suffix; - } - - public void writeSuffix(StringBuffer toAppendTo) { - toAppendTo.append(suffix); - } - - public void writePrefix(StringBuffer toAppendTo) { - toAppendTo.append(prefix); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof Unit)) { - return false; - } - Unit other = (Unit) obj; - return prefix.equals(other.prefix) && suffix.equals(other.suffix); - } - @Override - public String toString() { - return prefix + "/" + suffix; - } - } - - static final Unit NULL_UNIT = new Unit("", ""); - - // Note about rounding implementation - // - // The original design intended to skip rounding operation when roundingIncrement is not - // set. However, rounding may need to occur when fractional digits exceed the width of - // fractional part of pattern. - // - // DigitList class has built-in rounding mechanism, using ROUND_HALF_EVEN. This implementation - // forces non-null roundingIncrement if the setting is other than ROUND_HALF_EVEN, otherwise, - // when rounding occurs in DigitList by pattern's fractional digits' width, the result - // does not match the rounding mode. - // - // Ideally, all rounding operation should be done in one place like ICU4C trunk does - // (ICU4C rounding implementation was rewritten recently). This is intrim implemetation - // to fix various issues. In the future, we should entire implementation of rounding - // in this class, like ICU4C did. - // - // Once we fully implement rounding logic in DigitList, then following fields and methods - // should be gone. - - private transient BigDecimal actualRoundingIncrementICU = null; - private transient java.math.BigDecimal actualRoundingIncrement = null; - - /* - * The actual rounding increment as a double. - */ - private transient double roundingDouble = 0.0; - - /* - * If the roundingDouble is the reciprocal of an integer (the most common case!), this - * is set to be that integer. Otherwise it is 0.0. - */ - private transient double roundingDoubleReciprocal = 0.0; - - /* - * Set roundingDouble, roundingDoubleReciprocal and actualRoundingIncrement - * based on rounding mode and width of fractional digits. Whenever setting affecting - * rounding mode, rounding increment and maximum width of fractional digits, then - * this method must be called. - * - * roundingIncrementICU is the field storing the custom rounding increment value, - * while actual rounding increment could be larger. - */ - private void resetActualRounding() { - if (roundingIncrementICU != null) { - BigDecimal byWidth = getMaximumFractionDigits() > 0 ? - BigDecimal.ONE.movePointLeft(getMaximumFractionDigits()) : BigDecimal.ONE; - if (roundingIncrementICU.compareTo(byWidth) >= 0) { - actualRoundingIncrementICU = roundingIncrementICU; - } else { - actualRoundingIncrementICU = byWidth.equals(BigDecimal.ONE) ? null : byWidth; - } - } else { - if (roundingMode == BigDecimal.ROUND_HALF_EVEN || isScientificNotation()) { - // This rounding fix is irrelevant if mode is ROUND_HALF_EVEN as DigitList - // does ROUND_HALF_EVEN for us. This rounding fix won't work at all for - // scientific notation. - actualRoundingIncrementICU = null; - } else { - if (getMaximumFractionDigits() > 0) { - actualRoundingIncrementICU = BigDecimal.ONE.movePointLeft(getMaximumFractionDigits()); - } else { - actualRoundingIncrementICU = BigDecimal.ONE; - } - } - } - - if (actualRoundingIncrementICU == null) { - setRoundingDouble(0.0d); - actualRoundingIncrement = null; - } else { - setRoundingDouble(actualRoundingIncrementICU.doubleValue()); - actualRoundingIncrement = actualRoundingIncrementICU.toBigDecimal(); - } - } - - static final double roundingIncrementEpsilon = 0.000000001; - - private void setRoundingDouble(double newValue) { - roundingDouble = newValue; - if (roundingDouble > 0.0d) { - double rawRoundedReciprocal = 1.0d / roundingDouble; - roundingDoubleReciprocal = Math.rint(rawRoundedReciprocal); - if (Math.abs(rawRoundedReciprocal - roundingDoubleReciprocal) > roundingIncrementEpsilon) { - roundingDoubleReciprocal = 0.0d; - } - } else { - roundingDoubleReciprocal = 0.0d; - } - } -} - -// eof diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java deleted file mode 100644 index 1d80c5b08a2..00000000000 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/text/DigitList.java +++ /dev/null @@ -1,842 +0,0 @@ -// © 2016 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -/* - ******************************************************************************* - * Copyright (C) 1996-2015, International Business Machines Corporation and * - * others. All Rights Reserved. * - ******************************************************************************* - */ -package com.ibm.icu.dev.text; - -import java.math.BigInteger; - -import com.ibm.icu.text.DecimalFormat; -import com.ibm.icu.text.NumberFormat; - -/** - * DigitList handles the transcoding between numeric values and - * strings of characters. It only represents non-negative numbers. The - * division of labor between DigitList and - * DecimalFormat is that DigitList handles the radix - * 10 representation issues and numeric conversion, including rounding; - * DecimalFormat handles the locale-specific issues such as - * positive and negative representation, digit grouping, decimal point, - * currency, and so on. - * - *

A DigitList is a representation of a finite numeric value. - * DigitList objects do not represent NaN or infinite - * values. A DigitList value can be converted to a - * BigDecimal without loss of precision. Conversion to other - * numeric formats may involve loss of precision, depending on the specific - * value. - * - *

The DigitList representation consists of a string of - * characters, which are the digits radix 10, from '0' to '9'. It also has a - * base 10 exponent associated with it. The value represented by a - * DigitList object can be computed by mulitplying the fraction - * f, where 0 <= f < 1, derived by placing all the digits of - * the list to the right of the decimal point, by 10^exponent. - * - * @see java.util.Locale - * @see java.text.Format - * @see NumberFormat - * @see DecimalFormat - * @see java.text.ChoiceFormat - * @see java.text.MessageFormat - * @version 1.18 08/12/98 - * @author Mark Davis, Alan Liu - * */ -public final class DigitList { - /** - * The maximum number of significant digits in an IEEE 754 double, that - * is, in a Java double. This must not be increased, or garbage digits - * will be generated, and should not be decreased, or accuracy will be lost. - */ - public static final int MAX_LONG_DIGITS = 19; // == Long.toString(Long.MAX_VALUE).length() - public static final int DBL_DIG = 17; - - /** - * These data members are intentionally public and can be set directly. - * - * The value represented is given by placing the decimal point before - * digits[decimalAt]. If decimalAt is < 0, then leading zeros between - * the decimal point and the first nonzero digit are implied. If decimalAt - * is > count, then trailing zeros between the digits[count-1] and the - * decimal point are implied. - * - * Equivalently, the represented value is given by f * 10^decimalAt. Here - * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to - * the right of the decimal. - * - * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We - * don't allow denormalized numbers because our exponent is effectively of - * unlimited magnitude. The count value contains the number of significant - * digits present in digits[]. - * - * Zero is represented by any DigitList with count == 0 or with each digits[i] - * for all i <= count == '0'. - */ - public int decimalAt = 0; - public int count = 0; - public byte[] digits = new byte[MAX_LONG_DIGITS]; - - private final void ensureCapacity(int digitCapacity, int digitsToCopy) { - if (digitCapacity > digits.length) { - byte[] newDigits = new byte[digitCapacity * 2]; - System.arraycopy(digits, 0, newDigits, 0, digitsToCopy); - digits = newDigits; - } - } - - /** - * Return true if the represented number is zero. - */ - boolean isZero() - { - for (int i=0; iBigInteger representing the value stored in this - * DigitList. This method assumes that this object contains - * an integral value; if not, it will return an incorrect value. - * [bnf] - * @param isPositive determines the sign of the returned result - * @return the value of this object as a BigInteger - */ - public BigInteger getBigInteger(boolean isPositive) { - if (isZero()) return BigInteger.valueOf(0); - //Eclipse stated the following is "dead code" - /*if (false) { - StringBuilder stringRep = new StringBuilder(count); - if (!isPositive) { - stringRep.append('-'); - } - for (int i=0; i count) { - stringRep.append('0'); - } - return new BigInteger(stringRep.toString()); - } else*/ { - int len = decimalAt > count ? decimalAt : count; - if (!isPositive) { - len += 1; - } - char[] text = new char[len]; - int n = 0; - if (!isPositive) { - text[0] = '-'; - for (int i = 0; i < count; ++i) { - text[i+1] = (char)digits[i]; - } - n = count+1; - } else { - for (int i = 0; i < count; ++i) { - text[i] = (char)digits[i]; - } - n = count; - } - for (int i = n; i < text.length; ++i) { - text[i] = '0'; - } - return new BigInteger(new String(text)); - } - } - - private String getStringRep(boolean isPositive) { - if (isZero()) return "0"; - StringBuilder stringRep = new StringBuilder(count+1); - if (!isPositive) { - stringRep.append('-'); - } - int d = decimalAt; - if (d < 0) { - stringRep.append('.'); - while (d < 0) { - stringRep.append('0'); - ++d; - } - d = -1; - } - for (int i=0; i count) { - stringRep.append('0'); - } - return stringRep.toString(); - } - - /** - * Return an ICU BigDecimal representing the value stored in this - * DigitList. - * [bnf] - * @param isPositive determines the sign of the returned result - * @return the value of this object as a BigDecimal - */ - public com.ibm.icu.math.BigDecimal getBigDecimalICU(boolean isPositive) { - if (isZero()) { - return com.ibm.icu.math.BigDecimal.valueOf(0); - } - // if exponential notion is negative, - // we prefer to use BigDecimal constructor with scale, - // because it works better when extremely small value - // is used. See #5698. - long scale = (long)count - (long)decimalAt; - if (scale > 0) { - int numDigits = count; - if (scale > Integer.MAX_VALUE) { - // try to reduce the scale - long numShift = scale - Integer.MAX_VALUE; - if (numShift < count) { - numDigits -= numShift; - } else { - // fallback to 0 - return new com.ibm.icu.math.BigDecimal(0); - } - } - StringBuilder significantDigits = new StringBuilder(numDigits + 1); - if (!isPositive) { - significantDigits.append('-'); - } - for (int i = 0; i < numDigits; i++) { - significantDigits.append((char)digits[i]); - } - BigInteger unscaledVal = new BigInteger(significantDigits.toString()); - return new com.ibm.icu.math.BigDecimal(unscaledVal, (int)scale); - } else { - return new com.ibm.icu.math.BigDecimal(getStringRep(isPositive)); - } - } - - /** - * Return whether or not this objects represented value is an integer. - * [bnf] - * @return true if the represented value of this object is an integer - */ - boolean isIntegral() { - // Trim trailing zeros. This does not change the represented value. - while (count > 0 && digits[count - 1] == (byte)'0') --count; - return count == 0 || decimalAt >= count; - } - -// Unused as of ICU 2.6 - alan -// /** -// * Return true if the number represented by this object can fit into -// * a long. -// */ -// boolean fitsIntoLong(boolean isPositive) -// { -// // Figure out if the result will fit in a long. We have to -// // first look for nonzero digits after the decimal point; -// // then check the size. If the digit count is 18 or less, then -// // the value can definitely be represented as a long. If it is 19 -// // then it may be too large. -// -// // Trim trailing zeros. This does not change the represented value. -// while (count > 0 && digits[count - 1] == (byte)'0') --count; -// -// if (count == 0) { -// // Positive zero fits into a long, but negative zero can only -// // be represented as a double. - bug 4162852 -// return isPositive; -// } -// -// if (decimalAt < count || decimalAt > MAX_LONG_DIGITS) return false; -// -// if (decimalAt < MAX_LONG_DIGITS) return true; -// -// // At this point we have decimalAt == count, and count == MAX_LONG_DIGITS. -// // The number will overflow if it is larger than 9223372036854775807 -// // or smaller than -9223372036854775808. -// for (int i=0; i max) return false; -// if (dig < max) return true; -// } -// -// // At this point the first count digits match. If decimalAt is less -// // than count, then the remaining digits are zero, and we return true. -// if (count < decimalAt) return true; -// -// // Now we have a representation of Long.MIN_VALUE, without the leading -// // negative sign. If this represents a positive value, then it does -// // not fit; otherwise it fits. -// return !isPositive; -// } - -// Unused as of ICU 2.6 - alan -// /** -// * Set the digit list to a representation of the given double value. -// * This method supports fixed-point notation. -// * @param source Value to be converted; must not be Inf, -Inf, Nan, -// * or a value <= 0. -// * @param maximumFractionDigits The most fractional digits which should -// * be converted. -// */ -// public final void set(double source, int maximumFractionDigits) -// { -// set(source, maximumFractionDigits, true); -// } - - /** - * Set the digit list to a representation of the given double value. - * This method supports both fixed-point and exponential notation. - * @param source Value to be converted; must not be Inf, -Inf, Nan, - * or a value <= 0. - * @param maximumDigits The most fractional or total digits which should - * be converted. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - final void set(double source, int maximumDigits, boolean fixedPoint) - { - if (source == 0) source = 0; - // Generate a representation of the form DDDDD, DDDDD.DDDDD, or - // DDDDDE+/-DDDDD. - String rep = Double.toString(source); - - didRound = false; - - set(rep, MAX_LONG_DIGITS); - - if (fixedPoint) { - // The negative of the exponent represents the number of leading - // zeros between the decimal and the first non-zero digit, for - // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this - // is more than the maximum fraction digits, then we have an underflow - // for the printed representation. - if (-decimalAt > maximumDigits) { - count = 0; - return; - } else if (-decimalAt == maximumDigits) { - if (shouldRoundUp(0)) { - count = 1; - ++decimalAt; - digits[0] = (byte)'1'; - } else { - count = 0; - } - return; - } - // else fall through - } - - // Eliminate trailing zeros. - while (count > 1 && digits[count - 1] == '0') - --count; - - // Eliminate digits beyond maximum digits to be displayed. - // Round up if appropriate. - round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits); - } - - /** - * Given a string representation of the form DDDDD, DDDDD.DDDDD, - * or DDDDDE+/-DDDDD, set this object's value to it. Ignore - * any leading '-'. - */ - private void set(String rep, int maxCount) { - decimalAt = -1; - count = 0; - int exponent = 0; - // Number of zeros between decimal point and first non-zero digit after - // decimal point, for numbers < 1. - int leadingZerosAfterDecimal = 0; - boolean nonZeroDigitSeen = false; - // Skip over leading '-' - int i=0; - if (rep.charAt(i) == '-') { - ++i; - } - for (; i < rep.length(); ++i) { - char c = rep.charAt(i); - if (c == '.') { - decimalAt = count; - } else if (c == 'e' || c == 'E') { - ++i; - // Integer.parseInt doesn't handle leading '+' signs - if (rep.charAt(i) == '+') { - ++i; - } - exponent = Integer.valueOf(rep.substring(i)).intValue(); - break; - } else if (count < maxCount) { - if (!nonZeroDigitSeen) { - nonZeroDigitSeen = (c != '0'); - if (!nonZeroDigitSeen && decimalAt != -1) { - ++leadingZerosAfterDecimal; - } - } - - if (nonZeroDigitSeen) { - ensureCapacity(count+1, count); - digits[count++] = (byte)c; - } - } - } - if (decimalAt == -1) { - decimalAt = count; - } - decimalAt += exponent - leadingZerosAfterDecimal; - } - - /** - * Return true if truncating the representation to the given number - * of digits will result in an increment to the last digit. This - * method implements half-even rounding, the default rounding mode. - * [bnf] - * @param maximumDigits the number of digits to keep, from 0 to - * count-1. If 0, then all digits are rounded away, and - * this method returns true if a one should be generated (e.g., formatting - * 0.09 with "#.#"). - * @return true if digit maximumDigits-1 should be - * incremented - */ - private boolean shouldRoundUp(int maximumDigits) { - // variable not used boolean increment = false; - // Implement IEEE half-even rounding - /*Bug 4243108 - format(0.0) gives "0.1" if preceded by parse("99.99") [Richard/GCL] - */ - if (maximumDigits < count) { - if (digits[maximumDigits] > '5') { - return true; - } else if (digits[maximumDigits] == '5' ) { - for (int i=maximumDigits+1; i 0 && (digits[maximumDigits-1] % 2 != 0); - } - } - return false; - } - - /** - * Round the representation to the given number of digits. - * @param maximumDigits The maximum number of digits to be shown. - * Upon return, count will be less than or equal to maximumDigits. - * This now performs rounding when maximumDigits is 0, formerly it did not. - */ - public final void round(int maximumDigits) { - // Eliminate digits beyond maximum digits to be displayed. - // Round up if appropriate. - // [bnf] rewritten to fix 4179818 - if (maximumDigits >= 0 && maximumDigits < count) { - if (shouldRoundUp(maximumDigits)) { - // Rounding up involves incrementing digits from LSD to MSD. - // In most cases this is simple, but in a worst case situation - // (9999..99) we have to adjust the decimalAt value. - for (;;) - { - --maximumDigits; - if (maximumDigits < 0) - { - // We have all 9's, so we increment to a single digit - // of one and adjust the exponent. - digits[0] = (byte) '1'; - ++decimalAt; - maximumDigits = 0; // Adjust the count - didRound = true; - break; - } - - ++digits[maximumDigits]; - didRound = true; - if (digits[maximumDigits] <= '9') break; - // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this - } - ++maximumDigits; // Increment for use as count - } - count = maximumDigits; - } - // Bug 4217661 DecimalFormat formats 1.001 to "1.00" instead of "1" - // Eliminate trailing zeros. [Richard/GCL] - // [dlf] moved outside if block, see ticket #6408 - while (count > 1 && digits[count-1] == '0') { - --count; - } - } - - // Value to indicate that rounding was done. - private boolean didRound = false; - - /** - * Indicates if last digit set was rounded or not. - * true indicates it was rounded. - * false indicates rounding has not been done. - */ - public boolean wasRounded() { - return didRound; - } - - /** - * Utility routine to set the value of the digit list from a long - */ - public final void set(long source) - { - set(source, 0); - } - - /** - * Set the digit list to a representation of the given long value. - * @param source Value to be converted; must be >= 0 or == - * Long.MIN_VALUE. - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - */ - public final void set(long source, int maximumDigits) - { - // This method does not expect a negative number. However, - // "source" can be a Long.MIN_VALUE (-9223372036854775808), - // if the number being formatted is a Long.MIN_VALUE. In that - // case, it will be formatted as -Long.MIN_VALUE, a number - // which is outside the legal range of a long, but which can - // be represented by DigitList. - // [NEW] Faster implementation - didRound = false; - - if (source <= 0) { - if (source == Long.MIN_VALUE) { - decimalAt = count = MAX_LONG_DIGITS; - System.arraycopy(LONG_MIN_REP, 0, digits, 0, count); - } else { - count = 0; - decimalAt = 0; - } - } else { - int left = MAX_LONG_DIGITS; - int right; - while (source > 0) { - digits[--left] = (byte) (('0') + (source % 10)); - source /= 10; - } - decimalAt = MAX_LONG_DIGITS-left; - // Don't copy trailing zeros - // we are guaranteed that there is at least one non-zero digit, - // so we don't have to check lower bounds - for (right = MAX_LONG_DIGITS - 1; digits[right] == (byte) '0'; --right) {} - count = right - left + 1; - System.arraycopy(digits, left, digits, 0, count); - } - if (maximumDigits > 0) round(maximumDigits); - } - - /** - * Set the digit list to a representation of the given BigInteger value. - * [bnf] - * @param source Value to be converted - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - */ - public final void set(BigInteger source, int maximumDigits) { - String stringDigits = source.toString(); - - count = decimalAt = stringDigits.length(); - didRound = false; - - // Don't copy trailing zeros - while (count > 1 && stringDigits.charAt(count - 1) == '0') --count; - - int offset = 0; - if (stringDigits.charAt(0) == '-') { - ++offset; - --count; - --decimalAt; - } - - ensureCapacity(count, 0); - for (int i = 0; i < count; ++i) { - digits[i] = (byte) stringDigits.charAt(i + offset); - } - - if (maximumDigits > 0) round(maximumDigits); - } - - /** - * Internal method that sets this digit list to represent the - * given value. The value is given as a String of the format - * returned by BigDecimal. - * @param stringDigits value to be represented with the following - * syntax, expressed as a regular expression: -?\d*.?\d* - * Must not be an empty string. - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - private void setBigDecimalDigits(String stringDigits, - int maximumDigits, boolean fixedPoint) { -//| // Find the first non-zero digit, the decimal, and the last non-zero digit. -//| int first=-1, last=stringDigits.length()-1, decimal=-1; -//| for (int i=0; (first<0 || decimal<0) && i<=last; ++i) { -//| char c = stringDigits.charAt(i); -//| if (c == '.') { -//| decimal = i; -//| } else if (first < 0 && (c >= '1' && c <= '9')) { -//| first = i; -//| } -//| } -//| -//| if (first < 0) { -//| clear(); -//| return; -//| } -//| -//| // At this point we know there is at least one non-zero digit, so the -//| // following loop is safe. -//| for (;;) { -//| char c = stringDigits.charAt(last); -//| if (c != '0' && c != '.') { -//| break; -//| } -//| --last; -//| } -//| -//| if (decimal < 0) { -//| decimal = stringDigits.length(); -//| } -//| -//| count = last - first; -//| if (decimal < first || decimal > last) { -//| ++count; -//| } -//| decimalAt = decimal - first; -//| if (decimalAt < 0) { -//| ++decimalAt; -//| } -//| -//| ensureCapacity(count, 0); -//| for (int i = 0; i < count; ++i) { -//| digits[i] = (byte) stringDigits.charAt(first++); -//| if (first == decimal) { -//| ++first; -//| } -//| } - - didRound = false; - - // The maxDigits here could also be Integer.MAX_VALUE - set(stringDigits, stringDigits.length()); - - // Eliminate digits beyond maximum digits to be displayed. - // Round up if appropriate. - // {dlf} Some callers depend on passing '0' to round to mean 'don't round', but - // rather than pass that information explicitly, we rely on some magic with maximumDigits - // and decimalAt. Unfortunately, this is no good, because there are cases where maximumDigits - // is zero and we do want to round, e.g. BigDecimal values -1 < x < 1. So since round - // changed to perform rounding when the argument is 0, we now force the argument - // to -1 in the situations where it matters. - round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits == 0 ? -1 : maximumDigits); - } - - /** - * Set the digit list to a representation of the given BigDecimal value. - * [bnf] - * @param source Value to be converted - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - public final void set(java.math.BigDecimal source, - int maximumDigits, boolean fixedPoint) { - setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint); - } - - /* - * Set the digit list to a representation of the given BigDecimal value. - * [bnf] - * @param source Value to be converted - * @param maximumDigits The most digits which should be converted. - * If maximumDigits is lower than the number of significant digits - * in source, the representation will be rounded. Ignored if <= 0. - * @param fixedPoint If true, then maximumDigits is the maximum - * fractional digits to be converted. If false, total digits. - */ - public final void set(com.ibm.icu.math.BigDecimal source, - int maximumDigits, boolean fixedPoint) { - setBigDecimalDigits(source.toString(), maximumDigits, fixedPoint); - } - - /** - * Returns true if this DigitList represents Long.MIN_VALUE; - * false, otherwise. This is required so that getLong() works. - */ - private boolean isLongMIN_VALUE() - { - if (decimalAt != count || count != MAX_LONG_DIGITS) - return false; - - for (int i = 0; i < count; ++i) - { - if (digits[i] != LONG_MIN_REP[i]) return false; - } - - return true; - } - - private static byte[] LONG_MIN_REP; - - static - { - // Store the representation of LONG_MIN without the leading '-' - String s = Long.toString(Long.MIN_VALUE); - LONG_MIN_REP = new byte[MAX_LONG_DIGITS]; - for (int i=0; i < MAX_LONG_DIGITS; ++i) - { - LONG_MIN_REP[i] = (byte)s.charAt(i + 1); - } - } - -// Unused -- Alan 2003-05 -// /** -// * Return the floor of the log base 10 of a given double. -// * This method compensates for inaccuracies which arise naturally when -// * computing logs, and always give the correct value. The parameter -// * must be positive and finite. -// */ -// private static final int log10(double d) -// { -// // The reason this routine is needed is that simply taking the -// // log and dividing by log10 yields a result which may be off -// // by 1 due to rounding errors. For example, the naive log10 -// // of 1.0e300 taken this way is 299, rather than 300. -// double log10 = Math.log(d) / LOG10; -// int ilog10 = (int)Math.floor(log10); -// // Positive logs could be too small, e.g. 0.99 instead of 1.0 -// if (log10 > 0 && d >= Math.pow(10, ilog10 + 1)) -// { -// ++ilog10; -// } -// // Negative logs could be too big, e.g. -0.99 instead of -1.0 -// else if (log10 < 0 && d < Math.pow(10, ilog10)) -// { -// --ilog10; -// } -// return ilog10; -// } -// -// private static final double LOG10 = Math.log(10.0); - - /** - * equality test between two digit lists. - */ - @Override - public boolean equals(Object obj) { - if (this == obj) // quick check - return true; - if (!(obj instanceof DigitList)) // (1) same object? - return false; - DigitList other = (DigitList) obj; - if (count != other.count || - decimalAt != other.decimalAt) - return false; - for (int i = 0; i < count; i++) - if (digits[i] != other.digits[i]) - return false; - return true; - } - - /** - * Generates the hash code for the digit list. - */ - @Override - public int hashCode() { - int hashcode = decimalAt; - - for (int i = 0; i < count; i++) - hashcode = hashcode * 37 + digits[i]; - - return hashcode; - } - - @Override - public String toString() - { - if (isZero()) return "0"; - StringBuilder buf = new StringBuilder("0."); - for (int i=0; i