mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-08 06:53:45 +00:00
ICU-6433 currency plural format check in to trunk
X-SVN-Rev: 24679
This commit is contained in:
parent
025c58554c
commit
382e5bb891
4 changed files with 1124 additions and 174 deletions
|
@ -20,8 +20,10 @@ import com.ibm.icu.util.*;
|
|||
import com.ibm.icu.impl.LocaleUtility;
|
||||
import com.ibm.icu.impl.data.ResourceReader;
|
||||
import com.ibm.icu.impl.data.TokenIterator;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.math.BigDecimal;
|
||||
|
||||
import java.lang.Double;
|
||||
import java.math.BigInteger;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
|
@ -238,6 +240,104 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
|||
|
||||
}
|
||||
|
||||
public void TestMultiCurrencySign() {
|
||||
Object[][] DATA = {
|
||||
// US
|
||||
{Locale.US, "#,##0.00;-", "#,##0.00", "1234.56", "$1,234.56", "USD1,234.56", "US dollars1,234.56"},
|
||||
{Locale.US, "#,##0.00;-", "#,##0.00", "-1234.56", "-$1,234.56", "-USD1,234.56", "-US dollars1,234.56"},
|
||||
{Locale.US, "#,##0.00;-", "#,##0.00", "1", "$1.00", "USD1.00", "US dollar1.00"},
|
||||
// CHINA
|
||||
{Locale.CHINA, "#,##0.00;(", "#,##0.00)", "1234.56", "\uFFE51,234.56", "CNY1,234.56", "\u4EBA\u6C11\u5E011,234.56"},
|
||||
{Locale.CHINA, "#,##0.00;(", "#,##0.00)", "-1234.56", "(\uFFE51,234.56)", "(CNY1,234.56)", "(\u4EBA\u6C11\u5E011,234.56)"},
|
||||
{Locale.CHINA, "#,##0.00;(", "#,##0.00)", "1", "\uFFE51.00", "CNY1.00", "\u4EBA\u6C11\u5E011.00"}
|
||||
};
|
||||
|
||||
char currency = 0x00A4;
|
||||
for (int i=0; i<DATA.length; ++i) {
|
||||
// Locale.US
|
||||
DecimalFormatSymbols sym = new DecimalFormatSymbols((Locale)DATA[i][0]);
|
||||
for (int j=1; j<=3; ++j) {
|
||||
// "\xA4#,##0.00;-\xA4#,##0.00"
|
||||
StringBuffer pat = new StringBuffer("");
|
||||
for (int k=1; k<=j; k++) {
|
||||
pat.append(currency);
|
||||
}
|
||||
pat.append((String)DATA[i][1]);
|
||||
for (int k=1; k<=j; k++) {
|
||||
pat.append(currency);
|
||||
}
|
||||
pat.append((String)DATA[i][2]);
|
||||
DecimalFormat fmt = new DecimalFormat(pat.toString(), sym);
|
||||
String s = ((NumberFormat) fmt).format(new Double((String)DATA[i][3]));
|
||||
if (!s.equals((String)DATA[i][3+j])) {
|
||||
errln("FAIL format: Expected " + (String)DATA[i][3+j]);
|
||||
}
|
||||
try {
|
||||
for (int k=4; k<=6; ++k) {
|
||||
if (fmt.parse((String)DATA[i][k]).doubleValue() !=
|
||||
(new Double((String)DATA[i][3])).doubleValue()) {
|
||||
errln("FAILED parse " + DATA[i][k]);
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
errln("FAILED, DecimalFormat parse currency: " + e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TestCurrencyFormatForMixParsing() {
|
||||
MeasureFormat curFmt = MeasureFormat.getCurrencyFormat(new ULocale("en_US"));
|
||||
String[] formats = {
|
||||
"$1,234.56",
|
||||
"USD1,234.56",
|
||||
"US dollars1,234.56",
|
||||
"1,234.56 US dollars"
|
||||
};
|
||||
try {
|
||||
for (int i = 0; i < formats.length; ++i) {
|
||||
CurrencyAmount parsedVal = (CurrencyAmount)curFmt.parseObject(formats[i]);
|
||||
Number val = parsedVal.getNumber();
|
||||
if (!val.equals(new BigDecimal("1234.56"))) {
|
||||
errln("FAIL: getCurrencyFormat of default locale (en_US) failed roundtripping the number. val=" + val);
|
||||
}
|
||||
if (!parsedVal.getCurrency().equals(Currency.getInstance("USD"))) {
|
||||
errln("FAIL: getCurrencyFormat of default locale (en_US) failed roundtripping the currency");
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
errln("parse FAILED: " + e.toString());
|
||||
};
|
||||
}
|
||||
|
||||
public void TestDecimalFormatCurrencyParse() {
|
||||
// Locale.US
|
||||
DecimalFormatSymbols sym = new DecimalFormatSymbols(Locale.US);
|
||||
StringBuffer pat = new StringBuffer("");
|
||||
char currency = 0x00A4;
|
||||
// "\xA4#,##0.00;-\xA4#,##0.00"
|
||||
pat.append(currency).append(currency).append(currency).append("#,##0.00;-").append(currency).append(currency).append(currency).append("#,##0.00");
|
||||
DecimalFormat fmt = new DecimalFormat(pat.toString(), sym);
|
||||
String[] DATA = {
|
||||
"$1.00", "1",
|
||||
"USD1.00", "1",
|
||||
"1.00 US dollar", "1",
|
||||
"$1,234.56", "1234.56",
|
||||
"USD1,234.56", "1234.56",
|
||||
"1,234.56 US dollar", "1234.56",
|
||||
};
|
||||
try {
|
||||
for (int i = 0; i < DATA.length; i +=2) {
|
||||
Number num = fmt.parse(DATA[i]);
|
||||
if (num.doubleValue() != (new Double(DATA[i+1])).doubleValue()){
|
||||
errln("FAIL parse: Expected " + DATA[i+1]);
|
||||
}
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
errln("FAILED, DecimalFormat parse currency: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test localized currency patterns.
|
||||
*/
|
||||
|
@ -286,6 +386,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
|||
ULocale.setDefault(ULocale.US);
|
||||
MeasureFormat curFmt = MeasureFormat.getCurrencyFormat();
|
||||
String strBuf = curFmt.format(new CurrencyAmount(new Float(1234.56), Currency.getInstance("USD")));
|
||||
|
||||
try {
|
||||
CurrencyAmount parsedVal = (CurrencyAmount)curFmt.parseObject(strBuf);
|
||||
Number val = parsedVal.getNumber();
|
||||
|
@ -302,6 +403,55 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
|||
ULocale.setDefault(save);
|
||||
}
|
||||
|
||||
public void TestCurrencyIsoPluralFormat() {
|
||||
String[] DATA = {
|
||||
//"sk", "1", "USD", "1.00 $", "1,00\u00A0USD", "1,00 US dol\u00E1r",
|
||||
"en_US", "1", "USD", "$1.00", "USD1.00", "1.00 US dollar",
|
||||
"en_US", "1234.56", "USD", "$1,234.56", "USD1,234.56", "1,234.56 US dollars",
|
||||
"en_US", "-1234.56", "USD", "($1,234.56)", "(USD1,234.56)", "-1,234.56 US dollars",
|
||||
"zh_CN", "1", "USD", "US$1.00", "USD1.00", "1.00 \u7F8E\u5143",
|
||||
"zh_CN", "1234.56", "USD", "US$1,234.56", "USD1,234.56", "1,234.56 \u7F8E\u5143",
|
||||
"zh_CN", "1", "CHY", "CHY1.00", "CHY1.00", "1.00 CHY",
|
||||
"zh_CN", "1234.56", "CHY", "CHY1,234.56", "CHY1,234.56", "1,234.56 CHY",
|
||||
"zh_CN", "1", "CNY", "\uFFE51.00", "CNY1.00", "1.00 \u4EBA\u6C11\u5E01",
|
||||
"zh_CN", "1234.56", "CNY", "\uFFE51,234.56", "CNY1,234.56", "1,234.56 \u4EBA\u6C11\u5E01",
|
||||
};
|
||||
|
||||
for (int i=0; i<DATA.length; i+=6) {
|
||||
for (int k = NumberFormat.CURRENCYSTYLE;
|
||||
k <= NumberFormat.PLURALCURRENCYSTYLE;
|
||||
++k) {
|
||||
if ( k != NumberFormat.CURRENCYSTYLE &&
|
||||
k != NumberFormat.ISOCURRENCYSTYLE &&
|
||||
k != NumberFormat.PLURALCURRENCYSTYLE ) {
|
||||
continue;
|
||||
}
|
||||
NumberFormat numFmt = NumberFormat.getInstance(new ULocale(DATA[i]), k);
|
||||
numFmt.setCurrency(Currency.getInstance(DATA[i+2]));
|
||||
String strBuf = numFmt.format(new Double(DATA[i+1]));
|
||||
int resultDataIndex = i+k-1;
|
||||
if ( k == NumberFormat.CURRENCYSTYLE ) {
|
||||
resultDataIndex = i+k+2;
|
||||
}
|
||||
if (!strBuf.equals(Utility.unescape(DATA[resultDataIndex]))) {
|
||||
errln("FAIL: Expected " + DATA[resultDataIndex] + " actual: " + Utility.escape(strBuf));
|
||||
}
|
||||
try {
|
||||
// test parsing, and test parsing for all currency formats.
|
||||
for (int j = i+3; j < i+6; ++j) {
|
||||
Number val = numFmt.parse(DATA[j]);
|
||||
if (val.doubleValue() != ((new Double(DATA[i+1])).doubleValue())) {
|
||||
errln("FAIL: getCurrencyFormat of locale " + DATA[i] + " failed roundtripping the number. val=" + val + "; expected: " + DATA[i+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ParseException e) {
|
||||
errln("FAIL: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the Currency object handling, new as of ICU 2.2.
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,18 @@ import java.math.BigInteger;
|
|||
import java.text.ChoiceFormat;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
|
||||
|
||||
//#if defined(FOUNDATION10)
|
||||
//#else
|
||||
|
@ -42,7 +54,7 @@ import com.ibm.icu.util.ULocale;
|
|||
* 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"). All of these flavors can be easily
|
||||
* ("12%"), and currency amounts ("$123.00", "USD123.00", "123.00 US dollars"). All of these flavors can be easily
|
||||
* localized.
|
||||
*
|
||||
*
|
||||
|
@ -95,6 +107,27 @@ import com.ibm.icu.util.ULocale;
|
|||
* }
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* <P>
|
||||
* Another example use getInstance(style)
|
||||
* <P>
|
||||
* <pre>
|
||||
* <strong>// Print out a number using the localized number, currency,
|
||||
* // percent, scientific, integer, iso currency, and plural currency
|
||||
* // format for each locale</strong>
|
||||
* 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) {}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* <h4>Patterns</h4>
|
||||
*
|
||||
* <p>A <code>DecimalFormat</code> consists of a <em>pattern</em> and a set of
|
||||
|
@ -205,6 +238,8 @@ import com.ibm.icu.util.ULocale;
|
|||
* <td>No
|
||||
* <td>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.
|
||||
* <tr valign=top bgcolor="#eeeeff">
|
||||
|
@ -323,6 +358,12 @@ import com.ibm.icu.util.ULocale;
|
|||
*
|
||||
* <p>During parsing, grouping separators are ignored.
|
||||
*
|
||||
* <p>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 get from
|
||||
* NumberFormat.getInstance(ULocale, NumberFormat.CURRENCYSTYLE) can parse
|
||||
* formats such as "USD1.00" and "3.00 US dollars".
|
||||
*
|
||||
* <p>If {@link #parse(String, ParsePosition)} fails to parse
|
||||
* a string, it returns <code>null</code> and leaves the parse position
|
||||
* unchanged. The convenience method {@link #parse(String)}
|
||||
|
@ -629,7 +670,15 @@ public class DecimalFormat extends NumberFormat {
|
|||
// Always applyPattern after the symbols are set
|
||||
this.symbols = new DecimalFormatSymbols(def);
|
||||
setCurrency(Currency.getInstance(def));
|
||||
applyPattern(pattern, false);
|
||||
applyPatternWithoutExpandAffix(pattern, false);
|
||||
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||
// plural rule is only needed for currency plural format.
|
||||
pluralRules = PluralRules.forLocale(def);
|
||||
// the exact pattern is not known until the plural count is known.
|
||||
// so, no need to expand affix now.
|
||||
} else {
|
||||
expandAffixAdjustWidth(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -655,7 +704,13 @@ public class DecimalFormat extends NumberFormat {
|
|||
ULocale def = ULocale.getDefault();
|
||||
this.symbols = new DecimalFormatSymbols(def);
|
||||
setCurrency(Currency.getInstance(def));
|
||||
applyPattern( pattern, false );
|
||||
applyPatternWithoutExpandAffix( pattern, false );
|
||||
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||
// plural rule is only needed for currency plural format.
|
||||
pluralRules = PluralRules.forLocale(def);
|
||||
} else {
|
||||
expandAffixAdjustWidth(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -680,10 +735,87 @@ public class DecimalFormat extends NumberFormat {
|
|||
* @stable ICU 2.0
|
||||
*/
|
||||
public DecimalFormat(String pattern, DecimalFormatSymbols symbols) {
|
||||
createFromPatternAndSymbols(pattern, symbols);
|
||||
}
|
||||
|
||||
private void createFromPatternAndSymbols(String pattern, DecimalFormatSymbols symbols) {
|
||||
// Always applyPattern after the symbols are set
|
||||
this.symbols = (DecimalFormatSymbols) symbols.clone();
|
||||
setCurrencyForSymbols();
|
||||
applyPattern( pattern, false );
|
||||
applyPatternWithoutExpandAffix(pattern, false);
|
||||
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||
// plural rule is only needed for currency plural format.
|
||||
ULocale uloc = getLocale(ULocale.VALID_LOCALE);
|
||||
if (uloc == null) {
|
||||
uloc = symbols.getLocale(ULocale.VALID_LOCALE);
|
||||
}
|
||||
pluralRules = PluralRules.forLocale(uloc);
|
||||
} else {
|
||||
expandAffixAdjustWidth(null);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a DecimalFormat for currency plural format
|
||||
* from the given pattern, symbols, and style.
|
||||
*/
|
||||
DecimalFormat(String pattern, DecimalFormatSymbols symbols, int style) {
|
||||
if (style != NumberFormat.PLURALCURRENCYSTYLE) {
|
||||
createFromPatternAndSymbols(pattern, symbols);
|
||||
} else {
|
||||
// Always applyPattern after the symbols are set
|
||||
this.symbols = (DecimalFormatSymbols) symbols.clone();
|
||||
ULocale uloc = getLocale(ULocale.VALID_LOCALE);
|
||||
if (uloc == null) {
|
||||
uloc = symbols.getLocale(ULocale.VALID_LOCALE);
|
||||
}
|
||||
setupCurrencyPluralPattern(uloc);
|
||||
// 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 = (String)pluralCountToCurrencyUnitPattern.get("other");
|
||||
if (currencyPluralPatternForOther == null) {
|
||||
currencyPluralPatternForOther = defaultCurrencyPluralPattern;
|
||||
}
|
||||
applyPatternWithoutExpandAffix(currencyPluralPatternForOther,false);
|
||||
setCurrencyForSymbols();
|
||||
pluralRules = PluralRules.forLocale(uloc);
|
||||
}
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
|
||||
private void setupCurrencyPluralPattern(ULocale uloc) {
|
||||
pluralCountToCurrencyUnitPattern = new HashMap();
|
||||
Set pluralCountSet = new HashSet();
|
||||
ULocale parentLocale = uloc;
|
||||
String numberStylePattern = getPattern(uloc, NumberFormat.NUMBERSTYLE);
|
||||
while (parentLocale != null) {
|
||||
try {
|
||||
ICUResourceBundle resource = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, parentLocale);
|
||||
ICUResourceBundle currencyRes = resource.getWithFallback("CurrencyUnitPatterns");
|
||||
int size = currencyRes.getSize();
|
||||
for (int index = 0; index < size; ++index) {
|
||||
String pluralCount = currencyRes.get(index).getKey();
|
||||
if (pluralCountSet.contains(pluralCount)) {
|
||||
continue;
|
||||
}
|
||||
String pattern = currencyRes.get(index).getString();
|
||||
// replace {0} with numberStylePattern
|
||||
// and {1} with triple currency sign
|
||||
String patternWithNumber = pattern.replace("{0}", numberStylePattern);
|
||||
String patternWithCurrencySign = patternWithNumber.replace("{1}", tripleCurrencyStr);
|
||||
pluralCountToCurrencyUnitPattern.put(pluralCount, patternWithCurrencySign);
|
||||
pluralCountSet.add(pluralCount);
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
}
|
||||
parentLocale = parentLocale.getFallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -783,7 +915,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
synchronized(digitList) {
|
||||
digitList.set(number, precision(false),
|
||||
!useExponentialNotation && !areSignificantDigitsUsed());
|
||||
return subformat(result, fieldPosition, isNegative, false,
|
||||
return subformat(number, result, fieldPosition, isNegative, false,
|
||||
parseAttr);
|
||||
}
|
||||
}
|
||||
|
@ -935,7 +1067,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
number *= multiplier;
|
||||
synchronized(digitList) {
|
||||
digitList.set(number, precision(true));
|
||||
return subformat(result, fieldPosition, isNegative, true, parseAttr);
|
||||
return subformat(number, result, fieldPosition, isNegative, true, parseAttr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -967,7 +1099,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
// number.
|
||||
synchronized(digitList) {
|
||||
digitList.set(number, precision(true));
|
||||
return subformat(result, fieldPosition, number.signum() < 0, true, parseAttr);
|
||||
return subformat(number.intValue(), result, fieldPosition, number.signum() < 0, true, parseAttr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -997,7 +1129,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
synchronized(digitList) {
|
||||
digitList.set(number, precision(false),
|
||||
!useExponentialNotation && !areSignificantDigitsUsed());
|
||||
return subformat(result, fieldPosition, number.signum() < 0, false, parseAttr);
|
||||
return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, false, parseAttr);
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
|
@ -1027,7 +1159,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
synchronized(digitList) {
|
||||
digitList.set(number, precision(false),
|
||||
!useExponentialNotation && !areSignificantDigitsUsed());
|
||||
return subformat(result, fieldPosition, number.signum() < 0, false);
|
||||
return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1068,15 +1200,86 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
}
|
||||
|
||||
private StringBuffer subformat(int number, StringBuffer result,
|
||||
FieldPosition fieldPosition,
|
||||
boolean isNegative, boolean isInteger,
|
||||
boolean parseAttr) {
|
||||
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||
return subformat(pluralRules.select(number), result, fieldPosition,
|
||||
isNegative, isInteger, parseAttr);
|
||||
} else {
|
||||
return subformat(result, fieldPosition, isNegative, isInteger, parseAttr);
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuffer subformat(double number, StringBuffer result,
|
||||
FieldPosition fieldPosition,
|
||||
boolean isNegative, boolean isInteger,
|
||||
boolean parseAttr) {
|
||||
if (currencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||
return subformat(pluralRules.select(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 =
|
||||
(String)pluralCountToCurrencyUnitPattern.get(pluralCount);
|
||||
if (currencyPluralPattern == null) {
|
||||
// fall back to "other"
|
||||
currencyPluralPattern =
|
||||
(String)pluralCountToCurrencyUnitPattern.get("other");
|
||||
if (currencyPluralPattern == null) {
|
||||
// no currencyUnitPatterns defined,
|
||||
// fallback to SYMBOL_NAME format.
|
||||
// This should never happen when ICU resource files are
|
||||
// available, since currencyUnitPattern of "other" is always
|
||||
// defined in root.
|
||||
currencyPluralPattern = defaultCurrencyPluralPattern;
|
||||
}
|
||||
}
|
||||
if (pattern.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){
|
||||
return subformat(result, fieldPosition, isNegative, isInteger, false);
|
||||
}
|
||||
|
||||
private StringBuffer subformat(StringBuffer result,
|
||||
FieldPosition fieldPosition, boolean isNegative, boolean isInteger,
|
||||
boolean parseAttr)
|
||||
|
@ -1098,10 +1301,10 @@ public class DecimalFormat extends NumberFormat {
|
|||
int i;
|
||||
char zero = symbols.getZeroDigit();
|
||||
int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
|
||||
char grouping = isCurrencyFormat ?
|
||||
char grouping = currencySignCount > 0 ?
|
||||
symbols.getMonetaryGroupingSeparator() :
|
||||
symbols.getGroupingSeparator();
|
||||
char decimal = isCurrencyFormat ?
|
||||
char decimal = currencySignCount > 0 ?
|
||||
symbols.getMonetaryDecimalSeparator() :
|
||||
symbols.getDecimalSeparator();
|
||||
boolean useSigDig = areSignificantDigitsUsed();
|
||||
|
@ -1717,6 +1920,10 @@ public class DecimalFormat extends NumberFormat {
|
|||
: (Object) n;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -1792,8 +1999,14 @@ public class DecimalFormat extends NumberFormat {
|
|||
} else if (negMatch >= 0) {
|
||||
position += negMatch;
|
||||
} else {
|
||||
parsePosition.setErrorIndex(position);
|
||||
return false;
|
||||
if (currencySignCount <= 0) {
|
||||
// For currency, need to parse against all currency patterns,
|
||||
// including default, ISO, and plural patterns.
|
||||
// So, do not set error status here,
|
||||
// since it might be another currency style format.
|
||||
parsePosition.setErrorIndex(position);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Match padding after prefix
|
||||
|
@ -1818,7 +2031,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
|
||||
digits.decimalAt = digits.count = 0;
|
||||
char zero = symbols.getZeroDigit();
|
||||
char decimal = isCurrencyFormat ?
|
||||
char decimal = currencySignCount > 0 ?
|
||||
symbols.getMonetaryDecimalSeparator() : symbols.getDecimalSeparator();
|
||||
char grouping = symbols.getGroupingSeparator();
|
||||
|
||||
|
@ -2103,11 +2316,18 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
|
||||
// Match positive and negative suffixes; prefer longest match.
|
||||
if (posMatch >= 0) {
|
||||
// special handling for currency parsing, since currency parsing
|
||||
// could parse different kinds of style formats.
|
||||
if (posMatch < 0 && negMatch < 0 && currencySignCount > 0) {
|
||||
posMatch = compareAffix(text, position, false, false, currency);
|
||||
}
|
||||
if (negMatch >= 0) {
|
||||
negMatch = compareAffix(text, position, true, false, currency);
|
||||
} else {
|
||||
if (posMatch >= 0) {
|
||||
posMatch = compareAffix(text, position, false, false, currency);
|
||||
}
|
||||
if (negMatch >= 0) {
|
||||
negMatch = compareAffix(text, position, true, false, currency);
|
||||
}
|
||||
}
|
||||
if (posMatch >= 0 && negMatch >= 0) {
|
||||
if (posMatch > negMatch) {
|
||||
|
@ -2187,14 +2407,56 @@ public class DecimalFormat extends NumberFormat {
|
|||
*/
|
||||
private int compareAffix(String text, int pos, boolean isNegative,
|
||||
boolean isPrefix, Currency[] currency) {
|
||||
if (currency != null || currencyChoice != null) {
|
||||
if (isPrefix) {
|
||||
return compareComplexAffix(isNegative ? negPrefixPattern : posPrefixPattern,
|
||||
text, pos, currency);
|
||||
} else {
|
||||
return compareComplexAffix(isNegative ? negSuffixPattern : posSuffixPattern,
|
||||
text, pos, currency);
|
||||
if (currency != null || currencyChoice != null ||
|
||||
currencySignCount > 0) {
|
||||
// Need to parse against all possible currency format patterns,
|
||||
// including default currency style pattern,
|
||||
// ISO currency style pattern, plural currency style pattern.
|
||||
// Try current pattern first.
|
||||
// Since pattern can be reset by applyPattern.
|
||||
// There is no way to know which style the current pattern is.
|
||||
if (!isReadyForParsing) {
|
||||
int savedCurrencySignCount = currencySignCount;
|
||||
setupCurrencyAffixForAllPattern();
|
||||
// reset pattern back
|
||||
if (savedCurrencySignCount == CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT) {
|
||||
applyPatternWithoutExpandAffix(pattern, false);
|
||||
} else {
|
||||
applyPattern(pattern, false);
|
||||
}
|
||||
isReadyForParsing = true;
|
||||
}
|
||||
int posAfterParsingAffix;
|
||||
int tmpPos;
|
||||
if (isPrefix) {
|
||||
posAfterParsingAffix = compareComplexAffix(
|
||||
isNegative ? negPrefixPattern : posPrefixPattern,
|
||||
text, pos, currency);
|
||||
tmpPos = comparePatternSet(
|
||||
isNegative ? negPrefixPatternForCurrency :
|
||||
posPrefixPatternForCurrency, text, pos, currency);
|
||||
posAfterParsingAffix = tmpPos > posAfterParsingAffix ?
|
||||
tmpPos : posAfterParsingAffix;
|
||||
// simple affix too to parse the case "-\u00A40,00"
|
||||
tmpPos = compareSimpleAffix(
|
||||
isNegative ? negativePrefix : positivePrefix, text, pos);
|
||||
posAfterParsingAffix = tmpPos > posAfterParsingAffix ?
|
||||
tmpPos : posAfterParsingAffix;
|
||||
} else {
|
||||
posAfterParsingAffix = compareComplexAffix(
|
||||
isNegative ? negSuffixPattern : posSuffixPattern,
|
||||
text, pos, currency);
|
||||
tmpPos = comparePatternSet(
|
||||
isNegative ? negSuffixPatternForCurrency :
|
||||
posSuffixPatternForCurrency, text, pos, currency);
|
||||
posAfterParsingAffix = tmpPos > posAfterParsingAffix ?
|
||||
tmpPos : posAfterParsingAffix;
|
||||
tmpPos = compareSimpleAffix(
|
||||
isNegative ? negativeSuffix : positiveSuffix, text, pos);
|
||||
posAfterParsingAffix = tmpPos > posAfterParsingAffix ?
|
||||
tmpPos : posAfterParsingAffix;
|
||||
}
|
||||
return posAfterParsingAffix;
|
||||
}
|
||||
|
||||
if (isPrefix) {
|
||||
|
@ -2206,6 +2468,78 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private int comparePatternSet(Set patternSet, String text, int pos, Currency[] currency) {
|
||||
int posAfterParsing = pos;
|
||||
Iterator iter = patternSet.iterator();
|
||||
while (iter.hasNext()) {
|
||||
posAfterParsing = compareComplexAffix((String)iter.next(), text, pos, currency);
|
||||
if (posAfterParsing != -1) {
|
||||
return posAfterParsing;
|
||||
}
|
||||
}
|
||||
return posAfterParsing;
|
||||
}
|
||||
|
||||
|
||||
private void setupCurrencyAffixForAllPattern() {
|
||||
ULocale uloc = getLocale(ULocale.VALID_LOCALE);
|
||||
if (uloc == null) {
|
||||
uloc = symbols.getLocale(ULocale.VALID_LOCALE);
|
||||
}
|
||||
if (pluralCountToCurrencyUnitPattern == null) {
|
||||
setupCurrencyPluralPattern(uloc);
|
||||
}
|
||||
|
||||
// CURRENCYSTYLE and ISOCURRENCYSTYLE should have the same
|
||||
// prefix and suffix, so, only need to save one of them.
|
||||
// do not save the actualy pattern.
|
||||
onlyApplyPatternWithoutExpandAffix(getPattern(uloc, NumberFormat.CURRENCYSTYLE), false);
|
||||
// fill in those affixes
|
||||
negPrefixPatternForCurrency = new HashSet();
|
||||
posPrefixPatternForCurrency = new HashSet();
|
||||
negSuffixPatternForCurrency = new HashSet();
|
||||
posSuffixPatternForCurrency = new HashSet();
|
||||
if (negPrefixPattern != null) {
|
||||
negPrefixPatternForCurrency.add(negPrefixPattern);
|
||||
}
|
||||
if (posPrefixPattern != null) {
|
||||
posPrefixPatternForCurrency.add(posPrefixPattern);
|
||||
}
|
||||
if (negSuffixPattern != null) {
|
||||
negSuffixPatternForCurrency.add(negSuffixPattern);
|
||||
}
|
||||
if (posSuffixPattern != null) {
|
||||
posSuffixPatternForCurrency.add(posSuffixPattern);
|
||||
}
|
||||
Iterator iter = pluralCountToCurrencyUnitPattern.keySet().iterator();
|
||||
Set currencyUnitPatternSet = new HashSet();
|
||||
while (iter.hasNext()) {
|
||||
String pluralCount = (String)iter.next();
|
||||
String currencyPattern = (String)pluralCountToCurrencyUnitPattern.get(pluralCount);
|
||||
if (currencyPattern != null &&
|
||||
currencyUnitPatternSet.contains(currencyPattern) == false) {
|
||||
currencyUnitPatternSet.add(currencyPattern);
|
||||
// do not save the pattern
|
||||
onlyApplyPatternWithoutExpandAffix(currencyPattern, false);
|
||||
// fill in those affixes
|
||||
if (negPrefixPattern != null) {
|
||||
negPrefixPatternForCurrency.add(negPrefixPattern);
|
||||
}
|
||||
if (posPrefixPattern != null) {
|
||||
posPrefixPatternForCurrency.add(posPrefixPattern);
|
||||
}
|
||||
if (negSuffixPattern != null) {
|
||||
negSuffixPatternForCurrency.add(negSuffixPattern);
|
||||
}
|
||||
if (posSuffixPattern != null) {
|
||||
posSuffixPatternForCurrency.add(posSuffixPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -2347,16 +2681,32 @@ public class DecimalFormat extends NumberFormat {
|
|||
|
||||
switch (c) {
|
||||
case CURRENCY_SIGN:
|
||||
// If currency != null, then perform generic currency matching.
|
||||
// Otherwise, do currency choice parsing.
|
||||
// do currency choice parsing when currency choice format
|
||||
// is not null and it is the simple currency format style.
|
||||
// otherwise, perform generic currency matching, which is
|
||||
// a mixed styled parsing.
|
||||
//assert(currency != null ||
|
||||
// (getCurrency() != null && currencyChoice != null));
|
||||
boolean intl = i<affixPat.length() &&
|
||||
affixPat.charAt(i) == CURRENCY_SIGN;
|
||||
|
||||
// Parse generic currency -- anything for which we
|
||||
// have a display name, or any 3-letter ISO code.
|
||||
if (currency != null) {
|
||||
if (intl) {
|
||||
++i;
|
||||
}
|
||||
boolean plural = i<affixPat.length() &&
|
||||
affixPat.charAt(i) == CURRENCY_SIGN;
|
||||
if (plural) {
|
||||
++i;
|
||||
intl = false;
|
||||
}
|
||||
if (currency == null && currencyChoice != null &&
|
||||
!intl && !plural) {
|
||||
ParsePosition ppos = new ParsePosition(pos);
|
||||
// Number n =
|
||||
currencyChoice.parse(text, ppos);
|
||||
pos = (ppos.getIndex() == pos) ? -1 : ppos.getIndex();
|
||||
} else {
|
||||
// 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.
|
||||
ULocale uloc = getLocale(ULocale.VALID_LOCALE);
|
||||
|
@ -2366,24 +2716,18 @@ public class DecimalFormat extends NumberFormat {
|
|||
}
|
||||
// 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, ppos);
|
||||
|
||||
|
||||
// If parse succeeds, populate currency[0]
|
||||
if (iso != null) {
|
||||
currency[0] = Currency.getInstance(iso);
|
||||
if (currency != null) {
|
||||
currency[0] = Currency.getInstance(iso);
|
||||
}
|
||||
pos = ppos.getIndex();
|
||||
} else {
|
||||
pos = -1;
|
||||
}
|
||||
} else {
|
||||
if (intl) {
|
||||
++i;
|
||||
pos = match(text, pos, getCurrency().getCurrencyCode());
|
||||
} else {
|
||||
ParsePosition ppos = new ParsePosition(pos);
|
||||
/* Number n = */currencyChoice.parse(text, ppos);
|
||||
pos = (ppos.getIndex() == pos) ? -1 : ppos.getIndex();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case PATTERN_PERCENT:
|
||||
|
@ -2411,6 +2755,9 @@ public class DecimalFormat extends NumberFormat {
|
|||
* isRuleWhiteSpace(ch) then match a run of white space in text.
|
||||
*/
|
||||
static final int match(String text, int pos, int ch) {
|
||||
if (pos >= text.length()) {
|
||||
return -1;
|
||||
}
|
||||
if (UCharacterProperty.isRuleWhiteSpace(ch)) {
|
||||
// Advance over run of white space in input text
|
||||
// Must see at least one white space char in input
|
||||
|
@ -2468,7 +2815,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
|
||||
symbols = (DecimalFormatSymbols) newSymbols.clone();
|
||||
setCurrencyForSymbols();
|
||||
expandAffixes();
|
||||
expandAffixes(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3107,14 +3454,21 @@ public class DecimalFormat extends NumberFormat {
|
|||
* posPrefixPattern, posSuffixPattern, negPrefixPattern, negSuffixPattern.
|
||||
* [Richard/GCL]
|
||||
*/
|
||||
return (posPrefixPattern != null &&
|
||||
// following are added to accomodate changes for currency plural format.
|
||||
return style == other.style
|
||||
&& (pluralRules == null && other.pluralRules == null ||
|
||||
pluralRules.equals(other.pluralRules))
|
||||
&& (style == NumberFormat.PLURALCURRENCYSTYLE)?
|
||||
(pluralCountToCurrencyUnitPattern ==
|
||||
other.pluralCountToCurrencyUnitPattern):
|
||||
((posPrefixPattern != null &&
|
||||
equals(posPrefixPattern, other.posPrefixPattern))
|
||||
&& (posSuffixPattern != null &&
|
||||
&& (posSuffixPattern != null &&
|
||||
equals(posSuffixPattern, other.posSuffixPattern))
|
||||
&& (negPrefixPattern != null &&
|
||||
&& (negPrefixPattern != null &&
|
||||
equals(negPrefixPattern, other.negPrefixPattern))
|
||||
&& (negSuffixPattern != null &&
|
||||
equals(negSuffixPattern, other.negSuffixPattern))
|
||||
&& (negSuffixPattern != null &&
|
||||
equals(negSuffixPattern, other.negSuffixPattern)))
|
||||
&& multiplier == other.multiplier
|
||||
&& groupingSize == other.groupingSize
|
||||
&& groupingSize2 == other.groupingSize2
|
||||
|
@ -3128,6 +3482,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
maxSignificantDigits == other.maxSignificantDigits)
|
||||
&& symbols.equals(other.symbols);
|
||||
}
|
||||
|
||||
//method to unquote the strings and compare
|
||||
private boolean equals(String pat1, String pat2){
|
||||
//fast path
|
||||
|
@ -3184,6 +3539,15 @@ public class DecimalFormat extends NumberFormat {
|
|||
* @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 pattern;
|
||||
}
|
||||
return toPattern( false );
|
||||
}
|
||||
|
||||
|
@ -3194,6 +3558,9 @@ public class DecimalFormat extends NumberFormat {
|
|||
* @stable ICU 2.0
|
||||
*/
|
||||
public String toLocalizedPattern() {
|
||||
if (style == NumberFormat.PLURALCURRENCYSTYLE) {
|
||||
return pattern;
|
||||
}
|
||||
return toPattern( true );
|
||||
}
|
||||
|
||||
|
@ -3202,9 +3569,17 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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() {
|
||||
private void expandAffixes(String pluralCount) {
|
||||
// expandAffix() will set currencyChoice to a non-null value if
|
||||
// appropriate AND if it is null.
|
||||
currencyChoice = null;
|
||||
|
@ -3212,19 +3587,19 @@ public class DecimalFormat extends NumberFormat {
|
|||
// Reuse one StringBuffer for better performance
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
if (posPrefixPattern != null) {
|
||||
expandAffix(posPrefixPattern, buffer, false);
|
||||
expandAffix(posPrefixPattern, pluralCount, buffer, false);
|
||||
positivePrefix = buffer.toString();
|
||||
}
|
||||
if (posSuffixPattern != null) {
|
||||
expandAffix(posSuffixPattern, buffer, false);
|
||||
expandAffix(posSuffixPattern, pluralCount, buffer, false);
|
||||
positiveSuffix = buffer.toString();
|
||||
}
|
||||
if (negPrefixPattern != null) {
|
||||
expandAffix(negPrefixPattern, buffer, false);
|
||||
expandAffix(negPrefixPattern, pluralCount, buffer, false);
|
||||
negativePrefix = buffer.toString();
|
||||
}
|
||||
if (negSuffixPattern != null) {
|
||||
expandAffix(negSuffixPattern, buffer, false);
|
||||
expandAffix(negSuffixPattern, pluralCount, buffer, false);
|
||||
negativeSuffix = buffer.toString();
|
||||
}
|
||||
}
|
||||
|
@ -3235,7 +3610,9 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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. Any other character outside
|
||||
* 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
|
||||
|
@ -3251,6 +3628,12 @@ public class DecimalFormat extends NumberFormat {
|
|||
* 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
|
||||
* @param doFormat if false, then the pattern will be expanded, and if a
|
||||
* currency symbol is encountered that expands to a ChoiceFormat, the
|
||||
|
@ -3260,7 +3643,9 @@ public class DecimalFormat extends NumberFormat {
|
|||
* @return the expanded equivalent of pattern
|
||||
*/
|
||||
//Bug 4212072 [Richard/GCL]
|
||||
private void expandAffix(String pattern, StringBuffer buffer,
|
||||
private void expandAffix(String pattern,
|
||||
String pluralCount,
|
||||
StringBuffer buffer,
|
||||
boolean doFormat) {
|
||||
buffer.setLength(0);
|
||||
for (int i=0; i<pattern.length(); ) {
|
||||
|
@ -3301,13 +3686,31 @@ public class DecimalFormat extends NumberFormat {
|
|||
// 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) {
|
||||
if (!intl) {
|
||||
// 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) {
|
||||
boolean isChoiceFormat[] = new boolean[1];
|
||||
s = currency.getName(symbols.getULocale(),
|
||||
Currency.PLURAL_LONG_NAME,
|
||||
pluralCount,
|
||||
isChoiceFormat);
|
||||
}
|
||||
else if (!intl) {
|
||||
boolean isChoiceFormat[] = new boolean[1];
|
||||
s = currency.getName(symbols.getULocale(),
|
||||
Currency.SYMBOL_NAME,
|
||||
|
@ -3378,7 +3781,7 @@ public class DecimalFormat extends NumberFormat {
|
|||
affixPat = isNegative ? negSuffixPattern : posSuffixPattern;
|
||||
}
|
||||
StringBuffer affixBuf = new StringBuffer();
|
||||
expandAffix(affixPat, affixBuf, true);
|
||||
expandAffix(affixPat, null, affixBuf, true);
|
||||
buf.append(affixBuf.toString());
|
||||
return affixBuf.length();
|
||||
}
|
||||
|
@ -3762,11 +4165,39 @@ public class DecimalFormat extends NumberFormat {
|
|||
applyPattern( pattern, true );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <strong><font face=helvetica color=red>CHANGED</font></strong>
|
||||
* 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 accroding 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) {
|
||||
onlyApplyPatternWithoutExpandAffix(pattern, localized);
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
// only used when formatting currency plural format.
|
||||
// in which, the right affix (based on the formatted number)
|
||||
// is not known until formatting.
|
||||
private void onlyApplyPatternWithoutExpandAffix(String pattern, boolean localized) {
|
||||
char zeroDigit = PATTERN_ZERO_DIGIT; // '0'
|
||||
char sigDigit = PATTERN_SIGNIFICANT_DIGIT; // '@'
|
||||
char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
|
||||
|
@ -3827,7 +4258,6 @@ public class DecimalFormat extends NumberFormat {
|
|||
long incrementVal = 0;
|
||||
byte expDigits = -1;
|
||||
boolean expSignAlways = false;
|
||||
boolean isCurrency = false;
|
||||
|
||||
// The affix is either the prefix or the suffix.
|
||||
StringBuffer affix = prefix;
|
||||
|
@ -4017,8 +4447,17 @@ public class DecimalFormat extends NumberFormat {
|
|||
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
|
||||
currencySignCount = CURRENCY_SIGN_COUNT_IN_PLURAL_FORMAT;
|
||||
} else {
|
||||
currencySignCount = CURRENCY_SIGN_COUNT_IN_ISO_FORMAT;
|
||||
}
|
||||
} else {
|
||||
currencySignCount = CURRENCY_SIGN_COUNT_IN_SYMBOL_FORMAT;
|
||||
}
|
||||
isCurrency = true;
|
||||
// Fall through to append(ch)
|
||||
} else if (ch == QUOTE) {
|
||||
// A quote outside quotes indicates either the opening
|
||||
|
@ -4167,7 +4606,6 @@ public class DecimalFormat extends NumberFormat {
|
|||
minExponentDigits = expDigits;
|
||||
exponentSignAlwaysShown = expSignAlways;
|
||||
}
|
||||
isCurrencyFormat = isCurrency;
|
||||
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
|
||||
|
@ -4257,18 +4695,6 @@ public class DecimalFormat extends NumberFormat {
|
|||
negSuffixPattern = posSuffixPattern;
|
||||
negPrefixPattern = PATTERN_MINUS + posPrefixPattern;
|
||||
}
|
||||
/*Bug 4212072
|
||||
Update the affix strings accroding to symbols in order to keep
|
||||
the affix strings up to date.
|
||||
[Richard/GCL]
|
||||
*/
|
||||
expandAffixes();
|
||||
|
||||
// Now that we have the actual prefix and suffix, fix up formatWidth
|
||||
if (formatWidth > 0) {
|
||||
formatWidth += positivePrefix.length() + positiveSuffix.length();
|
||||
}
|
||||
|
||||
setLocale(null, null);
|
||||
}
|
||||
|
||||
|
@ -4420,15 +4846,14 @@ public class DecimalFormat extends NumberFormat {
|
|||
symbols.setInternationalCurrencySymbol(theCurrency.getCurrencyCode());
|
||||
}
|
||||
|
||||
if (isCurrencyFormat) {
|
||||
if (currencySignCount > 0) {
|
||||
if (theCurrency != null) {
|
||||
setRoundingIncrement(theCurrency.getRoundingIncrement());
|
||||
|
||||
int d = theCurrency.getDefaultFractionDigits();
|
||||
setMinimumFractionDigits(d);
|
||||
setMaximumFractionDigits(d);
|
||||
}
|
||||
expandAffixes();
|
||||
expandAffixes(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4668,6 +5093,54 @@ public class DecimalFormat extends NumberFormat {
|
|||
//[Richard/GCL]
|
||||
private String negSuffixPattern;
|
||||
|
||||
/*
|
||||
* Following are used in currency format
|
||||
*/
|
||||
private static final char[] tripleCurrencySign = {0xA4, 0xA4, 0xA4};
|
||||
private static final String tripleCurrencyStr = new String(tripleCurrencySign);
|
||||
private static final char[] defaultCurrencyPluralPatternChar = {0, '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4};
|
||||
private static final String defaultCurrencyPluralPattern = new String(defaultCurrencyPluralPatternChar);
|
||||
|
||||
private Map pluralCountToCurrencyUnitPattern = null;
|
||||
// pattern used in this formatter
|
||||
private String pattern = "";
|
||||
// style is only valid when decimal formatter is constructed by
|
||||
// DecimalFormat(pattern, decimalFormatSymbol, style)
|
||||
private int style = NumberFormat.NUMBERSTYLE;
|
||||
/* For parsing purose,
|
||||
* Need to remember all prefix patterns and suffix patterns of
|
||||
* every currency format pattern,
|
||||
* including the pattern of default currecny style, ISO currency style,
|
||||
* and plural currency style. And the patterns are set through applyPattern.
|
||||
* Following are used to represent the affix patterns in currency plural
|
||||
* formats.
|
||||
*/
|
||||
private Set negPrefixPatternForCurrency = null;
|
||||
private Set posPrefixPatternForCurrency = null;
|
||||
private Set negSuffixPatternForCurrency = null;
|
||||
private Set posSuffixPatternForCurrency = null;
|
||||
private boolean isReadyForParsing = false;
|
||||
/*
|
||||
* 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 transient int currencySignCount = 0;
|
||||
|
||||
/*
|
||||
* The plural rule is used to format currency plural name,
|
||||
* for example: "3.00 US Dollars".
|
||||
* If there are 3 currency signs in the currency patttern,
|
||||
* the 3 currency signs will be replaced by currency plural name.
|
||||
*/
|
||||
private PluralRules pluralRules = null;
|
||||
|
||||
|
||||
/**
|
||||
* Formatter for ChoiceFormat-based currency names. If this field
|
||||
* is not null, then delegate to it to format currency symbols.
|
||||
|
@ -4713,11 +5186,6 @@ public class DecimalFormat extends NumberFormat {
|
|||
*/
|
||||
private boolean decimalSeparatorAlwaysShown = false;
|
||||
|
||||
/**
|
||||
* True if this object represents a currency format. This determines
|
||||
* whether the monetary decimal separator is used instead of the normal one.
|
||||
*/
|
||||
private transient boolean isCurrencyFormat = false;
|
||||
|
||||
/**
|
||||
* The <code>DecimalFormatSymbols</code> object used by this format.
|
||||
|
|
|
@ -84,6 +84,23 @@ import com.ibm.icu.util.UResourceBundle;
|
|||
* 0.53 is displayed as 53%.
|
||||
*
|
||||
* <p>
|
||||
* Starting from ICU 4.2, you can use getInstance() by passing in a 'style'
|
||||
* as parameter to get the correct instance.
|
||||
* For example,
|
||||
* use getInstance(...NUMBERSTYLE) to get the normal number format,
|
||||
* getInstance(...PERCENTSTYLE) to get a format for displaying percentage,
|
||||
* getInstance(...SCIENTIFICSTYLE) to get a format for displaying scientific number,
|
||||
* getInstance(...INTEGERSTYLE) to get an integer number format,
|
||||
* getInstance(...CURRENCYSTYLE) to get the currency number format,
|
||||
* in which the currency is represented by its symbol, for example, "$3.00".
|
||||
* getInstance(...ISOCURRENCYSTYLE) to get the currency number format,
|
||||
* in which the currency is represented by its ISO code, for example "USD3.00".
|
||||
* getInstance(...PLURALCURRENCYSTYLE) to get the currency number format,
|
||||
* in which the currency is represented by its full name in plural format,
|
||||
* for example, "3.00 US dollars" or "1.00 US dollar".
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* You can also control the display of numbers with such methods as
|
||||
* <code>setMinimumFractionDigits</code>.
|
||||
* If you want even more control over the format or parsing,
|
||||
|
@ -152,12 +169,52 @@ import com.ibm.icu.util.UResourceBundle;
|
|||
*/
|
||||
public abstract class NumberFormat extends UFormat {
|
||||
|
||||
// Constants used by factory methods to specify a style of format.
|
||||
private static final int NUMBERSTYLE = 0;
|
||||
private static final int CURRENCYSTYLE = 1;
|
||||
private static final int PERCENTSTYLE = 2;
|
||||
private static final int SCIENTIFICSTYLE = 3;
|
||||
private static final int INTEGERSTYLE = 4;
|
||||
/**
|
||||
* Constants to specify normal number style of format.
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int NUMBERSTYLE = 0;
|
||||
/**
|
||||
* Constants to specify currency style of format which uses currency symbol
|
||||
* to represent currency, for example: "$3.00".
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int CURRENCYSTYLE = 1;
|
||||
/**
|
||||
* Constants to specify a style of format to display percent.
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int PERCENTSTYLE = 2;
|
||||
/**
|
||||
* Constants to specify a style of format to display scientific number.
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int SCIENTIFICSTYLE = 3;
|
||||
/**
|
||||
* Constants to specify a integer number style format.
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int INTEGERSTYLE = 4;
|
||||
/**
|
||||
* Constants to specify currency style of format which uses currency
|
||||
* ISO code to represent currency, for example: "USD3.00".
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int ISOCURRENCYSTYLE = 5;
|
||||
/**
|
||||
* Constants to specify currency style of format which uses currency
|
||||
* long name with plural format to represent currency, for example,
|
||||
* "3.00 US Dollars".
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int PLURALCURRENCYSTYLE = 6;
|
||||
|
||||
/**
|
||||
* Field constant used to construct a FieldPosition object. Signifies that
|
||||
|
@ -501,6 +558,28 @@ public abstract class NumberFormat extends UFormat {
|
|||
return getInstance(inLocale, NUMBERSTYLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific style number format for default locale.
|
||||
* @param style number format style
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public final static NumberFormat getInstance(int style) {
|
||||
return getInstance(ULocale.getDefault(), style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific style number format for a specific locale.
|
||||
* @param inLocale the specific locale.
|
||||
* @param style number format style
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static NumberFormat getInstance(Locale inLocale, int style) {
|
||||
return getInstance(ULocale.forLocale(inLocale), style);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a general-purpose number format for the current default locale.
|
||||
* @stable ICU 2.0
|
||||
|
@ -1184,10 +1263,23 @@ public abstract class NumberFormat extends UFormat {
|
|||
throw new UnsupportedOperationException("setRoundingMode must be implemented by the subclass implementation.");
|
||||
}
|
||||
|
||||
// =======================privates===============================
|
||||
|
||||
// Hook for service
|
||||
private static NumberFormat getInstance(ULocale desiredLocale, int choice) {
|
||||
/**
|
||||
* Returns a specific style number format for a specific locale.
|
||||
* @param desiredLocale the specific locale.
|
||||
* @param choice number format style
|
||||
* @throws IllegalArgumentException if choice is not one of
|
||||
* NUMBERSTYLE, CURRENCYSTYLE,
|
||||
* PERCENTSTYLE, SCIENTIFICSTYLE,
|
||||
* INTEGERSTYLE,
|
||||
* ISOCURRENCYSTYLE, PLURALCURRENCYSTYLE,
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static NumberFormat getInstance(ULocale desiredLocale, int choice) {
|
||||
if (choice < NUMBERSTYLE || choice > PLURALCURRENCYSTYLE) {
|
||||
throw new IllegalArgumentException("choice should be from NUMBERSTYLE to PLURALCURRENCYSTYLE");
|
||||
}
|
||||
// if (shim == null) {
|
||||
// return createInstance(desiredLocale, choice);
|
||||
// } else {
|
||||
|
@ -1197,21 +1289,37 @@ public abstract class NumberFormat extends UFormat {
|
|||
return getShim().createInstance(desiredLocale, choice);
|
||||
}
|
||||
|
||||
// =======================privates===============================
|
||||
// Hook for service
|
||||
// [NEW]
|
||||
static NumberFormat createInstance(ULocale desiredLocale, int choice) {
|
||||
// If the choice is PLURALCURRENCYSTYLE, the pattern is not a single
|
||||
// pattern, it is a pattern set, so we do not need to get them here.
|
||||
// If the choice is ISOCURRENCYSTYLE, the pattern is the currrency
|
||||
// pattern in the locale but by replacing the single currency sign
|
||||
// with double currency sign.
|
||||
String pattern = getPattern(desiredLocale, choice);
|
||||
DecimalFormatSymbols symbols = new DecimalFormatSymbols(desiredLocale);
|
||||
|
||||
// Here we assume that the locale passed in is in the canonical
|
||||
// form, e.g: pt_PT_@currency=PTE not pt_PT_PREEURO
|
||||
if(choice == CURRENCYSTYLE){
|
||||
// This style wont work for currency plural format.
|
||||
// For currency plural fomrat, the pattern is get from
|
||||
// the locale (from CurrencyUnitPatterns) without override.
|
||||
if(choice == CURRENCYSTYLE || choice == ISOCURRENCYSTYLE){
|
||||
String temp = symbols.getCurrencyPattern();
|
||||
if(temp!=null){
|
||||
pattern = temp;
|
||||
}
|
||||
}
|
||||
|
||||
DecimalFormat format = new DecimalFormat(pattern, symbols);
|
||||
|
||||
// replace single currency sign in the pattern with double currency sign
|
||||
// if the choice is ISOCURRENCYSTYLE.
|
||||
if (choice == ISOCURRENCYSTYLE) {
|
||||
pattern = pattern.replaceAll("\\xA4", doubleCurrencyStr);
|
||||
}
|
||||
|
||||
DecimalFormat format = new DecimalFormat(pattern, symbols, choice);
|
||||
// System.out.println("loc: " + desiredLocale + " choice: " + choice + " pat: " + pattern + " sym: " + symbols + " result: " + format);
|
||||
|
||||
/*Bug 4408066
|
||||
|
@ -1311,7 +1419,14 @@ public abstract class NumberFormat extends UFormat {
|
|||
/*Bug 4408066
|
||||
Add codes for the new method getIntegerInstance() [Richard/GCL]
|
||||
*/
|
||||
int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice; //[Richard/GCL]
|
||||
/* for ISOCURRENCYSTYLE and PLURALCURRENCYSTYLE,
|
||||
* the pattern is the same as the pattern of CURRENCYSTYLE
|
||||
* but by replacing the single currency sign with
|
||||
* double currency sign or triple currency sign.
|
||||
*/
|
||||
int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE :
|
||||
((choice == ISOCURRENCYSTYLE || choice == PLURALCURRENCYSTYLE)?
|
||||
CURRENCYSTYLE : choice); //[Richard/GCL]
|
||||
return numberPatterns[entry]; //[Richard/GCL]
|
||||
}
|
||||
|
||||
|
@ -1378,6 +1493,9 @@ public abstract class NumberFormat extends UFormat {
|
|||
// */
|
||||
// private static final Hashtable cachedLocaleData = new Hashtable(3);
|
||||
|
||||
private static final char[] doubleCurrencySign = {0xA4, 0xA4};
|
||||
private static final String doubleCurrencyStr = new String(doubleCurrencySign);
|
||||
|
||||
/*Bug 4408066
|
||||
Add Field for the new method getIntegerInstance() [Richard/GCL]
|
||||
*/
|
||||
|
|
|
@ -12,6 +12,10 @@ import java.text.ParsePosition;
|
|||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
|
||||
import com.ibm.icu.impl.ICUDebug;
|
||||
|
@ -59,6 +63,15 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
* @stable ICU 2.6
|
||||
*/
|
||||
public static final int LONG_NAME = 1;
|
||||
|
||||
/**
|
||||
* Selector for getName() indicating the plural long name for a
|
||||
* currency, such as "US dollar" for USD in "1 US dollar",
|
||||
* and "US dollars" for USD in "2 US dollars".
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final int PLURAL_LONG_NAME = 2;
|
||||
|
||||
// begin registry stuff
|
||||
|
||||
|
@ -421,17 +434,9 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
|
||||
/**
|
||||
* Returns the display name for the given currency in the
|
||||
* given locale. For example, the display name for the USD
|
||||
* currency object in the en_US locale is "$".
|
||||
* @param locale locale in which to display currency
|
||||
* @param nameStyle selector for which kind of name to return
|
||||
* @param isChoiceFormat fill-in; isChoiceFormat[0] is set to true
|
||||
* if the returned value is a ChoiceFormat pattern; otherwise it
|
||||
* is set to false
|
||||
* @return display string for this currency. If the resource data
|
||||
* contains no entry for this currency, then the ISO 4217 code is
|
||||
* returned. If isChoiceFormat[0] is true, then the result is a
|
||||
* ChoiceFormat pattern. Otherwise it is a static string.
|
||||
* given locale.
|
||||
* This is a convenient method for
|
||||
* getName(ULocale, int, boolean[]);
|
||||
* @stable ICU 3.2
|
||||
*/
|
||||
public String getName(Locale locale,
|
||||
|
@ -445,7 +450,9 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
* given locale. For example, the display name for the USD
|
||||
* currency object in the en_US locale is "$".
|
||||
* @param locale locale in which to display currency
|
||||
* @param nameStyle selector for which kind of name to return
|
||||
* @param nameStyle selector for which kind of name to return.
|
||||
* The nameStyle should be either SYMBOL_NAME or
|
||||
* LONG_NAME. Otherwise, throw IllegalArgumentException.
|
||||
* @param isChoiceFormat fill-in; isChoiceFormat[0] is set to true
|
||||
* if the returned value is a ChoiceFormat pattern; otherwise it
|
||||
* is set to false
|
||||
|
@ -453,6 +460,8 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
* contains no entry for this currency, then the ISO 4217 code is
|
||||
* returned. If isChoiceFormat[0] is true, then the result is a
|
||||
* ChoiceFormat pattern. Otherwise it is a static string.
|
||||
* @throws IllegalArgumentException if the nameStyle is not SYMBOL_NAME
|
||||
* or LONG_NAME.
|
||||
* @stable ICU 3.2
|
||||
*/
|
||||
public String getName(ULocale locale,
|
||||
|
@ -509,6 +518,127 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
return isoCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name for the given currency in the
|
||||
* given locale.
|
||||
* This is a convenient method of
|
||||
* getName(ULocale, int, String, boolean[]);
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public String getName(Locale locale,
|
||||
int nameStyle,
|
||||
String pluralCount,
|
||||
boolean[] isChoiceFormat) {
|
||||
return getName(ULocale.forLocale(locale), nameStyle,
|
||||
pluralCount, isChoiceFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name for the given currency in the
|
||||
* given locale. For example, the SYMBOL_NAME for the USD
|
||||
* currency object in the en_US locale is "$".
|
||||
* The PLURAL_LONG_NAME for the USD currency object when the currency
|
||||
* amount is plural is "US dollars", such as in "3.00 US dollars";
|
||||
* while the PLURAL_LONG_NAME for the USD currency object when the currency
|
||||
* amount is singular is "US dollar", such as in "1.00 US dollar".
|
||||
* @param locale locale in which to display currency
|
||||
* @param nameStyle selector for which kind of name to return
|
||||
* @param pluralCount plural count string for this locale
|
||||
* @param isChoiceFormat fill-in; isChoiceFormat[0] is set to true
|
||||
* if the returned value is a ChoiceFormat pattern; otherwise it
|
||||
* is set to false
|
||||
* @return display string for this currency. If the resource data
|
||||
* contains no entry for this currency, then the ISO 4217 code is
|
||||
* returned. If isChoiceFormat[0] is true, then the result is a
|
||||
* ChoiceFormat pattern. Otherwise it is a static string.
|
||||
* @throws IllegalArgumentException if the nameStyle is not SYMBOL_NAME
|
||||
* or LONG_NAME, or PLURAL_LONG_NAME.
|
||||
* @draft ICU 4.2
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public String getName(ULocale locale,
|
||||
int nameStyle,
|
||||
String pluralCount,
|
||||
boolean[] isChoiceFormat) {
|
||||
if (nameStyle != PLURAL_LONG_NAME) {
|
||||
return getName(locale, nameStyle, isChoiceFormat);
|
||||
}
|
||||
|
||||
// Look up the CurrencyPlurals resource for the given locale. The
|
||||
// CurrencyPlurals locale data looks like this:
|
||||
//|en {
|
||||
//| CurrencyPlurals {
|
||||
//| USD{
|
||||
//| one{"US dollar"}
|
||||
//| other{"US dollars"}
|
||||
//| }
|
||||
//| ...
|
||||
//| }
|
||||
//|}
|
||||
//
|
||||
// Algorithm detail: http://unicode.org/reports/tr35/#Currencies
|
||||
// especially the fallback rule.
|
||||
String s = null;
|
||||
ICUResourceBundle isoCodeBundle;
|
||||
// search at run time, not saved in initialization
|
||||
try {
|
||||
UResourceBundle rb = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,locale);
|
||||
// get handles fallback
|
||||
ICUResourceBundle currencies = (ICUResourceBundle)rb.get("CurrencyPlurals");
|
||||
|
||||
// Fetch resource with multi-level resource inheritance fallback
|
||||
isoCodeBundle = currencies.getWithFallback(isoCode);
|
||||
} catch (MissingResourceException e) {
|
||||
// if there is no CurrencyPlurals defined or no plural long names
|
||||
// defined in the locale chain, fall back to long name.
|
||||
return getName(locale, LONG_NAME, isChoiceFormat);
|
||||
}
|
||||
try {
|
||||
s = isoCodeBundle.getStringWithFallback(pluralCount);
|
||||
} catch (MissingResourceException e1) {
|
||||
try {
|
||||
// if there is no name corresponding to 'pluralCount' defined,
|
||||
// fall back to name corresponding to "other".
|
||||
s = isoCodeBundle.getStringWithFallback("other");
|
||||
} catch (MissingResourceException e) {
|
||||
// if there is no name corresponding to plural count "other",
|
||||
// fall back to long name.
|
||||
return getName(locale, LONG_NAME, isChoiceFormat);
|
||||
}
|
||||
}
|
||||
// No support for choice format for getting plural currency names.
|
||||
if (s != null) {
|
||||
return s;
|
||||
}
|
||||
// If we fail to find a match, use the ISO 4217 code
|
||||
return isoCode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Represent the match's ISO code and the maximum matching length
|
||||
*/
|
||||
private static final class LongestMatchISOAndLength {
|
||||
String ISO;
|
||||
int length;
|
||||
public LongestMatchISOAndLength(String isoCode, int maxLength) {
|
||||
ISO = isoCode;
|
||||
length = maxLength;
|
||||
}
|
||||
private void setISO(String isoCode) {
|
||||
ISO = isoCode;
|
||||
}
|
||||
private void setLength(int maxLength) {
|
||||
length = maxLength;
|
||||
}
|
||||
public String iso() {
|
||||
return ISO;
|
||||
}
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to parse the given string as a currency, either as a
|
||||
* display name in the given locale, or as a 3-letter ISO 4217
|
||||
|
@ -530,7 +660,49 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
* @deprecated This API is ICU internal only.
|
||||
*/
|
||||
public static String parse(ULocale locale, String text, ParsePosition pos) {
|
||||
LongestMatchISOAndLength isoAndLen = new LongestMatchISOAndLength(null, 0);
|
||||
// We support currency symbol name, ISO name,
|
||||
// and plural name formatting.
|
||||
// For parsing, we will go through all the related currency data
|
||||
// to find the longest match.
|
||||
// TODO(xji): too bad for performance
|
||||
// First, parse against all currency names in "CurrencyPlural".
|
||||
parseCurrencyPlural(locale, text, isoAndLen, pos);
|
||||
// Then, parse against all currency names in "Currencies" including
|
||||
// long name. Need to parse against currency non plural long names
|
||||
// for currencies without plural name data.
|
||||
parseCurrency(locale, text, isoAndLen, pos);
|
||||
|
||||
int start = pos.getIndex();
|
||||
if (isoAndLen.iso() == null ||
|
||||
isoAndLen.length() < 3 && (text.length() - start) >= 3) {
|
||||
// If display name parse fails or if it matches fewer than 3
|
||||
// characters, try to parse 3-letter ISO. Do this after the
|
||||
// display name processing so 3-letter display names are
|
||||
// preferred. Consider /[A-Z]{3}/ to be valid ISO, and parse
|
||||
// it manually--UnicodeSet/regex are too slow and heavy.
|
||||
boolean valid = true;
|
||||
for (int k=0; k<3; ++k) {
|
||||
char ch = text.charAt(start + k); // 16-bit ok
|
||||
if (ch < 'A' || ch > 'Z') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
pos.setIndex(pos.getIndex() + 3);
|
||||
return text.substring(start, start+3);
|
||||
}
|
||||
}
|
||||
pos.setIndex(start + isoAndLen.length());
|
||||
return isoAndLen.iso();
|
||||
}
|
||||
|
||||
|
||||
// pass against all names (symbol, ISO code, long name)
|
||||
private static void parseCurrency(ULocale locale, String text,
|
||||
LongestMatchISOAndLength isoAndLen,
|
||||
ParsePosition pos) {
|
||||
// TODO: There is a slight problem with the pseudo-multi-level
|
||||
// fallback implemented here. More-specific locales don't
|
||||
// properly shield duplicate entries in less-specific locales.
|
||||
|
@ -542,9 +714,6 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
int start = pos.getIndex();
|
||||
String fragment = text.substring(start);
|
||||
|
||||
String iso = null;
|
||||
int max = 0;
|
||||
|
||||
// Look up the Currencies resource for the given locale. The
|
||||
// Currencies locale data looks like this:
|
||||
//|en {
|
||||
|
@ -567,47 +736,6 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
|
||||
// Multi-level resource inheritance fallback loop
|
||||
|
||||
while (locale != null) {
|
||||
UResourceBundle rb = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,locale);
|
||||
// We can't cast this to String[][]; the cast has to happen later
|
||||
|
||||
try {
|
||||
UResourceBundle currencies = rb.get("Currencies");
|
||||
// Do a linear search
|
||||
for (int i=0; i<currencies.getSize(); ++i) {
|
||||
//String name = ((String[]) currencies[i][1])[0];
|
||||
UResourceBundle item = currencies.get(i);
|
||||
String name = item.getString(0);
|
||||
if (name.length() < 1) {
|
||||
// Ignore zero-length names -- later, change this
|
||||
// when zero-length is used to mean something.
|
||||
continue;
|
||||
} else if (name.charAt(0) == '=') {
|
||||
name = name.substring(1);
|
||||
if (name.length() > 0 && name.charAt(0) != '=') {
|
||||
ChoiceFormat choice = new ChoiceFormat(name);
|
||||
// Number n =
|
||||
choice.parse(text, pos);
|
||||
int len = pos.getIndex() - start;
|
||||
if (len > max) {
|
||||
iso = item.getKey();
|
||||
max = len;
|
||||
}
|
||||
pos.setIndex(start);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (name.length() > max && fragment.startsWith(name)) {
|
||||
iso = item.getKey();
|
||||
max = name.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MissingResourceException e) {}
|
||||
|
||||
locale = locale.getFallback();
|
||||
}
|
||||
|
||||
/*
|
||||
1. Look at the Currencies array from the locale
|
||||
1a. Iterate through it, and check each row to see if row[1] matches
|
||||
|
@ -616,32 +744,118 @@ public class Currency extends MeasureUnit implements Serializable {
|
|||
2. If there is no match, fall back to "en" and try again
|
||||
3. If there is no match, fall back to root and try again
|
||||
4. If still no match, parse 3-letter ISO {this code is probably unchanged}.
|
||||
|
||||
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(UResourceBundle.ICU_BASE_NAME, locale);
|
||||
ICUResourceBundle currencies = rb.get("Currencies");
|
||||
*/
|
||||
// If display name parse fails or if it matches fewer than 3
|
||||
// characters, try to parse 3-letter ISO. Do this after the
|
||||
// display name processing so 3-letter display names are
|
||||
// preferred. Consider /[A-Z]{3}/ to be valid ISO, and parse
|
||||
// it manually--UnicodeSet/regex are too slow and heavy.
|
||||
if (max < 3 && (text.length() - start) >= 3) {
|
||||
boolean valid = true;
|
||||
for (int k=0; k<3; ++k) {
|
||||
char ch = text.charAt(start + k); // 16-bit ok
|
||||
if (ch < 'A' || ch > 'Z') {
|
||||
valid = false;
|
||||
break;
|
||||
HashSet visited = new HashSet();
|
||||
while (locale != null) {
|
||||
UResourceBundle rb = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,locale);
|
||||
// We can't cast this to String[][]; the cast has to happen later
|
||||
try {
|
||||
UResourceBundle currencies = rb.get("Currencies");
|
||||
// Do a linear search
|
||||
for (int i=0; i<currencies.getSize(); ++i) {
|
||||
UResourceBundle item = currencies.get(i);
|
||||
String ISOCode = item.getKey();
|
||||
if (!visited.contains(ISOCode)) {
|
||||
parseOneName(item, text, fragment, isoAndLen, ISOCode,
|
||||
start, pos);
|
||||
parseOneName(item, text, fragment, isoAndLen,
|
||||
item.getString(0), start, pos);
|
||||
// parse against long name
|
||||
parseOneName(item, text, fragment, isoAndLen,
|
||||
item.getString(1), start, pos);
|
||||
visited.add(ISOCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
iso = text.substring(start, start+3);
|
||||
max = 3;
|
||||
catch (MissingResourceException e) {}
|
||||
|
||||
locale = locale.getFallback();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse currency against currency plural names.
|
||||
*/
|
||||
private static void parseCurrencyPlural(ULocale locale, String text,
|
||||
LongestMatchISOAndLength isoAndLen,
|
||||
ParsePosition pos) {
|
||||
int start = pos.getIndex();
|
||||
String fragment = text.substring(start);
|
||||
|
||||
// Look up the Currencies resource for the given locale. The
|
||||
// Currencies locale data looks like this:
|
||||
//|en {
|
||||
//| CurrencyPlurals {
|
||||
//| USD {
|
||||
//| one{"US Dollar"}
|
||||
//| other{"US dollars"}
|
||||
//| }
|
||||
//| //...
|
||||
//| }
|
||||
//|}
|
||||
|
||||
HashMap visited = new HashMap();
|
||||
// for simplicity, do *not* check the plural count match-ness.
|
||||
while (locale != null) {
|
||||
UResourceBundle rb = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,locale);
|
||||
try {
|
||||
UResourceBundle currencies;
|
||||
currencies = rb.get("CurrencyPlurals");
|
||||
// Do a linear search
|
||||
for (int i=0; i<currencies.getSize(); ++i) {
|
||||
UResourceBundle item = currencies.get(i);
|
||||
String ISOCode = item.getKey();
|
||||
HashSet visitPluralCount = (HashSet)visited.get(ISOCode);
|
||||
if (visitPluralCount == null) {
|
||||
visitPluralCount = new HashSet();
|
||||
visited.put(ISOCode, visitPluralCount);
|
||||
}
|
||||
for (int j=0; j<item.getSize(); ++j) {
|
||||
String count = item.get(j).getKey();
|
||||
if (!visitPluralCount.contains(count)) {
|
||||
parseOneName(item, text, fragment, isoAndLen,
|
||||
item.get(j).getString(), start, pos);
|
||||
visitPluralCount.add(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (MissingResourceException e) {}
|
||||
|
||||
locale = locale.getFallback();
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseOneName(UResourceBundle item,
|
||||
String text,
|
||||
String fragment,
|
||||
LongestMatchISOAndLength isoAndLen,
|
||||
String name,
|
||||
int start,
|
||||
ParsePosition pos) {
|
||||
if (name.length() < 1) {
|
||||
// Ignore zero-length names -- later, change this
|
||||
// when zero-length is used to mean something.
|
||||
return;
|
||||
} else if (name.charAt(0) == '=') {
|
||||
name = name.substring(1);
|
||||
if (name.length() > 0 && name.charAt(0) != '=') {
|
||||
ChoiceFormat choice = new ChoiceFormat(name);
|
||||
// Number n =
|
||||
choice.parse(text, pos);
|
||||
int len = pos.getIndex() - start;
|
||||
if (len > isoAndLen.length()) {
|
||||
isoAndLen.setISO(item.getKey());
|
||||
isoAndLen.setLength(len);
|
||||
}
|
||||
pos.setIndex(start);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pos.setIndex(start + max);
|
||||
return iso;
|
||||
if (name.length() > isoAndLen.length() && fragment.startsWith(name)) {
|
||||
isoAndLen.setISO(item.getKey());
|
||||
isoAndLen.setLength(name.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue