mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 22:44:49 +00:00
ICU-2825 add support for formatting and parsing CurrencyAmount objects
X-SVN-Rev: 14973
This commit is contained in:
parent
db00eaeec7
commit
29bfb5542a
5 changed files with 410 additions and 77 deletions
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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===============================
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue