From: Tom Zhang Date: Tue, 10 Jun 2014 18:48:49 +0000 (+0000) Subject: ICU-10344 add new field & factory method for currency CASH/STANDARD Usage X-Git-Tag: milestone-59-0-1~1863 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2330004e121cc5e86c40173fb6ff4b3aeb798432;p=icu ICU-10344 add new field & factory method for currency CASH/STANDARD Usage X-SVN-Rev: 35853 --- diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/CurrencyMetaInfo.java b/icu4j/main/classes/core/src/com/ibm/icu/text/CurrencyMetaInfo.java index bbb3b48384b..6340f830299 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/CurrencyMetaInfo.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/CurrencyMetaInfo.java @@ -12,6 +12,7 @@ import java.util.Date; import java.util.List; import com.ibm.icu.impl.Grego; +import com.ibm.icu.util.Currency.CurrencyUsage; /** * Provides information about currencies that is not specific to a locale. @@ -532,11 +533,24 @@ public class CurrencyMetaInfo { /** * Returns the CurrencyDigits for the currency code. + * This is equivalent to currencyDigits(isoCode, CurrencyUsage.STANDARD); * @param isoCode the currency code * @return the CurrencyDigits * @stable ICU 4.4 */ public CurrencyDigits currencyDigits(String isoCode) { + return currencyDigits(isoCode, CurrencyUsage.STANDARD); + } + + /** + * Returns the CurrencyDigits for the currency code with Context Usage. + * @param isoCode the currency code + * @param currencyUsage the currency usage + * @return the CurrencyDigits + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + public CurrencyDigits currencyDigits(String isoCode, CurrencyUsage currencyUsage) { return defaultDigits; } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java index fc1ff98cc13..7abba4e40ae 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java @@ -29,6 +29,7 @@ import com.ibm.icu.math.BigDecimal; import com.ibm.icu.math.MathContext; import com.ibm.icu.text.PluralRules.FixedDecimal; 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; @@ -2298,7 +2299,7 @@ public class DecimalFormat extends NumberFormat { 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") @@ -3832,6 +3833,7 @@ public class DecimalFormat extends NumberFormat { 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 @@ -3873,7 +3875,8 @@ public class DecimalFormat extends NumberFormat { && (!useSignificantDigits || minSignificantDigits == other.minSignificantDigits && maxSignificantDigits == other.maxSignificantDigits) && symbols.equals(other.symbols) - && Utility.objectEquals(currencyPluralInfo, other.currencyPluralInfo); + && Utility.objectEquals(currencyPluralInfo, other.currencyPluralInfo) + && currencyUsage.equals(other.currencyUsage); } // method to unquote the strings and compare @@ -5030,8 +5033,8 @@ public class DecimalFormat extends NumberFormat { // by the currency Currency theCurrency = getCurrency(); if (theCurrency != null) { - setRoundingIncrement(theCurrency.getRoundingIncrement()); - int d = theCurrency.getDefaultFractionDigits(); + setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage)); + int d = theCurrency.getDefaultFractionDigits(currencyUsage); setMinimumFractionDigits(d); _setMaximumFractionDigits(d); } @@ -5194,8 +5197,8 @@ public class DecimalFormat extends NumberFormat { if (currencySignCount != CURRENCY_SIGN_COUNT_ZERO) { if (theCurrency != null) { - setRoundingIncrement(theCurrency.getRoundingIncrement()); - int d = theCurrency.getDefaultFractionDigits(); + setRoundingIncrement(theCurrency.getRoundingIncrement(currencyUsage)); + int d = theCurrency.getDefaultFractionDigits(currencyUsage); setMinimumFractionDigits(d); setMaximumFractionDigits(d); } @@ -5206,7 +5209,40 @@ public class DecimalFormat extends NumberFormat { } } } + + /** + * 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. + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + 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 + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + 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. @@ -5362,6 +5398,9 @@ public class DecimalFormat extends NumberFormat { // the DecimalFormatSymbols object. setCurrencyForSymbols(); } + if (serialVersionOnStream < 4) { + currencyUsage = CurrencyUsage.STANDARD; + } serialVersionOnStream = currentSerialVersion; digitList = new DigitList(); @@ -5668,9 +5707,16 @@ public class DecimalFormat extends NumberFormat { */ 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 = 3; + static final int currentSerialVersion = 4; /** * The internal serial version which says which version was written Possible values @@ -5688,6 +5734,8 @@ public class DecimalFormat extends NumberFormat { * *
  • 3: ICU 2.2. Adds currency object. * + *
  • 4: ICU 54. Adds currency usage(standard vs cash) + * * * * @serial diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java index 088d07f308d..41133537f16 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormat.java @@ -23,6 +23,7 @@ import java.util.Set; import com.ibm.icu.impl.ICUResourceBundle; 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; @@ -215,6 +216,14 @@ public abstract class NumberFormat extends UFormat { * @provisional This API might change or be removed in a future release. */ public static final int ACCOUNTINGCURRENCYSTYLE = 7; + /** + * {@icu} Constant to specify currency cash style of format which uses currency + * ISO code to represent currency, for example: "NT$3" instead of "NT$3.23". + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + public static final int CASHCURRENCYSTYLE = 8; + /** /** * Field constant used to construct a FieldPosition object. Signifies that @@ -231,7 +240,7 @@ public abstract class NumberFormat extends UFormat { * @stable ICU 2.0 */ public static final int FRACTION_FIELD = 1; - + /** * Formats a number and appends the resulting text to the given string buffer. * {@icunote} recognizes BigInteger @@ -1240,7 +1249,7 @@ public abstract class NumberFormat extends UFormat { public Currency getCurrency() { return currency; } - + /** * Returns the currency in effect for this formatter. Subclasses * should override this method as needed. Unlike getCurrency(), @@ -1302,7 +1311,7 @@ public abstract class NumberFormat extends UFormat { * @stable ICU 4.2 */ public static NumberFormat getInstance(ULocale desiredLocale, int choice) { - if (choice < NUMBERSTYLE || choice > ACCOUNTINGCURRENCYSTYLE) { + if (choice < NUMBERSTYLE || choice > CASHCURRENCYSTYLE) { throw new IllegalArgumentException( "choice should be from NUMBERSTYLE to PLURALCURRENCYSTYLE"); } @@ -1331,7 +1340,8 @@ public abstract class NumberFormat extends UFormat { // This style wont work for currency plural format. // For currency plural format, the pattern is get from // the locale (from CurrencyUnitPatterns) without override. - if(choice == CURRENCYSTYLE || choice == ISOCURRENCYSTYLE || choice == ACCOUNTINGCURRENCYSTYLE){ + if (choice == CURRENCYSTYLE || choice == ISOCURRENCYSTYLE || choice == ACCOUNTINGCURRENCYSTYLE + || choice == CASHCURRENCYSTYLE) { String temp = symbols.getCurrencyPattern(); if(temp!=null){ pattern = temp; @@ -1394,6 +1404,10 @@ public abstract class NumberFormat extends UFormat { f.setDecimalSeparatorAlwaysShown(false); f.setParseIntegerOnly(true); } + + if (choice == CASHCURRENCYSTYLE) { + f.setCurrencyUsage(CurrencyUsage.CASH); + } format = f; } // TODO: the actual locale of the *pattern* may differ from that @@ -1477,6 +1491,7 @@ public abstract class NumberFormat extends UFormat { patternKey = "decimalFormat"; break; case CURRENCYSTYLE: + case CASHCURRENCYSTYLE: case ISOCURRENCYSTYLE: case PLURALCURRENCYSTYLE: patternKey = "currencyFormat"; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java b/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java index 2e405120bb6..2d66bcae9a1 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java @@ -89,6 +89,29 @@ public class Currency extends MeasureUnit { .add("\u20a8", "\u20b9") .add("\u00a3", "\u20a4"); + /** + * Currency Usage used for Decimal Format + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + public enum CurrencyUsage{ + /** + * a setting to specify currency usage which determines currency digit and rounding + * for standard usage, for example: "50.00 NT$" + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + STANDARD, + + /** + * a setting to specify currency usage which determines currency digit and rounding + * for cash usage, for example: "50 NT$" + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + CASH + } + // begin registry stuff // shim for service code @@ -729,25 +752,52 @@ public class Currency extends MeasureUnit { /** * Returns the number of the number of fraction digits that should * be displayed for this currency. + * This is equivalent to getDefaultFractionDigits(CurrencyUsage.STANDARD); * @return a non-negative number of fraction digits to be * displayed * @stable ICU 2.2 */ public int getDefaultFractionDigits() { + return getDefaultFractionDigits(CurrencyUsage.STANDARD); + } + + /** + * Returns the number of the number of fraction digits that should + * be displayed for this currency with Usage. + * @param Usage the usage of currency(Standard or Cash) + * @return a non-negative number of fraction digits to be + * displayed + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + public int getDefaultFractionDigits(CurrencyUsage Usage) { CurrencyMetaInfo info = CurrencyMetaInfo.getInstance(); - CurrencyDigits digits = info.currencyDigits(subType); + CurrencyDigits digits = info.currencyDigits(subType, Usage); return digits.fractionDigits; } /** * Returns the rounding increment for this currency, or 0.0 if no * rounding is done by this currency. + * This is equivalent to getRoundingIncrement(CurrencyUsage.STANDARD); * @return the non-negative rounding increment, or 0.0 if none * @stable ICU 2.2 */ public double getRoundingIncrement() { + return getRoundingIncrement(CurrencyUsage.STANDARD); + } + + /** + * Returns the rounding increment for this currency, or 0.0 if no + * rounding is done by this currency with the Usage. + * @param Usage the usage of currency(Standard or Cash) + * @return the non-negative rounding increment, or 0.0 if none + * @draft ICU 54 + * @provisional This API might change or be removed in a future release. + */ + public double getRoundingIncrement(CurrencyUsage Usage) { CurrencyMetaInfo info = CurrencyMetaInfo.getInstance(); - CurrencyDigits digits = info.currencyDigits(subType); + CurrencyDigits digits = info.currencyDigits(subType, Usage); int data1 = digits.roundingIncrement; @@ -764,7 +814,7 @@ public class Currency extends MeasureUnit { return 0.0; } - // Return data[1] / 10^(data[0]). The only actual rounding data, + // Return data[1] / 10^(data[0]). The only actual rounding data, // as of this writing, is CHF { 2, 25 }. return (double) data1 / POW10[data0]; } diff --git a/icu4j/main/classes/currdata/src/com/ibm/icu/impl/ICUCurrencyMetaInfo.java b/icu4j/main/classes/currdata/src/com/ibm/icu/impl/ICUCurrencyMetaInfo.java index 82245ba61f9..b2329bd221e 100644 --- a/icu4j/main/classes/currdata/src/com/ibm/icu/impl/ICUCurrencyMetaInfo.java +++ b/icu4j/main/classes/currdata/src/com/ibm/icu/impl/ICUCurrencyMetaInfo.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Set; import com.ibm.icu.text.CurrencyMetaInfo; +import com.ibm.icu.util.Currency.CurrencyUsage; /** * ICU's currency meta info data. @@ -46,14 +47,25 @@ public class ICUCurrencyMetaInfo extends CurrencyMetaInfo { @Override public CurrencyDigits currencyDigits(String isoCode) { + return currencyDigits(isoCode, CurrencyUsage.STANDARD); + } + + @Override + public CurrencyDigits currencyDigits(String isoCode, CurrencyUsage currencyPurpose) { ICUResourceBundle b = digitInfo.findWithFallback(isoCode); if (b == null) { b = digitInfo.findWithFallback("DEFAULT"); } int[] data = b.getIntVector(); - return new CurrencyDigits(data[0], data[1]); + if (currencyPurpose == CurrencyUsage.CASH) { + return new CurrencyDigits(data[2], data[3]); + } else if (currencyPurpose == CurrencyUsage.STANDARD) { + return new CurrencyDigits(data[0], data[1]); + } else { + return new CurrencyDigits(data[0], data[1]); + } } - + private List collect(Collector collector, CurrencyFilter filter) { // We rely on the fact that the data lists the regions in order, and the // priorities in order within region. This means we don't need 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 4f9bccc2cac..df67d0499b6 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 @@ -2735,7 +2735,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk { */ public void TestGetInstance() { // Tests "public final static NumberFormat getInstance(int style)" - int maxStyle = NumberFormat.ACCOUNTINGCURRENCYSTYLE; + int maxStyle = NumberFormat.CASHCURRENCYSTYLE; int[] invalid_cases = { NumberFormat.NUMBERSTYLE - 1, NumberFormat.NUMBERSTYLE - 2, maxStyle + 1, maxStyle + 2 }; @@ -3608,4 +3608,78 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk { expect(acfmt, num, fmt, rt); } } + + public void TestCurrencyUsage() { + // the 1st one is checking setter/getter, while the 2nd one checks for getInstance + // compare the Currency and Currency Cash Digits + for (int i = 0; i < 2; i++) { + String original_expected = "NT$123.57"; + DecimalFormat custom = null; + if (i == 0) { + custom = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=TWD"), + DecimalFormat.CURRENCYSTYLE); + + String original = custom.format(123.567); + assertEquals("Test Currency Context", original_expected, original); + + // test the getter + assertEquals("Test Currency Context Purpose", custom.getCurrencyUsage(), + Currency.CurrencyUsage.STANDARD); + custom.setCurrencyUsage(Currency.CurrencyUsage.CASH); + assertEquals("Test Currency Context Purpose", custom.getCurrencyUsage(), Currency.CurrencyUsage.CASH); + } else { + custom = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=TWD"), + DecimalFormat.CASHCURRENCYSTYLE); + + // test the getter + assertEquals("Test Currency Context Purpose", custom.getCurrencyUsage(), Currency.CurrencyUsage.CASH); + } + + String cash_currency = custom.format(123.567); + String cash_currency_expected = "NT$124"; + assertEquals("Test Currency Context", cash_currency_expected, cash_currency); + } + + // the 1st one is checking setter/getter, while the 2nd one checks for getInstance + // compare the Currency and Currency Cash Rounding + for (int i = 0; i < 2; i++) { + String original_rounding_expected = "CA$123.57"; + DecimalFormat fmt = null; + if (i == 0) { + fmt = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=CAD"), + DecimalFormat.CURRENCYSTYLE); + + String original_rounding = fmt.format(123.566); + assertEquals("Test Currency Context", original_rounding_expected, original_rounding); + + fmt.setCurrencyUsage(Currency.CurrencyUsage.CASH); + } else { + fmt = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=CAD"), + DecimalFormat.CASHCURRENCYSTYLE); + } + + String cash_rounding_currency = fmt.format(123.567); + String cash__rounding_currency_expected = "CA$123.55"; + assertEquals("Test Currency Context", cash__rounding_currency_expected, cash_rounding_currency); + } + + // the 1st one is checking setter/getter, while the 2nd one checks for getInstance + // Test the currency change + for (int i = 0; i < 2; i++) { + DecimalFormat fmt2 = null; + if (i == 1) { + fmt2 = (DecimalFormat) NumberFormat.getInstance(new ULocale("en_US@currency=JPY"), + NumberFormat.CURRENCYSTYLE); + fmt2.setCurrencyUsage(Currency.CurrencyUsage.CASH); + } else { + fmt2 = (DecimalFormat) NumberFormat.getInstance(new ULocale("en_US@currency=JPY"), + NumberFormat.CASHCURRENCYSTYLE); + } + + fmt2.setCurrency(Currency.getInstance("TWD")); + String TWD_changed = fmt2.format(123.567); + String TWD_changed_expected = "NT$124"; + assertEquals("Test Currency Context", TWD_changed_expected, TWD_changed); + } + } }