ICU-2825 add support for formatting and parsing CurrencyAmount objects

X-SVN-Rev: 14973
This commit is contained in:
Alan Liu 2004-04-15 06:03:00 +00:00
parent db00eaeec7
commit 29bfb5542a
5 changed files with 410 additions and 77 deletions

View file

@ -970,6 +970,18 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
}
}
/**
* Parse a CurrencyAmount using the given NumberFormat, with
* the 'delim' character separating the number and the currency.
*/
private static CurrencyAmount parseCurrencyAmount(String str, NumberFormat fmt,
char delim)
throws ParseException {
int i = str.indexOf(delim);
return new CurrencyAmount(fmt.parse(str.substring(0,i)),
Currency.getInstance(str.substring(i+1)));
}
/**
* Return an integer representing the next token from this
* iterator. The integer will be an index into the given list, or
@ -992,8 +1004,9 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
/*3*/ "fp:", // <pattern or '-'> <number> <exp. string> <exp. number>
/*4*/ "rt:", // <pattern or '-'> <(exp.) number> <(exp.) string>
/*5*/ "p:", // <pattern or '-'> <string> <exp. number>
/*6*/ "perr:", // <pattern or '-'> <invalid string>
/*6*/ "perr:", // <pattern or '-'> <invalid string>
/*7*/ "pat:", // <pattern or '-'> <exp. toPattern or '-' or 'err'>
/*8*/ "fpc:", // <pattern or '-'> <curr.amt> <exp. string> <exp. curr.amt>
};
public void TestCases() {
@ -1027,6 +1040,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
case 3: // fp:
case 4: // rt:
case 5: // p:
case 8: // fpc:
tok = tokens.next();
if (!tok.equals("-")) {
pat = tok;
@ -1037,7 +1051,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
iae.printStackTrace();
tokens.next(); // consume remaining tokens
tokens.next();
if (cmd == 3) tokens.next();
if (cmd == 3 || cmd == 8) tokens.next();
continue;
}
}
@ -1060,6 +1074,17 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
n, fmt.parse(str));
}
}
// fpc: <pattern or '-'> <curr.amt> <exp. string> <exp. curr.amt>
else if (cmd == 8) {
String currAmt = tokens.next();
str = tokens.next();
CurrencyAmount n = parseCurrencyAmount(currAmt, ref, '/');
assertEquals(where + '"' + pat + "\".format(" + currAmt + ")",
str, fmt.format(n));
n = parseCurrencyAmount(tokens.next(), ref, '/');
assertEquals(where + '"' + pat + "\".parse(\"" + str + "\")",
n, fmt.parseCurrency(str));
}
// p: <pattern or '-'> <string to parse> <exp. number>
else {
str = tokens.next();

View file

@ -57,6 +57,8 @@ rt: - 3 "3.0"
rt: - 5000 "5000"
rt: - 0.005 "0.0050"
pat: - -
fp: "@@@@@@" 123456.7 "123457" 123457
pat: - -
pat: "##,@@##" "#,@@##"
pat: "##@@##" "@@##"
@ -68,3 +70,16 @@ pat: "@@0" err # either @ or 0, not both
# NumberRegression/Test4140009
rt: "" 123.456 "123.456"
rt: "" -123.456 "-123.456"
# Currency
fpc: "\u00A4 0.00" 1234.56/USD "$ 1234.56" 1234.56/USD
fpc: - 1234.56/JPY "\u00A5 1235" 1235/JPY
# ISO codes that overlap display names (QQQ vs. Q)
fpc: - 123/QQQ "QQQ 123.00" 123/QQQ # QQQ is fake
fpc: - 123/GTQ "Q 123.00" 123/GTQ
# ChoiceFormat-based display names
fpc: - 1/INR "Re. 1.00" 1/INR
fpc: - 2/INR "Rs. 2.00" 2/INR
# Display names with shared prefix (YDD vs. Y)
fpc: - 100/YDD "YDD 100.00" 100/YDD
fpc: - 100/CNY "Y 100.00" 100/CNY

View file

@ -7,6 +7,8 @@
package com.ibm.icu.text;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.impl.UCharacterProperty;
import java.text.ParsePosition;
@ -1309,8 +1311,42 @@ public class DecimalFormat extends NumberFormat {
* <code>null</code> if the parse failed
* @stable ICU 2.0
*/
public Number parse(String text, ParsePosition parsePosition)
{
public Number parse(String text, ParsePosition parsePosition) {
return (Number) parse(text, parsePosition, false);
}
/**
* <strong><font face=helvetica color=red>NEW</font></strong>
* Parses text from the given string as a CurrencyAmount. This
* method will fail if this format is not a currency format, that
* is, if it does not contain the currency pattern symbol (U+00A4)
* in its prefix or suffix.
*
* @param text the string to parse
* @param pos input-output position; on input, the position within
* text to match; must have 0 <= pos.getIndex() < text.length();
* on output, the position after the last matched character. If
* the parse fails, the position in unchanged upon output.
* @return a CurrencyAmount, or null upon failure
* @draft ICU 3.0
*/
public CurrencyAmount parseCurrency(String text, ParsePosition pos) {
return (CurrencyAmount) parse(text, pos, true);
}
/**
* Parses the given text as either a Number or a CurrencyAmount.
* @param text the string to parse
* @param parsePosition input-output position; on input, the
* position within text to match; must have 0 <= pos.getIndex() <
* text.length(); on output, the position after the last matched
* character. If the parse fails, the position in unchanged upon
* output.
* @param parseCurrency if true, a CurrencyAmount is parsed and
* returned; otherwise a Number is parsed and returned
* @return a Number or CurrencyAmount or null
*/
private Object parse(String text, ParsePosition parsePosition, boolean parseCurrency) {
int backup;
int i = backup = parsePosition.getIndex();
@ -1337,46 +1373,55 @@ public class DecimalFormat extends NumberFormat {
i = backup;
boolean[] status = new boolean[STATUS_LENGTH];
if (!subparse(text, parsePosition, digitList, false, status)) {
Currency[] currency = parseCurrency ? new Currency[1] : null;
if (!subparse(text, parsePosition, digitList, false, status, currency)) {
parsePosition.setIndex(backup);
return null;
}
Number n = null;
// Handle infinity
if (status[STATUS_INFINITE]) {
return new Double(status[STATUS_POSITIVE]
? Double.POSITIVE_INFINITY
: Double.NEGATIVE_INFINITY);
n = new Double(status[STATUS_POSITIVE]
? Double.POSITIVE_INFINITY
: Double.NEGATIVE_INFINITY);
}
// Handle -0.0
if (!status[STATUS_POSITIVE] && digitList.isZero()) {
return new Double(-0.0);
else if (!status[STATUS_POSITIVE] && digitList.isZero()) {
n = new Double(-0.0);
}
// Do as much of the multiplier conversion as possible without
// losing accuracy.
int mult = multiplier; // Don't modify this.multiplier
while (mult % 10 == 0) {
--digitList.decimalAt;
mult /= 10;
else {
// Do as much of the multiplier conversion as possible without
// losing accuracy.
int mult = multiplier; // Don't modify this.multiplier
while (mult % 10 == 0) {
--digitList.decimalAt;
mult /= 10;
}
// Handle integral values
if (mult == 1 && digitList.isIntegral()) {
BigInteger big = digitList.getBigInteger(status[STATUS_POSITIVE]);
n = (big.bitLength() < 64) ?
(Number) new Long(big.longValue()) : (Number) big;
}
// Handle non-integral values
else {
java.math.BigDecimal big = digitList.getBigDecimal(status[STATUS_POSITIVE]);
n = big;
if (mult != 1) {
n = big.divide(java.math.BigDecimal.valueOf(mult),
java.math.BigDecimal.ROUND_HALF_EVEN);
}
}
}
// Handle integral values
if (mult == 1 && digitList.isIntegral()) {
BigInteger n = digitList.getBigInteger(status[STATUS_POSITIVE]);
return (n.bitLength() < 64)
? (Number) new Long(n.longValue())
: (Number) n;
}
// Handle non-integral values
java.math.BigDecimal n = digitList.getBigDecimal(status[STATUS_POSITIVE]);
if (mult != 1) {
n = n.divide(java.math.BigDecimal.valueOf(mult),
java.math.BigDecimal.ROUND_HALF_EVEN);
}
return n;
// Assemble into CurrencyAmount if necessary
return parseCurrency ? new CurrencyAmount(n, currency[0]) : n;
}
private static final int STATUS_INFINITE = 0;
@ -1395,10 +1440,14 @@ public class DecimalFormat extends NumberFormat {
* infinite values and integer only.
* @param status Upon return contains boolean status flags indicating
* whether the value was infinite and whether it was positive.
* @param currency return value for parsed currency, for generic
* currency parsing mode, or null for normal parsing. In generic
* currency parsing mode, any currency is parsed, not just the
* currency that this formatter is set to.
*/
private final boolean subparse(String text, ParsePosition parsePosition,
DigitList digits, boolean isExponent,
boolean status[])
boolean status[], Currency currency[])
{
int position = parsePosition.getIndex();
int oldStart = parsePosition.getIndex();
@ -1409,8 +1458,8 @@ public class DecimalFormat extends NumberFormat {
}
// Match positive and negative prefixes; prefer longest match.
int posMatch = compareAffix(text, position, false, true);
int negMatch = compareAffix(text, position, true, true);
int posMatch = compareAffix(text, position, false, true, currency);
int negMatch = compareAffix(text, position, true, true, currency);
if (posMatch >= 0 && negMatch >= 0) {
if (posMatch > negMatch) {
negMatch = -1;
@ -1611,10 +1660,10 @@ public class DecimalFormat extends NumberFormat {
// Match positive and negative suffixes; prefer longest match.
if (posMatch >= 0) {
posMatch = compareAffix(text, position, false, false);
posMatch = compareAffix(text, position, false, false, currency);
}
if (negMatch >= 0) {
negMatch = compareAffix(text, position, true, false);
negMatch = compareAffix(text, position, true, false, currency);
}
if (posMatch >= 0 && negMatch >= 0) {
if (posMatch > negMatch) {
@ -1669,17 +1718,21 @@ public class DecimalFormat extends NumberFormat {
* @param pos offset into input at which to begin matching
* @param isNegative
* @param isPrefix
* @param currency return value for parsed currency, for generic
* currency parsing mode, or null for normal parsing. In generic
* currency parsing mode, any currency is parsed, not just the
* currency that this formatter is set to.
* @return length of input that matches, or -1 if match failure
*/
private int compareAffix(String text, int pos,
boolean isNegative, boolean isPrefix) {
if (currencyChoice != null) {
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);
text, pos, currency);
} else {
return compareComplexAffix(isNegative ? negSuffixPattern : posSuffixPattern,
text, pos);
text, pos, currency);
}
}
@ -1789,9 +1842,14 @@ public class DecimalFormat extends NumberFormat {
* @param affixPat pattern string
* @param text input text
* @param pos offset into input at which to begin matching
* @param currency return value for parsed currency, for generic
* currency parsing mode, or null for normal parsing. In generic
* currency parsing mode, any currency is parsed, not just the
* currency that this formatter is set to.
* @return length of input that matches, or -1 if match failure
*/
private int compareComplexAffix(String affixPat, String text, int pos) {
private int compareComplexAffix(String affixPat, String text, int pos,
Currency[] currency) {
for (int i=0; i<affixPat.length() && pos >= 0; ) {
char c = affixPat.charAt(i++);
@ -1824,17 +1882,43 @@ public class DecimalFormat extends NumberFormat {
switch (c) {
case CURRENCY_SIGN:
// assert(currency != null);
// assert(currencyChoice != null);
// If currency != null, then perform generic currency matching.
// Otherwise, do currency choice parsing.
//assert(currency != null ||
// (getCurrency() != null && currencyChoice != null));
boolean intl = i<affixPat.length() &&
affixPat.charAt(i) == CURRENCY_SIGN;
if (intl) {
++i;
pos = match(text, pos, getCurrency().getCurrencyCode());
} else {
// Parse generic currency -- anything for which we
// have a display name, or any 3-letter ISO code.
if (currency != null) {
// Try to parse display name for our locale; first
// determine our locale.
ULocale uloc = getLocale(ULocale.VALID_LOCALE);
if (uloc == null) {
// applyPattern has been called; use the symbols
uloc = symbols.getLocale(ULocale.VALID_LOCALE);
}
// Delegate parse of display name => ISO code to Currency
ParsePosition ppos = new ParsePosition(pos);
/* Number n = */currencyChoice.parse(text, ppos);
pos = (ppos.getIndex() == pos) ? -1 : ppos.getIndex();
String iso = Currency.parse(uloc.toLocale(), text, ppos);
// If parse succeeds, populate currency[0]
if (iso != 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:
@ -2526,6 +2610,10 @@ public class DecimalFormat extends NumberFormat {
&& useExponentialNotation == other.useExponentialNotation
&& (!useExponentialNotation ||
minExponentDigits == other.minExponentDigits)
&& useSignificantDigits == other.useSignificantDigits
&& (!useSignificantDigits ||
minSignificantDigits == other.minSignificantDigits &&
maxSignificantDigits == other.maxSignificantDigits)
&& symbols.equals(other.symbols));
}
@ -3683,12 +3771,24 @@ public class DecimalFormat extends NumberFormat {
setMinimumFractionDigits(d);
setMaximumFractionDigits(d);
}
expandAffixes();
}
}
/**
* Returns the currency in effect for this formatter. Subclasses
* should override this method as needed. Unlike getCurrency(),
* this method should never return null.
* @internal
*/
protected Currency getEffectiveCurrency() {
Currency c = getCurrency();
if (c == null) {
c = Currency.getInstance(symbols.getInternationalCurrencySymbol());
}
return c;
}
/**
* Sets the maximum number of digits allowed in the fraction portion of a
* number. This override limits the fraction digit count to 340.

View file

@ -22,6 +22,7 @@ import java.util.Set;
import com.ibm.icu.impl.ICULocaleData;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.text.UFormat;
@ -193,6 +194,8 @@ public abstract class NumberFormat extends UFormat {
return format((java.math.BigDecimal) number, toAppendTo, pos);
} else if (number instanceof com.ibm.icu.math.BigDecimal) {
return format((com.ibm.icu.math.BigDecimal) number, toAppendTo, pos);
} else if (number instanceof CurrencyAmount) {
return format((CurrencyAmount)number, toAppendTo, pos);
} else if (number instanceof Number) {
return format(((Number)number).doubleValue(), toAppendTo, pos);
} else {
@ -209,22 +212,22 @@ public abstract class NumberFormat extends UFormat {
return parse(source, parsePosition);
}
/**
/**
* Specialization of format.
* @see java.text.Format#format(Object)
* @stable ICU 2.0
*/
public final String format (double number) {
public final String format(double number) {
return format(number,new StringBuffer(),
new FieldPosition(0)).toString();
}
/**
/**
* Specialization of format.
* @see java.text.Format#format(Object)
* @stable ICU 2.0
*/
public final String format (long number) {
public final String format(long number) {
return format(number,new StringBuffer(),
new FieldPosition(0)).toString();
}
@ -259,7 +262,17 @@ public abstract class NumberFormat extends UFormat {
new FieldPosition(0)).toString();
}
/**
/**
* <strong><font face=helvetica color=red>NEW</font></strong>
* Convenience method to format a CurrencyAmount.
* @draft ICU 3.0
*/
public final String format(CurrencyAmount currAmt) {
return format(currAmt, new StringBuffer(),
new FieldPosition(0)).toString();
}
/**
* Specialization of format.
* @see java.text.Format#format(Object, StringBuffer, FieldPosition)
* @stable ICU 2.0
@ -268,7 +281,7 @@ public abstract class NumberFormat extends UFormat {
StringBuffer toAppendTo,
FieldPosition pos);
/**
/**
* Specialization of format.
* @see java.text.Format#format(Object, StringBuffer, FieldPosition)
* @stable ICU 2.0
@ -306,8 +319,26 @@ public abstract class NumberFormat extends UFormat {
public abstract StringBuffer format(com.ibm.icu.math.BigDecimal number,
StringBuffer toAppendTo,
FieldPosition pos);
/**
/**
* <strong><font face=helvetica color=red>NEW</font></strong>
* Format a CurrencyAmount.
* @see java.text.Format#format(Object, StringBuffer, FieldPosition)
* @draft ICU 3.0
*/
public StringBuffer format(CurrencyAmount currAmt,
StringBuffer toAppendTo,
FieldPosition pos) {
// Default implementation -- subclasses may override
Currency save = getCurrency(), curr = currAmt.getCurrency();
boolean same = curr.equals(save);
if (!same) setCurrency(curr);
format(currAmt.getNumber(), toAppendTo, pos);
if (!same) setCurrency(save);
return toAppendTo;
}
/**
* Returns a Long if possible (e.g., within the range [Long.MIN_VALUE,
* Long.MAX_VALUE] and with no decimals), otherwise a Double.
* If IntegerOnly is set, will stop at a decimal
@ -337,9 +368,53 @@ public abstract class NumberFormat extends UFormat {
ParsePosition parsePosition = new ParsePosition(0);
Number result = parse(text, parsePosition);
if (parsePosition.getIndex() == 0) {
throw new ParseException("Unparseable number: \"" + text + "\"",
//PP:parsePosition.errorIndex);
0);
throw new ParseException("Unparseable number: \"" + text + '"',
parsePosition.getErrorIndex());
}
return result;
}
/**
* <strong><font face=helvetica color=red>NEW</font></strong>
* Parses text from the given string as a CurrencyAmount. This
* method will fail if this format is not a currency format, that
* is, if it does not contain the currency pattern symbol (U+00A4)
* in its prefix or suffix.
*
* @param text the string to parse
* @param pos input-output position; on input, the position within
* text to match; must have 0 <= pos.getIndex() < text.length();
* on output, the position after the last matched character. If
* the parse fails, the position in unchanged upon output.
* @return a CurrencyAmount, or null upon failure
* @draft ICU 3.0
*/
public CurrencyAmount parseCurrency(String text, ParsePosition pos) {
// Default implementation only -- subclasses should override
Number n = parse(text, pos);
return n == null ? null : new CurrencyAmount(n, getEffectiveCurrency());
}
/**
* <strong><font face=helvetica color=red>NEW</font></strong>
* Parses text from the beginning of the given string as a
* CurrencyAmount. The method might not use the entire text of
* the given string. This method will fail if this format is not
* a currency format, that is, if it does not contain the currency
* pattern symbol (U+00A4) in its prefix or suffix.
*
* @param text the string to parse
* @return a non-null CurrencyAmount
* @exception ParseException if the beginning of the specified string
* cannot be parsed.
* @draft ICU 3.0
*/
public CurrencyAmount parseCurrency(String text) throws ParseException {
ParsePosition pos = new ParsePosition(0);
CurrencyAmount result = parseCurrency(text, pos);
if (pos.getIndex() == 0) {
throw new ParseException("Unparseable currency: \"" + text + '"',
pos.getErrorIndex());
}
return result;
}
@ -912,21 +987,20 @@ public abstract class NumberFormat extends UFormat {
}
/**
* Sets the minimum integer digits count directly, with no range
* pinning. For use by subclasses.
* Returns the currency in effect for this formatter. Subclasses
* should override this method as needed. Unlike getCurrency(),
* this method should never return null.
* @return a non-null Currency
* @internal
*/
protected void internalSetMinimumIntegerDigits(int n) {
minimumIntegerDigits = n;
}
/**
* Sets the maximum integer digits count directly, with no range
* pinning. For use by subclasses.
* @internal
*/
protected void internalSetMaximumIntegerDigits(int n) {
maximumIntegerDigits = n;
protected Currency getEffectiveCurrency() {
Currency c = getCurrency();
if (c == null) {
ULocale uloc = getLocale(ULocale.VALID_LOCALE);
Locale loc = uloc != null ? uloc.toLocale() : Locale.getDefault();
c = Currency.getInstance(loc);
}
return c;
}
// =======================privates===============================

View file

@ -1,12 +1,14 @@
/**
*******************************************************************************
* Copyright (C) 2001-2003, International Business Machines Corporation and *
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
package com.ibm.icu.util;
import java.io.Serializable;
import java.text.ChoiceFormat;
import java.text.ParsePosition;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
@ -308,6 +310,123 @@ public class Currency implements Serializable {
return isoCode;
}
/**
* 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
* code. If multiple display names match, then the longest one is
* selected. If both a display name and a 3-letter ISO code
* match, then the display name is preferred, unless it's length
* is less than 3.
*
* @param locale the locale of the display names to match
* @param text the text to parse
* @param pos input-output position; on input, the position within
* text to match; must have 0 <= pos.getIndex() < text.length();
* on output, the position after the last matched character. If
* the parse fails, the position in unchanged upon output.
* @return the ISO 4217 code, as a string, of the best match, or
* null if there is no match
*
* @internal
*/
public static String parse(Locale locale, String text, 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.
// This problem will go away when real multi-level fallback is
// implemented. We could also fix this by recording (in a
// hash) which codes are used at each level of fallback, but
// this doesn't seem warranted.
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 {
//| Currencies {
//| USD { "US$", "US Dollar" }
//| CHF { "Sw F", "Swiss Franc" }
//| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
//| //...
//| }
//|}
// In the future, resource bundles may implement multi-level
// fallback. That is, if a currency is not found in the en_US
// Currencies data, then the en Currencies data will be searched.
// Currently, if a Currencies datum exists in en_US and en, the
// en_US entry hides that in en.
// We want multi-level fallback for this resource, so we implement
// it manually.
// Multi-level resource inheritance fallback loop
while (locale != null) {
ResourceBundle rb = ICULocaleData.getLocaleElements(locale);
// We can't cast this to String[][]; the cast has to happen later
try {
Object[][] currencies = (Object[][]) rb.getObject("Currencies");
// Do a linear search
for (int i=0; i<currencies.length; ++i) {
String name = ((String[]) currencies[i][1])[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 = (String) currencies[i][0];
max = len;
}
pos.setIndex(start);
continue;
}
}
if (name.length() > max && fragment.startsWith(name)) {
iso = (String) currencies[i][0];
max = name.length();
}
}
}
catch (MissingResourceException e) {}
locale = LocaleUtility.fallback(locale);
}
// 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;
}
}
if (valid) {
iso = text.substring(start, start+3);
max = 3;
}
}
pos.setIndex(start + max);
return iso;
}
/**
* Returns the number of the number of fraction digits that should
* be displayed for this currency.