ICU-13060 Adding tech preview for setSignAlwaysShown() and moving SignificantDigitsMode from internal class to public DecimalFormat (as @internal tech preview).

X-SVN-Rev: 39933
This commit is contained in:
Shane Carr 2017-03-24 22:04:22 +00:00
parent 2e887df8b8
commit 3998c8780f
10 changed files with 218 additions and 89 deletions

View file

@ -52,6 +52,8 @@ public class PNAffixGenerator {
private NumberStringBuilder sb2 = new NumberStringBuilder();
private NumberStringBuilder sb3 = new NumberStringBuilder();
private NumberStringBuilder sb4 = new NumberStringBuilder();
private NumberStringBuilder sb5 = new NumberStringBuilder();
private NumberStringBuilder sb6 = new NumberStringBuilder();
/**
* Generates modifiers using default currency symbols.
@ -110,7 +112,7 @@ public class PNAffixGenerator {
PositiveNegativeAffixFormat.IProperties properties) {
// Use a different code path for handling affixes with "always show plus sign"
if (properties.getPlusSignAlwaysShown()) {
if (properties.getSignAlwaysShown()) {
return getModifiersWithPlusSign(symbols, curr1, curr2, curr3, properties);
}
@ -213,74 +215,57 @@ public class PNAffixGenerator {
private void setPositiveResult(
NumberStringBuilder prefix, NumberStringBuilder suffix, IProperties properties) {
if (properties.getPositivePrefix() != null || properties.getPositiveSuffix() != null) {
// Override with custom affixes
String _prefix = properties.getPositivePrefix();
String _suffix = properties.getPositiveSuffix();
if (_prefix == null) _prefix = "";
if (_suffix == null) _suffix = "";
if (_prefix.length() == 0 && _suffix.length() == 0) {
resultInstance.positive = ConstantAffixModifier.EMPTY;
return;
}
if (resultInstance.positive != null
&& (resultInstance.positive instanceof ConstantAffixModifier)
&& ((ConstantAffixModifier) resultInstance.positive).contentEquals(_prefix, _suffix)) {
// Use the cached modifier
return;
}
resultInstance.positive =
new ConstantAffixModifier(_prefix, _suffix, null, false);
} else {
// Use pattern affixes
if (prefix.length() == 0 && suffix.length() == 0) {
resultInstance.positive = ConstantAffixModifier.EMPTY;
return;
}
if (resultInstance.positive != null
&& (resultInstance.positive instanceof ConstantMultiFieldModifier)
&& ((ConstantMultiFieldModifier) resultInstance.positive).contentEquals(prefix, suffix)) {
// Use the cached modifier
return;
}
resultInstance.positive = new ConstantMultiFieldModifier(prefix, suffix, false);
// Override with custom affixes. We need to put these into NumberStringBuilders so that they
// have the same datatype as the incoming prefix and suffix (important when testing for field
// equality in contentEquals).
// TODO: It is a little inefficient that we copy String -> NumberStringBuilder -> Modifier.
// Consider re-working the logic so that fewer copies are required.
String _prefix = properties.getPositivePrefix();
String _suffix = properties.getPositiveSuffix();
if (_prefix != null) {
prefix = sb5.clear();
prefix.append(_prefix, null);
}
if (_suffix != null) {
suffix = sb6.clear();
suffix.append(_suffix, null);
}
if (prefix.length() == 0 && suffix.length() == 0) {
resultInstance.positive = ConstantAffixModifier.EMPTY;
return;
}
if (resultInstance.positive != null
&& (resultInstance.positive instanceof ConstantMultiFieldModifier)
&& ((ConstantMultiFieldModifier) resultInstance.positive).contentEquals(prefix, suffix)) {
// Use the cached modifier
return;
}
resultInstance.positive = new ConstantMultiFieldModifier(prefix, suffix, false);
}
private void setNegativeResult(
NumberStringBuilder prefix, NumberStringBuilder suffix, IProperties properties) {
if (properties.getNegativePrefix() != null || properties.getNegativeSuffix() != null) {
// Override with custom affixes
String _prefix = properties.getNegativePrefix();
String _suffix = properties.getNegativeSuffix();
if (_prefix == null) _prefix = "";
if (_suffix == null) _suffix = "";
if (_prefix.length() == 0 && _suffix.length() == 0) {
resultInstance.negative = ConstantAffixModifier.EMPTY;
return;
}
if (resultInstance.negative != null
&& (resultInstance.negative instanceof ConstantAffixModifier)
&& ((ConstantAffixModifier) resultInstance.negative).contentEquals(_prefix, _suffix)) {
// Use the cached modifier
return;
}
resultInstance.negative =
new ConstantAffixModifier(_prefix, _suffix, null, false);
} else {
// Use pattern affixes
if (prefix.length() == 0 && suffix.length() == 0) {
resultInstance.negative = ConstantAffixModifier.EMPTY;
return;
}
if (resultInstance.negative != null
&& (resultInstance.negative instanceof ConstantMultiFieldModifier)
&& ((ConstantMultiFieldModifier) resultInstance.negative).contentEquals(prefix, suffix)) {
// Use the cached modifier
return;
}
resultInstance.negative = new ConstantMultiFieldModifier(prefix, suffix, false);
String _prefix = properties.getNegativePrefix();
String _suffix = properties.getNegativeSuffix();
if (_prefix != null) {
prefix = sb5.clear();
prefix.append(_prefix, null);
}
if (_suffix != null) {
suffix = sb6.clear();
suffix.append(_suffix, null);
}
if (prefix.length() == 0 && suffix.length() == 0) {
resultInstance.negative = ConstantAffixModifier.EMPTY;
return;
}
if (resultInstance.negative != null
&& (resultInstance.negative instanceof ConstantMultiFieldModifier)
&& ((ConstantMultiFieldModifier) resultInstance.negative).contentEquals(prefix, suffix)) {
// Use the cached modifier
return;
}
resultInstance.negative = new ConstantMultiFieldModifier(prefix, suffix, false);
}
/** A null-safe equals method for CharSequences. */

View file

@ -726,7 +726,7 @@ public class Parse {
AffixHolder ps = fromPropertiesPositiveString(properties);
AffixHolder ns = fromPropertiesNegativeString(properties);
if (pp == null && ps == null) {
if (properties.getPlusSignAlwaysShown()) {
if (properties.getSignAlwaysShown()) {
state.affixHolders.add(DEFAULT_POSITIVE);
} else {
state.affixHolders.add(EMPTY_POSITIVE);

View file

@ -28,9 +28,9 @@ import com.ibm.icu.impl.number.formatters.ScientificFormat;
import com.ibm.icu.impl.number.rounders.IncrementRounder;
import com.ibm.icu.impl.number.rounders.MagnitudeRounder;
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder.SignificantDigitsMode;
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.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
@ -106,7 +106,6 @@ public class Properties
private transient boolean parseIntegerOnly;
private transient ParseMode parseMode;
private transient boolean parseToBigDecimal;
private transient boolean plusSignAlwaysShown;
private transient String positivePrefix;
private transient String positivePrefixPattern;
private transient String positiveSuffix;
@ -114,6 +113,7 @@ public class Properties
private transient BigDecimal roundingIncrement;
private transient RoundingMode roundingMode;
private transient int secondaryGroupingSize;
private transient boolean signAlwaysShown;
private transient SignificantDigitsMode significantDigitsMode;
/*--------------------------------------------------------------------------------------------+/
@ -164,7 +164,6 @@ public class Properties
parseMode = DEFAULT_PARSE_MODE;
parseNoExponent = DEFAULT_PARSE_NO_EXPONENT;
parseToBigDecimal = DEFAULT_PARSE_TO_BIG_DECIMAL;
plusSignAlwaysShown = DEFAULT_PLUS_SIGN_ALWAYS_SHOWN;
positivePrefix = DEFAULT_POSITIVE_PREFIX;
positivePrefixPattern = DEFAULT_POSITIVE_PREFIX_PATTERN;
positiveSuffix = DEFAULT_POSITIVE_SUFFIX;
@ -172,6 +171,7 @@ public class Properties
roundingIncrement = DEFAULT_ROUNDING_INCREMENT;
roundingMode = DEFAULT_ROUNDING_MODE;
secondaryGroupingSize = DEFAULT_SECONDARY_GROUPING_SIZE;
signAlwaysShown = DEFAULT_SIGN_ALWAYS_SHOWN;
significantDigitsMode = DEFAULT_SIGNIFICANT_DIGITS_MODE;
return this;
}
@ -211,7 +211,6 @@ public class Properties
parseMode = other.parseMode;
parseNoExponent = other.parseNoExponent;
parseToBigDecimal = other.parseToBigDecimal;
plusSignAlwaysShown = other.plusSignAlwaysShown;
positivePrefix = other.positivePrefix;
positivePrefixPattern = other.positivePrefixPattern;
positiveSuffix = other.positiveSuffix;
@ -219,6 +218,7 @@ public class Properties
roundingIncrement = other.roundingIncrement;
roundingMode = other.roundingMode;
secondaryGroupingSize = other.secondaryGroupingSize;
signAlwaysShown = other.signAlwaysShown;
significantDigitsMode = other.significantDigitsMode;
return this;
}
@ -259,7 +259,6 @@ public class Properties
eq = eq && _equalsHelper(parseMode, other.parseMode);
eq = eq && _equalsHelper(parseNoExponent, other.parseNoExponent);
eq = eq && _equalsHelper(parseToBigDecimal, other.parseToBigDecimal);
eq = eq && _equalsHelper(plusSignAlwaysShown, other.plusSignAlwaysShown);
eq = eq && _equalsHelper(positivePrefix, other.positivePrefix);
eq = eq && _equalsHelper(positivePrefixPattern, other.positivePrefixPattern);
eq = eq && _equalsHelper(positiveSuffix, other.positiveSuffix);
@ -267,6 +266,7 @@ public class Properties
eq = eq && _equalsHelper(roundingIncrement, other.roundingIncrement);
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;
}
@ -321,7 +321,6 @@ public class Properties
hashCode ^= _hashCodeHelper(parseMode);
hashCode ^= _hashCodeHelper(parseNoExponent);
hashCode ^= _hashCodeHelper(parseToBigDecimal);
hashCode ^= _hashCodeHelper(plusSignAlwaysShown);
hashCode ^= _hashCodeHelper(positivePrefix);
hashCode ^= _hashCodeHelper(positivePrefixPattern);
hashCode ^= _hashCodeHelper(positiveSuffix);
@ -329,6 +328,7 @@ public class Properties
hashCode ^= _hashCodeHelper(roundingIncrement);
hashCode ^= _hashCodeHelper(roundingMode);
hashCode ^= _hashCodeHelper(secondaryGroupingSize);
hashCode ^= _hashCodeHelper(signAlwaysShown);
hashCode ^= _hashCodeHelper(significantDigitsMode);
return hashCode;
}
@ -554,8 +554,8 @@ public class Properties
}
@Override
public boolean getPlusSignAlwaysShown() {
return plusSignAlwaysShown;
public boolean getSignAlwaysShown() {
return signAlwaysShown;
}
@Override
@ -860,8 +860,8 @@ public class Properties
}
@Override
public Properties setPlusSignAlwaysShown(boolean plusSignAlwaysShown) {
this.plusSignAlwaysShown = plusSignAlwaysShown;
public Properties setSignAlwaysShown(boolean signAlwaysShown) {
this.signAlwaysShown = signAlwaysShown;
return this;
}

View file

@ -209,10 +209,10 @@ public class PositiveNegativeAffixFormat {
*/
public IProperties setNegativeSuffixPattern(String negativeSuffixPattern);
static boolean DEFAULT_PLUS_SIGN_ALWAYS_SHOWN = false;
static boolean DEFAULT_SIGN_ALWAYS_SHOWN = false;
/** @see #setPlusSignAlwaysShown */
public boolean getPlusSignAlwaysShown();
/** @see #setSignAlwaysShown */
public boolean getSignAlwaysShown();
/**
* Sets whether to always display of a plus sign on positive numbers.
@ -230,7 +230,7 @@ public class PositiveNegativeAffixFormat {
* @param plusSignAlwaysShown Whether positive numbers should display a plus sign.
* @return The property bag, for chaining.
*/
public IProperties setPlusSignAlwaysShown(boolean plusSignAlwaysShown);
public IProperties setSignAlwaysShown(boolean plusSignAlwaysShown);
}
public static PositiveNegativeAffixModifier getInstance(DecimalFormatSymbols symbols, IProperties properties) {

View file

@ -7,15 +7,10 @@ import java.math.RoundingMode;
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 {
public static enum SignificantDigitsMode {
OVERRIDE_MAXIMUM_FRACTION,
RESPECT_MAXIMUM_FRACTION,
ENSURE_MINIMUM_SIGNIFICANT
};
public static interface IProperties extends Rounder.IBasicRoundingProperties {
static int DEFAULT_MINIMUM_SIGNIFICANT_DIGITS = -1;

View file

@ -24,7 +24,6 @@ import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition;
import com.ibm.icu.impl.number.formatters.PositiveDecimalFormat;
import com.ibm.icu.impl.number.formatters.ScientificFormat;
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder;
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder.SignificantDigitsMode;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
@ -739,6 +738,34 @@ public class DecimalFormat extends NumberFormat {
refreshFormatter();
}
/**
* @return Whether the sign is being shown on positive numbers.
* @see #setSignAlwaysShown
* @category Affixes
* @internal
* @deprecated This API is technical preview.
*/
@Deprecated
public synchronized boolean getSignAlwaysShown() {
// This is not in the exported properties
return properties.getSignAlwaysShown();
}
/**
* Sets whether to always shown the plus sign ('+' in <em>en</em>) on positive numbers. The rules
* in UTS #35 section 3.2.1 will be followed to ensure a locale-aware placement of the sign.
*
* @param value true to always show a sign; false to hide the sign on positive numbers.
* @category Affixes
* @internal
* @deprecated ICU 59: This API is technical preview. It may change in an upcoming release.
*/
@Deprecated
public synchronized void setSignAlwaysShown(boolean value) {
properties.setSignAlwaysShown(value);
refreshFormatter();
}
/**
* @return The multiplier being applied to numbers before they are formatted.
* @see #setMultiplier
@ -2103,6 +2130,46 @@ public class DecimalFormat extends NumberFormat {
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 #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 #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 #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.

View file

@ -613,6 +613,19 @@ public class CompactDecimalFormatTest extends TestFmwk {
assertEquals("Should not display any extra fraction digits", "70K", actual);
}
@Test
public void TestLocaleGroupingForLargeNumbers() {
ULocale[] locs = {new ULocale("en"), new ULocale("it"), new ULocale("en_US_POSIX"), new ULocale("en-IN")};
String[] expecteds = {"5,800,000T", "5.800.000 Bln", "5800000T", "58,00,000T"};
for (int i=0; i<locs.length; i++) {
ULocale loc = locs[i];
String exp = expecteds[i];
CompactDecimalFormat cdf = CompactDecimalFormat.getInstance(loc, CompactStyle.SHORT);
String act = cdf.format(5.8e18);
assertEquals("Grouping sizes for very large numbers: " + loc, exp, act);
}
}
@Test
public void TestBug12422() {
CompactDecimalFormat cdf;

View file

@ -41,11 +41,11 @@ import com.ibm.icu.impl.ICUConfig;
import com.ibm.icu.impl.LocaleUtility;
import com.ibm.icu.impl.data.ResourceReader;
import com.ibm.icu.impl.data.TokenIterator;
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder.SignificantDigitsMode;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
import com.ibm.icu.text.CompactDecimalFormat;
import com.ibm.icu.text.DecimalFormat;
import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.DisplayContext;
import com.ibm.icu.text.MeasureFormat;
@ -4833,7 +4833,7 @@ public class NumberFormatTest extends TestFmwk {
df.setPositiveSuffix("0K");
df.setNegativeSuffix("0N");
expect2(df, 123, "1230K");
expect2(df, -123, "1230N");
expect2(df, -123, "-1230N");
}
@Test
@ -5110,6 +5110,13 @@ public class NumberFormatTest extends TestFmwk {
assertEquals("Should parse 12 and -5", 7, n1.intValue() + n2.intValue());
}
@Test
public void testSetPrefixDefaultSuffix() {
DecimalFormat df = (DecimalFormat) NumberFormat.getPercentInstance();
df.setPositivePrefix("+");
assertEquals("Should have manual plus sign and auto percent sign", "+100%", df.format(1));
}
@Test
public void testMultiCodePointPaddingInPattern() {
DecimalFormat df = new DecimalFormat("a*'நி'###0b");
@ -5231,4 +5238,66 @@ public class NumberFormatTest extends TestFmwk {
}
}
}
@Test
public void testPlusSignAlwaysShown() {
double[] numbers = {0.012, 5.78, 0, -0.012, -5.78};
ULocale[] locs = {new ULocale("en-US"), new ULocale("ar-EG"), new ULocale("es-CL")};
String[][][] expecteds = {
// en-US
{
// decimal
{ "+0.012", "+5.78", "+0", "-0.012", "-5.78" },
// currency
{ "+$0.01", "+$5.78", "+$0.00", "-$0.01", "-$5.78" }
},
// ar-EG (interesting because the plus sign string starts with \u061C)
{
// decimal
{
"\u061C+\u0660\u066B\u0660\u0661\u0662", // "؜+٠٫٠١٢"
"\u061C+\u0665\u066B\u0667\u0668", // "؜+٥٫٧٨"
"\u061C+\u0660", // "؜+٠"
"\u061C-\u0660\u066B\u0660\u0661\u0662", // "؜-٠٫٠١٢"
"\u061C-\u0665\u066B\u0667\u0668", // "؜-٥٫٧٨"
},
// currency (\062C.\0645.\200F is the currency sign in ar for EGP)
{
"\u061C+\u0660\u066B\u0660\u0661\u00A0\u062C.\u0645.\u200F",
"\u061C+\u0665\u066B\u0667\u0668\u00A0\u062C.\u0645.\u200F",
"\u061C+\u0660\u066B\u0660\u0660\u00A0\u062C.\u0645.\u200F",
"\u061C-\u0660\u066B\u0660\u0661\u00A0\u062C.\u0645.\u200F",
"\u061C-\u0665\u066B\u0667\u0668\u00A0\u062C.\u0645.\u200F"
}
},
// es-CL (interesting because of position of sign in currency)
{
// decimal
{ "+0,012", "+5,78", "+0", "-0,012", "-5,78" },
// currency (note: rounding for es-CL's currency, CLP, is 0 fraction digits)
{ "$+0", "$+6", "$+0", "$-0", "$-6" }
}
};
for (int i=0; i<locs.length; i++) {
ULocale loc = locs[i];
DecimalFormat df1 = (DecimalFormat) NumberFormat.getNumberInstance(loc);
assertFalse("Default should be false", df1.getSignAlwaysShown());
df1.setSignAlwaysShown(true);
assertTrue("Getter should now return true", df1.getSignAlwaysShown());
DecimalFormat df2 = (DecimalFormat) NumberFormat.getCurrencyInstance(loc);
assertFalse("Default should be false", df2.getSignAlwaysShown());
df2.setSignAlwaysShown(true);
assertTrue("Getter should now return true", df2.getSignAlwaysShown());
for (int j=0; j<2; j++) {
DecimalFormat df = (j == 0) ? df1 : df2;
for (int k=0; k<numbers.length; k++) {
double d = numbers[k];
String exp = expecteds[i][j][k];
String act = df.format(d);
assertEquals("Locale " + loc + ", type " + j + ", " + d, exp, act);
}
}
}
}
}

View file

@ -32,9 +32,9 @@ import com.ibm.icu.impl.number.PatternString;
import com.ibm.icu.impl.number.Properties;
import com.ibm.icu.impl.number.formatters.CurrencyFormat.CurrencyStyle;
import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition;
import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder.SignificantDigitsMode;
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.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;

View file

@ -10,7 +10,7 @@ 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.impl.number.rounders.SignificantDigitsRounder.SignificantDigitsMode;
import com.ibm.icu.text.DecimalFormat.SignificantDigitsMode;
public class RounderTest {