ICU-10368 String digit/symbol support in DecimalFormatSymbols - merged from the work branch.

X-SVN-Rev: 39202
This commit is contained in:
Yoshito Umaoka 2016-09-13 08:41:57 +00:00
parent 390d736f89
commit 72e1644c07
7 changed files with 1119 additions and 548 deletions

View File

@ -30,11 +30,11 @@ public class CurrencyData {
public static final class CurrencyFormatInfo {
public final String currencyPattern;
public final char monetarySeparator;
public final char monetaryGroupingSeparator;
public final String monetarySeparator;
public final String monetaryGroupingSeparator;
public CurrencyFormatInfo(String currencyPattern, char monetarySeparator,
char monetaryGroupingSeparator) {
public CurrencyFormatInfo(String currencyPattern, String monetarySeparator,
String monetaryGroupingSeparator) {
this.currencyPattern = currencyPattern;
this.monetarySeparator = monetarySeparator;
this.monetaryGroupingSeparator = monetaryGroupingSeparator;
@ -93,10 +93,12 @@ public class CurrencyData {
temp = (CurrencyDisplayInfoProvider) clzz.newInstance();
} catch (Throwable t) {
temp = new CurrencyDisplayInfoProvider() {
@Override
public CurrencyDisplayInfo getInstance(ULocale locale, boolean withFallback) {
return DefaultInfo.getWithFallback(withFallback);
}
@Override
public boolean hasData() {
return false;
}

View File

@ -896,9 +896,9 @@ public class DecimalFormat extends NumberFormat {
addPadding(result, fieldPosition, prefixLen, suffixLen);
return result;
}
int precision = precision(false);
// This is to fix rounding for scientific notation. See ticket:10542.
// This code should go away when a permanent fix is done for ticket:9931.
//
@ -957,7 +957,7 @@ public class DecimalFormat extends NumberFormat {
}
return toDigitList(number).getDouble();
}
@Deprecated
DigitList toDigitList(double number) {
DigitList result = new DigitList();
@ -1129,7 +1129,7 @@ public class DecimalFormat extends NumberFormat {
digitList.set(number, precision(true));
// Issue 11808
if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
throw new ArithmeticException("Rounding necessary");
throw new ArithmeticException("Rounding necessary");
}
return subformat(number, result, fieldPosition, isNegative, true, parseAttr);
}
@ -1164,7 +1164,7 @@ public class DecimalFormat extends NumberFormat {
digitList.set(number, precision(true));
// For issue 11808.
if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
throw new ArithmeticException("Rounding necessary");
throw new ArithmeticException("Rounding necessary");
}
return subformat(number.intValue(), result, fieldPosition, number.signum() < 0, true,
parseAttr);
@ -1198,7 +1198,7 @@ public class DecimalFormat extends NumberFormat {
!areSignificantDigitsUsed());
// For issue 11808.
if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
throw new ArithmeticException("Rounding necessary");
throw new ArithmeticException("Rounding necessary");
}
return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0,
false, parseAttr);
@ -1231,7 +1231,7 @@ public class DecimalFormat extends NumberFormat {
!areSignificantDigitsUsed());
// For issue 11808.
if (digitList.wasRounded() && roundingMode == BigDecimal.ROUND_UNNECESSARY) {
throw new ArithmeticException("Rounding necessary");
throw new ArithmeticException("Rounding necessary");
}
return subformat(number.doubleValue(), result, fieldPosition, number.signum() < 0,
false, false);
@ -1292,7 +1292,7 @@ public class DecimalFormat extends NumberFormat {
// get the visible fractions and the number of fraction digits.
return getFixedDecimal(number, digitList);
}
FixedDecimal getFixedDecimal(double number, DigitList dl) {
int fractionalDigitsInDigitList = dl.count - dl.decimalAt;
int v;
@ -1421,12 +1421,12 @@ public class DecimalFormat extends NumberFormat {
FieldPosition fieldPosition,
boolean isInteger,
boolean parseAttr) {
char [] digits = symbols.getDigitsLocal();
String[] digits = symbols.getDigitStrings();
char grouping = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
symbols.getGroupingSeparator(): symbols.getMonetaryGroupingSeparator();
char decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
symbols.getDecimalSeparator() : symbols.getMonetaryDecimalSeparator();
String grouping = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
symbols.getGroupingSeparatorString(): symbols.getMonetaryGroupingSeparatorString();
String decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString();
boolean useSigDig = areSignificantDigitsUsed();
int maxIntDig = getMaximumIntegerDigits();
int minIntDig = getMinimumIntegerDigits();
@ -1506,14 +1506,14 @@ public class DecimalFormat extends NumberFormat {
fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
fieldPosition.setEndIndex(result.length());
}
// This handles the special case of formatting 0. For zero only, we count the
// zero to the left of the decimal point as one signficant digit. Ordinarily we
// do not count any leading 0's as significant. If the number we are formatting
// is not zero, then either sigCount or digits.getCount() will be non-zero.
if (sigCount == 0 && digitList.count == 0) {
sigCount = 1;
}
}
// Determine whether or not there are any printable fractional digits. If
// we've used up the digits we know there aren't.
@ -1629,9 +1629,9 @@ public class DecimalFormat extends NumberFormat {
private void subformatExponential(StringBuffer result,
FieldPosition fieldPosition,
boolean parseAttr) {
char [] digits = symbols.getDigitsLocal();
char decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
symbols.getDecimalSeparator() : symbols.getMonetaryDecimalSeparator();
String[] digits = symbols.getDigitStringsLocal();
String decimal = currencySignCount == CURRENCY_SIGN_COUNT_ZERO ?
symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString();
boolean useSigDig = areSignificantDigitsUsed();
int maxIntDig = getMaximumIntegerDigits();
int minIntDig = getMinimumIntegerDigits();
@ -1777,7 +1777,7 @@ public class DecimalFormat extends NumberFormat {
addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin, result.length());
}
}
// Record field information
if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
if (fieldPosition.getEndIndex() < 0) {
@ -1841,7 +1841,7 @@ public class DecimalFormat extends NumberFormat {
if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
fieldPosition.setBeginIndex(result.length());
}
result.append(symbols.getMinusString());
result.append(symbols.getMinusSignString());
if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
fieldPosition.setEndIndex(result.length());
}
@ -1855,7 +1855,7 @@ public class DecimalFormat extends NumberFormat {
if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
fieldPosition.setBeginIndex(result.length());
}
result.append(symbols.getPlusString());
result.append(symbols.getPlusSignString());
if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) {
fieldPosition.setEndIndex(result.length());
}
@ -2353,7 +2353,7 @@ public class DecimalFormat extends NumberFormat {
0xFF0C, 0xFF0C,
0xFF0E, 0xFF0E,
0xFF61, 0xFF61).freeze();
static final UnicodeSet minusSigns =
new UnicodeSet(
0x002D, 0x002D,
@ -2363,7 +2363,7 @@ public class DecimalFormat extends NumberFormat {
0x2796, 0x2796,
0xFE63, 0xFE63,
0xFF0D, 0xFF0D).freeze();
static final UnicodeSet plusSigns =
new UnicodeSet(
0x002B, 0x002B,
@ -2373,7 +2373,7 @@ public class DecimalFormat extends NumberFormat {
0xFB29, 0xFB29,
0xFE62, 0xFE62,
0xFF0B, 0xFF0B).freeze();
// equivalent grouping and decimal support
static final boolean skipExtendedSeparatorParsing = ICUConfig.get(
"com.ibm.icu.text.DecimalFormat.SkipExtendedSeparatorParsing", "false")
@ -2460,25 +2460,22 @@ public class DecimalFormat extends NumberFormat {
// DigitList, and adjust the exponent as needed.
digits.decimalAt = digits.count = 0;
char [] digitSymbols = symbols.getDigitsLocal();
char decimal = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
symbols.getDecimalSeparator() : symbols.getMonetaryDecimalSeparator();
char grouping = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
symbols.getGroupingSeparator() : symbols.getMonetaryGroupingSeparator();
String decimal = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
symbols.getDecimalSeparatorString() : symbols.getMonetaryDecimalSeparatorString();
String grouping = (currencySignCount == CURRENCY_SIGN_COUNT_ZERO) ?
symbols.getGroupingSeparatorString() : symbols.getMonetaryGroupingSeparatorString();
String exponentSep = symbols.getExponentSeparator();
boolean sawDecimal = false;
boolean sawGrouping = false;
boolean sawExponent = false;
boolean sawDigit = false;
long exponent = 0; // Set to the exponent value, if any
int digit = 0;
// strict parsing
boolean strictParse = isParseStrict();
boolean strictFail = false; // did we exit with a strict parse failure?
int lastGroup = -1; // where did we last see a grouping separator?
int digitStart = position; // where did the digit start?
int groupedDigitCount = 0; // tracking count of digits delimited by grouping separator
int gs2 = groupingSize2 == 0 ? groupingSize : groupingSize2;
UnicodeSet decimalEquiv = skipExtendedSeparatorParsing ? UnicodeSet.EMPTY :
@ -2490,136 +2487,137 @@ public class DecimalFormat extends NumberFormat {
// the maximum allowable digits is reached.
int digitCount = 0;
int backup = -1;
int ch;
for (; position < text.length(); position += UTF16.getCharCount(ch)) {
ch = UTF16.charAt(text,position);
int backup = -1; // used for preserving the last confirmed position
int[] parsedDigit = {-1}; // allocates int[1] for parsing a single digit
// We recognize all digit ranges, not only the Latin digit range
// '0'..'9'. We do so by using the UCharacter.digit() method, which
// converts a valid Unicode digit to the range 0..9.
//
// The character 'ch' may be a digit. If so, place its value from 0 to 9
// in 'digit'. First try using the locale digit, which may or MAY NOT be a
// standard Unicode digit range. If this fails, try using the standard
// Unicode digit ranges by calling UCharacter.digit(). If this also fails,
// digit will have a value outside the range 0..9.
digit = ch - digitSymbols[0];
if (digit < 0 || digit > 9)
digit = UCharacter.digit(ch, 10);
if (digit < 0 || digit > 9) {
for ( digit = 0 ; digit < 10 ; digit++) {
if ( ch == digitSymbols[digit] )
break;
}
}
if (digit == 0) {
while (position < text.length()) {
// Check if the sequence at the current position matches a decimal digit
int matchLen = matchesDigit(text, position, parsedDigit);
if (matchLen > 0) {
// matched a digit
// Cancel out backup setting (see grouping handler below)
if (strictParse && backup != -1) {
// comma followed by digit, so group before comma is a secondary
// group. If there was a group separator before that, the group
// must == the secondary group length, else it can be <= the the
// secondary group length.
if ((lastGroup != -1 && countCodePoints(text, lastGroup, backup) - 1 != gs2)
|| (lastGroup == -1 && countCodePoints(text, digitStart, position) - 1 > gs2)) {
strictFail = true;
break;
if (backup != -1) {
if (strictParse) {
// comma followed by digit, so group before comma is a secondary
// group. If there was a group separator before that, the group
// must == the secondary group length, else it can be <= the the
// secondary group length.
if ((lastGroup != -1 && groupedDigitCount != gs2)
|| (lastGroup == -1 && groupedDigitCount > gs2)) {
strictFail = true;
break;
}
}
lastGroup = backup;
groupedDigitCount = 0;
}
backup = -1; // Do this BEFORE continue statement below!!!
sawDigit = true;
// Handle leading zeros
if (digits.count == 0) {
groupedDigitCount++;
position += matchLen;
backup = -1;
sawDigit = true;
if (parsedDigit[0] == 0 && digits.count == 0) {
// Handle leading zeros
if (!sawDecimal) {
// Ignore leading zeros in integer part of number.
continue;
}
// If we have seen the decimal, but no significant digits yet,
// then we account for leading zeros by decrementing the
// digits.decimalAt into negative values.
--digits.decimalAt;
} else {
++digitCount;
digits.append((char) (digit + '0'));
}
} else if (digit > 0 && digit <= 9) // [sic] digit==0 handled above
{
if (strictParse) {
if (backup != -1) {
if ((lastGroup != -1 && countCodePoints(text, lastGroup, backup) - 1 != gs2)
|| (lastGroup == -1 && countCodePoints(text, digitStart, position) - 1 > gs2)) {
strictFail = true;
break;
}
lastGroup = backup;
}
digits.append((char) (parsedDigit[0] + '0'));
}
continue;
}
sawDigit = true;
++digitCount;
digits.append((char) (digit + '0'));
// Cancel out backup setting (see grouping handler below)
backup = -1;
} else if (ch == decimal) {
// Check if the sequence at the current position matches locale's decimal separator
int decimalStrLen = decimal.length();
if (text.regionMatches(position, decimal, 0, decimalStrLen)) {
// matched a decimal separator
if (strictParse) {
if (backup != -1 ||
(lastGroup != -1 && countCodePoints(text,lastGroup,position) != groupingSize + 1)) {
(lastGroup != -1 && groupedDigitCount != groupingSize)) {
strictFail = true;
break;
}
}
// If we're only parsing integers, or if we ALREADY saw the decimal,
// then don't parse this one.
if (isParseIntegerOnly() || sawDecimal) {
break;
}
digits.decimalAt = digitCount; // Not digits.count!
sawDecimal = true;
} else if (isGroupingUsed() && ch == grouping) {
if (sawDecimal) {
break;
}
if (strictParse) {
if ((!sawDigit || backup != -1)) {
// leading group, or two group separators in a row
strictFail = true;
position += decimalStrLen;
continue;
}
if (isGroupingUsed()) {
// Check if the sequence at the current position matches locale's grouping separator
int groupingStrLen = grouping.length();
if (text.regionMatches(position, grouping, 0, groupingStrLen)) {
if (sawDecimal) {
break;
}
if (strictParse) {
if ((!sawDigit || backup != -1)) {
// leading group, or two group separators in a row
strictFail = true;
break;
}
}
// Ignore grouping characters, if we are using them, but require that
// they be followed by a digit. Otherwise we backup and reprocess
// them.
backup = position;
position += groupingStrLen;
sawGrouping = true;
continue;
}
// Ignore grouping characters, if we are using them, but require that
// they be followed by a digit. Otherwise we backup and reprocess
// them.
backup = position;
sawGrouping = true;
} else if (!sawDecimal && decimalEquiv.contains(ch)) {
}
// Check if the code point at the current position matches one of decimal/grouping equivalent group chars
int cp = text.codePointAt(position);
if (!sawDecimal && decimalEquiv.contains(cp)) {
// matched a decimal separator
if (strictParse) {
if (backup != -1 ||
(lastGroup != -1 && countCodePoints(text,lastGroup,position) != groupingSize + 1)) {
(lastGroup != -1 && groupedDigitCount != groupingSize)) {
strictFail = true;
break;
}
}
// If we're only parsing integers, then don't parse this one.
if (isParseIntegerOnly())
// If we're only parsing integers, or if we ALREADY saw the decimal,
// then don't parse this one.
if (isParseIntegerOnly() || sawDecimal) {
break;
}
digits.decimalAt = digitCount; // Not digits.count!
// Once we see a decimal separator character, we only accept that
// decimal separator character from then on.
decimal = (char) ch;
decimal = String.valueOf(Character.toChars(cp));
sawDecimal = true;
} else if (isGroupingUsed() && !sawGrouping && groupEquiv.contains(ch)) {
position += Character.charCount(cp);
continue;
}
if (isGroupingUsed() && !sawGrouping && groupEquiv.contains(cp)) {
// matched a grouping separator
if (sawDecimal) {
break;
}
if (strictParse) {
if ((!sawDigit || backup != -1)) {
// leading group, or two group separators in a row
@ -2627,25 +2625,33 @@ public class DecimalFormat extends NumberFormat {
break;
}
}
// Once we see a grouping character, we only accept that grouping
// character from then on.
grouping = (char) ch;
grouping = String.valueOf(Character.toChars(cp));
// Ignore grouping characters, if we are using them, but require that
// they be followed by a digit. Otherwise we backup and reprocess
// them.
backup = position;
position += Character.charCount(cp);
sawGrouping = true;
} else if (!sawExponent && text.regionMatches(true, position, exponentSep, 0, exponentSep.length())) {
// Parse sign, if present
continue;
}
// Check if the sequence at the current position matches locale's exponent separator
int exponentSepStrLen = exponentSep.length();
if (text.regionMatches(true, position, exponentSep, 0, exponentSepStrLen)) {
// parse sign, if present
boolean negExp = false;
int pos = position + exponentSep.length();
if (pos < text.length()) {
ch = UTF16.charAt(text,pos);
if (ch == symbols.getPlusSign()) {
++pos;
} else if (ch == symbols.getMinusSign()) {
++pos;
String plusSign = symbols.getPlusSignString();
String minusSign = symbols.getMinusSignString();
if (text.regionMatches(pos, plusSign, 0, plusSign.length())) {
pos += plusSign.length();
} else if (text.regionMatches(pos, minusSign, 0, minusSign.length())) {
pos += minusSign.length();
negExp = true;
}
}
@ -2653,16 +2659,10 @@ public class DecimalFormat extends NumberFormat {
DigitList exponentDigits = new DigitList();
exponentDigits.count = 0;
while (pos < text.length()) {
digit = UTF16.charAt(text,pos) - digitSymbols[0];
if (digit < 0 || digit > 9) {
// Can't parse "[1E0]" when pattern is "0.###E0;[0.###E0]"
// Should update reassign the value of 'ch' in the code: digit
// = Character.digit(ch, 10); [Richard/GCL]
digit = UCharacter.digit(UTF16.charAt(text,pos), 10);
}
if (digit >= 0 && digit <= 9) {
exponentDigits.append((char) (digit + '0'));
pos += UTF16.getCharCount(UTF16.charAt(text,pos));
int digitMatchLen = matchesDigit(text, pos, parsedDigit);
if (digitMatchLen > 0) {
exponentDigits.append((char) (parsedDigit[0] + '0'));
pos += digitMatchLen;
} else {
break;
}
@ -2670,11 +2670,9 @@ public class DecimalFormat extends NumberFormat {
if (exponentDigits.count > 0) {
// defer strict parse until we know we have a bona-fide exponent
if (strictParse) {
if (backup != -1 || lastGroup != -1) {
strictFail = true;
break;
}
if (strictParse && sawGrouping) {
strictFail = true;
break;
}
// Quick overflow check for exponential part. Actual limit check
@ -2695,33 +2693,34 @@ public class DecimalFormat extends NumberFormat {
}
}
position = pos; // Advance past the exponent
sawExponent = true;
}
break; // Whether we fail or succeed, we exit this loop
} else {
break;
}
// All other cases, stop parsing
break;
}
if(digits.decimalAt == 0 && isDecimalPatternMatchRequired()) {
if(this.formatPattern.indexOf(decimal) != -1) {
if (digits.decimalAt == 0 && isDecimalPatternMatchRequired()) {
if (this.formatPattern.indexOf(decimal) != -1) {
parsePosition.setIndex(oldStart);
parsePosition.setErrorIndex(position);
return false;
}
}
if (backup != -1)
position = backup;
// If there was no decimal point we have an integer
if (!sawDecimal)
if (!sawDecimal) {
digits.decimalAt = digitCount; // Not digits.count!
}
// check for strict parse errors
if (strictParse && !sawDecimal) {
if (lastGroup != -1 && countCodePoints(text,lastGroup,position) != groupingSize + 1) {
if (lastGroup != -1 && groupedDigitCount != groupingSize) {
strictFail = true;
}
}
@ -2799,21 +2798,44 @@ public class DecimalFormat extends NumberFormat {
return true;
}
// Utility method used to count the number of codepoints
private int countCodePoints(String str,int start, int end) {
int count = 0;
int index = start;
while ( index < end ) {
count++;
index += UTF16.getCharCount(UTF16.charAt(str, index));
/**
* Check if the substring at the specified position matches a decimal digit.
* If matched, this method sets the decimal value to <code>decVal</code> and
* returns matched length.
*
* @param str The input string
* @param start The start index
* @param decVal Receives decimal value
* @return Length of match, or 0 if the sequence at the position is not
* a decimal digit.
*/
private int matchesDigit(String str, int start, int[] decVal) {
String[] localeDigits = symbols.getDigitStringsLocal();
// Check if the sequence at the current position matches locale digits.
for (int i = 0; i < 10; i++) {
int digitStrLen = localeDigits[i].length();
if (str.regionMatches(start, localeDigits[i], 0, digitStrLen)) {
decVal[0] = i;
return digitStrLen;
}
}
return count;
// If no locale digit match, then check if this is a Unicode digit
int cp = str.codePointAt(start);
decVal[0] = UCharacter.digit(cp, 10);
if (decVal[0] >= 0) {
return Character.charCount(cp);
}
return 0;
}
/**
* Returns a set of characters equivalent to the given desimal separator used for
* parsing number. This method may return an empty set.
*/
private UnicodeSet getEquivalentDecimals(char decimal, boolean strictParse) {
private UnicodeSet getEquivalentDecimals(String decimal, boolean strictParse) {
UnicodeSet equivSet = UnicodeSet.EMPTY;
if (strictParse) {
if (strictDotEquivalents.contains(decimal)) {
@ -2883,31 +2905,31 @@ public class DecimalFormat extends NumberFormat {
/**
* Remove bidi marks from affix
*/
private static String trimMarksFromAffix(String affix) {
boolean hasBidiMark = false;
int idx = 0;
for (; idx < affix.length(); idx++) {
if (isBidiMark(affix.charAt(idx))) {
hasBidiMark = true;
break;
}
}
if (!hasBidiMark) {
return affix;
}
private static String trimMarksFromAffix(String affix) {
boolean hasBidiMark = false;
int idx = 0;
for (; idx < affix.length(); idx++) {
if (isBidiMark(affix.charAt(idx))) {
hasBidiMark = true;
break;
}
}
if (!hasBidiMark) {
return affix;
}
StringBuilder buf = new StringBuilder();
buf.append(affix, 0, idx);
idx++; // skip the first Bidi mark
for (; idx < affix.length(); idx++) {
char c = affix.charAt(idx);
if (!isBidiMark(c)) {
buf.append(c);
}
}
StringBuilder buf = new StringBuilder();
buf.append(affix, 0, idx);
idx++; // skip the first Bidi mark
for (; idx < affix.length(); idx++) {
char c = affix.charAt(idx);
if (!isBidiMark(c)) {
buf.append(c);
}
}
return buf.toString();
}
return buf.toString();
}
/**
* Return the length matched by the given affix, or -1 if none. Runs of white space in
@ -3083,6 +3105,8 @@ public class DecimalFormat extends NumberFormat {
continue;
}
String affix = null;
switch (c) {
case CURRENCY_SIGN:
// since the currency names in choice format is saved the same way as
@ -3132,15 +3156,27 @@ public class DecimalFormat extends NumberFormat {
}
continue;
case PATTERN_PERCENT:
c = symbols.getPercent();
affix = symbols.getPercentString();
break;
case PATTERN_PER_MILLE:
c = symbols.getPerMill();
affix = symbols.getPerMillString();
break;
case PATTERN_MINUS:
c = symbols.getMinusSign();
case PATTERN_PLUS_SIGN:
affix = symbols.getPlusSignString();
break;
case PATTERN_MINUS_SIGN:
affix = symbols.getMinusSignString();
break;
default:
// fall through to affix != null test, which will fail
break;
}
if (affix != null) {
pos = match(text, pos, affix);
continue;
}
pos = match(text, pos, c);
if (PatternProps.isWhiteSpace(c)) {
i = skipPatternWhiteSpace(affixPat, i);
@ -3186,6 +3222,9 @@ public class DecimalFormat extends NumberFormat {
for (int i = 0; i < str.length() && pos >= 0;) {
int ch = UTF16.charAt(str, i);
i += UTF16.getCharCount(ch);
if (isBidiMark(ch)) {
continue;
}
pos = match(text, pos, ch);
if (PatternProps.isWhiteSpace(ch)) {
i = skipPatternWhiteSpace(str, i);
@ -3850,12 +3889,12 @@ public class DecimalFormat extends NumberFormat {
public boolean isDecimalSeparatorAlwaysShown() {
return decimalSeparatorAlwaysShown;
}
/**
* When decimal match is not required, the input does not have to
* contain a decimal mark when there is a decimal mark specified in the
* pattern.
* @param value true if input must contain a match to decimal mark in pattern
* pattern.
* @param value true if input must contain a match to decimal mark in pattern
* Default is false.
* @stable ICU 54
*/
@ -4206,19 +4245,20 @@ public class DecimalFormat extends NumberFormat {
}
// Here is where FieldPosition could be set for CURRENCY PLURAL.
buffer.append(s);
continue;
break;
case PATTERN_PERCENT:
c = symbols.getPercent();
buffer.append(symbols.getPercentString());
break;
case PATTERN_PER_MILLE:
c = symbols.getPerMill();
buffer.append(symbols.getPerMillString());
break;
case PATTERN_MINUS_SIGN:
buffer.append(symbols.getMinusSignString());
break;
default:
buffer.append(c);
break;
case PATTERN_MINUS:
String minusString = symbols.getMinusString();
buffer.append(minusString);
continue;
}
buffer.append(c);
}
}
@ -4263,23 +4303,22 @@ public class DecimalFormat extends NumberFormat {
int offset = affix.indexOf(symbols.getCurrencySymbol());
if (offset > -1) {
formatAffix2Attribute(isPrefix, Field.CURRENCY, buf, offset,
symbols.getCurrencySymbol().length());
symbols.getCurrencySymbol().length());
}
offset = affix.indexOf(symbols.getMinusString());
offset = affix.indexOf(symbols.getMinusSignString());
if (offset > -1) {
formatAffix2Attribute(isPrefix, Field.SIGN, buf, offset,
symbols.getMinusString().length());
formatAffix2Attribute(isPrefix, Field.SIGN, buf, offset,
symbols.getMinusSignString().length());
}
// TODO: Consider if Percent and Permille can be more than one character.
offset = affix.indexOf(symbols.getPercent());
offset = affix.indexOf(symbols.getPercentString());
if (offset > -1) {
formatAffix2Attribute(isPrefix, Field.PERCENT, buf, offset,
1);
formatAffix2Attribute(isPrefix, Field.PERCENT, buf, offset,
symbols.getPercentString().length());
}
offset = affix.indexOf(symbols.getPerMill());
offset = affix.indexOf(symbols.getPerMillString());
if (offset > -1) {
formatAffix2Attribute(isPrefix, Field.PERMILLE, buf, offset,
1);
formatAffix2Attribute(isPrefix, Field.PERMILLE, buf, offset,
symbols.getPerMillString().length());
}
offset = pattern.indexOf("¤¤¤");
if (offset > -1) {
@ -4290,7 +4329,7 @@ public class DecimalFormat extends NumberFormat {
// Look for SIGN, PERCENT, PERMILLE in the formatted affix.
if (fieldPosition.getFieldAttribute() == NumberFormat.Field.SIGN) {
String sign = isNegative ? symbols.getMinusString() : symbols.getPlusString();
String sign = isNegative ? symbols.getMinusSignString() : symbols.getPlusSignString();
int firstPos = affix.indexOf(sign);
if (firstPos > -1) {
int startPos = buf.length() + firstPos;
@ -4298,18 +4337,18 @@ public class DecimalFormat extends NumberFormat {
fieldPosition.setEndIndex(startPos + sign.length());
}
} else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERCENT) {
int firstPos = affix.indexOf(symbols.getPercent());
int firstPos = affix.indexOf(symbols.getPercentString());
if (firstPos > -1) {
int startPos = buf.length() + firstPos;
fieldPosition.setBeginIndex(startPos);
fieldPosition.setEndIndex(startPos + 1);
fieldPosition.setEndIndex(startPos + symbols.getPercentString().length());
}
} else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERMILLE) {
int firstPos = affix.indexOf(symbols.getPerMill());
int firstPos = affix.indexOf(symbols.getPerMillString());
if (firstPos > -1) {
int startPos = buf.length() + firstPos;
fieldPosition.setBeginIndex(startPos);
fieldPosition.setEndIndex(startPos + 1);
fieldPosition.setEndIndex(startPos + symbols.getPerMillString().length());
}
} else
// If CurrencySymbol or InternationalCurrencySymbol is in the affix, check for currency symbol.
@ -4410,6 +4449,8 @@ public class DecimalFormat extends NumberFormat {
/**
* Appends an affix pattern to the given StringBuffer. Localize unquoted specials.
* <p>
* <b>Note:</b> This implementation does not support new String localized symbols.
*/
private void appendAffixPattern(StringBuffer buffer, boolean isNegative, boolean isPrefix,
boolean localized) {
@ -4462,7 +4503,7 @@ public class DecimalFormat extends NumberFormat {
case PATTERN_PERCENT:
ch = symbols.getPercent();
break;
case PATTERN_MINUS:
case PATTERN_MINUS_SIGN:
ch = symbols.getMinusSign();
break;
}
@ -4480,6 +4521,8 @@ public class DecimalFormat extends NumberFormat {
/**
* Does the real work of generating a pattern.
* <p>
* <b>Note:</b> This implementation does not support new String localized symbols.
*/
private String toPattern(boolean localized) {
StringBuffer result = new StringBuffer();
@ -4619,7 +4662,7 @@ public class DecimalFormat extends NumberFormat {
}
if (part == 0) {
if (negativeSuffix.equals(positiveSuffix) &&
negativePrefix.equals(PATTERN_MINUS + positivePrefix)) {
negativePrefix.equals(PATTERN_MINUS_SIGN + positivePrefix)) {
break;
} else {
result.append(localized ? symbols.getPatternSeparator() : PATTERN_SEPARATOR);
@ -4711,7 +4754,7 @@ public class DecimalFormat extends NumberFormat {
String exponent = String.valueOf(PATTERN_EXPONENT);
char plus = PATTERN_PLUS_SIGN;
char padEscape = PATTERN_PAD_ESCAPE;
char minus = PATTERN_MINUS; // Bug 4212072 [Richard/GCL]
char minus = PATTERN_MINUS_SIGN; // Bug 4212072 [Richard/GCL]
if (localized) {
zeroDigit = symbols.getZeroDigit();
sigDigit = symbols.getSignificantDigit();
@ -4973,7 +5016,7 @@ public class DecimalFormat extends NumberFormat {
// Fall through to append(ch)
} else if (ch == minus) {
// Convert to non-localized pattern
ch = PATTERN_MINUS;
ch = PATTERN_MINUS_SIGN;
// Fall through to append(ch)
} else if (ch == padEscape) {
if (padPos >= 0) {
@ -5167,7 +5210,7 @@ public class DecimalFormat extends NumberFormat {
(negPrefixPattern.equals(posPrefixPattern)
&& negSuffixPattern.equals(posSuffixPattern))) {
negSuffixPattern = posSuffixPattern;
negPrefixPattern = PATTERN_MINUS + posPrefixPattern;
negPrefixPattern = PATTERN_MINUS_SIGN + posPrefixPattern;
}
setLocale(null, null);
// save the pattern
@ -5254,8 +5297,8 @@ public class DecimalFormat extends NumberFormat {
/**
* {@icu} Sets the minimum number of significant digits that will be displayed. If
* <code>min</code> is less than one then it is set to one. If the maximum significant
* digits count is less than <code>min</code>, then it is set to <code>min</code>.
* This function also enables the use of significant digits by this formatter -
* digits count is less than <code>min</code>, then it is set to <code>min</code>.
* This function also enables the use of significant digits by this formatter -
* {@link #areSignificantDigitsUsed()} will return true.
*
* @param min the fewest significant digits to be shown
@ -5276,7 +5319,7 @@ public class DecimalFormat extends NumberFormat {
* {@icu} Sets the maximum number of significant digits that will be displayed. If
* <code>max</code> is less than one then it is set to one. If the minimum significant
* digits count is greater than <code>max</code>, then it is set to <code>max</code>.
* This function also enables the use of significant digits by this formatter -
* This function also enables the use of significant digits by this formatter -
* {@link #areSignificantDigitsUsed()} will return true.
*
* @param max the most significant digits to be shown
@ -5353,12 +5396,12 @@ public class DecimalFormat extends NumberFormat {
}
}
}
/**
* Sets the <tt>Currency Usage</tt> object used to display currency.
* This takes effect immediately, if this format is a
* currency format.
* @param newUsage new currency context object to use.
* currency format.
* @param newUsage new currency context object to use.
* @stable ICU 54
*/
public void setCurrencyUsage(CurrencyUsage newUsage) {
@ -5384,7 +5427,7 @@ public class DecimalFormat extends NumberFormat {
public CurrencyUsage getCurrencyUsage() {
return currencyUsage;
}
/**
* Returns the currency in effect for this formatter. Subclasses should override this
* method as needed. Unlike getCurrency(), this method should never return null.
@ -5456,9 +5499,9 @@ public class DecimalFormat extends NumberFormat {
public boolean isParseBigDecimal() {
return parseBigDecimal;
}
/**
* Set the maximum number of exponent digits when parsing a number.
* Set the maximum number of exponent digits when parsing a number.
* If the limit is set too high, an OutOfMemoryException may be triggered.
* The default value is 1000.
* @param newValue the new limit
@ -5469,7 +5512,7 @@ public class DecimalFormat extends NumberFormat {
PARSE_MAX_EXPONENT = newValue;
}
}
/**
* Get the current maximum number of exponent digits when parsing a
* number.
@ -5856,7 +5899,7 @@ public class DecimalFormat extends NumberFormat {
* @since ICU 54
*/
private CurrencyUsage currencyUsage = CurrencyUsage.STANDARD;
// ----------------------------------------------------------------------
static final int currentSerialVersion = 4;
@ -5878,7 +5921,7 @@ public class DecimalFormat extends NumberFormat {
* <li><b>3</b>: ICU 2.2. Adds currency object.
*
* <li><b>4</b>: ICU 54. Adds currency usage(standard vs cash)
*
*
* </ul>
*
* @serial
@ -5958,15 +6001,12 @@ public class DecimalFormat extends NumberFormat {
static final char PATTERN_SIGNIFICANT_DIGIT = '@';
static final char PATTERN_EXPONENT = 'E';
static final char PATTERN_PLUS_SIGN = '+';
static final char PATTERN_MINUS_SIGN = '-';
// Affix
private static final char PATTERN_PER_MILLE = '\u2030';
private static final char PATTERN_PERCENT = '%';
static final char PATTERN_PAD_ESCAPE = '*';
/**
* Bug 4212072 To meet the need of expandAffix(String, StirngBuffer) [Richard/GCL]
*/
private static final char PATTERN_MINUS = '-';
// Other
private static final char PATTERN_SEPARATOR = ';';
@ -6176,7 +6216,7 @@ public class DecimalFormat extends NumberFormat {
* based on rounding mode and width of fractional digits. Whenever setting affecting
* rounding mode, rounding increment and maximum width of fractional digits, then
* this method must be called.
*
*
* roundingIncrementICU is the field storing the custom rounding increment value,
* while actual rounding increment could be larger.
*/

View File

@ -17,7 +17,6 @@ import com.ibm.icu.impl.CacheBase;
import com.ibm.icu.impl.ICUData;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SoftCache;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
import com.ibm.icu.util.UResourceBundle;
@ -27,7 +26,7 @@ import com.ibm.icu.util.UResourceBundleIterator;
/**
* <code>NumberingSystem</code> is the base class for all number
* systems. This class provides the interface for setting different numbering
* system types, whether it be a simple alternate digit system such as
* system types, whether it be a simple alternate digit system such as
* Thai digits or Devanagari digits, or an algorithmic numbering system such
* as Hebrew numbering or Chinese numbering.
*
@ -51,7 +50,7 @@ public class NumberingSystem {
/**
* Factory method for creating a numbering system.
* @param radix_in The radix for this numbering system. ICU currently
* @param radix_in The radix for this numbering system. ICU currently
* supports only numbering systems whose radix is 10.
* @param isAlgorithmic_in Specifies whether the numbering system is algorithmic
* (true) or numeric (false).
@ -66,11 +65,11 @@ public class NumberingSystem {
public static NumberingSystem getInstance(int radix_in, boolean isAlgorithmic_in, String desc_in ) {
return getInstance(null,radix_in,isAlgorithmic_in,desc_in);
}
/**
* Factory method for creating a numbering system.
* @param name_in The string representing the name of the numbering system.
* @param radix_in The radix for this numbering system. ICU currently
* @param radix_in The radix for this numbering system. ICU currently
* supports only numbering systems whose radix is 10.
* @param isAlgorithmic_in Specifies whether the numbering system is algorithmic
* (true) or numeric (false).
@ -82,7 +81,7 @@ public class NumberingSystem {
* this numbering system.
* @stable ICU 4.6
*/
private static NumberingSystem getInstance(String name_in, int radix_in, boolean isAlgorithmic_in, String desc_in ) {
if ( radix_in < 2 ) {
throw new IllegalArgumentException("Invalid radix for numbering system");
@ -252,7 +251,7 @@ public class NumberingSystem {
* @stable ICU 4.2
*/
public static String [] getAvailableNames() {
UResourceBundle numberingSystemsInfo = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "numberingSystems");
UResourceBundle nsCurrent = numberingSystemsInfo.get("numberingSystems");
UResourceBundle temp;
@ -269,29 +268,14 @@ public class NumberingSystem {
}
/**
* Convenience method to determine if a given digit string is valid for use as a
* Convenience method to determine if a given digit string is valid for use as a
* descriptor of a numeric ( non-algorithmic ) numbering system. In order for
* a digit string to be valid, it must meet the following criteria:
* 1. Digits must be in Unicode's basic multilingual plane.
* a digit string to be valid, it must contain exactly ten Unicode code points.
* @stable ICU 4.2
*/
public static boolean isValidDigitString(String str) {
int c;
int i = 0;
UCharacterIterator it = UCharacterIterator.getInstance(str);
it.setToStart();
while ( (c = it.nextCodePoint()) != UCharacterIterator.DONE) {
if ( UCharacter.isSupplementary(c)) { // Digits outside the BMP are not currently supported
return false;
}
i++;
}
if ( i != 10 ) {
return false;
}
return true;
int numCodepoints = str.codePointCount(0, str.length());
return (numCodepoints == 10);
}
/**
@ -327,7 +311,7 @@ public class NumberingSystem {
* Returns the numbering system's algorithmic status. If true,
* the numbering system is algorithmic and uses an RBNF formatter to
* format numerals. If false, the numbering system is numeric and
* uses a fixed set of digits.
* uses a fixed set of digits.
* @stable ICU 4.2
*/
public boolean isAlgorithmic() {

View File

@ -177,8 +177,8 @@ public class ICUCurrencyDisplayInfoProvider implements CurrencyDisplayInfoProvid
crb = crb.at(2);
if (crb != null) {
String pattern = crb.getString(0);
char separator = crb.getString(1).charAt(0);
char groupingSeparator = crb.getString(2).charAt(0);
String separator = crb.getString(1);
String groupingSeparator = crb.getString(2);
return new CurrencyFormatInfo(pattern, separator, groupingSeparator);
}
}

View File

@ -6,11 +6,11 @@
* Corporation and others. All Rights Reserved.
**/
/**
/**
* Port From: JDK 1.4b1 : java.text.Format.IntlTestDecimalFormatSymbols
* Source File: java/text/format/IntlTestDecimalFormatSymbols.java
**/
/*
@test 1.4 98/03/06
@summary test International Decimal Format Symbols
@ -19,6 +19,7 @@
package com.ibm.icu.dev.test.format;
import java.util.Arrays;
import java.util.Locale;
import org.junit.Test;
@ -42,7 +43,7 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
}
// just do some VERY basic tests to make sure that get/set work
if(!en.getLocale().equals(Locale.ENGLISH)) {
errln("ERROR: getLocale failed");
}
@ -63,8 +64,14 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
errln("ERROR: get/set ZeroDigit failed");
}
String[] digits = en.getDigitStrings();
fr.setDigitStrings(digits);
if (!Arrays.equals(fr.getDigitStrings(), en.getDigitStrings())) {
errln("ERROR: get/set DigitStrings failed");
}
char sigDigit = en.getSignificantDigit();
fr.setSignificantDigit(sigDigit);
fr.setSignificantDigit(sigDigit);
if(fr.getSignificantDigit() != en.getSignificantDigit()) {
errln("ERROR: get/set SignificantDigit failed");
}
@ -74,47 +81,83 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
if (!fr.getCurrency().equals(currency)){
errln("ERROR: get/set Currency failed");
}
char group = en.getGroupingSeparator();
fr.setGroupingSeparator(group);
if(fr.getGroupingSeparator() != en.getGroupingSeparator()) {
errln("ERROR: get/set GroupingSeparator failed");
}
String groupStr = en.getGroupingSeparatorString();
fr.setGroupingSeparatorString(groupStr);
if (!fr.getGroupingSeparatorString().equals(en.getGroupingSeparatorString())) {
errln("ERROR: get/set GroupingSeparatorString failed");
}
char decimal = en.getDecimalSeparator();
fr.setDecimalSeparator(decimal);
if(fr.getDecimalSeparator() != en.getDecimalSeparator()) {
errln("ERROR: get/set DecimalSeparator failed");
}
String decimalStr = en.getDecimalSeparatorString();
fr.setDecimalSeparatorString(decimalStr);
if (!fr.getDecimalSeparatorString().equals(en.getDecimalSeparatorString())) {
errln("ERROR: get/set DecimalSeparatorString failed");
}
char monetaryGroup = en.getMonetaryGroupingSeparator();
fr.setMonetaryGroupingSeparator(monetaryGroup);
if(fr.getMonetaryGroupingSeparator() != en.getMonetaryGroupingSeparator()) {
errln("ERROR: get/set MonetaryGroupingSeparator failed");
}
String monetaryGroupStr = en.getMonetaryGroupingSeparatorString();
fr.setMonetaryGroupingSeparatorString(monetaryGroupStr);
if (!fr.getMonetaryGroupingSeparatorString().equals(en.getMonetaryGroupingSeparatorString())){
errln("ERROR: get/set MonetaryGroupingSeparatorString failed");
}
char monetaryDecimal = en.getMonetaryDecimalSeparator();
fr.setMonetaryDecimalSeparator(monetaryDecimal);
if(fr.getMonetaryDecimalSeparator() != en.getMonetaryDecimalSeparator()) {
errln("ERROR: get/set MonetaryDecimalSeparator failed");
}
String monetaryDecimalStr = en.getMonetaryDecimalSeparatorString();
fr.setMonetaryDecimalSeparatorString(monetaryDecimalStr);
if (!fr.getMonetaryDecimalSeparatorString().equals(en.getMonetaryDecimalSeparatorString())) {
errln("ERROR: get/set MonetaryDecimalSeparatorString failed");
}
char perMill = en.getPerMill();
fr.setPerMill(perMill);
if(fr.getPerMill() != en.getPerMill()) {
errln("ERROR: get/set PerMill failed");
}
String perMillStr = en.getPerMillString();
fr.setPerMillString(perMillStr);
if (!fr.getPerMillString().equals(en.getPerMillString())) {
errln("ERROR: get/set PerMillString failed");
}
char percent = en.getPercent();
fr.setPercent(percent);
if(fr.getPercent() != en.getPercent()) {
errln("ERROR: get/set Percent failed");
}
String percentStr = en.getPercentString();
fr.setPercentString(percentStr);
if (!fr.getPercentString().equals(en.getPercentString())) {
errln("ERROR: get/set PercentString failed");
}
char digit = en.getDigit();
fr.setDigit(digit);
if(fr.getPercent() != en.getPercent()) {
errln("ERROR: get/set Percent failed");
if(fr.getDigit() != en.getDigit()) {
errln("ERROR: get/set Digit failed");
}
char patternSeparator = en.getPatternSeparator();
@ -143,12 +186,24 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
errln("ERROR: get/set MinusSign failed");
}
String minusSignStr = en.getMinusSignString();
fr.setMinusSignString(minusSignStr);
if (!fr.getMinusSignString().equals(en.getMinusSignString())) {
errln("ERROR: get/set MinusSignString failed");
}
char plusSign = en.getPlusSign();
fr.setPlusSign(plusSign);
if(fr.getPlusSign() != en.getPlusSign()) {
errln("ERROR: get/set PlusSign failed");
}
String plusSignStr = en.getPlusSignString();
fr.setPlusSignString(plusSignStr);
if (!fr.getPlusSignString().equals(en.getPlusSignString())) {
errln("ERROR: get/set PlusSignString failed");
}
char padEscape = en.getPadEscape();
fr.setPadEscape(padEscape);
if(fr.getPadEscape() != en.getPadEscape()) {
@ -160,15 +215,15 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
if(fr.getExponentSeparator() != en.getExponentSeparator()) {
errln("ERROR: get/set Exponential failed");
}
String exponentMultiplicationSign = en.getExponentMultiplicationSign();
fr.setExponentMultiplicationSign(exponentMultiplicationSign);
if(fr.getExponentMultiplicationSign() != en.getExponentMultiplicationSign()) {
errln("ERROR: get/set ExponentMultiplicationSign failed");
}
// Test CurrencySpacing.
// In CLDR 1.7, only root.txt has CurrencySpacing data. This data might
// In CLDR 1.7, only root.txt has CurrencySpacing data. This data might
// be different between en and fr in the future.
for (int i = DecimalFormatSymbols.CURRENCY_SPC_CURRENCY_MATCH; i <= DecimalFormatSymbols.CURRENCY_SPC_INSERT; i++) {
if (en.getPatternForCurrencySpacing(i, true) !=
@ -180,7 +235,7 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
}
}
}
String dash = "-";
en.setPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_INSERT, true, dash);
if (dash != en.getPatternForCurrencySpacing(DecimalFormatSymbols.CURRENCY_SPC_INSERT, true)) {
@ -195,13 +250,43 @@ public class IntlTestDecimalFormatSymbols extends com.ibm.icu.dev.test.TestFmwk
errln("ERROR: Clone failed");
}
}
@Test
public void testCoverage() {
DecimalFormatSymbols df = new DecimalFormatSymbols();
DecimalFormatSymbols df2 = (DecimalFormatSymbols)df.clone();
if (!df.equals(df2) || df.hashCode() != df2.hashCode()) {
errln("decimal format symbols clone, equals, or hashCode failed");
errln("decimal format symbols clone, equals, or hashCode failed");
}
}
@Test
public void testDigitSymbols() {
final char defZero = '0';
final char[] defDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
final String[] defDigitStrings = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
final String[] osmanyaDigitStrings = {
"\uD801\uDCA0", "\uD801\uDCA1", "\uD801\uDCA2", "\uD801\uDCA3", "\uD801\uDCA4",
"\uD801\uDCA5", "\uD801\uDCA6", "\uD801\uDCA7", "\uD801\uDCA8", "\uD801\uDCA9"
};
DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ENGLISH);
symbols.setDigitStrings(osmanyaDigitStrings);
if (!Arrays.equals(symbols.getDigitStrings(), osmanyaDigitStrings)) {
errln("ERROR: Osmanya digits (supplementary) should be set");
}
if (defZero != symbols.getZeroDigit()) {
errln("ERROR: Zero digit should be 0");
}
if (!Arrays.equals(symbols.getDigits(), defDigits)) {
errln("ERROR: Char digits should be Latin digits");
}
// Reset digits to Latin
symbols.setZeroDigit(defZero);
if (!Arrays.equals(symbols.getDigitStrings(), defDigitStrings)) {
errln("ERROR: Latin digits should be set" + symbols.getDigitStrings()[0]);
}
}
}

View File

@ -53,9 +53,9 @@ import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
public class NumberFormatTest extends TestFmwk {
private static ULocale EN = new ULocale("en");
private static Number toNumber(String s) {
if (s.equals("NaN")) {
return Double.NaN;
@ -66,13 +66,13 @@ public class NumberFormatTest extends TestFmwk {
}
return new BigDecimal(s);
}
private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU =
new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
@Override
public Character Id() { return 'J'; }
@Override
public String format(NumberFormatTestData tuple) {
DecimalFormat fmt = newDecimalFormat(tuple);
@ -126,7 +126,7 @@ public class NumberFormatTest extends TestFmwk {
}
return null;
}
@Override
public String parseCurrency(NumberFormatTestData tuple) {
DecimalFormat fmt = newDecimalFormat(tuple);
@ -272,12 +272,12 @@ public class NumberFormatTest extends TestFmwk {
}
};
private DataDrivenNumberFormatTestUtility.CodeUnderTest JDK =
new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
@Override
public Character Id() { return 'K'; }
@Override
public String format(NumberFormatTestData tuple) {
java.text.DecimalFormat fmt = newDecimalFormat(tuple);
@ -331,8 +331,8 @@ public class NumberFormatTest extends TestFmwk {
}
return null;
}
/**
* @param tuple
@ -437,7 +437,7 @@ public class NumberFormatTest extends TestFmwk {
if (tuple.localizedPattern != null) {
fmt.applyLocalizedPattern(tuple.localizedPattern);
}
// lenient parsing not supported by JDK
if (tuple.parseIntegerOnly != null) {
fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
@ -455,7 +455,7 @@ public class NumberFormatTest extends TestFmwk {
public void TestRoundingScientific10542() {
DecimalFormat format =
new DecimalFormat("0.00E0");
int[] roundingModes = {
BigDecimal.ROUND_CEILING,
BigDecimal.ROUND_DOWN,
@ -472,7 +472,7 @@ public class NumberFormatTest extends TestFmwk {
"Round half even",
"Round half up",
"Round up"};
double[] values = {-0.003006, -0.003005, -0.003004, 0.003014, 0.003015, 0.003016};
// The order of these expected values correspond to the order of roundingModes and the order of values.
String[][] expected = {
@ -1048,22 +1048,22 @@ public class NumberFormatTest extends TestFmwk {
// format result using CURRENCYSTYLE,
// format result using ISOCURRENCYSTYLE,
// format result using PLURALCURRENCYSTYLE,
{"en_US", "1", "USD", "$1.00", "USD1.00", "1.00 US dollars"},
{"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美元"},
{"zh_CN", "1234.56", "USD", "US$1,234.56", "USD1,234.56", "1,234.56美元"},
{"zh_CN", "1", "CNY", "¥1.00", "CNY1.00", "1.00人民币"},
{"zh_CN", "1234.56", "CNY", "¥1,234.56", "CNY1,234.56", "1,234.56人民币"},
{"ru_RU", "1", "RUB", "1,00 \u20BD", "1,00 RUB", "1,00 российского рубля"},
{"ru_RU", "2", "RUB", "2,00 \u20BD", "2,00 RUB", "2,00 российского рубля"},
{"ru_RU", "5", "RUB", "5,00 \u20BD", "5,00 RUB", "5,00 российского рубля"},
// test locale without currency information
{"root", "-1.23", "USD", "-US$ 1.23", "-USD 1.23", "-1.23 USD"},
{"root@numbers=latn", "-1.23", "USD", "-US$ 1.23", "-USD 1.23", "-1.23 USD"}, // ensure that the root locale is still used with modifiers
{"en_US", "1", "USD", "$1.00", "USD1.00", "1.00 US dollars"},
{"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美元"},
{"zh_CN", "1234.56", "USD", "US$1,234.56", "USD1,234.56", "1,234.56美元"},
{"zh_CN", "1", "CNY", "¥1.00", "CNY1.00", "1.00人民币"},
{"zh_CN", "1234.56", "CNY", "¥1,234.56", "CNY1,234.56", "1,234.56人民币"},
{"ru_RU", "1", "RUB", "1,00 \u20BD", "1,00 RUB", "1,00 российского рубля"},
{"ru_RU", "2", "RUB", "2,00 \u20BD", "2,00 RUB", "2,00 российского рубля"},
{"ru_RU", "5", "RUB", "5,00 \u20BD", "5,00 RUB", "5,00 российского рубля"},
// test locale without currency information
{"root", "-1.23", "USD", "-US$ 1.23", "-USD 1.23", "-1.23 USD"},
{"root@numbers=latn", "-1.23", "USD", "-US$ 1.23", "-USD 1.23", "-1.23 USD"}, // ensure that the root locale is still used with modifiers
{"root@numbers=arab", "-1.23", "USD", "\u200F-\u0661\u066B\u0662\u0663\u00A0US$", "\u200F-\u0661\u066B\u0662\u0663\u00A0USD", "\u200F-\u0661\u066B\u0662\u0663 USD"}, // ensure that the root locale is still used with modifiers
{"es_AR", "1", "INR", "INR\u00A01,00", "INR\u00A01,00", "1,00 rupia india"},
{"ar_EG", "1", "USD", "١٫٠٠\u00A0US$", "١٫٠٠\u00A0USD", "١٫٠٠ دولار أمريكي"},
{"es_AR", "1", "INR", "INR\u00A01,00", "INR\u00A01,00", "1,00 rupia india"},
{"ar_EG", "1", "USD", "١٫٠٠\u00A0US$", "١٫٠٠\u00A0USD", "١٫٠٠ دولار أمريكي"},
};
for (int i=0; i<DATA.length; ++i) {
@ -1287,7 +1287,7 @@ public class NumberFormatTest extends TestFmwk {
public void TestCompatibleCurrencies() {
NumberFormat fmt =
NumberFormat.getCurrencyInstance(Locale.US);
expectParseCurrency(fmt, Currency.getInstance(Locale.JAPAN), "\u00A51,235"); // Yen half-width
expectParseCurrency(fmt, Currency.getInstance(Locale.JAPAN), "\u00A51,235"); // Yen half-width
expectParseCurrency(fmt, Currency.getInstance(Locale.JAPAN), "\uFFE51,235"); // Yen full-wdith
}
@ -1996,7 +1996,7 @@ public class NumberFormatTest extends TestFmwk {
}
}
}
// Coverage tests for methods not being called otherwise.
@Test
public void TestNumberingSystemCoverage() {
@ -2012,18 +2012,18 @@ public class NumberFormatTest extends TestFmwk {
break;
}
}
if (!latnFound) {
errln("ERROR: 'latn' numbering system not found on NumberingSystem.getAvailableNames().");
}
}
// Test NumberingSystem.getInstance()
NumberingSystem ns1 = NumberingSystem.getInstance();
if (ns1 == null || ns1.isAlgorithmic()) {
errln("ERROR: NumberingSystem.getInstance() returned a null or invalid NumberingSystem");
}
// Test NumberingSystem.getInstance(int,boolean,String)
/* Parameters used: the ones used in the default constructor
* radix = 10;
@ -2034,7 +2034,7 @@ public class NumberFormatTest extends TestFmwk {
if (ns2 == null || ns2.isAlgorithmic()) {
errln("ERROR: NumberingSystem.getInstance(int,boolean,String) returned a null or invalid NumberingSystem");
}
// Test NumberingSystem.getInstance(Locale)
NumberingSystem ns3 = NumberingSystem.getInstance(Locale.ENGLISH);
if (ns3 == null || ns3.isAlgorithmic()) {
@ -2072,6 +2072,7 @@ public class NumberFormatTest extends TestFmwk {
this.num = inc ? 0 : 10000;
}
@Override
public void run() {
if (inc) {
while (num < 10000) {
@ -2446,19 +2447,19 @@ public class NumberFormatTest extends TestFmwk {
double amount = 35.47;
double negAmount = -34.567;
FieldPosition cp = new FieldPosition(NumberFormat.Field.CURRENCY);
StringBuffer buffer0 = new StringBuffer();
nf.format(amount, buffer0, cp);
assertEquals("$35.47", "$35.47", buffer0.toString());
assertEquals("cp begin", 0, cp.getBeginIndex());
assertEquals("cp end", 1, cp.getEndIndex());
StringBuffer buffer01 = new StringBuffer();
nf.format(negAmount, buffer01, cp);
assertEquals("-$34.57", "-$34.57", buffer01.toString());
assertEquals("cp begin", 1, cp.getBeginIndex());
assertEquals("cp end", 2, cp.getEndIndex());
assertEquals("cp end", 2, cp.getEndIndex());
nf.setCurrency(Currency.getInstance(Locale.FRANCE));
StringBuffer buffer1 = new StringBuffer();
nf.format(amount, buffer1, cp);
@ -2478,7 +2479,7 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("-CHF34.57", "-CHF34.57", buffer20.toString());
assertEquals("cp begin", 1, cp.getBeginIndex());
assertEquals("cp end", 4, cp.getEndIndex());
nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(Locale.FRANCE);
StringBuffer buffer3 = new StringBuffer();
nf.format(amount, buffer3, cp);
@ -2491,21 +2492,21 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("-34,57 ", "-34,57 ", buffer4.toString());
assertEquals("cp begin", 7, cp.getBeginIndex());
assertEquals("cp end", 8, cp.getEndIndex());
nf.setCurrency(Currency.getInstance(new Locale("fr", "ch")));
StringBuffer buffer5 = new StringBuffer();
nf.format(negAmount, buffer5, cp);
assertEquals("-34,57 CHF", "-34,57 CHF", buffer5.toString());
assertEquals("cp begin", 7, cp.getBeginIndex());
assertEquals("cp end", 10, cp.getEndIndex());
NumberFormat plCurrencyFmt = NumberFormat.getInstance(new Locale("fr", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer6 = new StringBuffer();
plCurrencyFmt.format(negAmount, buffer6, cp);
assertEquals("-34.57 francs suisses", "-34.57 francs suisses", buffer6.toString());
assertEquals("cp begin", 7, cp.getBeginIndex());
assertEquals("cp end", 21, cp.getEndIndex());
assertEquals("cp end", 21, cp.getEndIndex());
// Positive value with PLURALCURRENCYSTYLE.
plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer7 = new StringBuffer();
@ -2513,7 +2514,7 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("35.47スイス フラン", "35.47スイス フラン", buffer7.toString());
assertEquals("cp begin", 5, cp.getBeginIndex());
assertEquals("cp end", 12, cp.getEndIndex());
// PLURALCURRENCYSTYLE for non-ASCII.
plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "de"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer8 = new StringBuffer();
@ -2521,7 +2522,7 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("-34.57ユーロ", "-34.57ユーロ", buffer8.toString());
assertEquals("cp begin", 6, cp.getBeginIndex());
assertEquals("cp end", 9, cp.getEndIndex());
nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(Locale.JAPAN);
nf.setCurrency(Currency.getInstance(new Locale("ja", "jp")));
StringBuffer buffer9 = new StringBuffer();
@ -2529,7 +2530,7 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("-¥35", "-¥35", buffer9.toString());
assertEquals("cp begin", 1, cp.getBeginIndex());
assertEquals("cp end", 2, cp.getEndIndex());
// Negative value with PLURALCURRENCYSTYLE.
plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer10 = new StringBuffer();
@ -2537,7 +2538,7 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("-34.57スイス フラン", "-34.57スイス フラン", buffer10.toString());
assertEquals("cp begin", 6, cp.getBeginIndex());
assertEquals("cp end", 13, cp.getEndIndex());
// Nagative value with PLURALCURRENCYSTYLE, Arabic digits.
nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(new Locale("ar", "eg"));
plCurrencyFmt = NumberFormat.getInstance(new Locale("ar", "eg"), NumberFormat.PLURALCURRENCYSTYLE);
@ -2547,7 +2548,7 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("cp begin", 8, cp.getBeginIndex());
assertEquals("cp end", 17, cp.getEndIndex());
}
@Test
public void TestRounding() {
DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
@ -2941,7 +2942,7 @@ public class NumberFormatTest extends TestFmwk {
ParsePosition pos = new ParsePosition(0);
CurrencyAmount currencyAmount = fmt.parseCurrency(text, pos);
assertTrue("Parse of " + text + " should have succeeded.", pos.getIndex() > 0);
assertEquals("Currency should be correct.", expected, currencyAmount.getCurrency());
assertEquals("Currency should be correct.", expected, currencyAmount.getCurrency());
}
@Test
@ -3379,7 +3380,7 @@ public class NumberFormatTest extends TestFmwk {
result = rbf.format(new BigDecimal(2000.43), sb, new FieldPosition(0)).toString();
if (!"two thousand point four three".equals(result)) {
errln("DecimalFormat failed. Expected: 'two thousand point four three' - Actual: '" + result + "'");
}
}
}
/*
@ -3900,6 +3901,7 @@ public class NumberFormatTest extends TestFmwk {
this.result = result;
}
@Override
public void run() {
for (int i = 0; i < result.length; i++) {
AttributedCharacterIterator acitr = fmt.formatToCharacterIterator(num);
@ -4001,7 +4003,7 @@ public class NumberFormatTest extends TestFmwk {
": ArithmeticException must be thrown, but got formatted result: " +
s);
} else {
assertEquals("Test case #" + testNum, (String)testCase[5], s);
assertEquals("Test case #" + testNum, testCase[5], s);
}
}
@ -4067,7 +4069,7 @@ public class NumberFormatTest extends TestFmwk {
numberFormat.setSignificantDigitsUsed(false);
numberFormat.setMaximumSignificantDigits(6);
assertTrue("", numberFormat.areSignificantDigitsUsed());
assertTrue("", numberFormat.areSignificantDigitsUsed());
}
@Test
@ -4177,7 +4179,7 @@ public class NumberFormatTest extends TestFmwk {
new SignsAndMarksItem("en@numbers=arabext", false, "\u200E-\u200E\u06F6\u06F7", -67 ),
new SignsAndMarksItem("en@numbers=arabext", true, "\u200E-\u200E\u06F6\u06F7", -67 ),
new SignsAndMarksItem("en@numbers=arabext", true, "\u200E-\u200E \u06F6\u06F7", -67 ), // ***
new SignsAndMarksItem("he", false, "12", 12 ),
new SignsAndMarksItem("he", true, "12", 12 ),
new SignsAndMarksItem("he", false, "-23", -23 ),
@ -4298,7 +4300,7 @@ public class NumberFormatTest extends TestFmwk {
expect(fmtAccount, num, fmtAccountExpected, rt);
}
}
@Test
public void TestCurrencyUsage() {
// the 1st one is checking setter/getter, while the 2nd one checks for getInstance
@ -4379,41 +4381,41 @@ public class NumberFormatTest extends TestFmwk {
@Test
public void TestParseRequiredDecimalPoint() {
String[] testPattern = { "00.####", "00.0", "00" };
String value2Parse = "99";
double parseValue = 99;
DecimalFormat parser = new DecimalFormat();
double result;
boolean hasDecimalPoint;
for (int i = 0; i < testPattern.length; i++) {
boolean hasDecimalPoint;
for (int i = 0; i < testPattern.length; i++) {
parser.applyPattern(testPattern[i]);
hasDecimalPoint = testPattern[i].contains(".");
parser.setDecimalPatternMatchRequired(false);
try {
result = parser.parse(value2Parse).doubleValue();
assertEquals("wrong parsed value", parseValue, result);
} catch (ParseException e) {
TestFmwk.errln("Parsing " + value2Parse + " should have succeeded with " + testPattern[i] +
TestFmwk.errln("Parsing " + value2Parse + " should have succeeded with " + testPattern[i] +
" and isDecimalPointMatchRequired set to: " + parser.isDecimalPatternMatchRequired());
}
parser.setDecimalPatternMatchRequired(true);
try {
result = parser.parse(value2Parse).doubleValue();
if(hasDecimalPoint){
TestFmwk.errln("Parsing " + value2Parse + " should NOT have succeeded with " + testPattern[i] +
TestFmwk.errln("Parsing " + value2Parse + " should NOT have succeeded with " + testPattern[i] +
" and isDecimalPointMatchRequired set to: " + parser.isDecimalPatternMatchRequired());
}
} catch (ParseException e) {
// OK, should fail
}
}
}
//TODO(junit): investigate
@Test
@ -4457,7 +4459,7 @@ public class NumberFormatTest extends TestFmwk {
// For each test, add assert that all the fields are present and in the right spot.
// TODO: Add tests for identify and position of each field, as in IntlTestDecimalFormatAPIC.
DecimalFormat dfDE = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.GERMANY);
String strDE = dfDE.format(number);
Set<AttributedCharacterIterator.Attribute> resultDE = dfDE.formatToCharacterIterator(number).getAllAttributeKeys();
@ -4511,7 +4513,7 @@ public class NumberFormatTest extends TestFmwk {
StringBuffer result = new StringBuffer("");
df.setRoundingMode(BigDecimal.ROUND_UNNECESSARY);
df.applyPattern("00.0#E0");
try {
df.format(99999.0, result, new FieldPosition(0));
fail("Missing ArithmeticException for double: " + result);
@ -4539,7 +4541,7 @@ public class NumberFormatTest extends TestFmwk {
} catch (ArithmeticException expected) {
// The exception should be thrown, since rounding is needed.
}
try {
result = df.format(new BigDecimal("-99999"), result, new FieldPosition(0));
fail("Missing ArithmeticException for BigDecimal: " + result);
@ -4885,4 +4887,18 @@ public class NumberFormatTest extends TestFmwk {
checkFormatWithField("exponent", fmtPosNegSign, negativeExp, negExpFormatted,
NumberFormat.Field.EXPONENT, 9, 11);
}
@Test
public void TestStringSymbols() {
DecimalFormatSymbols symbols = new DecimalFormatSymbols(ULocale.US);
String[] customDigits = {"(0)", "(1)", "(2)", "(3)", "(4)", "(5)", "(6)", "(7)", "(8)", "(9)"};
symbols.setDigitStrings(customDigits);
symbols.setDecimalSeparatorString("~~");
symbols.setGroupingSeparatorString("^^");
DecimalFormat fmt = new DecimalFormat("#,##0.0#", symbols);
expect2(fmt, 1234567.89, "(1)^^(2)(3)(4)^^(5)(6)(7)~~(8)(9)");
}
}