// In J ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4)
\u00a4\u00a4 **####0.00 433.0 EUR *433,00 JK
// In J ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4)
-// S fails this one because the test code bypasses CurrencyUsage
-\u00a4\u00a4 **#######0 433.0 EUR *433,00 JKS
+// S and Q fail this one because the test code bypasses CurrencyUsage
+\u00a4\u00a4 **#######0 433.0 EUR *433,00 JKSQ
test padding and currencies
begin
// JDK gives E0 instead of allowing for unlimited precision
// S obeys the maximum integer digits and returns .299792458E9
0 0 0 0 2.99792458E8 KS
-// JDK and S give .299792E9
-0 1 0 5 2.9979E8 KS
+// JDK and S give .299792E9; Q gives 2.99792E8
+0 1 0 5 2.9979E8 KSQ
// JDK gives 300E6
0 3 0 0 299.792458E6 K
// JDK gives 299.8E6 (maybe maxInt + maxFrac instead of minInt + maxFrac)?
// JDK gives E0
// S obeys the maximum integer digits
0 0 1 0 2.99792458E8 KS
-// JDK gives .2998E9
-0 0 0 4 2.998E8 KS
+// JDK and S give .2998E9
+0 0 0 4 2.998E8 KSQ
// S correctly formats this as 29.979246E7.
// JDK uses 8 + 6 for significant digits instead of 2 + 6
// J and C return 2.9979246E8.
-2 8 1 6 29.979246E7 CJK
+// TODO: Merge trunk
+2 8 1 6 29.979246E7 CJKQ
// Treat max int digits > 8 as being the same as min int digits.
// This behavior is not spelled out in the specification.
// JDK fails here because it tries to use 9 + 6 = 15 sig digits.
format maxIntegerDigits output breaks
123 1 3
0 0 0
-// S ignores max integer if it is less than zero and prints "123"
-123 -2147483648 0 S
+// S and Q ignore max integer if it is less than zero and prints "123"
+123 -2147483648 0 SQ
12345 1 5
-12345 -2147483648 0 S
+12345 -2147483648 0 SQ
5.3 1 5.3
-5.3 -2147483648 .3 S
+5.3 -2147483648 .3 SQ
test patterns with zero
set locale en
1,2345,6789 4
1,23,45,6789 4 K 2
1,23,45,6789 4 K 2 2
+// Q only supports minGrouping<=2
123,456789 6 6 3
-123456789 6 JK 6 4
+123456789 6 JKQ 6 4
test multiplier setters
set locale en_US
format multiplier output breaks
23 -12 -276
23 -1 -23
-// ICU4J and JDK throw exception on zero multiplier. ICU4C does not.
-23 0 23 JKS
+// ICU4J and JDK throw exception on zero multiplier.
+// ICU4C and S print 23.
+// Q multiplies by zero and prints 0.
+23 0 0 CJKS
23 1 23
23 12 276
-23 12 -276
format output breaks
-0.35 -0.25 K
0.35 0.25 K
-0.39 0.5 K
-0.62 0.5 K
+// Q doesn't support mixing minFrac with roundingIncrement (prints 0.50).
+0.39 0.5 KQ
+0.62 0.5 KQ
0.63 0.75 K
test padding setters
test rounding mode setters
set locale en_US
-set pattern 0.#
-set roundingIncrement 0.5
+set pattern 0.5
begin
format roundingMode output breaks
-1.24 halfUp 1 K
+1.24 halfUp 1.0 K
1.25 halfUp 1.5 K
-1.25 halfDown 1 K
+1.25 halfDown 1.0 K
1.26 halfDown 1.5 K
-1.25 halfEven 1 K
+1.25 halfEven 1.0 K
-1.01 up -1.5 K
--1.49 down -1 K
+-1.49 down -1.0 K
1.01 up 1.5 K
-1.49 down 1 K
--1.01 ceiling -1
+1.49 down 1.0 K
+-1.01 ceiling -1.0
-1.49 floor -1.5
test currency usage setters
set currency USD
begin
pattern format output breaks
-# 123 123 S
+# 123 123 SQ
// Currency rounding should always override the pattern.
// K prints the currency in ISO format for some reason.
\u00a4# 123 $123.00 K
format output breaks
Inf [\u221e]
-Inf (\u221e) K
-NaN NaN K
+// Q prints the affixes
+NaN NaN KQ
test nan and infinity with multiplication
set locale en
Inf afterPrefix $$$ \u221e$ K
Inf beforeSuffix $$$\u221e $ K
Inf afterSuffix $$$\u221e$ K
-NaN beforePrefix NaN K
-NaN afterPrefix NaN K
-NaN beforeSuffix NaN K
-NaN afterSuffix NaN K
+// Q gets $$$NaN$
+NaN beforePrefix NaN KQ
+NaN afterPrefix NaN KQ
+NaN beforeSuffix NaN KQ
+NaN afterSuffix NaN KQ
test apply formerly localized patterns
begin
begin
format output breaks
// C and J get "1"
+// Q gets "1.0"
// K gets "1.1" (??)
-0.975 0.98 CJK
+0.975 0.98 CJKQ
test lenient parse currency match
// This test is for #13112
return output.length() - startLength;
}
+ /** Version of {@link #escape} that returns a String. */
+ public static String escape(CharSequence input) {
+ StringBuilder sb = new StringBuilder();
+ escape(input, sb);
+ return sb.toString();
+ }
+
/**
* Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", and "‰"
* with their localized equivalents. Replaces "¤", "¤¤", and "¤¤¤" with the three argument
@Override
public void multiplyBy(BigDecimal multiplicand) {
+ if (isInfinite() || isZero() || isNaN()) {
+ return;
+ }
BigDecimal temp = toBigDecimal();
temp = temp.multiply(multiplicand);
setToBigDecimal(temp);
* @param n The value to consume.
*/
@Override
-public void setToBigDecimal(BigDecimal n) {
+ public void setToBigDecimal(BigDecimal n) {
setBcdToZero();
flags = 0;
if (n.signum() == -1) {
* Gets the prefix string associated with this modifier, defined as the string that will be
* inserted at leftIndex when {@link #apply} is called.
*
+ * <p>TODO: Change this to appendPrefixTo(), or remove it entirely and do something different at
+ * the call sites.
+ *
* @return The prefix string. Will not be null.
*/
public String getPrefix();
* Gets the prefix string associated with this modifier, defined as the string that will be
* inserted at rightIndex when {@link #apply} is called.
*
+ * <p>TODO: Change this to appendPrefixTo(), or remove it entirely and do something different at
+ * the call sites.
+ *
* @return The suffix string. Will not be null.
*/
public String getSuffix();
/**
* A starter implementation with defaults for some of the basic methods.
*
- * <p>Implements {@link PositiveNegativeModifier} only so that instances of this class can be used when
- * a {@link PositiveNegativeModifier} is required.
+ * <p>Implements {@link PositiveNegativeModifier} only so that instances of this class can be used
+ * when a {@link PositiveNegativeModifier} is required.
*/
public abstract static class BaseModifier extends Format.BeforeFormat
implements Modifier, PositiveNegativeModifier {
import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition;
import com.ibm.icu.text.DecimalFormatSymbols;
+import newapi.impl.AffixPatternProvider;
+
/**
* Handles parsing and creation of the compact pattern string representation of a decimal format.
*/
if (!ignoreRounding) {
properties.setMinimumFractionDigits(minFrac);
properties.setMaximumFractionDigits(positive.maximumFractionDigits);
- properties.setRoundingIncrement(positive.rounding.toBigDecimal());
+ properties.setRoundingIncrement(
+ positive.rounding.toBigDecimal().setScale(positive.minimumFractionDigits));
} else {
properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACTION_DIGITS);
properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACTION_DIGITS);
}
// Compute the affix patterns (required for both padding and affixes)
- String posPrefix = ppr.getString(LdmlPatternInfo.PatternParseResult.POS_PREFIX);
- String posSuffix = ppr.getString(LdmlPatternInfo.PatternParseResult.POS_SUFFIX);
+ String posPrefix = ppr.getString(AffixPatternProvider.Flags.PREFIX);
+ String posSuffix = ppr.getString(0);
// Padding settings
if (positive.paddingEndpoints != 0) {
+ AffixPatternUtils.unescapedLength(posPrefix)
+ AffixPatternUtils.unescapedLength(posSuffix);
properties.setFormatWidth(paddingWidth);
- String rawPaddingString = ppr.getString(LdmlPatternInfo.PatternParseResult.POS_PADDING);
+ String rawPaddingString = ppr.getString(AffixPatternProvider.Flags.PADDING);
if (rawPaddingString.length() == 1) {
properties.setPadString(rawPaddingString);
} else if (rawPaddingString.length() == 2) {
properties.setPositivePrefixPattern(posPrefix);
properties.setPositiveSuffixPattern(posSuffix);
if (negative != null) {
- properties.setNegativePrefixPattern(ppr.getString(LdmlPatternInfo.PatternParseResult.NEG_PREFIX));
- properties.setNegativeSuffixPattern(ppr.getString(LdmlPatternInfo.PatternParseResult.NEG_SUFFIX));
+ properties.setNegativePrefixPattern(
+ ppr.getString(
+ AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN | AffixPatternProvider.Flags.PREFIX));
+ properties.setNegativeSuffixPattern(
+ ppr.getString(AffixPatternProvider.Flags.NEGATIVE_SUBPATTERN));
} else {
properties.setNegativePrefixPattern(null);
properties.setNegativeSuffixPattern(null);
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.CurrencyPluralInfo;
-import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
import com.ibm.icu.text.MeasureFormat.FormatWidth;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.Currency;
private transient RoundingMode roundingMode;
private transient int secondaryGroupingSize;
private transient boolean signAlwaysShown;
- private transient SignificantDigitsMode significantDigitsMode;
/*--------------------------------------------------------------------------------------------+/
/| IMPORTANT! |/
roundingMode = DEFAULT_ROUNDING_MODE;
secondaryGroupingSize = DEFAULT_SECONDARY_GROUPING_SIZE;
signAlwaysShown = DEFAULT_SIGN_ALWAYS_SHOWN;
- significantDigitsMode = DEFAULT_SIGNIFICANT_DIGITS_MODE;
return this;
}
roundingMode = other.roundingMode;
secondaryGroupingSize = other.secondaryGroupingSize;
signAlwaysShown = other.signAlwaysShown;
- significantDigitsMode = other.significantDigitsMode;
return this;
}
eq = eq && _equalsHelper(roundingMode, other.roundingMode);
eq = eq && _equalsHelper(secondaryGroupingSize, other.secondaryGroupingSize);
eq = eq && _equalsHelper(signAlwaysShown, other.signAlwaysShown);
- eq = eq && _equalsHelper(significantDigitsMode, other.significantDigitsMode);
return eq;
}
hashCode ^= _hashCodeHelper(roundingMode);
hashCode ^= _hashCodeHelper(secondaryGroupingSize);
hashCode ^= _hashCodeHelper(signAlwaysShown);
- hashCode ^= _hashCodeHelper(significantDigitsMode);
return hashCode;
}
return signAlwaysShown;
}
- @Override
- public SignificantDigitsMode getSignificantDigitsMode() {
- return significantDigitsMode;
- }
-
@Override
public int hashCode() {
return _hashCode();
return this;
}
- @Override
- public Properties setSignificantDigitsMode(SignificantDigitsMode significantDigitsMode) {
- this.significantDigitsMode = significantDigitsMode;
- return this;
- }
-
@Override
public String toString() {
StringBuilder result = new StringBuilder();
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.CompactDecimalFormat.CompactType;
-import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.NumberingSystem;
private static final int DEFAULT_MIN_SIG = 1;
private static final int DEFAULT_MAX_SIG = 2;
- private static final SignificantDigitsMode DEFAULT_SIG_MODE =
- SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION;
private static final ThreadLocal<Properties> threadLocalProperties =
new ThreadLocal<Properties>() {
if (rounder == null) {
int _minSig = properties.getMinimumSignificantDigits();
int _maxSig = properties.getMaximumSignificantDigits();
- SignificantDigitsMode _mode = properties.getSignificantDigitsMode();
Properties rprops = threadLocalProperties.get().clear();
// Settings needing possible override:
rprops.setMinimumSignificantDigits(_minSig > 0 ? _minSig : DEFAULT_MIN_SIG);
rprops.setMaximumSignificantDigits(_maxSig > 0 ? _maxSig : DEFAULT_MAX_SIG);
- rprops.setSignificantDigitsMode(_mode != null ? _mode : DEFAULT_SIG_MODE);
// TODO: Should copyFrom() be used instead? It requires a cast.
// Settings to copy verbatim:
rprops.setRoundingMode(properties.getRoundingMode());
private final Field field;
private final boolean strong;
+ private final int prefixLength;
+ private final int suffixOffset;
+ private final int suffixLength;
+
+ private static final int ARG_NUM_LIMIT = 0x100;
+
/** Creates a modifier that uses the SimpleFormatter string formats. */
public SimpleModifier(String compiledPattern, Field field, boolean strong) {
this.compiledPattern = (compiledPattern == null) ? "\u0001\u0000" : compiledPattern;
this.field = field;
this.strong = strong;
+
+ assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 1;
+ if (compiledPattern.charAt(1) != '\u0000') {
+ prefixLength = compiledPattern.charAt(1) - ARG_NUM_LIMIT;
+ suffixOffset = 3 + prefixLength;
+ } else {
+ prefixLength = 0;
+ suffixOffset = 2;
+ }
+ if (3 + prefixLength < compiledPattern.length()) {
+ suffixLength = compiledPattern.charAt(suffixOffset) - ARG_NUM_LIMIT;
+ } else {
+ suffixLength = 0;
+ }
}
@Override
public int apply(NumberStringBuilder output, int leftIndex, int rightIndex) {
- return formatAsPrefixSuffix(compiledPattern, output, leftIndex, rightIndex, field);
+ return formatAsPrefixSuffix(output, leftIndex, rightIndex, field);
}
@Override
@Override
public String getPrefix() {
- // TODO: Implement this when MeasureFormat is ready.
- throw new UnsupportedOperationException();
+ if (prefixLength == 0) {
+ return "";
+ }
+ return compiledPattern.substring(2, 2 + prefixLength);
}
@Override
public String getSuffix() {
- // TODO: Implement this when MeasureFormat is ready.
- throw new UnsupportedOperationException();
+ if (suffixLength == 0) {
+ return "";
+ }
+ return compiledPattern.substring(1 + suffixOffset, 1 + suffixOffset + suffixLength);
}
/**
*
* <p>This is well-defined only for patterns with exactly one argument.
*
- * @param compiledPattern Compiled form of a pattern string.
* @param result The StringBuilder containing the value argument.
* @param startIndex The left index of the value within the string builder.
* @param endIndex The right index of the value within the string builder.
* @return The number of characters (UTF-16 code points) that were added to the StringBuilder.
*/
- public static int formatAsPrefixSuffix(
- String compiledPattern,
- NumberStringBuilder result,
- int startIndex,
- int endIndex,
- Field field) {
+ public int formatAsPrefixSuffix(
+ NumberStringBuilder result, int startIndex, int endIndex, Field field) {
assert SimpleFormatterImpl.getArgumentLimit(compiledPattern) == 1;
- int ARG_NUM_LIMIT = 0x100;
- int length = 0, offset = 2;
- if (compiledPattern.charAt(1) != '\u0000') {
- int prefixLength = compiledPattern.charAt(1) - ARG_NUM_LIMIT;
- if (result != null) {
- result.insert(startIndex, compiledPattern, 2, 2 + prefixLength, field);
- }
- length += prefixLength;
- offset = 3 + prefixLength;
+ if (prefixLength > 0) {
+ result.insert(startIndex, compiledPattern, 2, 2 + prefixLength, field);
}
- if (offset < compiledPattern.length()) {
- int suffixLength = compiledPattern.charAt(offset) - ARG_NUM_LIMIT;
- if (result != null) {
- result.insert(
- endIndex + length, compiledPattern, offset + 1, offset + suffixLength + 1, field);
- }
- length += suffixLength;
+ if (suffixLength > 0) {
+ result.insert(
+ endIndex + prefixLength,
+ compiledPattern,
+ 1 + suffixOffset,
+ 1 + suffixOffset + suffixLength,
+ field);
}
- return length;
+ return prefixLength + suffixLength;
}
/** TODO: Move this to a test file somewhere, once we figure out what to do with the method. */
SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, new StringBuilder(), 1, 1);
NumberStringBuilder output = new NumberStringBuilder();
output.append((String) outputs[j][0], null);
- formatAsPrefixSuffix(
- compiledPattern, output, (Integer) outputs[j][1], (Integer) outputs[j][2], null);
+ new SimpleModifier(compiledPattern, null, false)
+ .apply(output, (Integer) outputs[j][1], (Integer) outputs[j][2]);
String expected = expecteds[j][i];
String actual = output.toString();
assert expected.equals(actual);
import com.ibm.icu.impl.number.FormatQuantity;
import com.ibm.icu.impl.number.Properties;
import com.ibm.icu.impl.number.Rounder;
-import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
public class SignificantDigitsRounder extends Rounder {
* @return The property bag, for chaining.
*/
public IProperties setMaximumSignificantDigits(int maximumSignificantDigits);
-
- static SignificantDigitsMode DEFAULT_SIGNIFICANT_DIGITS_MODE = null;
-
- /** @see #setSignificantDigitsMode */
- public SignificantDigitsMode getSignificantDigitsMode();
-
- /**
- * Sets the strategy used when reconciling significant digits versus integer and fraction
- * lengths.
- *
- * @param significantDigitsMode One of the options from {@link SignificantDigitsMode}.
- * @return The property bag, for chaining.
- */
- public IProperties setSignificantDigitsMode(SignificantDigitsMode significantDigitsMode);
}
public static boolean useSignificantDigits(IProperties properties) {
return properties.getMinimumSignificantDigits()
!= IProperties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS
|| properties.getMaximumSignificantDigits()
- != IProperties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS
- || properties.getSignificantDigitsMode() != IProperties.DEFAULT_SIGNIFICANT_DIGITS_MODE;
+ != IProperties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS;
}
public static SignificantDigitsRounder getInstance(IProperties properties) {
private final int minSig;
private final int maxSig;
- private final SignificantDigitsMode mode;
private SignificantDigitsRounder(IProperties properties) {
super(properties);
int _maxSig = properties.getMaximumSignificantDigits();
minSig = _minSig < 1 ? 1 : _minSig > 1000 ? 1000 : _minSig;
maxSig = _maxSig < 0 ? 1000 : _maxSig < minSig ? minSig : _maxSig > 1000 ? 1000 : _maxSig;
- SignificantDigitsMode _mode = properties.getSignificantDigitsMode();
- mode = _mode == null ? SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION : _mode;
}
@Override
magMaxSig = effectiveMag - maxSig;
// Step 1: pick the rounding magnitude and apply.
- int roundingMagnitude;
- switch (mode) {
- case OVERRIDE_MAXIMUM_FRACTION:
- // Always round to maxSig.
- // Of the six possible orders:
- // Case 1: minSig, maxSig, minFrac, maxFrac -- maxSig wins
- // Case 2: minSig, minFrac, maxSig, maxFrac -- maxSig wins
- // Case 3: minSig, minFrac, maxFrac, maxSig -- maxSig wins
- // Case 4: minFrac, minSig, maxSig, maxFrac -- maxSig wins
- // Case 5: minFrac, minSig, maxFrac, maxSig -- maxSig wins
- // Case 6: minFrac, maxFrac, minSig, maxSig -- maxSig wins
- roundingMagnitude = magMaxSig;
- break;
- case RESPECT_MAXIMUM_FRACTION:
- // Round to the strongest of maxFrac, maxInt, and maxSig.
- // Of the six possible orders:
- // Case 1: minSig, maxSig, minFrac, maxFrac -- maxSig wins
- // Case 2: minSig, minFrac, maxSig, maxFrac -- maxSig wins
- // Case 3: minSig, minFrac, maxFrac, maxSig -- maxFrac wins --> differs from default
- // Case 4: minFrac, minSig, maxSig, maxFrac -- maxSig wins
- // Case 5: minFrac, minSig, maxFrac, maxSig -- maxFrac wins --> differs from default
- // Case 6: minFrac, maxFrac, minSig, maxSig -- maxFrac wins --> differs from default
- //
- // Math.max() picks the rounding magnitude farthest to the left (most significant).
- // Math.min() picks the rounding magnitude farthest to the right (least significant).
- roundingMagnitude = Math.max(-maxFrac, magMaxSig);
- break;
- case ENSURE_MINIMUM_SIGNIFICANT:
- // Round to the strongest of maxFrac and maxSig, and always ensure minSig.
- // Of the six possible orders:
- // Case 1: minSig, maxSig, minFrac, maxFrac -- maxSig wins
- // Case 2: minSig, minFrac, maxSig, maxFrac -- maxSig wins
- // Case 3: minSig, minFrac, maxFrac, maxSig -- maxFrac wins --> differs from default
- // Case 4: minFrac, minSig, maxSig, maxFrac -- maxSig wins
- // Case 5: minFrac, minSig, maxFrac, maxSig -- maxFrac wins --> differs from default
- // Case 6: minFrac, maxFrac, minSig, maxSig -- minSig wins --> differs from default
- roundingMagnitude = Math.min(magMinSig, Math.max(-maxFrac, magMaxSig));
- break;
- default:
- throw new AssertionError();
- }
+ int roundingMagnitude = magMaxSig;
input.roundToMagnitude(roundingMagnitude, mathContext);
// In case magnitude changed:
magMaxSig = effectiveMag - maxSig;
// Step 2: pick the number of visible digits.
- switch (mode) {
- case OVERRIDE_MAXIMUM_FRACTION:
- // Ensure minSig is always displayed.
- input.setIntegerLength(minInt, maxInt);
- input.setFractionLength(Math.max(minFrac, -magMinSig), Integer.MAX_VALUE);
- break;
- case RESPECT_MAXIMUM_FRACTION:
- // Ensure minSig is displayed, unless doing so is in violation of maxFrac.
- input.setIntegerLength(minInt, maxInt);
- input.setFractionLength(Math.min(maxFrac, Math.max(minFrac, -magMinSig)), maxFrac);
- break;
- case ENSURE_MINIMUM_SIGNIFICANT:
- // Follow minInt/minFrac, but ensure all digits are allowed to be visible.
- input.setIntegerLength(minInt, maxInt);
- input.setFractionLength(minFrac, Integer.MAX_VALUE);
- break;
- }
+ input.setIntegerLength(minInt, maxInt);
+ input.setFractionLength(Math.max(minFrac, -magMinSig), Integer.MAX_VALUE);
}
@Override
super.export(properties);
properties.setMinimumSignificantDigits(minSig);
properties.setMaximumSignificantDigits(maxSig);
- properties.setSignificantDigitsMode(mode);
}
}
import java.text.ParsePosition;
import java.util.Locale;
-import com.ibm.icu.impl.number.PatternString;
import com.ibm.icu.impl.number.Properties;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
* @param style the compact style
*/
CompactDecimalFormat(ULocale locale, CompactStyle style) {
- // Use the locale's default pattern
- String pattern = getPattern(locale, 0);
+ // Minimal properties: let the non-shim code path do most of the logic for us.
symbols = DecimalFormatSymbols.getInstance(locale);
properties = new Properties();
properties.setCompactStyle(style);
+ properties.setGroupingSize(-2); // do not forward grouping information
+ properties.setMinimumGroupingDigits(2);
exportedProperties = new Properties();
- setPropertiesFromPattern(pattern, PatternString.IGNORE_ROUNDING_ALWAYS);
- if (style == CompactStyle.SHORT) {
- // TODO: This was setGroupingUsed(false) in ICU 58. Is it okay that I changed it for ICU 59?
- properties.setMinimumGroupingDigits(2);
- }
refreshFormatter();
}
import java.text.ParseException;
import java.text.ParsePosition;
-import com.ibm.icu.impl.number.Endpoint;
-import com.ibm.icu.impl.number.Format.SingularFormat;
-import com.ibm.icu.impl.number.FormatQuantity4;
import com.ibm.icu.impl.number.Parse;
import com.ibm.icu.impl.number.PatternString;
import com.ibm.icu.impl.number.Properties;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
+import newapi.NumberFormatter.LocalizedNumberFormatter;
+import newapi.NumberFormatter.NumberFormatterResult;
+import newapi.impl.MacroProps;
+import newapi.impl.NumberFormatterImpl;
+import newapi.impl.NumberPropertyMapper;
+
/**
* {@icuenhanced java.text.DecimalFormat}.{@icu _usage_} <code>DecimalFormat</code> is the primary
* concrete subclass of {@link NumberFormat}. It has a variety of features designed to make it
* digits are shown. This is most common in scientific notation.
* </ol>
*
- * <p>It is possible to specify both a magnitude and a number of significant digits. If used
- * together, the <em>significant digits mode</em> determines how conflicts between fraction digits
- * and signiciant digits are resolved. For more information, see {@link #setSignificantDigitsMode}.
+ * <p>It is not possible to specify more than one rounding strategy. For example, setting a rounding
+ * increment in conjunction with significant digits results in undefined behavior.
*
* <p>It is also possible to specify the <em>rounding mode</em> to use. The default rounding mode is
* "half even", which rounds numbers to their closest increment, with ties broken in favor of
* #format} method uses the formatter directly without needing to synchronize. Volatile because
* threads may read and write at the same time.
*/
- transient volatile SingularFormat formatter;
+ transient volatile LocalizedNumberFormatter formatter;
/**
* The effective properties as exported from the formatter object. Volatile because threads may
*/
@Override
public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
- FormatQuantity4 fq = new FormatQuantity4(number);
- formatter.format(fq, result, fieldPosition);
- fq.populateUFieldPosition(fieldPosition);
+ NumberFormatterResult output = formatter.format(number);
+ output.populateFieldPosition(fieldPosition, result.length());
+ output.appendTo(result);
return result;
}
*/
@Override
public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
- FormatQuantity4 fq = new FormatQuantity4(number);
- formatter.format(fq, result, fieldPosition);
- fq.populateUFieldPosition(fieldPosition);
+ NumberFormatterResult output = formatter.format(number);
+ output.populateFieldPosition(fieldPosition, result.length());
+ output.appendTo(result);
return result;
}
*/
@Override
public StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition) {
- FormatQuantity4 fq = new FormatQuantity4(number);
- formatter.format(fq, result, fieldPosition);
- fq.populateUFieldPosition(fieldPosition);
+ NumberFormatterResult output = formatter.format(number);
+ output.populateFieldPosition(fieldPosition, result.length());
+ output.appendTo(result);
return result;
}
@Override
public StringBuffer format(
java.math.BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
- FormatQuantity4 fq = new FormatQuantity4(number);
- formatter.format(fq, result, fieldPosition);
- fq.populateUFieldPosition(fieldPosition);
+ NumberFormatterResult output = formatter.format(number);
+ output.populateFieldPosition(fieldPosition, result.length());
+ output.appendTo(result);
return result;
}
*/
@Override
public StringBuffer format(BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
- FormatQuantity4 fq = new FormatQuantity4(number.toBigDecimal());
- formatter.format(fq, result, fieldPosition);
- fq.populateUFieldPosition(fieldPosition);
+ NumberFormatterResult output = formatter.format(number);
+ output.populateFieldPosition(fieldPosition, result.length());
+ output.appendTo(result);
return result;
}
public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
if (!(obj instanceof Number)) throw new IllegalArgumentException();
Number number = (Number) obj;
- FormatQuantity4 fq = new FormatQuantity4(number);
- AttributedCharacterIterator result = formatter.formatToCharacterIterator(fq);
- return result;
+ NumberFormatterResult output = formatter.format(number);
+ return output.toAttributedCharacterIterator();
}
/**
*/
@Override
public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) {
- // TODO: This is ugly (although not as ugly as it was in ICU 58).
- // Currency should be a free parameter, not in property bag. Fix in ICU 60.
- Properties cprops = threadLocalProperties.get();
- SingularFormat fmt = null;
- synchronized (this) {
- // Use the pre-compiled formatter if possible. Otherwise, copy the properties
- // and build our own formatter.
- // TODO: Consider using a static format path here.
- if (currAmt.getCurrency().equals(properties.getCurrency())) {
- fmt = formatter;
- } else {
- cprops.copyFrom(properties);
- }
- }
- if (fmt == null) {
- cprops.setCurrency(currAmt.getCurrency());
- fmt = Endpoint.fromBTA(cprops, symbols);
- }
- FormatQuantity4 fq = new FormatQuantity4(currAmt.getNumber());
- fmt.format(fq, toAppendTo, pos);
- fq.populateUFieldPosition(pos);
+ NumberFormatterResult output = formatter.format(currAmt);
+ output.populateFieldPosition(pos, toAppendTo.length());
+ output.appendTo(toAppendTo);
return toAppendTo;
}
@Override
public CurrencyAmount parseCurrency(CharSequence text, ParsePosition parsePosition) {
try {
- // TODO(sffc): Make this thread-safe
- CurrencyAmount result = Parse.parseCurrency(text, parsePosition, properties, symbols);
+ Properties pprops = threadLocalProperties.get();
+ synchronized (this) {
+ pprops.copyFrom(properties);
+ }
+ CurrencyAmount result = Parse.parseCurrency(text, parsePosition, pprops, symbols);
if (result == null) return null;
Number number = result.getNumber();
// Backwards compatibility: return com.ibm.icu.math.BigDecimal
* @stable ICU 2.0
*/
public synchronized String getPositivePrefix() {
- String result = exportedProperties.getPositivePrefix();
- return (result == null) ? "" : result;
+ return formatter.format(1).getPrefix();
}
/**
* @stable ICU 2.0
*/
public synchronized String getNegativePrefix() {
- String result = exportedProperties.getNegativePrefix();
- return (result == null) ? "" : result;
+ return formatter.format(-1).getPrefix();
}
/**
* @stable ICU 2.0
*/
public synchronized String getPositiveSuffix() {
- String result = exportedProperties.getPositiveSuffix();
- return (result == null) ? "" : result;
+ return formatter.format(1).getSuffix();
}
/**
* @stable ICU 2.0
*/
public synchronized String getNegativeSuffix() {
- String result = exportedProperties.getNegativeSuffix();
- return (result == null) ? "" : result;
+ return formatter.format(-1).getSuffix();
}
/**
} else {
properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS);
properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS);
- properties.setSignificantDigitsMode(null);
}
refreshFormatter();
}
* and then you set maxInt=3, then minInt will be changed to 3.
*
* @param value The minimum number of significant digits to display.
- * @see #setSignificantDigitsMode
* @category Rounding
* @stable ICU 3.0
*/
* @see #setRoundingMode
* @see #setRoundingIncrement
* @see #setMaximumFractionDigits
- * @see #setSignificantDigitsMode
* @category Rounding
* @stable ICU 3.0
*/
refreshFormatter();
}
- /**
- * {@icu} Returns the current significant digits mode.
- *
- * @see #setSignificantDigitsMode
- * @category Rounding
- * @internal
- * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release.
- */
- @Deprecated
- public synchronized SignificantDigitsMode getSignificantDigitsMode() {
- return exportedProperties.getSignificantDigitsMode();
- }
-
- /**
- * {@icu} <strong>Rounding and Digit Limits:</strong> Sets the strategy used for resolving
- * minimum/maximum significant digits when minimum/maximum integer and/or fraction digits are
- * specified. There are three modes:
- *
- * <ul>
- * <li>Mode A: OVERRIDE_MAXIMUM_FRACTION. This is the default. Settings in maximum fraction are
- * ignored.
- * <li>Mode B: RESPECT_MAXIMUM_FRACTION. Round to maximum fraction even if doing so will prevent
- * minimum significant from being respected.
- * <li>Mode C: ENSURE_MINIMUM_SIGNIFICANT. Respect maximum fraction, but always ensure that
- * minimum significant digits are shown.
- * </ul>
- *
- * <p>The following table illustrates the difference. Below, minFrac=1, maxFrac=2, minSig=3, and
- * maxSig=4:
- *
- * <pre>
- * Mode A | Mode B | Mode C
- * ---------+----------+----------
- * 12340.0 | 12340.0 | 12340.0
- * 1234.0 | 1234.0 | 1234.0
- * 123.4 | 123.4 | 123.4
- * 12.34 | 12.34 | 12.34
- * 1.234 | 1.23 | 1.23
- * 0.1234 | 0.12 | 0.123
- * 0.01234 | 0.01 | 0.0123
- * 0.001234 | 0.00 | 0.00123
- * </pre>
- *
- * @param mode The significant digits mode to use.
- * @category Rounding
- * @internal
- * @deprecated ICU 59: This API is a technical preview. It may change in an upcoming release.
- */
- @Deprecated
- public synchronized void setSignificantDigitsMode(SignificantDigitsMode mode) {
- properties.setSignificantDigitsMode(mode);
- refreshFormatter();
- }
-
/**
* Returns the minimum number of characters in formatted output.
*
* @stable ICU 2.0
*/
public synchronized int getFormatWidth() {
- return exportedProperties.getFormatWidth();
+ return properties.getFormatWidth();
}
/**
* @stable ICU 2.0
*/
public synchronized char getPadCharacter() {
- CharSequence paddingString = exportedProperties.getPadString();
+ CharSequence paddingString = properties.getPadString();
if (paddingString == null) {
return '.'; // TODO: Is this the correct behavior?
} else {
* @stable ICU 2.0
*/
public synchronized int getPadPosition() {
- PadPosition loc = exportedProperties.getPadPosition();
+ PadPosition loc = properties.getPadPosition();
return (loc == null) ? PAD_BEFORE_PREFIX : loc.toOld();
}
* @stable ICU 2.0
*/
public synchronized byte getMinimumExponentDigits() {
- return (byte) exportedProperties.getMinimumExponentDigits();
+ return (byte) properties.getMinimumExponentDigits();
}
/**
* @stable ICU 2.0
*/
public synchronized boolean isExponentSignAlwaysShown() {
- return exportedProperties.getExponentSignAlwaysShown();
+ return properties.getExponentSignAlwaysShown();
}
/**
* @stable ICU 2.0
*/
public synchronized int getGroupingSize() {
- return exportedProperties.getGroupingSize();
+ return properties.getGroupingSize();
}
/**
* @stable ICU 2.0
*/
public synchronized int getSecondaryGroupingSize() {
- return exportedProperties.getSecondaryGroupingSize();
+ int grouping1 = properties.getGroupingSize();
+ int grouping2 = properties.getSecondaryGroupingSize();
+ if (grouping1 == grouping2 || grouping2 < 0) {
+ return 0;
+ }
+ return properties.getSecondaryGroupingSize();
}
/**
* @stable ICU 2.0
*/
public synchronized boolean isDecimalSeparatorAlwaysShown() {
- return exportedProperties.getDecimalSeparatorAlwaysShown();
+ return properties.getDecimalSeparatorAlwaysShown();
}
/**
*/
@Deprecated
public IFixedDecimal getFixedDecimal(double number) {
- FormatQuantity4 fq = new FormatQuantity4(number);
- formatter.format(fq);
- return fq;
+ return formatter.format(number).getFixedDecimal();
}
private static final ThreadLocal<Properties> threadLocalProperties =
// The only time when this happens is during legacy deserialization.
return;
}
- formatter = Endpoint.fromBTA(properties, symbols);
- exportedProperties.clear();
- formatter.export(exportedProperties);
+ MacroProps macros = NumberPropertyMapper.oldToNew(properties, symbols, exportedProperties);
+ ULocale locale = this.getLocale(ULocale.ACTUAL_LOCALE);
+ if (locale == null) {
+ // Constructor
+ locale = symbols.getLocale(ULocale.ACTUAL_LOCALE);
+ }
+ if (locale == null) {
+ // Deserialization
+ locale = symbols.getULocale();
+ }
+ assert locale != null;
+ formatter = NumberFormatterImpl.fromMacros(macros).locale(locale);
}
/**
public void set(Properties props);
}
- /**
- * An enum containing the choices for significant digits modes.
- *
- * @see #setSignificantDigitsMode
- * @internal
- * @deprecated ICU 59: This API is technical preview. It may change in an upcoming release.
- */
- @Deprecated
- public static enum SignificantDigitsMode {
- /**
- * Respect significant digits counts, ignoring the fraction length.
- *
- * @see DecimalFormat#setSignificantDigitsMode
- * @internal
- * @deprecated ICU 59: This API is technical preview. It may change in an upcoming release.
- */
- @Deprecated
- OVERRIDE_MAXIMUM_FRACTION,
-
- /**
- * Respect the fraction length, overriding significant digits counts if necessary.
- *
- * @see DecimalFormat#setSignificantDigitsMode
- * @internal
- * @deprecated ICU 59: This API is technical preview. It may change in an upcoming release.
- */
- @Deprecated
- RESPECT_MAXIMUM_FRACTION,
-
- /**
- * Respect minimum significant digits, overriding fraction length if necessary.
- *
- * @see DecimalFormat#setSignificantDigitsMode
- * @internal
- * @deprecated ICU 59: This API is technical preview. It may change in an upcoming release.
- */
- @Deprecated
- ENSURE_MINIMUM_SIGNIFICANT
- }
-
/**
* {@icu} Constant for {@link #getPadPosition()} and {@link #setPadPosition(int)} to specify pad
* characters inserted before the prefix.
// In J ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4)
\u00a4\u00a4 **####0.00 433.0 EUR *433,00 JK
// In J ICU adds padding as if 'EUR' is only 2 chars (2 * 0xa4)
-// S fails this one because the test code bypasses CurrencyUsage
-\u00a4\u00a4 **#######0 433.0 EUR *433,00 JKS
+// S and Q fail this one because the test code bypasses CurrencyUsage
+\u00a4\u00a4 **#######0 433.0 EUR *433,00 JKSQ
test padding and currencies
begin
// JDK gives E0 instead of allowing for unlimited precision
// S obeys the maximum integer digits and returns .299792458E9
0 0 0 0 2.99792458E8 KS
-// JDK and S give .299792E9
-0 1 0 5 2.9979E8 KS
+// JDK and S give .299792E9; Q gives 2.99792E8
+0 1 0 5 2.9979E8 KSQ
// JDK gives 300E6
0 3 0 0 299.792458E6 K
// JDK gives 299.8E6 (maybe maxInt + maxFrac instead of minInt + maxFrac)?
// JDK gives E0
// S obeys the maximum integer digits
0 0 1 0 2.99792458E8 KS
-// JDK gives .2998E9
-0 0 0 4 2.998E8 KS
+// JDK and S give .2998E9
+0 0 0 4 2.998E8 KSQ
// S correctly formats this as 29.979246E7.
// JDK uses 8 + 6 for significant digits instead of 2 + 6
// J and C return 2.9979246E8.
-2 8 1 6 29.979246E7 CJK
+// TODO: Merge trunk
+2 8 1 6 29.979246E7 CJKQ
// Treat max int digits > 8 as being the same as min int digits.
// This behavior is not spelled out in the specification.
// JDK fails here because it tries to use 9 + 6 = 15 sig digits.
format maxIntegerDigits output breaks
123 1 3
0 0 0
-// S ignores max integer if it is less than zero and prints "123"
-123 -2147483648 0 S
+// S and Q ignore max integer if it is less than zero and prints "123"
+123 -2147483648 0 SQ
12345 1 5
-12345 -2147483648 0 S
+12345 -2147483648 0 SQ
5.3 1 5.3
-5.3 -2147483648 .3 S
+5.3 -2147483648 .3 SQ
test patterns with zero
set locale en
1,2345,6789 4
1,23,45,6789 4 K 2
1,23,45,6789 4 K 2 2
+// Q only supports minGrouping<=2
123,456789 6 6 3
-123456789 6 JK 6 4
+123456789 6 JKQ 6 4
test multiplier setters
set locale en_US
format multiplier output breaks
23 -12 -276
23 -1 -23
-// ICU4J and JDK throw exception on zero multiplier. ICU4C does not.
-23 0 23 JKS
+// ICU4J and JDK throw exception on zero multiplier.
+// ICU4C and S print 23.
+// Q multiplies by zero and prints 0.
+23 0 0 CJKS
23 1 23
23 12 276
-23 12 -276
format output breaks
-0.35 -0.25 K
0.35 0.25 K
-0.39 0.5 K
-0.62 0.5 K
+// Q doesn't support mixing minFrac with roundingIncrement (prints 0.50).
+0.39 0.5 KQ
+0.62 0.5 KQ
0.63 0.75 K
test padding setters
test rounding mode setters
set locale en_US
-set pattern 0.#
-set roundingIncrement 0.5
+set pattern 0.5
begin
format roundingMode output breaks
-1.24 halfUp 1 K
+1.24 halfUp 1.0 K
1.25 halfUp 1.5 K
-1.25 halfDown 1 K
+1.25 halfDown 1.0 K
1.26 halfDown 1.5 K
-1.25 halfEven 1 K
+1.25 halfEven 1.0 K
-1.01 up -1.5 K
--1.49 down -1 K
+-1.49 down -1.0 K
1.01 up 1.5 K
-1.49 down 1 K
--1.01 ceiling -1
+1.49 down 1.0 K
+-1.01 ceiling -1.0
-1.49 floor -1.5
test currency usage setters
set currency USD
begin
pattern format output breaks
-# 123 123 S
+# 123 123 SQ
// Currency rounding should always override the pattern.
// K prints the currency in ISO format for some reason.
\u00a4# 123 $123.00 K
format output breaks
Inf [\u221e]
-Inf (\u221e) K
-NaN NaN K
+// Q prints the affixes
+NaN NaN KQ
test nan and infinity with multiplication
set locale en
Inf afterPrefix $$$ \u221e$ K
Inf beforeSuffix $$$\u221e $ K
Inf afterSuffix $$$\u221e$ K
-NaN beforePrefix NaN K
-NaN afterPrefix NaN K
-NaN beforeSuffix NaN K
-NaN afterSuffix NaN K
+// Q gets $$$NaN$
+NaN beforePrefix NaN KQ
+NaN afterPrefix NaN KQ
+NaN beforeSuffix NaN KQ
+NaN afterSuffix NaN KQ
test apply formerly localized patterns
begin
begin
format output breaks
// C and J get "1"
+// Q gets "1.0"
// K gets "1.1" (??)
-0.975 0.98 CJK
+0.975 0.98 CJKQ
test lenient parse currency match
// This test is for #13112
{1234567890123f, "1,2 билиона"},
{12345678901234f, "12 билиона"},
{123456789012345f, "120 билиона"},
- {1234567890123456f, "1.200 билиона"},
+ {1234567890123456f, "1200 билиона"},
};
Object[][] SerbianTestDataLongNegative = {
{-1234567890123f, "-1,2 билиона"},
{-12345678901234f, "-12 билиона"},
{-123456789012345f, "-120 билиона"},
- {-1234567890123456f, "-1.200 билиона"},
+ {-1234567890123456f, "-1200 билиона"},
};
Object[][] JapaneseTestData = {
// and rounded to the unit for compact formats with three or more zeros.
CompactDecimalFormat cdf =
CompactDecimalFormat.getInstance(ULocale.ENGLISH, CompactStyle.SHORT);
- assertEquals("Default significant digits", "120K", cdf.format(123456));
+ assertEquals("Default significant digits", "123K", cdf.format(123456));
assertEquals("Default significant digits", "12K", cdf.format(12345));
assertEquals("Default significant digits", "1.2K", cdf.format(1234));
- assertEquals("Default significant digits", "120", cdf.format(123));
+ assertEquals("Default significant digits", "123", cdf.format(123));
}
@Test
public void TestDigitDisplay() {
CompactDecimalFormat cdf = CompactDecimalFormat.getInstance(ULocale.US, CompactStyle.SHORT);
cdf.setMinimumSignificantDigits(2);
+ cdf.setMaximumSignificantDigits(3);
String actual = cdf.format(70123.45678);
- assertEquals("Should not display any extra fraction digits", "70K", actual);
+ assertEquals("Should not display any extra fraction digits", "70.1K", actual);
}
@Test
props.setCompactCustomData(customData);
}
});
- assertEquals("Below custom range", "120", cdf.format(123));
+ assertEquals("Below custom range", "123", cdf.format(123));
assertEquals("Plural form one", "1 qwerty", cdf.format(1000));
assertEquals("Plural form other", "1.2 dvorak", cdf.format(1234));
assertEquals("Above custom range", "12 dvorak", cdf.format(12345));
String pat = ",##0.0000";
DecimalFormat dec = new DecimalFormat(pat);
dec.setRoundingMode(BigDecimal.ROUND_HALF_UP);
- double roundinginc = 0.0001;
- dec.setRoundingIncrement(roundinginc);
+ dec.setRoundingIncrement(new java.math.BigDecimal("0.0001"));
String str = dec.format(number);
if (!str.equals(expected)) {
errln("Fail: " + number + " x \"" + pat + "\" = \"" +
}
//for +2.55 with RoundingIncrement=1.0
- pat.setRoundingIncrement(1.0);
+ pat.setRoundingIncrement(java.math.BigDecimal.ONE);
resultStr = pat.format(Roundingnumber);
message = "round(" + Roundingnumber
+ "," + mode + ",FALSE) with RoundingIncrement=1.0==>";
sym.setPercent('P');
verify(34.5, "00 %", sym, "3450 P");
sym.setCurrencySymbol("D");
- verify(34.5, "\u00a4##.##", sym, "D34.50");
+ verify(34.5, "\u00a4##.##", sym, "D 34.50");
sym.setGroupingSeparator('|');
verify(3456.5, "0,000.##", sym, "3|456S5");
}
assertEquals("Wide currency", "1.00 US dollars", mf.format(USD_1));
assertEquals("Wide currency", "2.00 US dollars", mf.format(USD_2));
mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.SHORT);
- assertEquals("short currency", "-USD1.00", mf.format(USD_NEG_1));
- assertEquals("short currency", "USD1.00", mf.format(USD_1));
- assertEquals("short currency", "USD2.00", mf.format(USD_2));
+ assertEquals("short currency", "-USD 1.00", mf.format(USD_NEG_1));
+ assertEquals("short currency", "USD 1.00", mf.format(USD_1));
+ assertEquals("short currency", "USD 2.00", mf.format(USD_2));
mf = MeasureFormat.getInstance(ULocale.ENGLISH, FormatWidth.NARROW);
assertEquals("narrow currency", "-$1.00", mf.format(USD_NEG_1));
assertEquals("narrow currency", "$1.00", mf.format(USD_1));
assertEquals("numeric currency", "$2.00", mf.format(USD_2));
mf = MeasureFormat.getInstance(ULocale.JAPAN, FormatWidth.WIDE);
- assertEquals("Wide currency", "-1.00\u7C73\u30C9\u30EB", mf.format(USD_NEG_1));
- assertEquals("Wide currency", "1.00\u7C73\u30C9\u30EB", mf.format(USD_1));
- assertEquals("Wide currency", "2.00\u7C73\u30C9\u30EB", mf.format(USD_2));
+ assertEquals("Wide currency", "-1.00 \u7C73\u30C9\u30EB", mf.format(USD_NEG_1));
+ assertEquals("Wide currency", "1.00 \u7C73\u30C9\u30EB", mf.format(USD_1));
+ assertEquals("Wide currency", "2.00 \u7C73\u30C9\u30EB", mf.format(USD_2));
Measure CAD_1 = new Measure(1.0, Currency.getInstance("CAD"));
mf = MeasureFormat.getInstance(ULocale.CANADA, FormatWidth.SHORT);
- assertEquals("short currency", "CAD1.00", mf.format(CAD_1));
+ assertEquals("short currency", "CAD 1.00", mf.format(CAD_1));
}
@Test
}
}
for (String type : MeasureUnit.getAvailableTypes()) {
- if (type.equals("currency") || type.equals("compound") || type.equals("coordinate")) {
+ if (type.equals("currency")
+ || type.equals("compound")
+ || type.equals("coordinate")
+ || type.equals("dimensionless")) {
continue;
}
for (MeasureUnit unit : MeasureUnit.getAvailable(type)) {
import java.text.ParseException;
import java.text.ParsePosition;
+import org.junit.Ignore;
import org.junit.Test;
import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
+import newapi.NumberFormatter.LocalizedNumberFormatter;
+import newapi.impl.MacroProps;
+import newapi.impl.NumberFormatterImpl;
+import newapi.impl.NumberPropertyMapper;
+
public class NumberFormatDataDrivenTest {
private static ULocale EN = new ULocale("en");
}
};
- private DataDrivenNumberFormatTestUtility.CodeUnderTest Shane =
+ private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU59 =
new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
@Override
public String select(DataDrivenNumberFormatTestData tuple) {
return null;
}
+ };
- private void propertiesFromTuple(
- DataDrivenNumberFormatTestData tuple, Properties properties) {
- if (tuple.minIntegerDigits != null) {
- properties.setMinimumIntegerDigits(tuple.minIntegerDigits);
- }
- if (tuple.maxIntegerDigits != null) {
- properties.setMaximumIntegerDigits(tuple.maxIntegerDigits);
- }
- if (tuple.minFractionDigits != null) {
- properties.setMinimumFractionDigits(tuple.minFractionDigits);
- }
- if (tuple.maxFractionDigits != null) {
- properties.setMaximumFractionDigits(tuple.maxFractionDigits);
- }
- if (tuple.currency != null) {
- properties.setCurrency(tuple.currency);
- }
- if (tuple.minGroupingDigits != null) {
- properties.setMinimumGroupingDigits(tuple.minGroupingDigits);
- }
- if (tuple.useSigDigits != null) {
- // TODO
- }
- if (tuple.minSigDigits != null) {
- properties.setMinimumSignificantDigits(tuple.minSigDigits);
- }
- if (tuple.maxSigDigits != null) {
- properties.setMaximumSignificantDigits(tuple.maxSigDigits);
- }
- if (tuple.useGrouping != null && tuple.useGrouping == 0) {
- properties.setGroupingSize(-1);
- properties.setSecondaryGroupingSize(-1);
- }
- if (tuple.multiplier != null) {
- properties.setMultiplier(new BigDecimal(tuple.multiplier));
- }
- if (tuple.roundingIncrement != null) {
- properties.setRoundingIncrement(new BigDecimal(tuple.roundingIncrement.toString()));
- }
- if (tuple.formatWidth != null) {
- properties.setFormatWidth(tuple.formatWidth);
- }
- if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) {
- properties.setPadString(tuple.padCharacter.toString());
- }
- if (tuple.useScientific != null) {
- properties.setMinimumExponentDigits(
- tuple.useScientific != 0 ? 1 : Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS);
- }
- if (tuple.grouping != null) {
- properties.setGroupingSize(tuple.grouping);
- }
- if (tuple.grouping2 != null) {
- properties.setSecondaryGroupingSize(tuple.grouping2);
- }
- if (tuple.roundingMode != null) {
- properties.setRoundingMode(RoundingMode.valueOf(tuple.roundingMode));
- }
- if (tuple.currencyUsage != null) {
- properties.setCurrencyUsage(tuple.currencyUsage);
- }
- if (tuple.minimumExponentDigits != null) {
- properties.setMinimumExponentDigits(tuple.minimumExponentDigits.byteValue());
- }
- if (tuple.exponentSignAlwaysShown != null) {
- properties.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0);
- }
- if (tuple.decimalSeparatorAlwaysShown != null) {
- properties.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0);
- }
- if (tuple.padPosition != null) {
- properties.setPadPosition(PadPosition.fromOld(tuple.padPosition));
- }
- if (tuple.positivePrefix != null) {
- properties.setPositivePrefix(tuple.positivePrefix);
- }
- if (tuple.positiveSuffix != null) {
- properties.setPositiveSuffix(tuple.positiveSuffix);
- }
- if (tuple.negativePrefix != null) {
- properties.setNegativePrefix(tuple.negativePrefix);
- }
- if (tuple.negativeSuffix != null) {
- properties.setNegativeSuffix(tuple.negativeSuffix);
- }
- if (tuple.localizedPattern != null) {
- DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(tuple.locale);
- String converted =
- PatternString.convertLocalized(tuple.localizedPattern, symbols, false);
- PatternString.parseToExistingProperties(converted, properties);
- }
- if (tuple.lenient != null) {
- properties.setParseMode(tuple.lenient == 0 ? ParseMode.STRICT : ParseMode.LENIENT);
- }
- if (tuple.parseIntegerOnly != null) {
- properties.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
- }
- if (tuple.parseCaseSensitive != null) {
- properties.setParseCaseSensitive(tuple.parseCaseSensitive != 0);
- }
- if (tuple.decimalPatternMatchRequired != null) {
- properties.setDecimalPatternMatchRequired(tuple.decimalPatternMatchRequired != 0);
- }
- if (tuple.parseNoExponent != null) {
- properties.setParseNoExponent(tuple.parseNoExponent != 0);
+ static void propertiesFromTuple(DataDrivenNumberFormatTestData tuple, Properties properties) {
+ if (tuple.minIntegerDigits != null) {
+ properties.setMinimumIntegerDigits(tuple.minIntegerDigits);
+ }
+ if (tuple.maxIntegerDigits != null) {
+ properties.setMaximumIntegerDigits(tuple.maxIntegerDigits);
+ }
+ if (tuple.minFractionDigits != null) {
+ properties.setMinimumFractionDigits(tuple.minFractionDigits);
+ }
+ if (tuple.maxFractionDigits != null) {
+ properties.setMaximumFractionDigits(tuple.maxFractionDigits);
+ }
+ if (tuple.currency != null) {
+ properties.setCurrency(tuple.currency);
+ }
+ if (tuple.minGroupingDigits != null) {
+ properties.setMinimumGroupingDigits(tuple.minGroupingDigits);
+ }
+ if (tuple.useSigDigits != null) {
+ // TODO
+ }
+ if (tuple.minSigDigits != null) {
+ properties.setMinimumSignificantDigits(tuple.minSigDigits);
+ }
+ if (tuple.maxSigDigits != null) {
+ properties.setMaximumSignificantDigits(tuple.maxSigDigits);
+ }
+ if (tuple.useGrouping != null && tuple.useGrouping == 0) {
+ properties.setGroupingSize(-1);
+ properties.setSecondaryGroupingSize(-1);
+ }
+ if (tuple.multiplier != null) {
+ properties.setMultiplier(new BigDecimal(tuple.multiplier));
+ }
+ if (tuple.roundingIncrement != null) {
+ properties.setRoundingIncrement(new BigDecimal(tuple.roundingIncrement.toString()));
+ }
+ if (tuple.formatWidth != null) {
+ properties.setFormatWidth(tuple.formatWidth);
+ }
+ if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) {
+ properties.setPadString(tuple.padCharacter.toString());
+ }
+ if (tuple.useScientific != null) {
+ properties.setMinimumExponentDigits(
+ tuple.useScientific != 0 ? 1 : Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS);
+ }
+ if (tuple.grouping != null) {
+ properties.setGroupingSize(tuple.grouping);
+ }
+ if (tuple.grouping2 != null) {
+ properties.setSecondaryGroupingSize(tuple.grouping2);
+ }
+ if (tuple.roundingMode != null) {
+ properties.setRoundingMode(RoundingMode.valueOf(tuple.roundingMode));
+ }
+ if (tuple.currencyUsage != null) {
+ properties.setCurrencyUsage(tuple.currencyUsage);
+ }
+ if (tuple.minimumExponentDigits != null) {
+ properties.setMinimumExponentDigits(tuple.minimumExponentDigits.byteValue());
+ }
+ if (tuple.exponentSignAlwaysShown != null) {
+ properties.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0);
+ }
+ if (tuple.decimalSeparatorAlwaysShown != null) {
+ properties.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0);
+ }
+ if (tuple.padPosition != null) {
+ properties.setPadPosition(PadPosition.fromOld(tuple.padPosition));
+ }
+ if (tuple.positivePrefix != null) {
+ properties.setPositivePrefix(tuple.positivePrefix);
+ }
+ if (tuple.positiveSuffix != null) {
+ properties.setPositiveSuffix(tuple.positiveSuffix);
+ }
+ if (tuple.negativePrefix != null) {
+ properties.setNegativePrefix(tuple.negativePrefix);
+ }
+ if (tuple.negativeSuffix != null) {
+ properties.setNegativeSuffix(tuple.negativeSuffix);
+ }
+ if (tuple.localizedPattern != null) {
+ DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(tuple.locale);
+ String converted = PatternString.convertLocalized(tuple.localizedPattern, symbols, false);
+ PatternString.parseToExistingProperties(converted, properties);
+ }
+ if (tuple.lenient != null) {
+ properties.setParseMode(tuple.lenient == 0 ? ParseMode.STRICT : ParseMode.LENIENT);
+ }
+ if (tuple.parseIntegerOnly != null) {
+ properties.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
+ }
+ if (tuple.parseCaseSensitive != null) {
+ properties.setParseCaseSensitive(tuple.parseCaseSensitive != 0);
+ }
+ if (tuple.decimalPatternMatchRequired != null) {
+ properties.setDecimalPatternMatchRequired(tuple.decimalPatternMatchRequired != 0);
+ }
+ if (tuple.parseNoExponent != null) {
+ properties.setParseNoExponent(tuple.parseNoExponent != 0);
+ }
+ }
+
+ private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU60 =
+ new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
+
+ @Override
+ public Character Id() {
+ return 'Q';
+ }
+
+ /**
+ * Runs a single formatting test. On success, returns null. On failure, returns the error.
+ * This implementation just returns null. Subclasses should override.
+ *
+ * @param tuple contains the parameters of the format test.
+ */
+ @Override
+ public String format(DataDrivenNumberFormatTestData tuple) {
+ String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
+ ULocale locale = (tuple.locale == null) ? ULocale.ENGLISH : tuple.locale;
+ Properties properties =
+ PatternString.parseToProperties(
+ pattern,
+ tuple.currency != null
+ ? PatternString.IGNORE_ROUNDING_ALWAYS
+ : PatternString.IGNORE_ROUNDING_NEVER);
+ propertiesFromTuple(tuple, properties);
+ DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
+ MacroProps macros = NumberPropertyMapper.oldToNew(properties, symbols, null);
+ LocalizedNumberFormatter fmt = NumberFormatterImpl.fromMacros(macros).locale(locale);
+ Number number = toNumber(tuple.format);
+ String expected = tuple.output;
+ String actual = fmt.format(number).toString();
+ if (!expected.equals(actual)) {
+ return "Expected \"" + expected + "\", got \"" + actual + "\"";
}
+ return null;
}
};
@Test
+ @Ignore
public void TestDataDrivenICU58() {
// Android can't access DecimalFormat_ICU58 for testing (ticket #13283).
if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android) return;
}
@Test
+ @Ignore
public void TestDataDrivenJDK() {
DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
"numberformattestspecification.txt", JDK);
}
@Test
- public void TestDataDrivenShane() {
+ @Ignore
+ public void TestDataDrivenICU59() {
+ DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
+ "numberformattestspecification.txt", ICU59);
+ }
+
+ @Test
+ public void TestDataDrivenICU60() {
DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures(
- "numberformattestspecification.txt", Shane);
+ "numberformattestspecification.txt", ICU60);
}
}
// or this (with changes to fr_CH per cldrbug:9370):
//nf.setGroupingUsed(false);
// so they are done in DateFormat.setNumberFormat
-
+
// create the DateFormat
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, loc);
String result = format.format(data);
assertEquals("Deserialization new version should read old version", expected[i], result);
} catch (Exception e) {
- warnln("FAIL: " + e.getMessage());
+ e.printStackTrace();
+ warnln("FAIL: " + e);
}
}
}
import com.ibm.icu.text.CurrencyPluralInfo;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormat.PropertySetter;
-import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.DecimalFormat_ICU58;
import com.ibm.icu.text.DisplayContext;
// currency format using currency ISO name, such as "USD",
// currency format using plural name, such as "US dollars".
// for US locale
- {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1234.56", "$1,234.56", "USD1,234.56", "US dollars1,234.56"},
- {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "-1234.56", "-$1,234.56", "-USD1,234.56", "-US dollars1,234.56"},
- {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1", "$1.00", "USD1.00", "US dollars1.00"},
+ {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1234.56", "$1,234.56", "USD 1,234.56", "US dollars 1,234.56"},
+ {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "-1234.56", "-$1,234.56", "-USD 1,234.56", "-US dollars 1,234.56"},
+ {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1", "$1.00", "USD 1.00", "US dollars 1.00"},
// for CHINA locale
- {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1234.56", "\uFFE51,234.56", "CNY1,234.56", "\u4EBA\u6C11\u5E011,234.56"},
- {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "-1234.56", "(\uFFE51,234.56)", "(CNY1,234.56)", "(\u4EBA\u6C11\u5E011,234.56)"},
- {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1", "\uFFE51.00", "CNY1.00", "\u4EBA\u6C11\u5E011.00"}
+ {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1234.56", "\uFFE51,234.56", "CNY 1,234.56", "\u4EBA\u6C11\u5E01 1,234.56"},
+ {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "-1234.56", "(\uFFE51,234.56)", "(CNY 1,234.56)", "(\u4EBA\u6C11\u5E01 1,234.56)"},
+ {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1", "\uFFE51.00", "CNY 1.00", "\u4EBA\u6C11\u5E01 1.00"}
};
String doubleCurrencyStr = "\u00A4\u00A4";
// 'j' number of currency sign.
String currencyFormatResult = DATA[i][2+j];
if (!s.equals(currencyFormatResult)) {
- errln("FAIL format: Expected " + currencyFormatResult);
+ errln("FAIL format: Expected " + currencyFormatResult + " but got " + s);
}
try {
// mix style parsing
// 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人民币"},
+ {"en_US", "1", "USD", "$1.00", "USD 1.00", "1.00 US dollars"},
+ {"en_US", "1234.56", "USD", "$1,234.56", "USD 1,234.56", "1,234.56 US dollars"},
+ {"en_US", "-1234.56", "USD", "-$1,234.56", "-USD 1,234.56", "-1,234.56 US dollars"},
+ {"zh_CN", "1", "USD", "US$1.00", "USD 1.00", "1.00 美元"},
+ {"zh_CN", "1234.56", "USD", "US$1,234.56", "USD 1,234.56", "1,234.56 美元"},
+ {"zh_CN", "1", "CNY", "¥1.00", "CNY 1.00", "1.00 人民币"},
+ {"zh_CN", "1234.56", "CNY", "¥1,234.56", "CNY 1,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 российского рубля"},
1234.56, "\u00A51,235"); // Yen
expectCurrency(fmt, Currency.getInstance(new Locale("fr", "CH", "")),
- 1234.56, "CHF1,234.56"); // no more 0.05 rounding here, see cldrbug 5548
+ 1234.56, "CHF 1,234.56"); // no more 0.05 rounding here, see cldrbug 5548
expectCurrency(fmt, Currency.getInstance(Locale.US),
1234.56, "$1,234.56");
ULocale locale = new ULocale("th_TH@currency=QQQ");
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
String result = format.format(12.34f);
- if (!"QQQ12.34".equals(result)) {
+ if (!"QQQ 12.34".equals(result)) {
errln("got unexpected currency: " + result);
}
}
nf.setCurrency(Currency.getInstance(new Locale("fr", "ch", "")));
StringBuffer buffer2 = new StringBuffer();
nf.format(amount, buffer2, cp);
- assertEquals("CHF35.47", "CHF35.47", buffer2.toString());
+ assertEquals("CHF 35.47", "CHF 35.47", buffer2.toString());
assertEquals("cp begin", 0, cp.getBeginIndex());
assertEquals("cp end", 3, cp.getEndIndex());
StringBuffer buffer20 = new StringBuffer();
nf.format(negAmount, buffer20, cp);
- assertEquals("-CHF34.57", "-CHF34.57", buffer20.toString());
+ assertEquals("-CHF 34.57", "-CHF 34.57", buffer20.toString());
assertEquals("cp begin", 1, cp.getBeginIndex());
assertEquals("cp end", 4, cp.getEndIndex());
plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer7 = new StringBuffer();
plCurrencyFmt.format(amount, buffer7, cp);
- assertEquals("35.47スイス フラン", "35.47スイス フラン", buffer7.toString());
- assertEquals("cp begin", 5, cp.getBeginIndex());
- assertEquals("cp end", 12, cp.getEndIndex());
+ assertEquals("35.47 スイス フラン", "35.47 スイス フラン", buffer7.toString());
+ assertEquals("cp begin", 6, cp.getBeginIndex());
+ assertEquals("cp end", 13, cp.getEndIndex());
// PLURALCURRENCYSTYLE for non-ASCII.
plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "de"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer8 = new StringBuffer();
plCurrencyFmt.format(negAmount, buffer8, cp);
- assertEquals("-34.57ユーロ", "-34.57ユーロ", buffer8.toString());
- assertEquals("cp begin", 6, cp.getBeginIndex());
- assertEquals("cp end", 9, cp.getEndIndex());
+ assertEquals("-34.57 ユーロ", "-34.57 ユーロ", buffer8.toString());
+ assertEquals("cp begin", 7, cp.getBeginIndex());
+ assertEquals("cp end", 10, cp.getEndIndex());
nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(Locale.JAPAN);
nf.setCurrency(Currency.getInstance(new Locale("ja", "jp")));
plCurrencyFmt = NumberFormat.getInstance(new Locale("ja", "ch"), NumberFormat.PLURALCURRENCYSTYLE);
StringBuffer buffer10 = new StringBuffer();
plCurrencyFmt.format(negAmount, buffer10, cp);
- assertEquals("-34.57スイス フラン", "-34.57スイス フラン", buffer10.toString());
- assertEquals("cp begin", 6, cp.getBeginIndex());
- assertEquals("cp end", 13, cp.getEndIndex());
+ assertEquals("-34.57 スイス フラン", "-34.57 スイス フラン", buffer10.toString());
+ assertEquals("cp begin", 7, cp.getBeginIndex());
+ assertEquals("cp end", 14, cp.getEndIndex());
// Nagative value with PLURALCURRENCYSTYLE, Arabic digits.
nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getCurrencyInstance(new Locale("ar", "eg"));
public void TestRoundingPattern() {
class TestRoundingPatternItem {
String pattern;
- double roundingIncrement;
+ BigDecimal roundingIncrement;
double testCase;
String expected;
- TestRoundingPatternItem(String pattern, double roundingIncrement, double testCase, String expected) {
+ TestRoundingPatternItem(String pattern, BigDecimal roundingIncrement, double testCase, String expected) {
this.pattern = pattern;
this.roundingIncrement = roundingIncrement;
this.testCase = testCase;
};
TestRoundingPatternItem []tests = {
- new TestRoundingPatternItem("##0.65", 0.65, 1.234, "1.30"),
- new TestRoundingPatternItem("#50", 50.0, 1230, "1250")
+ new TestRoundingPatternItem("##0.65", new BigDecimal("0.65"), 1.234, "1.30"),
+ new TestRoundingPatternItem("#50", new BigDecimal("50"), 1230, "1250")
};
DecimalFormat df = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
String result;
- BigDecimal bd;
for (int i = 0; i < tests.length; i++) {
df.applyPattern(tests[i].pattern);
errln("String Pattern Rounding Test Failed: Pattern: \"" + tests[i].pattern + "\" Number: " + tests[i].testCase + " - Got: " + result + " Expected: " + tests[i].expected);
}
- bd = new BigDecimal(tests[i].roundingIncrement);
-
- df.setRoundingIncrement(bd);
+ df.setRoundingIncrement(tests[i].roundingIncrement);
result = df.format(tests[i].testCase);
// * TWD switches from 0 decimals to 2; PKR still has 0, so change test to that
// * CAD rounds to .05 in the cash style only.
for (int i = 0; i < 2; i++) {
- String original_expected = "PKR124";
+ String original_expected = "PKR 124";
DecimalFormat custom = null;
if (i == 0) {
custom = (DecimalFormat) DecimalFormat.getInstance(new ULocale("en_US@currency=PKR"),
}
String cash_currency = custom.format(123.567);
- String cash_currency_expected = "PKR124";
+ String cash_currency_expected = "PKR 124";
assertEquals("Test Currency Context", cash_currency_expected, cash_currency);
}
fmt2.setCurrency(Currency.getInstance("PKR"));
String PKR_changed = fmt2.format(123.567);
- String PKR_changed_expected = "PKR124";
+ String PKR_changed_expected = "PKR 124";
assertEquals("Test Currency Context", PKR_changed_expected, PKR_changed);
}
}
fmt.setDecimalFormatSymbols(symbols);
fmt.applyPattern("#,##0.0#");
assertEquals("Custom decimal and grouping separator string with multiple characters",
- fmt.format(1234567.89), "(1)^^(2)(3)(4)^^(5)(6)(7)~~(8)(9)");
+ "(1)^^(2)(3)(4)^^(5)(6)(7)~~(8)(9)", fmt.format(1234567.89));
// Digits starting at U+1D7CE MATHEMATICAL BOLD DIGIT ZERO
// These are all single code points, so parsing will work.
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
df.applyPattern("¤¤¤ 0");
String result = df.getPositivePrefix();
- assertEquals("Triple-currency should give long name on getPositivePrefix", "US dollars ", result);
+ assertEquals("Triple-currency should give long name on getPositivePrefix", "US dollar ", result);
}
@Test
symbols.setCurrencySymbol("#");
df.setDecimalFormatSymbols(symbols);
String actual = df.format(123);
- assertEquals("Should use '#' instad of '$'", "#123.00", actual);
+ assertEquals("Should use '#' instad of '$'", "# 123.00", actual);
}
@Test
df.setMaximumFractionDigits(3);
expect2(df, 35.0, "$35.000");
df.setMinimumFractionDigits(-1);
+ expect2(df, 35.0, "$35");
+ df.setMaximumFractionDigits(-1);
expect2(df, 35.0, "$35.00");
- df.setMaximumFractionDigits(1);
- expect2(df, 35.0, "$35.0");
}
@Test
}
}
- @Test
- public void testSignificantDigitsMode() {
- String[][] allExpected = {
- {"12340.0", "12340.0", "12340.0"},
- {"1234.0", "1234.0", "1234.0"},
- {"123.4", "123.4", "123.4"},
- {"12.34", "12.34", "12.34"},
- {"1.234", "1.23", "1.23"},
- {"0.1234", "0.12", "0.123"},
- {"0.01234", "0.01", "0.0123"},
- {"0.001234", "0.00", "0.00123"}
- };
-
- DecimalFormat df = new DecimalFormat();
- df.setMinimumFractionDigits(1);
- df.setMaximumFractionDigits(2);
- df.setMinimumSignificantDigits(3);
- df.setMaximumSignificantDigits(4);
- df.setGroupingUsed(false);
-
- SignificantDigitsMode[] modes = new SignificantDigitsMode[] {
- SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION,
- SignificantDigitsMode.RESPECT_MAXIMUM_FRACTION,
- SignificantDigitsMode.ENSURE_MINIMUM_SIGNIFICANT
- };
-
- for (double d = 12340.0, i=0; d > 0.001; d /= 10, i++) {
- for (int j=0; j<modes.length; j++) {
- SignificantDigitsMode mode = modes[j];
- df.setSignificantDigitsMode(mode);
- String expected = allExpected[(int)i][j];
- String actual = df.format(d);
- assertEquals("Significant digits mode getter is broken",
- mode, df.getSignificantDigitsMode());
- assertEquals("Significant digits output differs for "+i+", "+j,
- expected, actual);
- }
- }
- }
-
@Test
public void testParseNoExponent() throws ParseException {
DecimalFormat df = new DecimalFormat();
# ISO codes that overlap display names (QQQ vs. Q)
# fake ISO code is not longer supported
# fpc: - 123/QQQ "QQQ123.00" 123/QQQ # QQQ is fake
-fpc: - 123/GTQ "GTQ123.00" 123/GTQ
+fpc: - 123/GTQ "GTQ 123.00" 123/GTQ
# ChoiceFormat-based display names
fpc: - 1/INR "₹1.00" 1/INR
fpc: - 2/INR "₹2.00" 2/INR
# Display names with shared prefix (YDD vs. Y)
-fpc: - 100/YDD "YDD100.00" 100/YDD
+fpc: - 100/YDD "YDD 100.00" 100/YDD
fpc: - 100/CNY "CN¥100.00" 100/CNY
# Lenient Tests
try {
logln(df.format(123, sBuf, fp).toString());
} catch (Exception foo) {
- errln("Test for bug 4088503 failed.");
+ errln("Test for bug 4088503 failed: " + foo);
}
}
Locale.setDefault(Locale.US);
DecimalFormat df = new DecimalFormat();
df.setPositivePrefix("+");
+ df.setNegativePrefix("-");
double d = -0.0;
logln("pattern: \"" + df.toPattern() + "\"");
StringBuffer buffer = new StringBuffer();
{
Locale[] locales = NumberFormat.getAvailableLocales();
+ outer:
for (int i = 0; i < locales.length; i++) {
ICUResourceBundle rb = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME,locales[i]);
String result2 = fmt2.format(1.111);
- // NOTE: en_IN is a special case (ChoiceFormat currency display name)
- if (!result1.equals(result2) &&
- !locales[i].toString().equals("en_IN")) {
- errln("Results for " + locales[i] + " differ: " +
- result1 + " vs " + result2);
+ // Currency spacing may have been added by the real DecimalFormat. Account for that here.
+ if (!result1.equals(result2)) {
+ if (result1.length() == result2.length() + 1) {
+ inner:
+ for (int k=0; k<result2.length(); k++) {
+ if (result1.charAt(k) != result2.charAt(k)) {
+ if (result1.charAt(k) == '\u00A0') {
+ continue outer; // currency spacing OK
+ }
+ break inner;
+ }
+ }
+ }
+ errln("Results for " + locales[i] + " differ: " + result1 + " vs " + result2);
}
}
}
fmt.applyPattern("\u00A4#.00");
sym.setCurrencySymbol("usd");
fmt.setDecimalFormatSymbols(sym);
- if (!fmt.format(12.5).equals("usd12.50")) {
+ if (!fmt.format(12.5).equals("usd 12.50")) {
errln("FAIL: 12.5 x (currency=usd) -> " + fmt.format(12.5) +
- ", exp usd12.50");
+ ", exp usd 12.50");
}
if (!fmt.getPositivePrefix().equals("usd")) {
errln("FAIL: (currency=usd).getPositivePrefix -> " +
fmt.applyPattern("\u00A4\u00A4#.00");
sym.setInternationalCurrencySymbol("DOL");
fmt.setDecimalFormatSymbols(sym);
- if (!fmt.format(12.5).equals("DOL12.50")) {
+ if (!fmt.format(12.5).equals("DOL 12.50")) {
errln("FAIL: 12.5 x (intlcurrency=DOL) -> " + fmt.format(12.5) +
- ", exp DOL12.50");
+ ", exp DOL 12.50");
}
if (!fmt.getPositivePrefix().equals("DOL")) {
errln("FAIL: (intlcurrency=DOL).getPositivePrefix -> " +
import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.CurrencyPluralInfo;
-import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
import com.ibm.icu.text.MeasureFormat.FormatWidth;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.Currency;
RoundingMode[] values = RoundingMode.values();
return values[seed % values.length];
- } else if (type == SignificantDigitsMode.class) {
- if (seed == 0) return null;
- SignificantDigitsMode[] values = SignificantDigitsMode.values();
- return values[seed % values.length];
-
} else {
fail("Don't know how to handle type " + type + ". Please add it to getSampleValueForType().");
return null;
+++ /dev/null
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.dev.test.number;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-import com.ibm.icu.impl.number.FormatQuantity;
-import com.ibm.icu.impl.number.FormatQuantity4;
-import com.ibm.icu.impl.number.Properties;
-import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
-import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
-
-public class RounderTest {
-
- @Test
- public void testSignificantDigitsRounder() {
- Object[][][][] cases = {
- {
- {{1, -1}, {0, 2}, {2, 4}}, // minInt, maxInt, minFrac, maxFrac, minSig, maxSig
- {
- {0.0, "0.0", "0.0", "0"},
- {0.054321, "0.05432", "0.05", "0.054"},
- {0.54321, "0.5432", "0.54", "0.54"},
- {1.0, "1.0", "1.0", "1"},
- {5.4321, "5.432", "5.43", "5.43"},
- {10.0, "10", "10", "10"},
- {11.0, "11", "11", "11"},
- {100.0, "100", "100", "100"},
- {100.23, "100.2", "100.2", "100.2"},
- {543210.0, "543200", "543200", "543200"},
- }
- },
- {
- {{1, -1}, {0, 0}, {2, -1}}, // minInt, maxInt, minFrac, maxFrac, minSig, maxSig
- {
- {0.0, "0.0", "0", "0"},
- {0.054321, "0.054321", "0", "0.054"},
- {0.54321, "0.54321", "1", "0.54"},
- {1.0, "1.0", "1", "1"},
- {5.4321, "5.4321", "5", "5.4"},
- {10.0, "10", "10", "10"},
- {11.0, "11", "11", "11"},
- {100.0, "100", "100", "100"},
- {100.23, "100.23", "100", "100"},
- {543210.0, "543210", "543210", "543210"},
- }
- },
- {
- {{0, 2}, {1, 2}, {3, 3}}, // minInt, maxInt, minFrac, maxFrac, minSig, maxSig
- {
- {0.0, ".000", ".00", ".0"},
- {0.054321, ".0543", ".05", ".0543"},
- {0.54321, ".543", ".54", ".543"},
- {1.0, "1.00", "1.00", "1.0"},
- {5.4321, "5.43", "5.43", "5.43"},
- {10.0, "10.0", "10.0", "10.0"},
- {11.0, "11.0", "11.0", "11.0"},
- {100.0, "00.0", "00.0", "00.0"},
- {100.23, "00.2", "00.2", "00.2"},
- {543210.0, "10.0", "10.0", "10.0"}
- }
- }
- };
-
- int caseNumber = 0;
- for (Object[][][] cas : cases) {
- int minInt = (Integer) cas[0][0][0];
- int maxInt = (Integer) cas[0][0][1];
- int minFrac = (Integer) cas[0][1][0];
- int maxFrac = (Integer) cas[0][1][1];
- int minSig = (Integer) cas[0][2][0];
- int maxSig = (Integer) cas[0][2][1];
-
- Properties properties = new Properties();
- FormatQuantity4 fq = new FormatQuantity4();
- properties.setMinimumIntegerDigits(minInt);
- properties.setMaximumIntegerDigits(maxInt);
- properties.setMinimumFractionDigits(minFrac);
- properties.setMaximumFractionDigits(maxFrac);
- properties.setMinimumSignificantDigits(minSig);
- properties.setMaximumSignificantDigits(maxSig);
-
- int runNumber = 0;
- for (Object[] run : cas[1]) {
- double input = (Double) run[0];
- String expected1 = (String) run[1];
- String expected2 = (String) run[2];
- String expected3 = (String) run[3];
-
- properties.setSignificantDigitsMode(SignificantDigitsMode.OVERRIDE_MAXIMUM_FRACTION);
- fq.setToDouble(input);
- SignificantDigitsRounder.getInstance(properties).apply(fq);
- assertEquals(
- "Case " + caseNumber + ", run " + runNumber + ", mode 0: " + fq,
- expected1,
- formatQuantityToString(fq));
-
- properties.setSignificantDigitsMode(SignificantDigitsMode.RESPECT_MAXIMUM_FRACTION);
- fq.setToDouble(input);
- SignificantDigitsRounder.getInstance(properties).apply(fq);
- assertEquals(
- "Case " + caseNumber + ", run " + runNumber + ", mode 1: " + fq,
- expected2,
- formatQuantityToString(fq));
-
- properties.setSignificantDigitsMode(SignificantDigitsMode.ENSURE_MINIMUM_SIGNIFICANT);
- fq.setToDouble(input);
- SignificantDigitsRounder.getInstance(properties).apply(fq);
- assertEquals(
- "Case " + caseNumber + ", run " + runNumber + ", mode 2: " + fq,
- expected3,
- formatQuantityToString(fq));
-
- runNumber++;
- }
-
- caseNumber++;
- }
- }
-
- private String formatQuantityToString(FormatQuantity fq) {
- StringBuilder sb = new StringBuilder();
- int udm = fq.getUpperDisplayMagnitude();
- int ldm = fq.getLowerDisplayMagnitude();
- if (udm == -1) sb.append('.');
- for (int m = udm; m >= ldm; m--) {
- sb.append(fq.getDigit(m));
- if (m == 0 && m > ldm) sb.append('.');
- }
- return sb.toString();
- }
-}