// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
public interface AffixPatternProvider {
public static final class Flags {
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
import java.util.Arrays;
import java.util.Map;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
-import newapi.CompactNotation.CompactType;
-
public class CompactData implements MultiplierProducer {
+ public enum CompactType {
+ DECIMAL, CURRENCY
+ }
+
// A dummy object used when a "0" compact decimal entry is encountered. This is necessary
// in order to prevent falling back to root. Object equality ("==") is intended.
private static final String USE_FALLBACK = "<USE FALLBACK>";
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number.modifiers;
+package com.ibm.icu.impl.number;
-import com.ibm.icu.impl.number.Modifier;
-
-// TODO: This class is currently unused, but it might be useful for something in the future.
-// Should probably be moved to a different package.
-
-import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.text.NumberFormat.Field;
/**
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number.modifiers;
+package com.ibm.icu.impl.number;
-import com.ibm.icu.impl.number.Modifier;
-import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.text.NumberFormat.Field;
/**
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number.modifiers;
+package com.ibm.icu.impl.number;
-import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.UnicodeSet;
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.Currency;
import java.util.ArrayList;
import java.util.Map;
+import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.Parse.GroupingMode;
import com.ibm.icu.impl.number.Parse.ParseMode;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
-import newapi.impl.Padder.PadPosition;
-
public class DecimalFormatProperties implements Cloneable, Serializable {
private static final DecimalFormatProperties DEFAULT = new DecimalFormatProperties();
return precision == 0;
}
- @Override
- public DecimalQuantity createCopy() {
- if (this instanceof DecimalQuantity_64BitBCD) {
- return new DecimalQuantity_64BitBCD((DecimalQuantity_64BitBCD) this);
- } else if (this instanceof DecimalQuantity_ByteArrayBCD) {
- return new DecimalQuantity_ByteArrayBCD((DecimalQuantity_ByteArrayBCD) this);
- } else if (this instanceof DecimalQuantity_DualStorageBCD) {
- return new DecimalQuantity_DualStorageBCD((DecimalQuantity_DualStorageBCD) this);
- } else {
- throw new IllegalArgumentException("Don't know how to copy " + this.getClass());
- }
- }
-
public void setToInt(int n) {
setBcdToZero();
flags = 0;
* to digits. Since double arithmetic is inexact, the resulting digits may not be accurate.
*/
private void _setToDoubleFast(double n) {
+ isApproximate = true;
+ origDouble = n;
+ origDelta = 0;
+
+ // NOTE: Unlike ICU4C, doubles are always IEEE 754 doubles.
long ieeeBits = Double.doubleToLongBits(n);
int exponent = (int) ((ieeeBits & 0x7ff0000000000000L) >> 52) - 0x3ff;
return;
}
- isApproximate = true;
- origDouble = n;
- origDelta = 0;
-
// 3.3219... is log2(10)
int fracLength = (int) ((52 - exponent) / 3.32192809489);
if (fracLength >= 0) {
}
}
+ @Override
+ public DecimalQuantity createCopy() {
+ return new DecimalQuantity_DualStorageBCD(this);
+ }
+
@Override
protected byte getDigitPos(int position) {
if (usingBytes) {
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
import java.util.EnumMap;
import java.util.Map;
import com.ibm.icu.impl.SimpleFormatterImpl;
import com.ibm.icu.impl.StandardPlural;
import com.ibm.icu.impl.UResource;
-import com.ibm.icu.impl.number.DecimalQuantity;
-import com.ibm.icu.impl.number.modifiers.SimpleModifier;
-import com.ibm.icu.text.NumberFormat.Field;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
+import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.ICUException;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
-import newapi.NumberFormatter.UnitWidth;
-
public class LongNameHandler implements MicroPropsGenerator {
- private final Map<StandardPlural, SimpleModifier> modifiers;
- private PluralRules rules;
- private MicroPropsGenerator parent;
-
- private LongNameHandler(Map<StandardPlural, SimpleModifier> modifiers) {
- this.modifiers = modifiers;
- }
-
- /** For use by the "safe" code path */
- private LongNameHandler(LongNameHandler other) {
- this.modifiers = other.modifiers;
- }
-
- public static LongNameHandler forCurrencyLongNames(ULocale loc, Currency currency) {
- Map<String, String> data = CurrencyData.provider.getInstance(loc, true).getUnitPatterns();
- Map<StandardPlural, SimpleModifier> result = new EnumMap<StandardPlural, SimpleModifier>(StandardPlural.class);
- StringBuilder sb = new StringBuilder();
- for (Map.Entry<String, String> e : data.entrySet()) {
- String pluralKeyword = e.getKey();
- StandardPlural plural = StandardPlural.fromString(e.getKey());
- String longName = currency.getName(loc, Currency.PLURAL_LONG_NAME, pluralKeyword, null);
- String simpleFormat = e.getValue();
- // Example pattern from data: "{0} {1}"
- // Example output after find-and-replace: "{0} US dollars"
- simpleFormat = simpleFormat.replace("{1}", longName);
- String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
- SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY, false);
- result.put(plural, mod);
- }
- return new LongNameHandler(result);
- }
-
- public static LongNameHandler forMeasureUnit(ULocale loc, MeasureUnit unit, UnitWidth width) {
- Map<StandardPlural, String> simpleFormats = getMeasureData(loc, unit, width);
- Map<StandardPlural, SimpleModifier> result = new EnumMap<StandardPlural, SimpleModifier>(StandardPlural.class);
- StringBuilder sb = new StringBuilder();
- for (StandardPlural plural : StandardPlural.VALUES) {
- String simpleFormat = simpleFormats.get(plural);
- if (simpleFormat == null) {
- simpleFormat = simpleFormats.get(StandardPlural.OTHER);
- }
- if (simpleFormat == null) {
- // There should always be data in the "other" plural variant.
- throw new ICUException("Could not find data in 'other' plural variant for unit " + unit);
- }
- String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
- // TODO: What field to use for units?
- SimpleModifier mod = new SimpleModifier(compiled, null, false);
- result.put(plural, mod);
- }
- return new LongNameHandler(result);
- }
-
- /**
- * Applies locale data and inserts a long-name handler into the quantity chain.
- *
- * @param rules
- * The PluralRules instance to reference.
- * @param parent
- * The old head of the quantity chain.
- * @return The new head of the quantity chain.
- */
- public MicroPropsGenerator withLocaleData(PluralRules rules, MicroPropsGenerator parent) {
- this.rules = rules;
- this.parent = parent;
- return this;
- }
-
- @Override
- public MicroProps processQuantity(DecimalQuantity quantity) {
- MicroProps micros = parent.processQuantity(quantity);
- // TODO: Avoid the copy here?
- DecimalQuantity copy = quantity.createCopy();
- micros.rounding.apply(copy);
- micros.modOuter = modifiers.get(copy.getStandardPlural(rules));
- return micros;
- }
+ //////////////////////////
+ /// BEGIN DATA LOADING ///
+ //////////////////////////
- ///////////////////////////////////////
- /// BEGIN MEASURE UNIT DATA LOADING ///
- ///////////////////////////////////////
-
- private static final class MeasureUnitSink extends UResource.Sink {
+ private static final class PluralTableSink extends UResource.Sink {
Map<StandardPlural, String> output;
- public MeasureUnitSink(Map<StandardPlural, String> output) {
+ public PluralTableSink(Map<StandardPlural, String> output) {
this.output = output;
}
}
}
- private static Map<StandardPlural, String> getMeasureData(ULocale locale, MeasureUnit unit, UnitWidth width) {
- ICUResourceBundle resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME,
- locale);
+ private static void getMeasureData(ULocale locale, MeasureUnit unit, UnitWidth width,
+ Map<StandardPlural, String> output) {
+ PluralTableSink sink = new PluralTableSink(output);
+ ICUResourceBundle resource;
+ resource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_UNIT_BASE_NAME, locale);
StringBuilder key = new StringBuilder();
key.append("units");
if (width == UnitWidth.NARROW) {
key.append(unit.getType());
key.append("/");
key.append(unit.getSubtype());
- Map<StandardPlural, String> output = new EnumMap<StandardPlural, String>(StandardPlural.class);
- MeasureUnitSink sink = new MeasureUnitSink(output);
resource.getAllItemsWithFallback(key.toString(), sink);
- return output;
}
- /////////////////////////////////////
- /// END MEASURE UNIT DATA LOADING ///
- /////////////////////////////////////
+ private static void getCurrencyLongNameData(ULocale locale, Currency currency, Map<StandardPlural, String> output) {
+ // In ICU4J, this method gets a CurrencyData from CurrencyData.provider.
+ // TODO(ICU4J): Implement this without going through CurrencyData, like in ICU4C?
+ Map<String, String> data = CurrencyData.provider.getInstance(locale, true).getUnitPatterns();
+ for (Map.Entry<String, String> e : data.entrySet()) {
+ String pluralKeyword = e.getKey();
+ StandardPlural plural = StandardPlural.fromString(e.getKey());
+ String longName = currency.getName(locale, Currency.PLURAL_LONG_NAME, pluralKeyword, null);
+ String simpleFormat = e.getValue();
+ // Example pattern from data: "{0} {1}"
+ // Example output after find-and-replace: "{0} US dollars"
+ simpleFormat = simpleFormat.replace("{1}", longName);
+ // String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
+ // SimpleModifier mod = new SimpleModifier(compiled, Field.CURRENCY, false);
+ output.put(plural, simpleFormat);
+ }
+ }
+
+ ////////////////////////
+ /// END DATA LOADING ///
+ ////////////////////////
+
+ private final Map<StandardPlural, SimpleModifier> modifiers;
+ private final PluralRules rules;
+ private final MicroPropsGenerator parent;
+
+ private LongNameHandler(Map<StandardPlural, SimpleModifier> modifiers, PluralRules rules,
+ MicroPropsGenerator parent) {
+ this.modifiers = modifiers;
+ this.rules = rules;
+ this.parent = parent;
+ }
+
+ public static LongNameHandler forCurrencyLongNames(ULocale locale, Currency currency, PluralRules rules,
+ MicroPropsGenerator parent) {
+ Map<StandardPlural, String> simpleFormats = new EnumMap<StandardPlural, String>(StandardPlural.class);
+ getCurrencyLongNameData(locale, currency, simpleFormats);
+ // TODO(ICU4J): Reduce the number of object creations here?
+ Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlural, SimpleModifier>(
+ StandardPlural.class);
+ simpleFormatsToModifiers(simpleFormats, null, modifiers);
+ return new LongNameHandler(modifiers, rules, parent);
+ }
+
+ public static LongNameHandler forMeasureUnit(ULocale locale, MeasureUnit unit, UnitWidth width, PluralRules rules,
+ MicroPropsGenerator parent) {
+ Map<StandardPlural, String> simpleFormats = new EnumMap<StandardPlural, String>(StandardPlural.class);
+ getMeasureData(locale, unit, width, simpleFormats);
+ // TODO: What field to use for units?
+ // TODO(ICU4J): Reduce the number of object creations here?
+ Map<StandardPlural, SimpleModifier> modifiers = new EnumMap<StandardPlural, SimpleModifier>(
+ StandardPlural.class);
+ simpleFormatsToModifiers(simpleFormats, null, modifiers);
+ return new LongNameHandler(modifiers, rules, parent);
+ }
+
+ private static void simpleFormatsToModifiers(Map<StandardPlural, String> simpleFormats, NumberFormat.Field field,
+ Map<StandardPlural, SimpleModifier> output) {
+ StringBuilder sb = new StringBuilder();
+ for (StandardPlural plural : StandardPlural.VALUES) {
+ String simpleFormat = simpleFormats.get(plural);
+ if (simpleFormat == null) {
+ simpleFormat = simpleFormats.get(StandardPlural.OTHER);
+ }
+ if (simpleFormat == null) {
+ // There should always be data in the "other" plural variant.
+ throw new ICUException("Could not find data in 'other' plural variant with field " + field);
+ }
+ String compiled = SimpleFormatterImpl.compileToStringMinMaxArguments(simpleFormat, sb, 1, 1);
+ output.put(plural, new SimpleModifier(compiled, null, false));
+ }
+ }
+
+ @Override
+ public MicroProps processQuantity(DecimalQuantity quantity) {
+ MicroProps micros = parent.processQuantity(quantity);
+ // TODO: Avoid the copy here?
+ DecimalQuantity copy = quantity.createCopy();
+ micros.rounding.apply(copy);
+ micros.modOuter = modifiers.get(copy.getStandardPlural(rules));
+ return micros;
+ }
}
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
import java.util.Objects;
+import com.ibm.icu.number.Grouper;
+import com.ibm.icu.number.IntegerWidth;
+import com.ibm.icu.number.Notation;
+import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
+import com.ibm.icu.number.Rounder;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.ULocale;
-import newapi.Grouper;
-import newapi.IntegerWidth;
-import newapi.Notation;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.Rounder;
-
public class MacroProps implements Cloneable {
public Notation notation;
public MeasureUnit unit;
public Object symbols;
public UnitWidth unitWidth;
public SignDisplay sign;
- public DecimalMarkDisplay decimal;
+ public DecimalSeparatorDisplay decimal;
public AffixPatternProvider affixProvider; // not in API; for JDK compatibility mode only
public MultiplierImpl multiplier; // not in API; for JDK compatibility mode only
public PluralRules rules; // not in API; could be made public in the future
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
-import com.ibm.icu.impl.number.DecimalQuantity;
-import com.ibm.icu.impl.number.Modifier;
+import com.ibm.icu.number.Grouper;
+import com.ibm.icu.number.IntegerWidth;
+import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
import com.ibm.icu.text.DecimalFormatSymbols;
-import newapi.Grouper;
-import newapi.IntegerWidth;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.Rounder;
-
public class MicroProps implements Cloneable, MicroPropsGenerator {
// Populated globally:
public SignDisplay sign;
public DecimalFormatSymbols symbols;
public Padder padding;
- public DecimalMarkDisplay decimal;
+ public DecimalSeparatorDisplay decimal;
public IntegerWidth integerWidth;
// Populated by notation/unit:
public Modifier modInner;
public Rounder rounding;
public Grouper grouping;
- public int multiplier;
public boolean useCurrency;
// Internal fields:
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
-
-import com.ibm.icu.impl.number.DecimalQuantity;
+package com.ibm.icu.impl.number;
/**
* This interface is used when all number formatting settings, including the locale, are known, except for the quantity
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
/**
* @author sffc
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.impl.number;
-import newapi.impl.MutablePatternModifier;
-
/**
* A Modifier is an object that can be passed through the formatting pipeline until it is finally applied to the string
* builder. A Modifier usually contains a prefix and a suffix that are applied, but it could contain something else,
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
import java.math.BigDecimal;
-import com.ibm.icu.impl.number.DecimalQuantity;
-
public class MultiplierImpl implements MicroPropsGenerator, Cloneable {
final int magnitudeMultiplier;
final BigDecimal bigDecimalMultiplier;
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
public interface MultiplierProducer {
int getMultiplier(int magnitude);
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
+package com.ibm.icu.impl.number;
import com.ibm.icu.impl.StandardPlural;
-import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.AffixUtils.SymbolProvider;
-import com.ibm.icu.impl.number.DecimalQuantity;
-import com.ibm.icu.impl.number.Modifier;
-import com.ibm.icu.impl.number.NumberStringBuilder;
-import com.ibm.icu.impl.number.ParameterizedModifier;
-import com.ibm.icu.impl.number.PatternStringParser;
-import com.ibm.icu.impl.number.modifiers.ConstantMultiFieldModifier;
-import com.ibm.icu.impl.number.modifiers.CurrencySpacingEnabledModifier;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.Currency;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-
/**
* This class is a {@link Modifier} that wraps a decimal format pattern. It applies the pattern's affixes in
* {@link Modifier#apply}.
* it from multiple threads! Instead, you can obtain a safe, immutable decimal format pattern modifier by calling
* {@link MutablePatternModifier#createImmutable}, in effect treating this instance as a builder for the immutable
* variant.
- *
- * FIXME: Make this package-private
*/
public class MutablePatternModifier implements Modifier, SymbolProvider, CharSequence, MicroPropsGenerator {
case AffixUtils.TYPE_PERMILLE:
return symbols.getPerMillString();
case AffixUtils.TYPE_CURRENCY_SINGLE:
- // UnitWidth ISO overrides the singular currency symbol.
+ // UnitWidth ISO or HIDDEN overrides the singular currency symbol.
if (unitWidth == UnitWidth.ISO_CODE) {
return currency.getCurrencyCode();
+ } else if (unitWidth == UnitWidth.HIDDEN) {
+ return "";
} else {
return currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null);
}
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
-
-import com.ibm.icu.impl.number.Modifier;
-import com.ibm.icu.impl.number.NumberStringBuilder;
+package com.ibm.icu.impl.number;
public class Padder {
public static final String FALLBACK_PADDING_STRING = "\u0020"; // i.e. a space
}
}
- private static final Padder NONE = new Padder(null, -1, null);
+ /* like package-private */ public static final Padder NONE = new Padder(null, -1, null);
String paddingString;
int targetWidth;
public Padder(String paddingString, int targetWidth, PadPosition position) {
// TODO: Add a few default instances
- this.paddingString = (paddingString == null) ? " " : paddingString;
+ this.paddingString = (paddingString == null) ? FALLBACK_PADDING_STRING : paddingString;
this.targetWidth = targetWidth;
this.position = (position == null) ? PadPosition.BEFORE_PREFIX : position;
}
length += addPaddingHelper(paddingString, requiredPadding, string, rightIndex + length);
}
- // The length might not be exactly right due to currency spacing.
- // Make an adjustment if needed.
- while (string.codePointCount() < targetWidth) {
- int insertIndex = mod1.getPrefixLength() + mod2.getPrefixLength();
- switch (position) {
- case AFTER_PREFIX:
- insertIndex += leftIndex;
- break;
- case BEFORE_SUFFIX:
- insertIndex += rightIndex;
- break;
- default:
- // Should not happen since currency spacing is always on the inside.
- throw new AssertionError();
- }
- addPaddingHelper(paddingString, requiredPadding, string, insertIndex);
- }
-
return length;
}
--- /dev/null
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number;
+
+import com.ibm.icu.impl.StandardPlural;
+
+/**
+ * A ParameterizedModifier by itself is NOT a Modifier. Rather, it wraps a data structure containing two or more
+ * Modifiers and returns the modifier appropriate for the current situation.
+ */
+public class ParameterizedModifier {
+ private final Modifier positive;
+ private final Modifier negative;
+ final Modifier[] mods;
+ boolean frozen;
+
+ /**
+ * This constructor populates the ParameterizedModifier with a single positive and negative form.
+ *
+ * <p>
+ * If this constructor is used, a plural form CANNOT be passed to {@link #getModifier}.
+ */
+ public ParameterizedModifier(Modifier positive, Modifier negative) {
+ this.positive = positive;
+ this.negative = negative;
+ this.mods = null;
+ this.frozen = true;
+ }
+
+ /**
+ * This constructor prepares the ParameterizedModifier to be populated with a positive and negative Modifier for
+ * multiple plural forms.
+ *
+ * <p>
+ * If this constructor is used, a plural form MUST be passed to {@link #getModifier}.
+ */
+ public ParameterizedModifier() {
+ this.positive = null;
+ this.negative = null;
+ this.mods = new Modifier[2 * StandardPlural.COUNT];
+ this.frozen = false;
+ }
+
+ public void setModifier(boolean isNegative, StandardPlural plural, Modifier mod) {
+ assert !frozen;
+ mods[getModIndex(isNegative, plural)] = mod;
+ }
+
+ public void freeze() {
+ frozen = true;
+ }
+
+ public Modifier getModifier(boolean isNegative) {
+ assert frozen;
+ assert mods == null;
+ return isNegative ? negative : positive;
+ }
+
+ public Modifier getModifier(boolean isNegative, StandardPlural plural) {
+ assert frozen;
+ assert positive == null;
+ return mods[getModIndex(isNegative, plural)];
+ }
+
+ private static int getModIndex(boolean isNegative, StandardPlural plural) {
+ return plural.ordinal() * 2 + (isNegative ? 1 : 0);
+ }
+}
// License & terms of use: http://www.unicode.org/copyright.html#License
package com.ibm.icu.impl.number;
-import newapi.impl.AffixPatternProvider;
-import newapi.impl.Padder.PadPosition;
+import com.ibm.icu.impl.number.Padder.PadPosition;
/** Implements a recursive descent parser for decimal format patterns. */
public class PatternStringParser {
import java.math.BigDecimal;
+import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.text.DecimalFormatSymbols;
-import newapi.impl.Padder;
-import newapi.impl.Padder.PadPosition;
-
/**
* Assorted utilities relating to decimal formatting pattern strings.
*/
public static final int SECTION_MIDPOINT = 2;
public static final int SECTION_UPPER = 3;
+ /**
+ * The default rounding mode.
+ */
+ public static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_EVEN;
+
+ /**
+ * The maximum number of fraction places, integer numerals, or significant digits.
+ * TODO: This does not feel like the best home for this value.
+ */
+ public static final int MAX_INT_FRAC_SIG = 100;
+
/**
* Converts a rounding mode and metadata about the quantity being rounded to a boolean determining
* whether the value should be rounded toward infinity or toward zero.
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package com.ibm.icu.impl.number.modifiers;
+package com.ibm.icu.impl.number;
import com.ibm.icu.impl.SimpleFormatterImpl;
-import com.ibm.icu.impl.number.Modifier;
-import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.text.NumberFormat.Field;
/**
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.number.CompactData;
+import com.ibm.icu.impl.number.CompactData.CompactType;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.MicroProps;
+import com.ibm.icu.impl.number.MicroPropsGenerator;
+import com.ibm.icu.impl.number.MutablePatternModifier;
+import com.ibm.icu.impl.number.MutablePatternModifier.ImmutablePatternModifier;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.ULocale;
-import newapi.impl.CompactData;
-import newapi.impl.MicroProps;
-import newapi.impl.MicroPropsGenerator;
-import newapi.impl.MutablePatternModifier;
-import newapi.impl.MutablePatternModifier.ImmutablePatternModifier;
+/**
+ * A class that defines the scientific notation style to be used when formatting numbers in NumberFormatter.
+ *
+ * <p>
+ * This class exposes no public functionality. To create a CompactNotation, use one of the factory methods in
+ * {@link Notation}.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public class CompactNotation extends Notation {
final CompactStyle compactStyle;
final Map<String, Map<String, String>> compactCustomData;
- public enum CompactType {
- DECIMAL, CURRENCY
- }
-
- public CompactNotation(CompactStyle compactStyle) {
+ /* package-private */ CompactNotation(CompactStyle compactStyle) {
compactCustomData = null;
this.compactStyle = compactStyle;
}
- public CompactNotation(Map<String, Map<String, String>> compactCustomData) {
+ /* package-private */ CompactNotation(Map<String, Map<String, String>> compactCustomData) {
compactStyle = null;
this.compactCustomData = compactCustomData;
}
// No need to take any action.
} else if (precomputedMods != null) {
// Safe code path.
- // Java uses a hash set here for O(1) lookup. C++ uses a linear search.
+ // Java uses a hash set here for O(1) lookup. C++ uses a linear search.
CompactModInfo info = precomputedMods.get(patternString);
info.mod.applyToMicros(micros, quantity);
numDigits = info.numDigits;
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.Currency.CurrencyUsage;
-import com.ibm.icu.util.CurrencyAmount;
-import com.ibm.icu.util.Measure;
-import com.ibm.icu.util.MeasureUnit;
-/** A rounding strategy parameterized by a currency. */
+/**
+ * A class that defines a rounding strategy parameterized by a currency to be used when formatting numbers in
+ * NumberFormatter.
+ *
+ * <p>
+ * To create a CurrencyRounder, use one of the factory methods on Rounder.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public abstract class CurrencyRounder extends Rounder {
/* package-private */ CurrencyRounder() {
}
/**
- * Associates a {@link com.ibm.icu.util.Currency} with this rounding strategy. Only applies to rounding strategies
- * returned from {@link #currency(CurrencyUsage)}.
+ * Associates a currency with this rounding strategy.
*
* <p>
- * <strong>Calling this method is <em>not required</em></strong>, because the currency specified in
- * {@link NumberFormatterSettings#unit(MeasureUnit)} or via a {@link CurrencyAmount} passed into
- * {@link LocalizedNumberFormatter#format(Measure)} is automatically applied to currency rounding strategies.
- * However, this method enables you to override that automatic association.
+ * <strong>Calling this method is <em>not required</em></strong>, because the currency specified in unit() or via a
+ * CurrencyAmount passed into format(Measure) is automatically applied to currency rounding strategies. However,
+ * this method enables you to override that automatic association.
*
* <p>
* This method also enables numbers to be formatted using currency rounding rules without explicitly using a
*
* @param currency
* The currency to associate with this rounding strategy.
- * @return An immutable object for chaining.
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public Rounder withCurrency(Currency currency) {
if (currency != null) {
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.MicroProps;
import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
import com.ibm.icu.util.ICUUncheckedIOException;
-import newapi.impl.MicroProps;
-
public class FormattedNumber {
NumberStringBuilder nsb;
DecimalQuantity fq;
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
+
+import com.ibm.icu.impl.number.RoundingUtils;
/**
- * A rounding strategy based on a minimum and/or maximum number of fraction digits. Allows for a minimum or maximum
- * number of significant digits to be specified.
+ * A class that defines a rounding strategy based on a number of fraction places and optionally significant digits to be
+ * used when formatting numbers in NumberFormatter.
+ *
+ * <p>
+ * To create a FractionRounder, use one of the factory methods on Rounder.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public abstract class FractionRounder extends Rounder {
}
/**
- * Ensures that no less than this number of significant figures are retained when rounding according to fraction
+ * Ensure that no less than this number of significant digits are retained when rounding according to fraction
* rules.
*
* <p>
*
* @param minSignificantDigits
* The number of significant figures to guarantee.
- * @return An immutable object for chaining.
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public Rounder withMinDigits(int minSignificantDigits) {
- if (minSignificantDigits > 0 && minSignificantDigits <= MAX_VALUE) {
+ if (minSignificantDigits > 0 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
return constructFractionSignificant(this, minSignificantDigits, -1);
} else {
- throw new IllegalArgumentException("Significant digits must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
/**
- * Ensures that no more than this number of significant figures are retained when rounding according to fraction
+ * Ensure that no more than this number of significant digits are retained when rounding according to fraction
* rules.
*
* <p>
*
* @param maxSignificantDigits
* Round the number to no more than this number of significant figures.
- * @return An immutable object for chaining.
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public Rounder withMaxDigits(int maxSignificantDigits) {
- if (maxSignificantDigits > 0 && maxSignificantDigits <= MAX_VALUE) {
+ if (maxSignificantDigits > 0 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
return constructFractionSignificant(this, -1, maxSignificantDigits);
} else {
- throw new IllegalArgumentException("Significant digits must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
}
\ No newline at end of file
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
return DEFAULTS;
}
- public static Grouper min2() {
+ public static Grouper minTwoDigits() {
return MIN2;
}
}
}
- static Grouper normalizeType(Grouper grouping, ParsedPatternInfo patternInfo) {
- return grouping.withLocaleData(patternInfo);
- }
-
Grouper withLocaleData(ParsedPatternInfo patternInfo) {
if (grouping1 != -2) {
return this;
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
+
+import com.ibm.icu.impl.number.RoundingUtils;
@SuppressWarnings("unused")
public class IntegerWidth {
- private static final IntegerWidth DEFAULT = new IntegerWidth(1, -1);
+ /* package-private */ static final IntegerWidth DEFAULT = new IntegerWidth(1, -1);
final int minInt;
final int maxInt;
public static IntegerWidth zeroFillTo(int minInt) {
if (minInt == 1) {
return DEFAULT;
- } else if (minInt >= 0 && minInt < Rounder.MAX_VALUE) {
+ } else if (minInt >= 0 && minInt < RoundingUtils.MAX_INT_FRAC_SIG) {
return new IntegerWidth(minInt, -1);
} else {
- throw new IllegalArgumentException("Integer digits must be between 0 and " + Rounder.MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Integer digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
public IntegerWidth truncateAt(int maxInt) {
if (maxInt == this.maxInt) {
return this;
- } else if (maxInt >= 0 && maxInt < Rounder.MAX_VALUE) {
+ } else if (maxInt >= 0 && maxInt < RoundingUtils.MAX_INT_FRAC_SIG) {
return new IntegerWidth(minInt, maxInt);
} else if (maxInt == -1) {
return new IntegerWidth(minInt, maxInt);
} else {
- throw new IllegalArgumentException("Integer digits must be between 0 and " + Rounder.MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Integer digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
}
\ No newline at end of file
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
+import com.ibm.icu.impl.number.MacroProps;
+import com.ibm.icu.impl.number.MicroProps;
import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.util.Measure;
import com.ibm.icu.util.MeasureUnit;
-import newapi.impl.MacroProps;
-import newapi.impl.MicroProps;
-
public class LocalizedNumberFormatter extends NumberFormatterSettings<LocalizedNumberFormatter> {
static final AtomicLongFieldUpdater<LocalizedNumberFormatter> callCount = AtomicLongFieldUpdater
* This is the core entrypoint to the number formatting pipeline. It performs self-regulation: a static code path
* for the first few calls, and compiling a more efficient data structure if called repeatedly.
*
+ * <p>
+ * This function is very hot, being called in every call to the number formatting pipeline.
+ *
* @param fq
* The quantity to be formatted.
* @return The formatted number result.
@Deprecated
public FormattedNumber format(DecimalQuantity fq) {
MacroProps macros = resolve();
- NumberStringBuilder string = new NumberStringBuilder();
- // TODO: Make this more like C++, where we get and then conditionally atomic-increment?
+ // NOTE: In Java, the atomic increment logic is slightly different than ICU4C.
+ // It seems to be more efficient to make just one function call instead of two.
+ // Further benchmarking is required.
long currentCount = callCount.incrementAndGet(this);
+ NumberStringBuilder string = new NumberStringBuilder();
MicroProps micros;
if (currentCount == macros.threshold.longValue()) {
compiled = NumberFormatterImpl.fromMacros(macros);
--- /dev/null
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.number;
+
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
+
+/**
+ * A class that defines the notation style to be used when formatting numbers in NumberFormatter.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+public class Notation {
+
+ // TODO: Support engineering intervals other than 3?
+ private static final ScientificNotation SCIENTIFIC = new ScientificNotation(1, false, 1, SignDisplay.AUTO);
+ private static final ScientificNotation ENGINEERING = new ScientificNotation(3, false, 1, SignDisplay.AUTO);
+ private static final CompactNotation COMPACT_SHORT = new CompactNotation(CompactStyle.SHORT);
+ private static final CompactNotation COMPACT_LONG = new CompactNotation(CompactStyle.LONG);
+ private static final SimpleNotation SIMPLE = new SimpleNotation();
+
+ /* package-private */ Notation() {
+ }
+
+ /**
+ * Print the number using scientific notation (also known as scientific form, standard index form, or standard form
+ * in the UK). The format for scientific notation varies by locale; for example, many Western locales display the
+ * number in the form "#E0", where the number is displayed with one digit before the decimal separator, zero or more
+ * digits after the decimal separator, and the corresponding power of 10 displayed after the "E".
+ *
+ * <p>
+ * Example outputs in <em>en-US</em> when printing 8.765E4 through 8.765E-3:
+ *
+ * <pre>
+ * 8.765E4
+ * 8.765E3
+ * 8.765E2
+ * 8.765E1
+ * 8.765E0
+ * 8.765E-1
+ * 8.765E-2
+ * 8.765E-3
+ * 0E0
+ * </pre>
+ *
+ * @return A ScientificNotation for chaining or passing to the NumberFormatter notation() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static ScientificNotation scientific() {
+ return SCIENTIFIC;
+ }
+
+ /**
+ * Print the number using engineering notation, a variant of scientific notation in which the exponent must be
+ * divisible by 3.
+ *
+ * <p>
+ * Example outputs in <em>en-US</em> when printing 8.765E4 through 8.765E-3:
+ *
+ * <pre>
+ * 87.65E3
+ * 8.765E3
+ * 876.5E0
+ * 87.65E0
+ * 8.765E0
+ * 876.5E-3
+ * 87.65E-3
+ * 8.765E-3
+ * 0E0
+ * </pre>
+ *
+ * @return A ScientificNotation for chaining or passing to the NumberFormatter notation() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static ScientificNotation engineering() {
+ return ENGINEERING;
+ }
+
+ /**
+ * Print the number using short-form compact notation.
+ *
+ * <p>
+ * <em>Compact notation</em>, defined in Unicode Technical Standard #35 Part 3 Section 2.4.1, prints numbers with
+ * localized prefixes or suffixes corresponding to different powers of ten. Compact notation is similar to
+ * engineering notation in how it scales numbers.
+ *
+ * <p>
+ * Compact notation is ideal for displaying large numbers (over ~1000) to humans while at the same time minimizing
+ * screen real estate.
+ *
+ * <p>
+ * In short form, the powers of ten are abbreviated. In <em>en-US</em>, the abbreviations are "K" for thousands, "M"
+ * for millions, "B" for billions, and "T" for trillions. Example outputs in <em>en-US</em> when printing 8.765E7
+ * through 8.765E0:
+ *
+ * <pre>
+ * 88M
+ * 8.8M
+ * 876K
+ * 88K
+ * 8.8K
+ * 876
+ * 88
+ * 8.8
+ * </pre>
+ *
+ * <p>
+ * When compact notation is specified without an explicit rounding strategy, numbers are rounded off to the closest
+ * integer after scaling the number by the corresponding power of 10, but with a digit shown after the decimal
+ * separator if there is only one digit before the decimal separator. The default compact notation rounding strategy
+ * is equivalent to:
+ *
+ * <pre>
+ * Rounder.integer().withMinDigits(2)
+ * </pre>
+ *
+ * @return A CompactNotation for passing to the NumberFormatter notation() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static CompactNotation compactShort() {
+ return COMPACT_SHORT;
+ }
+
+ /**
+ * Print the number using long-form compact notation. For more information on compact notation, see
+ * {@link #compactShort}.
+ *
+ * <p>
+ * In long form, the powers of ten are spelled out fully. Example outputs in <em>en-US</em> when printing 8.765E7
+ * through 8.765E0:
+ *
+ * <pre>
+ * 88 million
+ * 8.8 million
+ * 876 thousand
+ * 88 thousand
+ * 8.8 thousand
+ * 876
+ * 88
+ * 8.8
+ * </pre>
+ *
+ * @return A CompactNotation for passing to the NumberFormatter notation() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static CompactNotation compactLong() {
+ return COMPACT_LONG;
+ }
+
+ /**
+ * Print the number using simple notation without any scaling by powers of ten. This is the default behavior.
+ *
+ * <p>
+ * Since this is the default behavior, this method needs to be called only when it is necessary to override a
+ * previous setting.
+ *
+ * <p>
+ * Example outputs in <em>en-US</em> when printing 8.765E7 through 8.765E0:
+ *
+ * <pre>
+ * 87,650,000
+ * 8,765,000
+ * 876,500
+ * 87,650
+ * 8,765
+ * 876.5
+ * 87.65
+ * 8.765
+ * </pre>
+ *
+ * @return A SimpleNotation for passing to the NumberFormatter notation() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static SimpleNotation simple() {
+ return SIMPLE;
+ }
+}
\ No newline at end of file
--- /dev/null
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.number;
+
+import java.util.Locale;
+
+import com.ibm.icu.impl.number.DecimalFormatProperties;
+import com.ibm.icu.impl.number.MacroProps;
+import com.ibm.icu.text.DecimalFormatSymbols;
+import com.ibm.icu.util.ULocale;
+
+public final class NumberFormatter {
+
+ private static final UnlocalizedNumberFormatter BASE = new UnlocalizedNumberFormatter();
+
+ /**
+ * An enum declaring how to render units, including currencies. Example outputs when formatting 123 USD and 123
+ * meters in <em>en-CA</em>:
+ *
+ * <p>
+ * <ul>
+ * <li>NARROW*: "$123.00" and "123 m"
+ * <li>SHORT: "US$ 123.00" and "123 m"
+ * <li>FULL_NAME: "123.00 US dollars" and "123 meters"
+ * <li>ISO_CODE: "USD 123.00" and undefined behavior
+ * <li>HIDDEN: "123.00" and "123"
+ * </ul>
+ *
+ * <p>
+ * * The narrow format for currencies is not currently supported; this is a known issue that will be fixed in a
+ * future version. See #11666 for more information.
+ *
+ * <p>
+ * This enum is similar to {@link com.ibm.icu.text.MeasureFormat.FormatWidth}.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static enum UnitWidth {
+ /**
+ * Print an abbreviated version of the unit name. Similar to SHORT, but always use the shortest available
+ * abbreviation or symbol. This option can be used when the context hints at the identity of the unit. For more
+ * information on the difference between NARROW and SHORT, see SHORT.
+ *
+ * <p>
+ * In CLDR, this option corresponds to the "Narrow" format for measure units and the "¤¤¤¤¤" placeholder for
+ * currencies.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ NARROW,
+
+ /**
+ * Print an abbreviated version of the unit name. Similar to NARROW, but use a slightly wider abbreviation or
+ * symbol when there may be ambiguity. This is the default behavior.
+ *
+ * <p>
+ * For example, in <em>es-US</em>, the SHORT form for Fahrenheit is "{0} °F", but the NARROW form is "{0}°",
+ * since Fahrenheit is the customary unit for temperature in that locale.
+ *
+ * <p>
+ * In CLDR, this option corresponds to the "Short" format for measure units and the "¤" placeholder for
+ * currencies.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ SHORT,
+
+ /**
+ * Print the full name of the unit, without any abbreviations.
+ *
+ * <p>
+ * In CLDR, this option corresponds to the default format for measure units and the "¤¤¤" placeholder for
+ * currencies.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ FULL_NAME,
+
+ /**
+ * Use the three-digit ISO XXX code in place of the symbol for displaying currencies. The behavior of this
+ * option is currently undefined for use with measure units.
+ *
+ * <p>
+ * In CLDR, this option corresponds to the "¤¤" placeholder for currencies.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ ISO_CODE,
+
+ /**
+ * Format the number according to the specified unit, but do not display the unit. For currencies, apply
+ * monetary symbols and formats as with SHORT, but omit the currency symbol. For measure units, the behavior is
+ * equivalent to not specifying the unit at all.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ HIDDEN,
+ }
+
+ /**
+ * An enum declaring how to denote positive and negative numbers. Example outputs when formatting 123 and -123 in
+ * <em>en-US</em>:
+ *
+ * <p>
+ * <ul>
+ * <li>AUTO: "123" and "-123"
+ * <li>ALWAYS: "+123" and "-123"
+ * <li>NEVER: "123" and "123"
+ * <li>ACCOUNTING: "$123" and "($123)"
+ * <li>ACCOUNTING_ALWAYS: "+$123" and "($123)"
+ * </ul>
+ *
+ * <p>
+ * The exact format, including the position and the code point of the sign, differ by locale.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ public static enum SignDisplay {
+ /**
+ * Show the minus sign on negative numbers, and do not show the sign on positive numbers. This is the default
+ * behavior.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ AUTO,
+
+ /**
+ * Show the minus sign on negative numbers and the plus sign on positive numbers.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ ALWAYS,
+
+ /**
+ * Do not show the sign on positive or negative numbers.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ NEVER,
+
+ /**
+ * Use the locale-dependent accounting format on negative numbers, and do not show the sign on positive numbers.
+ *
+ * <p>
+ * The accounting format is defined in CLDR and varies by locale; in many Western locales, the format is a pair
+ * of parentheses around the number.
+ *
+ * <p>
+ * Note: Since CLDR defines the accounting format in the monetary context only, this option falls back to the
+ * AUTO sign display strategy when formatting without a currency unit. This limitation may be lifted in the
+ * future.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ ACCOUNTING,
+
+ /**
+ * Use the locale-dependent accounting format on negative numbers, and show the plus sign on positive numbers.
+ * For more information on the accounting format, see the ACCOUNTING sign display strategy.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ ACCOUNTING_ALWAYS,
+ }
+
+ /**
+ * An enum declaring how to render the decimal separator. Example outputs when formatting 1 and 1.1 in
+ * <em>en-US</em>:
+ *
+ * <p>
+ * <ul>
+ * <li>AUTO: "1" and "1.1"
+ * <li>ALWAYS: "1." and "1.1"
+ * </ul>
+ */
+ public static enum DecimalSeparatorDisplay {
+ /**
+ * Show the decimal separator when there are one or more digits to display after the separator, and do not show
+ * it otherwise. This is the default behavior.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ AUTO,
+
+ /**
+ * Always show the decimal separator, even if there are no digits to display after the separator.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
+ ALWAYS,
+ }
+
+ /**
+ * Use a default threshold of 3. This means that the third time .format() is called, the data structures get built
+ * using the "safe" code path. The first two calls to .format() will trigger the unsafe code path.
+ */
+ static final long DEFAULT_THRESHOLD = 3;
+
+ /**
+ * Call this method at the beginning of a NumberFormatter fluent chain in which the locale is not currently known at
+ * the call site.
+ *
+ * @return An {@link UnlocalizedNumberFormatter}, to be used for chaining.
+ * @draft ICU 60
+ */
+ public static UnlocalizedNumberFormatter with() {
+ return BASE;
+ }
+
+ /**
+ * Call this method at the beginning of a NumberFormatter fluent chain in which the locale is known at the call
+ * site.
+ *
+ * @param locale
+ * The locale from which to load formats and symbols for number formatting.
+ * @return A {@link LocalizedNumberFormatter}, to be used for chaining.
+ * @draft ICU 60
+ */
+ public static LocalizedNumberFormatter withLocale(Locale locale) {
+ return BASE.locale(locale);
+ }
+
+ /**
+ * Call this method at the beginning of a NumberFormatter fluent chain in which the locale is known at the call
+ * site.
+ *
+ * @param locale
+ * The locale from which to load formats and symbols for number formatting.
+ * @return A {@link LocalizedNumberFormatter}, to be used for chaining.
+ * @draft ICU 60
+ */
+ public static LocalizedNumberFormatter withLocale(ULocale locale) {
+ return BASE.locale(locale);
+ }
+
+ /**
+ * @internal
+ * @deprecated ICU 60 This API is ICU internal only.
+ */
+ @Deprecated
+ public static UnlocalizedNumberFormatter fromDecimalFormat(DecimalFormatProperties properties,
+ DecimalFormatSymbols symbols, DecimalFormatProperties exportedProperties) {
+ MacroProps macros = NumberPropertyMapper.oldToNew(properties, symbols, exportedProperties);
+ return NumberFormatter.with().macros(macros);
+ }
+}
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
+import com.ibm.icu.impl.number.CompactData.CompactType;
+import com.ibm.icu.impl.number.ConstantAffixModifier;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.LongNameHandler;
+import com.ibm.icu.impl.number.MacroProps;
+import com.ibm.icu.impl.number.MicroProps;
+import com.ibm.icu.impl.number.MicroPropsGenerator;
+import com.ibm.icu.impl.number.MutablePatternModifier;
import com.ibm.icu.impl.number.NumberStringBuilder;
+import com.ibm.icu.impl.number.Padder;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
-import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier;
+import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.NumberingSystem;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.Currency.CurrencyUsage;
-import com.ibm.icu.util.NoUnit;
-import com.ibm.icu.util.ULocale;
-
-import newapi.CompactNotation.CompactType;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.impl.LongNameHandler;
-import newapi.impl.MacroProps;
-import newapi.impl.MicroProps;
-import newapi.impl.MicroPropsGenerator;
-import newapi.impl.MutablePatternModifier;
-import newapi.impl.Padder;
+import com.ibm.icu.util.MeasureUnit;
/**
* This is the "brain" of the number formatting pipeline. It ties all the pieces together, taking in a MacroProps and a
//////////
+ private static boolean unitIsCurrency(MeasureUnit unit) {
+ // TODO: Check using "instanceof" operator instead?
+ return unit != null && "currency".equals(unit.getType());
+ }
+
+ private static boolean unitIsNoUnit(MeasureUnit unit) {
+ // NOTE: In ICU4C, units cannot be null, and the default unit is a NoUnit.
+ // In ICU4J, return TRUE for a null unit from this method.
+ return unit == null || "none".equals(unit.getType());
+ }
+
+ private static boolean unitIsPercent(MeasureUnit unit) {
+ return unit != null && "percent".equals(unit.getSubtype());
+ }
+
+ private static boolean unitIsPermille(MeasureUnit unit) {
+ return unit != null && "permille".equals(unit.getSubtype());
+ }
+
/**
* Synthesizes the MacroProps into a MicroPropsGenerator. All information, including the locale, is encoded into the
* MicroPropsGenerator, except for the quantity itself, which is left abstract and must be provided to the returned
* object is more expensive.
*/
private static MicroPropsGenerator macrosToMicroGenerator(MacroProps macros, boolean safe) {
-
- String innerPattern = null;
- LongNameHandler longNames = null;
- Rounder defaultRounder = Rounder.unlimited();
- Currency currency = DEFAULT_CURRENCY;
- UnitWidth unitWidth = (macros.unitWidth == null) ? UnitWidth.SHORT : macros.unitWidth;
- boolean perMille = false;
- PluralRules rules = macros.rules;
-
- // FIXME
- String nsName = NumberingSystem.getInstance(macros.loc).getName();
-
MicroProps micros = new MicroProps(safe);
MicroPropsGenerator chain = micros;
- // Copy over the simple settings
- micros.sign = macros.sign == null ? SignDisplay.AUTO : macros.sign;
- micros.decimal = macros.decimal == null ? DecimalMarkDisplay.AUTO : macros.decimal;
- micros.multiplier = 0;
- micros.integerWidth = macros.integerWidth == null ? IntegerWidth.zeroFillTo(1) : macros.integerWidth;
-
- if (macros.unit == null || macros.unit == NoUnit.BASE) {
- // No units; default format
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.NUMBERSTYLE);
- } else if (macros.unit == NoUnit.PERCENT) {
- // Percent
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.PERCENTSTYLE);
- micros.multiplier += 2;
- } else if (macros.unit == NoUnit.PERMILLE) {
- // Permille
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.PERCENTSTYLE);
- micros.multiplier += 3;
- perMille = true;
- } else if (macros.unit instanceof Currency && macros.unitWidth != UnitWidth.FULL_NAME) {
- // Narrow, short, or ISO currency.
- // TODO: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now,
- // the API contract allows us to add support to other units.
- if (macros.sign == SignDisplay.ACCOUNTING || macros.sign == SignDisplay.ACCOUNTING_ALWAYS) {
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.ACCOUNTINGCURRENCYSTYLE);
- } else {
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.CURRENCYSTYLE);
- }
- defaultRounder = Rounder.currency(CurrencyUsage.STANDARD);
- currency = (Currency) macros.unit;
- micros.useCurrency = true;
- } else if (macros.unit instanceof Currency) {
- // Currency long name
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.NUMBERSTYLE);
- longNames = LongNameHandler.forCurrencyLongNames(macros.loc, (Currency) macros.unit);
- defaultRounder = Rounder.currency(CurrencyUsage.STANDARD);
- currency = (Currency) macros.unit;
- micros.useCurrency = true;
+ // TODO: Normalize the currency (accept symbols from DecimalFormatSymbols)?
+ // currency = CustomSymbolCurrency.resolve(currency, input.loc, micros.symbols);
+
+ // Pre-compute a few values for efficiency.
+ boolean isCurrency = unitIsCurrency(macros.unit);
+ boolean isNoUnit = unitIsNoUnit(macros.unit);
+ boolean isPercent = isNoUnit && unitIsPercent(macros.unit);
+ boolean isPermille = isNoUnit && unitIsPermille(macros.unit);
+ boolean isCldrUnit = !isCurrency && !isNoUnit;
+ boolean isAccounting = macros.sign == SignDisplay.ACCOUNTING || macros.sign == SignDisplay.ACCOUNTING_ALWAYS;
+ Currency currency = isCurrency ? (Currency) macros.unit : DEFAULT_CURRENCY;
+ UnitWidth unitWidth = UnitWidth.SHORT;
+ if (macros.unitWidth != null) {
+ unitWidth = macros.unitWidth;
+ }
+ PluralRules rules = macros.rules;
+
+ // Select the numbering system.
+ NumberingSystem ns;
+ if (macros.symbols instanceof NumberingSystem) {
+ ns = (NumberingSystem) macros.symbols;
} else {
- // MeasureUnit
- innerPattern = NumberFormat.getPatternForStyle(macros.loc, NumberFormat.NUMBERSTYLE);
- longNames = LongNameHandler.forMeasureUnit(macros.loc, macros.unit, unitWidth);
+ // TODO: Is there a way to avoid creating the NumberingSystem object?
+ ns = NumberingSystem.getInstance(macros.loc);
}
+ String nsName = ns.getName();
+
+ // Load and parse the pattern string. It is used for grouping sizes and affixes only.
+ int patternStyle;
+ if (isPercent || isPermille) {
+ patternStyle = NumberFormat.PERCENTSTYLE;
+ } else if (!isCurrency || unitWidth == UnitWidth.FULL_NAME) {
+ patternStyle = NumberFormat.NUMBERSTYLE;
+ } else if (isAccounting) {
+ // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now,
+ // the API contract allows us to add support to other units in the future.
+ patternStyle = NumberFormat.ACCOUNTINGCURRENCYSTYLE;
+ } else {
+ patternStyle = NumberFormat.CURRENCYSTYLE;
+ }
+ String pattern = NumberFormat.getPatternForStyleAndNumberingSystem(macros.loc, nsName, patternStyle);
+ ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(pattern);
- // Parse the pattern, which is used for grouping and affixes only.
- ParsedPatternInfo patternInfo = PatternStringParser.parseToPatternInfo(innerPattern);
+ /////////////////////////////////////////////////////////////////////////////////////
+ /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
+ /////////////////////////////////////////////////////////////////////////////////////
// Symbols
- // NOTE: C++ has a special class, SymbolsWrapper, in MacroProps. Java has all the resolution logic here
- // directly.
- if (macros.symbols == null) {
- micros.symbols = DecimalFormatSymbols.getInstance(macros.loc);
- } else if (macros.symbols instanceof DecimalFormatSymbols) {
+ if (macros.symbols instanceof DecimalFormatSymbols) {
micros.symbols = (DecimalFormatSymbols) macros.symbols;
- } else if (macros.symbols instanceof NumberingSystem) {
- // TODO: Do this more efficiently. Will require modifying DecimalFormatSymbols.
- NumberingSystem ns = (NumberingSystem) macros.symbols;
- ULocale temp = macros.loc.setKeywordValue("numbers", ns.getName());
- micros.symbols = DecimalFormatSymbols.getInstance(temp);
} else {
- throw new AssertionError();
+ micros.symbols = DecimalFormatSymbols.forNumberingSystem(macros.loc, ns);
}
- // TODO: Normalize the currency (accept symbols from DecimalFormatSymbols)?
- // currency = CustomSymbolCurrency.resolve(currency, input.loc, micros.symbols);
-
// Multiplier (compatibility mode value).
- // An int magnitude multiplier is used when not in compatibility mode to
- // reduce object creations.
if (macros.multiplier != null) {
chain = macros.multiplier.copyAndChain(chain);
}
// Rounding strategy
if (macros.rounder != null) {
- micros.rounding = Rounder.normalizeType(macros.rounder, currency);
+ micros.rounding = macros.rounder;
} else if (macros.notation instanceof CompactNotation) {
micros.rounding = Rounder.COMPACT_STRATEGY;
+ } else if (isCurrency) {
+ micros.rounding = Rounder.MONETARY_STANDARD;
} else {
- micros.rounding = Rounder.normalizeType(defaultRounder, currency);
+ micros.rounding = Rounder.MAX_FRAC_6;
}
+ micros.rounding = micros.rounding.withLocaleData(currency);
// Grouping strategy
if (macros.grouper != null) {
- micros.grouping = Grouper.normalizeType(macros.grouper, patternInfo);
+ micros.grouping = macros.grouper;
} else if (macros.notation instanceof CompactNotation) {
// Compact notation uses minGrouping by default since ICU 59
- micros.grouping = Grouper.normalizeType(Grouper.min2(), patternInfo);
+ micros.grouping = Grouper.minTwoDigits();
} else {
- micros.grouping = Grouper.normalizeType(Grouper.defaults(), patternInfo);
+ micros.grouping = Grouper.defaults();
}
+ micros.grouping = micros.grouping.withLocaleData(patternInfo);
// Padding strategy
if (macros.padder != null) {
micros.padding = macros.padder;
} else {
- micros.padding = Padder.none();
+ micros.padding = Padder.NONE;
}
+ // Integer width
+ if (macros.integerWidth != null) {
+ micros.integerWidth = macros.integerWidth;
+ } else {
+ micros.integerWidth = IntegerWidth.DEFAULT;
+ }
+
+ // Sign display
+ if (macros.sign != null) {
+ micros.sign = macros.sign;
+ } else {
+ micros.sign = SignDisplay.AUTO;
+ }
+
+ // Decimal mark display
+ if (macros.decimal != null) {
+ micros.decimal = macros.decimal;
+ } else {
+ micros.decimal = DecimalSeparatorDisplay.AUTO;
+ }
+
+ // Use monetary separator symbols
+ micros.useCurrency = isCurrency;
+
// Inner modifier (scientific notation)
if (macros.notation instanceof ScientificNotation) {
chain = ((ScientificNotation) macros.notation).withLocaleData(micros.symbols, safe, chain);
// The default middle modifier is weak (thus the false argument).
MutablePatternModifier patternMod = new MutablePatternModifier(false);
patternMod.setPatternInfo((macros.affixProvider != null) ? macros.affixProvider : patternInfo);
- patternMod.setPatternAttributes(micros.sign, perMille);
+ patternMod.setPatternAttributes(micros.sign, isPermille);
if (patternMod.needsPlurals()) {
if (rules == null) {
// Lazily create PluralRules
}
// Outer modifier (CLDR units and currency long names)
- if (longNames != null) {
+ if (isCldrUnit) {
+ if (rules == null) {
+ // Lazily create PluralRules
+ rules = PluralRules.forLocale(macros.loc);
+ }
+ chain = LongNameHandler.forMeasureUnit(macros.loc, macros.unit, unitWidth, rules, chain);
+ } else if (isCurrency && unitWidth == UnitWidth.FULL_NAME) {
if (rules == null) {
// Lazily create PluralRules
rules = PluralRules.forLocale(macros.loc);
}
- chain = longNames.withLocaleData(rules, chain);
+ chain = LongNameHandler.forCurrencyLongNames(macros.loc, currency, rules, chain);
} else {
// No outer modifier required
micros.modOuter = ConstantAffixModifier.EMPTY;
* The output string. Will be mutated.
*/
private static void microsToString(MicroProps micros, DecimalQuantity quantity, NumberStringBuilder string) {
- quantity.adjustMagnitude(micros.multiplier);
micros.rounding.apply(quantity);
if (micros.integerWidth.maxInt == -1) {
quantity.setIntegerLength(micros.integerWidth.minInt, Integer.MAX_VALUE);
length += writeIntegerDigits(micros, quantity, string);
// Add the decimal point
- if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == DecimalMarkDisplay.ALWAYS) {
+ if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == DecimalSeparatorDisplay.ALWAYS) {
length += string.insert(length, micros.useCurrency ? micros.symbols.getMonetaryDecimalSeparatorString()
: micros.symbols.getDecimalSeparatorString(), NumberFormat.Field.DECIMAL_SEPARATOR);
}
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
+import com.ibm.icu.impl.number.MacroProps;
+import com.ibm.icu.impl.number.Padder;
+import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.MeasureFormat.FormatWidth;
import com.ibm.icu.text.NumberingSystem;
import com.ibm.icu.util.NoUnit;
import com.ibm.icu.util.ULocale;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.impl.MacroProps;
-import newapi.impl.Padder;
-
/**
* An abstract base class for specifying settings related to number formatting. This class is implemented by
* {@link UnlocalizedNumberFormatter} and {@link LocalizedNumberFormatter}.
return create(KEY_SIGN, style);
}
- public T decimal(DecimalMarkDisplay style) {
+ public T decimal(DecimalSeparatorDisplay style) {
return create(KEY_DECIMAL, style);
}
return create(KEY_THRESHOLD, threshold);
}
- /** Non-public method */
- public String toSkeleton() {
- return SkeletonBuilder.macrosToSkeleton(resolve());
- }
-
abstract T create(int key, Object value);
MacroProps resolve() {
break;
case KEY_DECIMAL:
if (macros.decimal == null) {
- macros.decimal = (DecimalMarkDisplay) current.value;
+ macros.decimal = (DecimalSeparatorDisplay) current.value;
}
break;
case KEY_THRESHOLD:
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import java.math.BigDecimal;
import java.math.MathContext;
import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.number.AffixPatternProvider;
import com.ibm.icu.impl.number.AffixUtils;
+import com.ibm.icu.impl.number.CustomSymbolCurrency;
import com.ibm.icu.impl.number.DecimalFormatProperties;
+import com.ibm.icu.impl.number.MacroProps;
+import com.ibm.icu.impl.number.MultiplierImpl;
+import com.ibm.icu.impl.number.Padder;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringParser.ParsedPatternInfo;
+import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.Rounder.FractionRounderImpl;
+import com.ibm.icu.number.Rounder.IncrementRounderImpl;
+import com.ibm.icu.number.Rounder.SignificantRounderImpl;
import com.ibm.icu.impl.number.RoundingUtils;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.CurrencyPluralInfo;
import com.ibm.icu.util.Currency.CurrencyUsage;
import com.ibm.icu.util.ULocale;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.Rounder.FractionRounderImpl;
-import newapi.Rounder.IncrementRounderImpl;
-import newapi.Rounder.SignificantRounderImpl;
-import newapi.impl.AffixPatternProvider;
-import newapi.impl.CustomSymbolCurrency;
-import newapi.impl.MacroProps;
-import newapi.impl.MultiplierImpl;
-import newapi.impl.Padder;
-
/**
* <p>
* This class, as well as NumberFormatterImpl, could go into the impl package, but they depend on too many
// DECIMAL MARK ALWAYS SHOWN //
///////////////////////////////
- macros.decimal = properties.getDecimalSeparatorAlwaysShown() ? DecimalMarkDisplay.ALWAYS
- : DecimalMarkDisplay.AUTO;
+ macros.decimal = properties.getDecimalSeparatorAlwaysShown() ? DecimalSeparatorDisplay.ALWAYS
+ : DecimalSeparatorDisplay.AUTO;
///////////////////////
// SIGN ALWAYS SHOWN //
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.MultiplierProducer;
import com.ibm.icu.impl.number.RoundingUtils;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
-import newapi.impl.MultiplierProducer;
-
+/**
+ * A class that defines the rounding strategy to be used when formatting numbers in NumberFormatter.
+ *
+ * <p>
+ * To create a Rounder, use one of the factory methods.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public abstract class Rounder implements Cloneable {
- // FIXME
- /** @internal */
- public static final int MAX_VALUE = 100;
-
/* package-private final */ MathContext mathContext;
/* package-private */ Rounder() {
- mathContext = RoundingUtils.mathContextUnlimited(RoundingMode.HALF_EVEN);
+ mathContext = RoundingUtils.mathContextUnlimited(RoundingUtils.DEFAULT_ROUNDING_MODE);
}
/**
* Show all available digits to full precision.
*
* <p>
- * <strong>NOTE:</strong> If you are formatting <em>doubles</em> and you know that the number of fraction places or
- * significant digits is bounded, consider using {@link #maxFraction} or {@link #maxDigits} instead to maximize
- * performance.
+ * <strong>NOTE:</strong> When formatting a <em>double</em>, this method, along with {@link #minFraction} and
+ * {@link #minDigits}, will trigger complex algorithm similar to <em>Dragon4</em> to determine the low-order digits
+ * and the number of digits to display based on the value of the double. If the number of fraction places or
+ * significant digits can be bounded, consider using {@link #maxFraction} or {@link #maxDigits} instead to maximize
+ * performance. For more information, read the following blog post.
+ *
+ * <p>
+ * http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/
*
- * @return A rounding strategy for {@link NumberFormatterSettings#rounding}.
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public static Rounder unlimited() {
return constructInfinite();
/**
* Show numbers rounded if necessary to the nearest integer.
*
- * @return A rounding strategy for {@link NumberFormatterSettings#rounding}.
+ * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public static FractionRounder integer() {
return constructFraction(0, 0);
}
/**
- * Show numbers rounded if necessary to a certain number of fraction places (digits after the decimal mark).
- * Additionally, pad with zeros to ensure that this number digits are always shown.
+ * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator).
+ * Additionally, pad with zeros to ensure that this number of places are always shown.
*
* <p>
- * Example output with minMaxFractionDigits = 3:
+ * Example output with minMaxFractionPlaces = 3:
*
* <p>
* 87,650.000<br>
* <p>
* This method is equivalent to {@link #minMaxFraction} with both arguments equal.
*
- * @param minMaxFractionDigits
- * The minimum and maximum number of digits to display after the decimal mark (rounding if too long or
- * padding with zeros if too short).
- * @return A rounding strategy for {@link NumberFormatterSettings#rounding}.
+ * @param minMaxFractionPlaces
+ * The minimum and maximum number of numerals to display after the decimal separator (rounding if too
+ * long or padding with zeros if too short).
+ * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
- public static FractionRounder fixedFraction(int minMaxFractionDigits) {
- if (minMaxFractionDigits >= 0 && minMaxFractionDigits <= MAX_VALUE) {
- return constructFraction(minMaxFractionDigits, minMaxFractionDigits);
+ public static FractionRounder fixedFraction(int minMaxFractionPlaces) {
+ if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) {
+ return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces);
} else {
- throw new IllegalArgumentException("Fraction length must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
/**
- * Always show a certain number of digits after the decimal mark, padding with zeros if necessary. Do not perform
- * rounding (display numbers to their full precision).
+ * Always show at least a certain number of fraction places after the decimal separator, padding with zeros if
+ * necessary. Do not perform rounding (display numbers to their full precision).
*
* <p>
- * <strong>NOTE:</strong> If you are formatting <em>doubles</em> and you know that the number of fraction places is
- * bounded, consider using {@link #fixedFraction} or {@link #minMaxFraction} instead to maximize performance.
+ * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in {@link #unlimited}.
*
- * @param minFractionDigits
- * The minimum number of digits to display after the decimal mark (padding with zeros if necessary).
- * @return A rounding strategy for {@link NumberFormatterSettings#rounding}.
+ * @param minFractionPlaces
+ * The minimum number of numerals to display after the decimal separator (padding with zeros if
+ * necessary).
+ * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
- public static FractionRounder minFraction(int minFractionDigits) {
- if (minFractionDigits >= 0 && minFractionDigits < MAX_VALUE) {
- return constructFraction(minFractionDigits, -1);
+ public static FractionRounder minFraction(int minFractionPlaces) {
+ if (minFractionPlaces >= 0 && minFractionPlaces < RoundingUtils.MAX_INT_FRAC_SIG) {
+ return constructFraction(minFractionPlaces, -1);
} else {
- throw new IllegalArgumentException("Fraction length must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
/**
- * Show numbers rounded if necessary to a certain number of fraction places (digits after the decimal mark). Unlike
- * the other fraction rounding strategies, this strategy does <em>not</em> pad zeros to the end of the number.
+ * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator).
+ * Unlike the other fraction rounding strategies, this strategy does <em>not</em> pad zeros to the end of the
+ * number.
*
- * @param maxFractionDigits
- * The maximum number of digits to display after the decimal mark (rounding if necessary).
- * @return A rounding strategy for {@link NumberFormatterSettings#rounding}.
+ * @param maxFractionPlaces
+ * The maximum number of numerals to display after the decimal mark (rounding if necessary).
+ * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
- public static FractionRounder maxFraction(int maxFractionDigits) {
- if (maxFractionDigits >= 0 && maxFractionDigits < MAX_VALUE) {
- return constructFraction(0, maxFractionDigits);
+ public static FractionRounder maxFraction(int maxFractionPlaces) {
+ if (maxFractionPlaces >= 0 && maxFractionPlaces < RoundingUtils.MAX_INT_FRAC_SIG) {
+ return constructFraction(0, maxFractionPlaces);
} else {
- throw new IllegalArgumentException("Fraction length must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
/**
- * Show numbers rounded if necessary to a certain number of fraction places (digits after the decimal mark); in
- * addition, always show a certain number of digits after the decimal mark, padding with zeros if necessary.
+ * Show numbers rounded if necessary to a certain number of fraction places (numerals after the decimal separator);
+ * in addition, always show at least a certain number of places after the decimal separator, padding with zeros if
+ * necessary.
*
- * @param minFractionDigits
- * The minimum number of digits to display after the decimal mark (padding with zeros if necessary).
- * @param maxFractionDigits
- * The maximum number of digits to display after the decimal mark (rounding if necessary).
- * @return A rounding strategy for {@link NumberFormatterSettings#rounding}.
+ * @param minFractionPlaces
+ * The minimum number of numerals to display after the decimal separator (padding with zeros if
+ * necessary).
+ * @param maxFractionPlaces
+ * The maximum number of numerals to display after the decimal separator (rounding if necessary).
+ * @return A FractionRounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
- public static FractionRounder minMaxFraction(int minFractionDigits, int maxFractionDigits) {
- if (minFractionDigits >= 0 && maxFractionDigits <= MAX_VALUE && minFractionDigits <= maxFractionDigits) {
- return constructFraction(minFractionDigits, maxFractionDigits);
+ public static FractionRounder minMaxFraction(int minFractionPlaces, int maxFractionPlaces) {
+ if (minFractionPlaces >= 0 && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG
+ && minFractionPlaces <= maxFractionPlaces) {
+ return constructFraction(minFractionPlaces, maxFractionPlaces);
} else {
- throw new IllegalArgumentException("Fraction length must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Fraction length must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
+ /**
+ * Show numbers rounded if necessary to a certain number of significant digits or significant figures. Additionally,
+ * pad with zeros to ensure that this number of significant digits/figures are always shown.
+ *
+ * <p>
+ * This method is equivalent to {@link #minMaxDigits} with both arguments equal.
+ *
+ * @param minMaxSignificantDigits
+ * The minimum and maximum number of significant digits to display (rounding if too long or padding with
+ * zeros if too short).
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public static Rounder fixedDigits(int minMaxSignificantDigits) {
- if (minMaxSignificantDigits > 0 && minMaxSignificantDigits <= MAX_VALUE) {
+ if (minMaxSignificantDigits > 0 && minMaxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits);
} else {
- throw new IllegalArgumentException("Significant digits must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
+ /**
+ * Always show at least a certain number of significant digits/figures, padding with zeros if necessary. Do not
+ * perform rounding (display numbers to their full precision).
+ *
+ * <p>
+ * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in {@link #unlimited}.
+ *
+ * @param minSignificantDigits
+ * The minimum number of significant digits to display (padding with zeros if too short).
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public static Rounder minDigits(int minSignificantDigits) {
- if (minSignificantDigits > 0 && minSignificantDigits <= MAX_VALUE) {
+ if (minSignificantDigits > 0 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
return constructSignificant(minSignificantDigits, -1);
} else {
- throw new IllegalArgumentException("Significant digits must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
+ /**
+ * Show numbers rounded if necessary to a certain number of significant digits/figures.
+ *
+ * @param maxSignificantDigits
+ * The maximum number of significant digits to display (rounding if too long).
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public static Rounder maxDigits(int maxSignificantDigits) {
- if (maxSignificantDigits > 0 && maxSignificantDigits <= MAX_VALUE) {
+ if (maxSignificantDigits > 0 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) {
return constructSignificant(0, maxSignificantDigits);
} else {
- throw new IllegalArgumentException("Significant digits must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
+ /**
+ * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition, always show at
+ * least a certain number of significant digits, padding with zeros if necessary.
+ *
+ * @param minSignificantDigits
+ * The minimum number of significant digits to display (padding with zeros if necessary).
+ * @param maxSignificantDigits
+ * The maximum number of significant digits to display (rounding if necessary).
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public static Rounder minMaxDigits(int minSignificantDigits, int maxSignificantDigits) {
- if (minSignificantDigits > 0 && maxSignificantDigits <= MAX_VALUE
+ if (minSignificantDigits > 0 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG
&& minSignificantDigits <= maxSignificantDigits) {
return constructSignificant(minSignificantDigits, maxSignificantDigits);
} else {
- throw new IllegalArgumentException("Significant digits must be between 0 and " + MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Significant digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
+ /**
+ * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For example, if the
+ * rounding increment is 0.5, then round 1.2 to 1 and round 1.3 to 1.5.
+ *
+ * <p>
+ * In order to ensure that numbers are padded to the appropriate number of fraction places, set the scale on the
+ * rounding increment BigDecimal. For example, to round to the nearest 0.5 and always display 2 numerals after the
+ * decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you can run:
+ *
+ * <pre>
+ * Rounder.increment(new BigDecimal("0.50"))
+ * </pre>
+ *
+ * <p>
+ * For more information on the scale of Java BigDecimal, see {@link java.math.BigDecimal#scale()}.
+ *
+ * @param roundingIncrement
+ * The increment to which to round numbers.
+ * @return A Rounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public static Rounder increment(BigDecimal roundingIncrement) {
if (roundingIncrement != null && roundingIncrement.compareTo(BigDecimal.ZERO) > 0) {
return constructIncrement(roundingIncrement);
}
}
+ /**
+ * Show numbers rounded and padded according to the rules for the currency unit. The most common rounding settings
+ * for currencies include <code>Rounder.fixedFraction(2)</code>, <code>Rounder.integer()</code>, and
+ * <code>Rounder.increment(0.05)</code> for cash transactions ("nickel rounding").
+ *
+ * <p>
+ * The exact rounding details will be resolved at runtime based on the currency unit specified in the
+ * NumberFormatter chain. To round according to the rules for one currency while displaying the symbol for another
+ * currency, the withCurrency() method can be called on the return value of this method.
+ *
+ * @param currencyUsage
+ * Either STANDARD (for digital transactions) or CASH (for transactions where the rounding increment may
+ * be limited by the available denominations of cash or coins).
+ * @return A CurrencyRounder for chaining or passing to the NumberFormatter rounding() setter.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public static CurrencyRounder currency(CurrencyUsage currencyUsage) {
if (currencyUsage != null) {
return constructCurrency(currencyUsage);
}
/**
- * Sets the {@link java.math.RoundingMode} to use when picking the direction to round (up or down).
- *
- * <p>
- * Common values include {@link RoundingMode#HALF_EVEN}, {@link RoundingMode#HALF_UP}, and
- * {@link RoundingMode#CEILING}. The default is HALF_EVEN.
+ * Sets the {@link java.math.RoundingMode} to use when picking the direction to round (up or down). Common values
+ * include HALF_EVEN, HALF_UP, and FLOOR. The default is HALF_EVEN.
*
* @param roundingMode
* The RoundingMode to use.
- * @return An immutable object for chaining.
+ * @return A Rounder for chaining.
+ * @draft ICU 60
+ * @see NumberFormatter
*/
public Rounder withMode(RoundingMode roundingMode) {
return withMode(RoundingUtils.mathContextUnlimited(roundingMode));
return other;
}
+ /**
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ @Deprecated
@Override
public Object clone() {
try {
// PACKAGE-PRIVATE APIS //
//////////////////////////
- private static final InfiniteRounderImpl NONE = new InfiniteRounderImpl();
+ static final InfiniteRounderImpl NONE = new InfiniteRounderImpl();
- private static final FractionRounderImpl FIXED_FRAC_0 = new FractionRounderImpl(0, 0);
- private static final FractionRounderImpl FIXED_FRAC_2 = new FractionRounderImpl(2, 2);
+ static final FractionRounderImpl FIXED_FRAC_0 = new FractionRounderImpl(0, 0);
+ static final FractionRounderImpl FIXED_FRAC_2 = new FractionRounderImpl(2, 2);
+ static final FractionRounderImpl MAX_FRAC_6 = new FractionRounderImpl(0, 6);
- private static final SignificantRounderImpl FIXED_SIG_2 = new SignificantRounderImpl(2, 2);
- private static final SignificantRounderImpl FIXED_SIG_3 = new SignificantRounderImpl(3, 3);
- private static final SignificantRounderImpl RANGE_SIG_2_3 = new SignificantRounderImpl(2, 3);
+ static final SignificantRounderImpl FIXED_SIG_2 = new SignificantRounderImpl(2, 2);
+ static final SignificantRounderImpl FIXED_SIG_3 = new SignificantRounderImpl(3, 3);
+ static final SignificantRounderImpl RANGE_SIG_2_3 = new SignificantRounderImpl(2, 3);
- /* package-private */ static final FracSigRounderImpl COMPACT_STRATEGY = new FracSigRounderImpl(0, 0, 2, -1);
+ static final FracSigRounderImpl COMPACT_STRATEGY = new FracSigRounderImpl(0, 0, 2, -1);
- private static final IncrementRounderImpl NICKEL = new IncrementRounderImpl(BigDecimal.valueOf(0.5));
+ static final IncrementRounderImpl NICKEL = new IncrementRounderImpl(BigDecimal.valueOf(0.05));
- private static final CurrencyRounderImpl MONETARY_STANDARD = new CurrencyRounderImpl(CurrencyUsage.STANDARD);
- private static final CurrencyRounderImpl MONETARY_CASH = new CurrencyRounderImpl(CurrencyUsage.CASH);
+ static final CurrencyRounderImpl MONETARY_STANDARD = new CurrencyRounderImpl(CurrencyUsage.STANDARD);
+ static final CurrencyRounderImpl MONETARY_CASH = new CurrencyRounderImpl(CurrencyUsage.CASH);
- private static final PassThroughRounderImpl PASS_THROUGH = new PassThroughRounderImpl();
+ static final PassThroughRounderImpl PASS_THROUGH = new PassThroughRounderImpl();
static Rounder constructInfinite() {
return NONE;
return FIXED_FRAC_0;
} else if (minFrac == 2 && maxFrac == 2) {
return FIXED_FRAC_2;
+ } else if (minFrac == 0 && maxFrac == 6) {
+ return MAX_FRAC_6;
} else {
return new FractionRounderImpl(minFrac, maxFrac);
}
}
static Rounder constructIncrement(BigDecimal increment) {
- if (increment.compareTo(NICKEL.increment) == 0) {
+ // NOTE: .equals() is what we want, not .compareTo()
+ if (increment.equals(NICKEL.increment)) {
return NICKEL;
} else {
return new IncrementRounderImpl(increment);
* Returns a valid working Rounder. If the Rounder is a CurrencyRounder, applies the given currency. Otherwise,
* simply passes through the argument.
*
- * @param rounder
- * The input object.
* @param currency
* A currency object to use in case the input object needs it.
* @return A Rounder object ready for use.
*/
- static Rounder normalizeType(Rounder rounder, Currency currency) {
- if (rounder instanceof CurrencyRounder) {
- return ((CurrencyRounder) rounder).withCurrency(currency);
+ Rounder withLocaleData(Currency currency) {
+ if (this instanceof CurrencyRounder) {
+ return ((CurrencyRounder) this).withCurrency(currency);
} else {
- return rounder;
+ return this;
}
}
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import com.ibm.icu.impl.number.DecimalQuantity;
+import com.ibm.icu.impl.number.MicroProps;
+import com.ibm.icu.impl.number.MicroPropsGenerator;
import com.ibm.icu.impl.number.Modifier;
+import com.ibm.icu.impl.number.MultiplierProducer;
import com.ibm.icu.impl.number.NumberStringBuilder;
+import com.ibm.icu.impl.number.RoundingUtils;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.Rounder.SignificantRounderImpl;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.Rounder.SignificantRounderImpl;
-import newapi.impl.MicroProps;
-import newapi.impl.MicroPropsGenerator;
-import newapi.impl.MultiplierProducer;
-
-@SuppressWarnings("unused")
+/**
+ * A class that defines the scientific notation style to be used when formatting numbers in NumberFormatter.
+ *
+ * <p>
+ * To create a ScientificNotation, use one of the factory methods in {@link Notation}.
+ *
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public class ScientificNotation extends Notation implements Cloneable {
int engineeringInterval;
this.exponentSignDisplay = exponentSignDisplay;
}
+ /**
+ * Sets the minimum number of digits to show in the exponent of scientific notation, padding with zeros if
+ * necessary. Useful for fixed-width display.
+ *
+ * <p>
+ * For example, with minExponentDigits=2, the number 123 will be printed as "1.23E02" in <em>en-US</em> instead of
+ * the default "1.23E2".
+ *
+ * @param minExponentDigits
+ * The minimum number of digits to show in the exponent.
+ * @return A ScientificNotation, for chaining.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public ScientificNotation withMinExponentDigits(int minExponentDigits) {
- if (minExponentDigits >= 0 && minExponentDigits < Rounder.MAX_VALUE) {
+ if (minExponentDigits >= 0 && minExponentDigits < RoundingUtils.MAX_INT_FRAC_SIG) {
ScientificNotation other = (ScientificNotation) this.clone();
other.minExponentDigits = minExponentDigits;
return other;
} else {
- throw new IllegalArgumentException("Integer digits must be between 0 and " + Rounder.MAX_VALUE);
+ throw new IllegalArgumentException(
+ "Integer digits must be between 0 and " + RoundingUtils.MAX_INT_FRAC_SIG);
}
}
+ /**
+ * Sets whether to show the sign on positive and negative exponents in scientific notation. The default is AUTO,
+ * showing the minus sign but not the plus sign.
+ *
+ * <p>
+ * For example, with exponentSignDisplay=ALWAYS, the number 123 will be printed as "1.23E+2" in <em>en-US</em>
+ * instead of the default "1.23E2".
+ *
+ * @param exponentSignDisplay
+ * The strategy for displaying the sign in the exponent.
+ * @return A ScientificNotation, for chaining.
+ * @draft ICU 60
+ * @see NumberFormatter
+ */
public ScientificNotation withExponentSignDisplay(SignDisplay exponentSignDisplay) {
ScientificNotation other = (ScientificNotation) this.clone();
other.exponentSignDisplay = exponentSignDisplay;
return other;
}
+ /**
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ @Deprecated
@Override
public Object clone() {
try {
i += output.insert(i, symbols.getExponentSeparator(), NumberFormat.Field.EXPONENT_SYMBOL);
if (exponent < 0 && notation.exponentSignDisplay != SignDisplay.NEVER) {
i += output.insert(i, symbols.getMinusSignString(), NumberFormat.Field.EXPONENT_SIGN);
- } else if (notation.exponentSignDisplay == SignDisplay.ALWAYS) {
+ } else if (exponent >= 0 && notation.exponentSignDisplay == SignDisplay.ALWAYS) {
i += output.insert(i, symbols.getPlusSignString(), NumberFormat.Field.EXPONENT_SIGN);
}
// Append the exponent digits (using a simple inline algorithm)
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
public class SimpleNotation extends Notation {
/* package-private */ SimpleNotation() {
// © 2017 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
+package com.ibm.icu.number;
import java.util.Locale;
import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.DecimalFormatProperties;
+import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.Parse;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.math.MathContext;
+import com.ibm.icu.number.FormattedNumber;
+import com.ibm.icu.number.LocalizedNumberFormatter;
+import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Currency.CurrencyUsage;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
-import newapi.FormattedNumber;
-import newapi.LocalizedNumberFormatter;
-import newapi.NumberFormatter;
-import newapi.impl.Padder.PadPosition;
-
/**
* {@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
*/
@Deprecated
public static String getPatternForStyle(ULocale forLocale, int choice) {
+ NumberingSystem ns = NumberingSystem.getInstance(forLocale);
+ String nsName = ns.getName();
+ return getPatternForStyleAndNumberingSystem(forLocale, nsName, choice);
+ }
+
+ /**
+ * Returns the pattern for the provided locale, numbering system, and choice.
+ * @param forLocale the locale of the data.
+ * @param nsName The name of the numbering system, like "latn".
+ * @param choice the pattern format.
+ * @return the pattern
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ @Deprecated
+ public static String getPatternForStyleAndNumberingSystem(ULocale forLocale, String nsName, int choice) {
/* for ISOCURRENCYSTYLE and PLURALCURRENCYSTYLE,
* the pattern is the same as the pattern of CURRENCYSTYLE
* but by replacing the single currency sign with
ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.
getBundleInstance(ICUData.ICU_BASE_NAME, forLocale);
- NumberingSystem ns = NumberingSystem.getInstance(forLocale);
String result = rb.findStringWithFallback(
- "NumberElements/" + ns.getName() + "/patterns/" + patternKey);
+ "NumberElements/" + nsName + "/patterns/" + patternKey);
if (result == null) {
result = rb.getStringWithFallback("NumberElements/latn/patterns/" + patternKey);
}
+++ /dev/null
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
-
-import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
-
-import newapi.NumberFormatter.SignDisplay;
-
-public class Notation {
-
- // FIXME: Support engineering intervals other than 3?
- private static final ScientificNotation SCIENTIFIC = new ScientificNotation(1, false, 1, SignDisplay.AUTO);
- private static final ScientificNotation ENGINEERING = new ScientificNotation(3, false, 1, SignDisplay.AUTO);
- private static final CompactNotation COMPACT_SHORT = new CompactNotation(CompactStyle.SHORT);
- private static final CompactNotation COMPACT_LONG = new CompactNotation(CompactStyle.LONG);
- private static final SimpleNotation SIMPLE = new SimpleNotation();
-
- /* package-private */ Notation() {
- }
-
- public static ScientificNotation scientific() {
- return SCIENTIFIC;
- }
-
- public static ScientificNotation engineering() {
- return ENGINEERING;
- }
-
- public static CompactNotation compactShort() {
- return COMPACT_SHORT;
- }
-
- public static CompactNotation compactLong() {
- return COMPACT_LONG;
- }
-
- public static SimpleNotation simple() {
- return SIMPLE;
- }
-}
\ No newline at end of file
+++ /dev/null
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
-
-import java.util.Locale;
-
-import com.ibm.icu.impl.number.DecimalFormatProperties;
-import com.ibm.icu.text.DecimalFormatSymbols;
-import com.ibm.icu.util.ULocale;
-
-import newapi.impl.MacroProps;
-
-public final class NumberFormatter {
-
- private static final UnlocalizedNumberFormatter BASE = new UnlocalizedNumberFormatter();
-
- public static enum UnitWidth {
- NARROW, // ¤¤¤¤¤ or narrow measure unit
- SHORT, // ¤ or short measure unit (DEFAULT)
- ISO_CODE, // ¤¤; undefined for measure unit
- FULL_NAME, // ¤¤¤ or wide unit
- HIDDEN, // no unit is displayed, but other unit effects are obeyed (like currency rounding)
- // TODO: For hidden, what to do if currency symbol appears in the middle, as in Portugal ?
- }
-
- public static enum DecimalMarkDisplay {
- AUTO, ALWAYS,
- }
-
- public static enum SignDisplay {
- AUTO, ALWAYS, NEVER, ACCOUNTING, ACCOUNTING_ALWAYS,
- }
-
- /**
- * Use a default threshold of 3. This means that the third time .format() is called, the data structures get built
- * using the "safe" code path. The first two calls to .format() will trigger the unsafe code path.
- */
- static final long DEFAULT_THRESHOLD = 3;
-
- public static UnlocalizedNumberFormatter with() {
- return BASE;
- }
-
- public static LocalizedNumberFormatter withLocale(Locale locale) {
- return BASE.locale(locale);
- }
-
- public static LocalizedNumberFormatter withLocale(ULocale locale) {
- return BASE.locale(locale);
- }
-
- /**
- * @internal
- * @deprecated ICU 60 This API is ICU internal only.
- */
- @Deprecated
- public static UnlocalizedNumberFormatter fromDecimalFormat(DecimalFormatProperties properties,
- DecimalFormatSymbols symbols, DecimalFormatProperties exportedProperties) {
- MacroProps macros = NumberPropertyMapper.oldToNew(properties, symbols, exportedProperties);
- return NumberFormatter.with().macros(macros);
- }
-}
+++ /dev/null
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi;
-
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-
-import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
-import com.ibm.icu.text.DecimalFormatSymbols;
-import com.ibm.icu.text.NumberingSystem;
-import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.Currency.CurrencyUsage;
-import com.ibm.icu.util.MeasureUnit;
-import com.ibm.icu.util.NoUnit;
-
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.Rounder.CurrencyRounderImpl;
-import newapi.Rounder.FracSigRounderImpl;
-import newapi.Rounder.FractionRounderImpl;
-import newapi.Rounder.IncrementRounderImpl;
-import newapi.Rounder.InfiniteRounderImpl;
-import newapi.Rounder.SignificantRounderImpl;
-import newapi.impl.MacroProps;
-
-final class SkeletonBuilder {
-
- public static String macrosToSkeleton(MacroProps macros) {
- // Print out the values in their canonical order.
- StringBuilder sb = new StringBuilder();
- if (macros.notation != null) {
- // sb.append("notation=");
- notationToSkeleton(macros.notation, sb);
- sb.append(' ');
- }
- if (macros.unit != null) {
- // sb.append("unit=");
- unitToSkeleton(macros.unit, sb);
- sb.append(' ');
- }
- if (macros.rounder != null) {
- // sb.append("rounding=");
- rounderToSkeleton(macros.rounder, sb);
- sb.append(' ');
- }
- if (macros.grouper != null) {
- sb.append("grouping=");
- grouperToSkeleton(macros.grouper, sb);
- sb.append(' ');
- }
-// if (macros.padder != null) {
-// sb.append("padding=");
-// paddingToSkeleton(macros.padder, sb);
-// sb.append(' ');
-// }
- if (macros.integerWidth != null) {
- sb.append("integer-width=");
- integerWidthToSkeleton(macros.integerWidth, sb);
- sb.append(' ');
- }
- if (macros.symbols != null) {
- sb.append("symbols=");
- symbolsToSkeleton(macros.symbols, sb);
- sb.append(' ');
- }
- if (macros.unitWidth != null) {
- sb.append("unit-width=");
- unitWidthToSkeleton(macros.unitWidth, sb);
- sb.append(' ');
- }
- if (macros.sign != null) {
- sb.append("sign=");
- signToSkeleton(macros.sign, sb);
- sb.append(' ');
- }
- if (macros.decimal != null) {
- sb.append("decimal=");
- decimalToSkeleton(macros.decimal, sb);
- sb.append(' ');
- }
- if (sb.length() > 0) {
- // Remove the trailing space
- sb.setLength(sb.length() - 1);
- }
- return sb.toString();
- }
-
- public static MacroProps skeletonToMacros(String skeleton) {
- MacroProps macros = new MacroProps();
- for (int offset = 0; offset < skeleton.length();) {
- char c = skeleton.charAt(offset);
- switch (c) {
- case ' ':
- offset++;
- break;
- case 'E':
- case 'C':
- case 'I':
- offset += skeletonToNotation(skeleton, offset, macros);
- break;
- case '%':
- case 'B':
- case '$':
- case 'U':
- offset += skeletonToUnit(skeleton, offset, macros);
- break;
- case 'F':
- case 'S':
- case 'M':
- case 'G':
- case 'Y':
- offset += skeletonToRounding(skeleton, offset, macros);
- break;
- default:
- if (skeleton.regionMatches(offset, "notation=", 0, 9)) {
- offset += 9;
- offset += skeletonToNotation(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "unit=", 0, 5)) {
- offset += 5;
- offset += skeletonToUnit(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "rounding=", 0, 9)) {
- offset += 9;
- offset += skeletonToRounding(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "grouping=", 0, 9)) {
- offset += 9;
- offset += skeletonToGrouping(skeleton, offset, macros);
-// } else if (skeleton.regionMatches(offset, "padding=", 0, 9)) {
-// offset += 8;
-// offset += skeletonToPadding(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "integer-width=", 0, 9)) {
- offset += 14;
- offset += skeletonToIntegerWidth(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "symbols=", 0, 9)) {
- offset += 8;
- offset += skeletonToSymbols(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "unit-width=", 0, 9)) {
- offset += 11;
- offset += skeletonToUnitWidth(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "sign=", 0, 9)) {
- offset += 5;
- offset += skeletonToSign(skeleton, offset, macros);
- } else if (skeleton.regionMatches(offset, "decimal=", 0, 9)) {
- offset += 8;
- offset += skeletonToDecimal(skeleton, offset, macros);
- } else {
- throw new IllegalArgumentException(
- "Unexpected token at offset " + offset + " in skeleton string: " + c);
- }
- }
- }
- return macros;
- }
-
- private static void notationToSkeleton(Notation value, StringBuilder sb) {
- if (value instanceof ScientificNotation) {
- ScientificNotation notation = (ScientificNotation) value;
- sb.append('E');
- if (notation.engineeringInterval != 1) {
- sb.append(notation.engineeringInterval);
- }
- if (notation.exponentSignDisplay == SignDisplay.ALWAYS) {
- sb.append('+');
- } else if (notation.exponentSignDisplay == SignDisplay.NEVER) {
- sb.append('!');
- } else {
- assert notation.exponentSignDisplay == SignDisplay.AUTO;
- }
- if (notation.minExponentDigits != 1) {
- for (int i = 0; i < notation.minExponentDigits; i++) {
- sb.append('0');
- }
- }
- } else if (value instanceof CompactNotation) {
- CompactNotation notation = (CompactNotation) value;
- if (notation.compactStyle == CompactStyle.SHORT) {
- sb.append('C');
- } else {
- // FIXME: CCC or CCCC instead?
- sb.append("CC");
- }
- } else {
- assert value instanceof SimpleNotation;
- sb.append('I');
- }
- }
-
- private static int skeletonToNotation(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- char c0 = skeleton.charAt(offset++);
- Notation result = null;
- if (c0 == 'E') {
- int engineering = 1;
- SignDisplay sign = SignDisplay.AUTO;
- int minExponentDigits = 0;
- char c = safeCharAt(skeleton, offset++);
- if (c >= '1' && c <= '9') {
- engineering = c - '0';
- c = safeCharAt(skeleton, offset++);
- }
- if (c == '+') {
- sign = SignDisplay.ALWAYS;
- c = safeCharAt(skeleton, offset++);
- }
- if (c == '!') {
- sign = SignDisplay.NEVER;
- c = safeCharAt(skeleton, offset++);
- }
- while (c == '0') {
- minExponentDigits++;
- c = safeCharAt(skeleton, offset++);
- }
- minExponentDigits = Math.max(1, minExponentDigits);
- result = new ScientificNotation(engineering, false, minExponentDigits, sign);
- } else if (c0 == 'C') {
- char c = safeCharAt(skeleton, offset++);
- if (c == 'C') {
- result = Notation.compactLong();
- } else {
- result = Notation.compactShort();
- }
- } else if (c0 == 'I') {
- result = Notation.simple();
- }
- output.notation = result;
- return offset - originalOffset;
- }
-
- private static void unitToSkeleton(MeasureUnit value, StringBuilder sb) {
- if (value.getType().equals("none")) {
- if (value.getSubtype().equals("percent")) {
- sb.append('%');
- } else if (value.getSubtype().equals("permille")) {
- sb.append("%%");
- } else {
- assert value.getSubtype().equals("base");
- sb.append('B');
- }
- } else if (value.getType().equals("currency")) {
- sb.append('$');
- sb.append(value.getSubtype());
- } else {
- sb.append("U:");
- sb.append(value.getType());
- sb.append(':');
- sb.append(value.getSubtype());
- }
- }
-
- private static int skeletonToUnit(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- char c0 = skeleton.charAt(offset++);
- MeasureUnit result = null;
- if (c0 == '%') {
- char c = safeCharAt(skeleton, offset++);
- if (c == '%') {
- result = NoUnit.PERCENT;
- } else {
- result = NoUnit.PERMILLE;
- }
- } else if (c0 == 'B') {
- result = NoUnit.BASE;
- } else if (c0 == '$') {
- String currencyCode = skeleton.substring(offset, offset + 3);
- offset += 3;
- result = Currency.getInstance(currencyCode);
- } else if (c0 == 'U') {
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, offset, ':', sb);
- String type = sb.toString();
- sb.setLength(0);
- offset += consumeUntil(skeleton, offset, ' ', sb);
- String subtype = sb.toString();
- for (MeasureUnit candidate : MeasureUnit.getAvailable(type)) {
- if (candidate.getSubtype().equals(subtype)) {
- result = candidate;
- break;
- }
- }
- }
- output.unit = result;
- return offset - originalOffset;
- }
-
- private static void rounderToSkeleton(Rounder value, StringBuilder sb) {
- if (!(value instanceof Rounder)) {
- // FIXME: Throw an exception here instead?
- return;
- }
- MathContext mathContext;
- if (value instanceof FractionRounderImpl) {
- FractionRounderImpl rounder = (FractionRounderImpl) value;
- sb.append('F');
- minMaxToSkeletonHelper(rounder.minFrac, rounder.maxFrac, sb);
- mathContext = rounder.mathContext;
- } else if (value instanceof SignificantRounderImpl) {
- SignificantRounderImpl rounder = (SignificantRounderImpl) value;
- sb.append('S');
- minMaxToSkeletonHelper(rounder.minSig, rounder.maxSig, sb);
- mathContext = rounder.mathContext;
- } else if (value instanceof FracSigRounderImpl) {
- FracSigRounderImpl rounder = (FracSigRounderImpl) value;
- sb.append('F');
- minMaxToSkeletonHelper(rounder.minFrac, rounder.maxFrac, sb);
- if (rounder.minSig != -1) {
- sb.append('>');
- sb.append(rounder.minSig);
- } else {
- sb.append('<');
- sb.append(rounder.maxSig);
- }
- mathContext = rounder.mathContext;
- } else if (value instanceof IncrementRounderImpl) {
- IncrementRounderImpl rounder = (IncrementRounderImpl) value;
- sb.append('M');
- sb.append(rounder.increment.toString());
- mathContext = rounder.mathContext;
- } else if (value instanceof CurrencyRounderImpl) {
- CurrencyRounderImpl rounder = (CurrencyRounderImpl) value;
- sb.append('G');
- sb.append(rounder.usage.name());
- mathContext = rounder.mathContext;
- } else {
- InfiniteRounderImpl rounder = (InfiniteRounderImpl) value;
- sb.append('Y');
- mathContext = rounder.mathContext;
- }
- // RoundingMode
- RoundingMode roundingMode = mathContext.getRoundingMode();
- if (roundingMode != RoundingMode.HALF_EVEN) {
- sb.append(';');
- sb.append(roundingMode.name());
- }
- }
-
- private static void minMaxToSkeletonHelper(int minFrac, int maxFrac, StringBuilder sb) {
- if (minFrac == maxFrac) {
- sb.append(minFrac);
- } else {
- boolean showMaxFrac = (maxFrac >= 0 && maxFrac < Integer.MAX_VALUE);
- if (minFrac > 0 || !showMaxFrac) {
- sb.append(minFrac);
- }
- sb.append('-');
- if (showMaxFrac) {
- sb.append(maxFrac);
- }
- }
- }
-
- private static int skeletonToRounding(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- char c0 = skeleton.charAt(offset++);
- Rounder result = null;
- if (c0 == 'F') {
- int[] minMax = new int[2];
- offset += skeletonToMinMaxHelper(skeleton, offset, minMax);
- FractionRounder temp = Rounder.constructFraction(minMax[0], minMax[1]);
- char c1 = skeleton.charAt(offset++);
- if (c1 == '<') {
- char c2 = skeleton.charAt(offset++);
- result = temp.withMaxDigits(c2 - '0');
- } else if (c1 == '>') {
- char c2 = skeleton.charAt(offset++);
- result = temp.withMinDigits(c2 - '0');
- } else {
- result = temp;
- }
- } else if (c0 == 'S') {
- int[] minMax = new int[2];
- offset += skeletonToMinMaxHelper(skeleton, offset, minMax);
- result = Rounder.constructSignificant(minMax[0], minMax[1]);
- } else if (c0 == 'M') {
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, offset, ' ', sb);
- BigDecimal increment = new BigDecimal(sb.toString());
- result = Rounder.constructIncrement(increment);
- } else if (c0 == 'G') {
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, offset, ' ', sb);
- CurrencyUsage usage = Enum.valueOf(CurrencyUsage.class, sb.toString());
- result = Rounder.constructCurrency(usage);
- } else if (c0 == 'Y') {
- result = Rounder.constructInfinite();
- }
- output.rounder = result;
- return offset - originalOffset;
- }
-
- private static int skeletonToMinMaxHelper(String skeleton, int offset, int[] output) {
- int originalOffset = offset;
- char c0 = safeCharAt(skeleton, offset++);
- char c1 = safeCharAt(skeleton, offset++);
- // TODO: This algorithm breaks if the number is more than 1 char wide.
- if (c1 == '-') {
- output[0] = c0 - '0';
- char c2 = safeCharAt(skeleton, offset++);
- if (c2 == ' ') {
- output[1] = Integer.MAX_VALUE;
- } else {
- output[1] = c2 - '0';
- }
- } else if ('0' <= c1 && c1 <= '9') {
- output[0] = 0;
- output[1] = c1 - '0';
- } else {
- offset--;
- output[0] = c0 - '0';
- output[1] = c0 - '0';
- }
- return offset - originalOffset;
- }
-
- private static void grouperToSkeleton(Grouper value, StringBuilder sb) {
- if (value.equals(Grouper.defaults())) {
- sb.append("defaults");
- } else if (value.equals(Grouper.min2())) {
- sb.append("min2");
- } else if (value.equals(Grouper.none())) {
- sb.append("none");
- } else {
- // Not supported in skeleton string
- sb.append("defaults");
- }
- }
-
- private static int skeletonToGrouping(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- char c0 = skeleton.charAt(offset++);
- Grouper result = null;
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, --offset, ' ', sb);
- String name = sb.toString();
- if (name.equals("defaults")) {
- result = Grouper.defaults();
- } else if (name.equals("min2")) {
- result = Grouper.min2();
- } else if (name.equals("none")) {
- result = Grouper.none();
- }
- output.grouper = result;
- return offset - originalOffset;
- }
-
-// private static void paddingToSkeleton(Padder value, StringBuilder sb) {
-// PaddingImpl padding = (PaddingImpl) value;
-// if (padding == Padder.NONE) {
-// sb.append("NONE");
-// return;
-// }
-// sb.append(padding.targetWidth);
-// sb.append(':');
-// sb.append(padding.position.name());
-// sb.append(':');
-// if (!padding.paddingString.equals(" ")) {
-// sb.append(padding.paddingString);
-// }
-// }
-//
-// private static int skeletonToPadding(String skeleton, int offset, MacroProps output) {
-// int originalOffset = offset;
-// char c0 = skeleton.charAt(offset++);
-// if (c0 == 'N') {
-// offset += consumeUntil(skeleton, --offset, ' ', null);
-// } else if ('0' <= c0 && c0 <= '9') {
-// long intResult = consumeInt(skeleton, --offset);
-// offset += intResult & 0xffffffff;
-// int width = (int) (intResult >>> 32);
-// char c1 = safeCharAt(skeleton, offset++);
-// if (c1 != ':') {
-// return offset - originalOffset - 1;
-// }
-// StringBuilder sb = new StringBuilder();
-// offset += consumeUntil(skeleton, offset, ':', sb);
-// String padPositionString = sb.toString();
-// sb.setLength(0);
-// offset += consumeUntil(skeleton, offset, ' ', sb);
-// String string = (sb.length() == 0) ? " " : sb.toString();
-// PadPosition position = Enum.valueOf(PadPosition.class, padPositionString);
-// output.padder = PaddingImpl.getInstance(string, width, position);
-// }
-// return offset - originalOffset;
-// }
-
- private static void integerWidthToSkeleton(IntegerWidth value, StringBuilder sb) {
- sb.append(value.minInt);
- if (value.maxInt != value.minInt) {
- sb.append('-');
- if (value.maxInt != -1) {
- sb.append(value.maxInt);
- }
- }
- }
-
- private static int skeletonToIntegerWidth(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- long intResult = consumeInt(skeleton, offset);
- offset += intResult & 0xffffffff;
- int minInt = (int) (intResult >>> 32);
- char c1 = safeCharAt(skeleton, --offset);
- int maxInt;
- if (c1 == '-') {
- intResult = consumeInt(skeleton, offset);
- offset += intResult & 0xffffffff;
- maxInt = (int) (intResult >>> 32);
- }
- }
-
- private static void symbolsToSkeleton(Object value, StringBuilder sb) {
- if (value instanceof DecimalFormatSymbols) {
- // TODO: Check to see if any of the symbols are not default?
- sb.append("loc:");
- sb.append(((DecimalFormatSymbols) value).getULocale());
- } else {
- sb.append("ns:");
- sb.append(((NumberingSystem) value).getName());
- }
- }
-
- private static void unitWidthToSkeleton(UnitWidth value, StringBuilder sb) {
- sb.append(value.name());
- }
-
- private static int skeletonToUnitWidth(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, offset, ' ', sb);
- output.unitWidth = Enum.valueOf(UnitWidth.class, sb.toString());
- return offset - originalOffset;
- }
-
- private static void signToSkeleton(SignDisplay value, StringBuilder sb) {
- sb.append(value.name());
- }
-
- private static int skeletonToSign(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, offset, ' ', sb);
- output.sign = Enum.valueOf(SignDisplay.class, sb.toString());
- return offset - originalOffset;
- }
-
- private static void decimalToSkeleton(DecimalMarkDisplay value, StringBuilder sb) {
- sb.append(value.name());
- }
-
- private static int skeletonToDecimal(String skeleton, int offset, MacroProps output) {
- int originalOffset = offset;
- StringBuilder sb = new StringBuilder();
- offset += consumeUntil(skeleton, offset, ' ', sb);
- output.decimal = Enum.valueOf(DecimalMarkDisplay.class, sb.toString());
- return offset - originalOffset;
- }
-
- private static char safeCharAt(String str, int offset) {
- if (offset < str.length()) {
- return str.charAt(offset);
- } else {
- return ' ';
- }
- }
-
- private static int consumeUntil(String skeleton, int offset, char brk, StringBuilder sb) {
- int originalOffset = offset;
- char c = safeCharAt(skeleton, offset++);
- while (c != brk) {
- if (sb != null)
- sb.append(c);
- c = safeCharAt(skeleton, offset++);
- }
- return offset - originalOffset;
- }
-
- private static long consumeInt(String skeleton, int offset) {
- int originalOffset = offset;
- char c = safeCharAt(skeleton, offset++);
- int result = 0;
- while ('0' <= c && c <= '9') {
- result = (result * 10) + (c - '0');
- c = safeCharAt(skeleton, offset++);
- }
- return (offset - originalOffset) | (((long) result) << 32);
- }
-}
+++ /dev/null
-// © 2017 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html#License
-package newapi.impl;
-
-import java.math.RoundingMode;
-
-import com.ibm.icu.text.DecimalFormatSymbols;
-import com.ibm.icu.text.NumberingSystem;
-import com.ibm.icu.util.Currency;
-import com.ibm.icu.util.Currency.CurrencyUsage;
-import com.ibm.icu.util.MeasureUnit;
-import com.ibm.icu.util.NoUnit;
-import com.ibm.icu.util.ULocale;
-
-import newapi.Grouper;
-import newapi.Notation;
-import newapi.NumberFormatter;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.Rounder;
-import newapi.UnlocalizedNumberFormatter;
-
-public class demo {
- public static void main(String[] args) {
- System.out.println(NumberingSystem.LATIN.getDescription());
- UnlocalizedNumberFormatter formatter =
- NumberFormatter.with()
- .notation(Notation.compactShort())
- .notation(Notation.scientific().withExponentSignDisplay(SignDisplay.ALWAYS))
- .notation(Notation.engineering().withMinExponentDigits(2))
- .notation(Notation.simple())
- .unit(Currency.getInstance("GBP"))
- .unit(NoUnit.PERCENT)
- .unit(MeasureUnit.CUBIC_METER)
- .unitWidth(UnitWidth.SHORT)
- // .rounding(Rounding.fixedSignificantDigits(3))
-// .rounding(
-// (BigDecimal input) -> {
-// return input.divide(new BigDecimal("0.02"), 0).multiply(new BigDecimal("0.02"));
-// })
- .rounding(Rounder.fixedFraction(2).withMode(RoundingMode.HALF_UP))
- .rounding(Rounder.integer().withMode(RoundingMode.CEILING))
- .rounding(Rounder.currency(CurrencyUsage.STANDARD))
-// .grouping(
-// (int position, BigDecimal number) -> {
-// return (position % 3) == 0;
-// })
- .grouping(Grouper.defaults())
- .grouping(Grouper.none())
- .grouping(Grouper.min2())
- // .padding(Padding.codePoints(' ', 8, PadPosition.AFTER_PREFIX))
- .sign(SignDisplay.ALWAYS)
- .decimal(DecimalMarkDisplay.ALWAYS)
- .symbols(DecimalFormatSymbols.getInstance(new ULocale("fr@digits=ascii")))
- .symbols(NumberingSystem.getInstanceByName("arab"))
- .symbols(NumberingSystem.LATIN);
- System.out.println(formatter.toSkeleton());
- System.out.println(formatter.locale(ULocale.ENGLISH).format(0.98381).toString());
- // .locale(Locale.ENGLISH)
- // .format(123.45)
- // .toString();
- }
-}
#E0 52413 5,2413E4 K
0E0 52413 5E4
-test scientific with grouping
-set locale en
-set pattern #,##0.000E0
-begin
-format output breaks
-// J throws an IllegalArgumentException when parsing the pattern.
-// C sets error code to U_MALFORMED_EXPONENTIAL_PATTERN.
-1 1.000E0 CJ
-11 11.00E0 CJ
-111 111.0E0 CJ
-// K doesn't print the grouping separator ("1111E0")
-1111 1,111E0 CJK
-// K prints too many digits ("1.1111E4")
-11111 1.111E4 CJK
-111111 11.11E4 CJK
-1111111 111.1E4 CJK
-11111111 1,111E4 CJK
-111111111 1.111E8 CJK
-
test percents
set locale fr
begin
import com.ibm.icu.dev.test.TestUtil;
import com.ibm.icu.impl.number.DecimalFormatProperties;
+import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.Parse.ParseMode;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
+import com.ibm.icu.number.LocalizedNumberFormatter;
+import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.DecimalFormat_ICU58;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;
-import newapi.LocalizedNumberFormatter;
-import newapi.NumberFormatter;
-import newapi.impl.Padder.PadPosition;
-
public class NumberFormatDataDrivenTest {
private static ULocale EN = new ULocale("en");
@Test
public void TestScientificWithGrouping() {
- DecimalFormat df = new DecimalFormat("#,##0.000E0");
+ // Grouping separators are not allowed in the pattern, but we can enable them via the API.
+ DecimalFormat df = new DecimalFormat("###0.000E0");
+ df.setGroupingUsed(true);
expect2(df, 123, "123.0E0");
expect2(df, 1234, "1,234E0");
expect2(df, 12340, "1.234E4");
import com.ibm.icu.impl.number.DecimalQuantity_ByteArrayBCD;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
import com.ibm.icu.impl.number.DecimalQuantity_SimpleStorage;
+import com.ibm.icu.number.LocalizedNumberFormatter;
+import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.ULocale;
-import newapi.LocalizedNumberFormatter;
-import newapi.NumberFormatter;
-
/** TODO: This is a temporary name for this class. Suggestions for a better name? */
public class DecimalQuantityTest extends TestFmwk {
import org.junit.Test;
import com.ibm.icu.impl.SimpleFormatterImpl;
+import com.ibm.icu.impl.number.ConstantAffixModifier;
+import com.ibm.icu.impl.number.ConstantMultiFieldModifier;
+import com.ibm.icu.impl.number.CurrencySpacingEnabledModifier;
import com.ibm.icu.impl.number.Modifier;
import com.ibm.icu.impl.number.NumberStringBuilder;
-import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier;
-import com.ibm.icu.impl.number.modifiers.ConstantMultiFieldModifier;
-import com.ibm.icu.impl.number.modifiers.CurrencySpacingEnabledModifier;
-import com.ibm.icu.impl.number.modifiers.SimpleModifier;
+import com.ibm.icu.impl.number.SimpleModifier;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
+import com.ibm.icu.impl.number.MicroProps;
+import com.ibm.icu.impl.number.MutablePatternModifier;
import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.impl.number.PatternStringParser;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.ULocale;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.impl.MicroProps;
-import newapi.impl.MutablePatternModifier;
-
public class MutablePatternModifierTest {
@Test
import static org.junit.Assert.assertNotEquals;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.Locale;
import org.junit.Ignore;
import org.junit.Test;
+import com.ibm.icu.impl.number.Padder;
+import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.PatternStringParser;
+import com.ibm.icu.number.FormattedNumber;
+import com.ibm.icu.number.Grouper;
+import com.ibm.icu.number.IntegerWidth;
+import com.ibm.icu.number.LocalizedNumberFormatter;
+import com.ibm.icu.number.Notation;
+import com.ibm.icu.number.NumberFormatter;
+import com.ibm.icu.number.NumberFormatter.DecimalSeparatorDisplay;
+import com.ibm.icu.number.NumberFormatter.SignDisplay;
+import com.ibm.icu.number.NumberFormatter.UnitWidth;
+import com.ibm.icu.number.Rounder;
+import com.ibm.icu.number.UnlocalizedNumberFormatter;
import com.ibm.icu.text.DecimalFormatSymbols;
import com.ibm.icu.text.NumberingSystem;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.NoUnit;
import com.ibm.icu.util.ULocale;
-import newapi.FormattedNumber;
-import newapi.Grouper;
-import newapi.IntegerWidth;
-import newapi.LocalizedNumberFormatter;
-import newapi.Notation;
-import newapi.NumberFormatter;
-import newapi.NumberFormatter.DecimalMarkDisplay;
-import newapi.NumberFormatter.SignDisplay;
-import newapi.NumberFormatter.UnitWidth;
-import newapi.Rounder;
-import newapi.UnlocalizedNumberFormatter;
-import newapi.impl.Padder;
-import newapi.impl.Padder.PadPosition;
-
public class NumberFormatterTest {
private static final Currency USD = Currency.getInstance("USD");
private static final Currency GBP = Currency.getInstance("GBP");
private static final Currency CZK = Currency.getInstance("CZK");
+ private static final Currency CAD = Currency.getInstance("CAD");
@Test
public void notationSimple() {
"0.008765",
"0");
+ assertFormatDescendingBig(
+ "Big Simple",
+ "",
+ NumberFormatter.with().notation(Notation.simple()),
+ ULocale.ENGLISH,
+ "87,650,000",
+ "8,765,000",
+ "876,500",
+ "87,650",
+ "8,765",
+ "876.5",
+ "87.65",
+ "8.765",
+ "0");
+
assertFormatSingle(
"Basic with Negative Sign",
"",
@Test
public void notationCompact() {
- assertFormatDescending(
+ assertFormatDescendingBig(
"Compact Short",
"C",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
+ "88M",
+ "8.8M",
+ "876K",
"88K",
"8.8K",
"876",
"88",
"8.8",
- "0.88",
- "0.088",
- "0.0088",
"0");
- assertFormatDescending(
+ assertFormatDescendingBig(
"Compact Long",
"CC",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.ENGLISH,
+ "88 million",
+ "8.8 million",
+ "876 thousand",
"88 thousand",
"8.8 thousand",
"876",
"88",
"8.8",
- "0.88",
- "0.088",
- "0.0088",
"0");
assertFormatDescending(
assertFormatDescending(
"Compact Short with ISO Currency",
"C $USD unit-width=ISO_CODE",
- NumberFormatter.with()
- .notation(Notation.compactShort())
- .unit(USD)
- .unitWidth(UnitWidth.ISO_CODE),
+ NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K",
"USD 8.8K",
assertFormatDescending(
"Compact Short with Long Name Currency",
"C $USD unit-width=FULL_NAME",
- NumberFormatter.with()
- .notation(Notation.compactShort())
- .unit(USD)
- .unitWidth(UnitWidth.FULL_NAME),
+ NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88K US dollars",
"8.8K US dollars",
assertFormatDescending(
"Compact Long with ISO Currency",
"CC $USD unit-width=ISO_CODE",
- NumberFormatter.with()
- .notation(Notation.compactLong())
- .unit(USD)
- .unitWidth(UnitWidth.ISO_CODE),
+ NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K", // should be something like "USD 88 thousand"
"USD 8.8K",
assertFormatDescending(
"Compact Long with Long Name Currency",
"CC $USD unit-width=FULL_NAME",
- NumberFormatter.with()
- .notation(Notation.compactLong())
- .unit(USD)
- .unitWidth(UnitWidth.FULL_NAME),
+ NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88 thousand US dollars",
"8.8 thousand US dollars",
ULocale.forLanguageTag("en-GB"),
5.43,
"5.43 m²");
+
+ // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT.
+ // NOTE: This example is in the documentation.
+ assertFormatSingle(
+ "MeasureUnit Difference between Narrow and Short (Narrow Version)",
+ "",
+ NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.NARROW),
+ ULocale.forLanguageTag("es-US"),
+ 5.43,
+ "5.43°");
+
+ assertFormatSingle(
+ "MeasureUnit Difference between Narrow and Short (Short Version)",
+ "",
+ NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.SHORT),
+ ULocale.forLanguageTag("es-US"),
+ 5.43,
+ "5.43 °F");
}
@Test
"0.01 British pounds",
"0.00 British pounds");
+ assertFormatDescending(
+ "Currency Hidden",
+ "$GBP unit-width=HIDDEN",
+ NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.HIDDEN),
+ ULocale.ENGLISH,
+ "87,650.00",
+ "8,765.00",
+ "876.50",
+ "87.65",
+ "8.76",
+ "0.88",
+ "0.09",
+ "0.01",
+ "0.00");
+
assertFormatSingleMeasure(
"Currency with CurrencyAmount Input",
"",
ULocale.ENGLISH,
-9876543.21,
"-£9,876,543.21");
+
+ // The full currency symbol is not shown in NARROW format.
+ // NOTE: This example is in the documentation.
+ // FIXME: Narrow Currency does not currently work; see #11666
+ assertFormatSingle(
+ "Currency Difference between Narrow and Short (Narrow Version)",
+ "",
+ NumberFormatter.with().unit(USD).unitWidth(UnitWidth.NARROW),
+ ULocale.forLanguageTag("en-CA"),
+ 5.43,
+ "US$5.43");
+
+ assertFormatSingle(
+ "Currency Difference between Narrow and Short (Short Version)",
+ "",
+ NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
+ ULocale.forLanguageTag("en_CA"),
+ 5.43,
+ "US$ 5.43");
}
@Test
"%",
NumberFormatter.with().unit(NoUnit.PERCENT),
ULocale.ENGLISH,
- "8,765,000%",
- "876,500%",
"87,650%",
"8,765%",
"876.5%",
"87.65%",
"8.765%",
"0.8765%",
+ "0.08765%",
+ "0.008765%",
"0%");
assertFormatDescending(
"%%",
NumberFormatter.with().unit(NoUnit.PERMILLE),
ULocale.ENGLISH,
- "87,650,000‰",
- "8,765,000‰",
- "876,500‰",
"87,650‰",
"8,765‰",
"876.5‰",
"87.65‰",
"8.765‰",
+ "0.8765‰",
+ "0.08765‰",
+ "0.008765‰",
"0‰");
assertFormatSingle(
"%",
NumberFormatter.with().unit(NoUnit.PERCENT),
ULocale.ENGLISH,
- -0.987654321,
- "-98.7654321%");
+ -98.7654321,
+ "-98.765432%");
}
@Test
"0.0",
"0.0");
+ assertFormatDescending(
+ "Increment with Min Fraction",
+ "M0.5",
+ NumberFormatter.with().rounding(Rounder.increment(new BigDecimal("0.50"))),
+ ULocale.ENGLISH,
+ "87,650.00",
+ "8,765.00",
+ "876.50",
+ "87.50",
+ "9.00",
+ "1.00",
+ "0.00",
+ "0.00",
+ "0.00");
+
assertFormatDescending(
"Currency Standard",
"$CZK GSTANDARD",
"CZK 0",
"CZK 0");
+ assertFormatDescending(
+ "Currency Cash with Nickel Rounding",
+ "$CAD GCASH",
+ NumberFormatter.with().rounding(Rounder.currency(CurrencyUsage.CASH)).unit(CAD),
+ ULocale.ENGLISH,
+ "CA$87,650.00",
+ "CA$8,765.00",
+ "CA$876.50",
+ "CA$87.65",
+ "CA$8.75",
+ "CA$0.90",
+ "CA$0.10",
+ "CA$0.00",
+ "CA$0.00");
+
assertFormatDescending(
"Currency not in top-level fluent chain",
"F0",
"0",
"0",
"0");
+
+ // NOTE: Other tests cover the behavior of the other rounding modes.
+ assertFormatDescending(
+ "Rounding Mode CEILING",
+ "",
+ NumberFormatter.with().rounding(Rounder.integer().withMode(RoundingMode.CEILING)),
+ ULocale.ENGLISH,
+ "87,650",
+ "8,765",
+ "877",
+ "88",
+ "9",
+ "1",
+ "1",
+ "1",
+ "0");
}
@Test
public void grouping() {
- // NoUnit.PERMILLE multiplies all the number by 10^3 (good for testing grouping).
- // Note that en-US is already performed in the unitPercent() function.
- assertFormatDescending(
+ assertFormatDescendingBig(
+ "Western Grouping",
+ "grouping=defaults",
+ NumberFormatter.with().grouping(Grouper.defaults()),
+ ULocale.ENGLISH,
+ "87,650,000",
+ "8,765,000",
+ "876,500",
+ "87,650",
+ "8,765",
+ "876.5",
+ "87.65",
+ "8.765",
+ "0");
+
+ assertFormatDescendingBig(
"Indic Grouping",
- "%% grouping=defaults",
- NumberFormatter.with().unit(NoUnit.PERMILLE).grouping(Grouper.defaults()),
+ "grouping=defaults",
+ NumberFormatter.with().grouping(Grouper.defaults()),
new ULocale("en-IN"),
- "8,76,50,000‰",
- "87,65,000‰",
- "8,76,500‰",
- "87,650‰",
- "8,765‰",
- "876.5‰",
- "87.65‰",
- "8.765‰",
- "0‰");
+ "8,76,50,000",
+ "87,65,000",
+ "8,76,500",
+ "87,650",
+ "8,765",
+ "876.5",
+ "87.65",
+ "8.765",
+ "0");
- assertFormatDescending(
+ assertFormatDescendingBig(
"Western Grouping, Min 2",
- "%% grouping=min2",
- NumberFormatter.with().unit(NoUnit.PERMILLE).grouping(Grouper.min2()),
+ "grouping=min2",
+ NumberFormatter.with().grouping(Grouper.minTwoDigits()),
ULocale.ENGLISH,
- "87,650,000‰",
- "8,765,000‰",
- "876,500‰",
- "87,650‰",
- "8765‰",
- "876.5‰",
- "87.65‰",
- "8.765‰",
- "0‰");
+ "87,650,000",
+ "8,765,000",
+ "876,500",
+ "87,650",
+ "8765",
+ "876.5",
+ "87.65",
+ "8.765",
+ "0");
- assertFormatDescending(
+ assertFormatDescendingBig(
"Indic Grouping, Min 2",
- "%% grouping=min2",
- NumberFormatter.with().unit(NoUnit.PERMILLE).grouping(Grouper.min2()),
+ "grouping=min2",
+ NumberFormatter.with().grouping(Grouper.minTwoDigits()),
new ULocale("en-IN"),
- "8,76,50,000‰",
- "87,65,000‰",
- "8,76,500‰",
- "87,650‰",
- "8765‰",
- "876.5‰",
- "87.65‰",
- "8.765‰",
- "0‰");
+ "8,76,50,000",
+ "87,65,000",
+ "8,76,500",
+ "87,650",
+ "8765",
+ "876.5",
+ "87.65",
+ "8.765",
+ "0");
- assertFormatDescending(
+ assertFormatDescendingBig(
"No Grouping",
- "%% grouping=none",
- NumberFormatter.with().unit(NoUnit.PERMILLE).grouping(Grouper.none()),
+ "grouping=none",
+ NumberFormatter.with().grouping(Grouper.none()),
new ULocale("en-IN"),
- "87650000‰",
- "8765000‰",
- "876500‰",
- "87650‰",
- "8765‰",
- "876.5‰",
- "87.65‰",
- "8.765‰",
- "0‰");
+ "87650000",
+ "8765000",
+ "876500",
+ "87650",
+ "8765",
+ "876.5",
+ "87.65",
+ "8.765",
+ "0");
}
@Test
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_SUFFIX))
.unit(NoUnit.PERCENT),
ULocale.ENGLISH,
- 0.8888,
+ 88.88,
"88.88**%");
assertFormatSingle(
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_SUFFIX))
.unit(NoUnit.PERCENT),
ULocale.ENGLISH,
- 0.8888,
+ 88.88,
"88.88%**");
assertFormatSingle(
"Currency Spacing with Zero Digit Padding Broken",
"$GBP unit-width=ISO_CODE",
NumberFormatter.with().padding(Padder.codePoints('0', 12, PadPosition.AFTER_PREFIX)).unit(GBP)
- .unitWidth(UnitWidth.ISO_CODE),
+ .unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
514.23,
"GBP 000514.23"); // TODO: This is broken; it renders too wide (13 instead of 12).
"$USD symbols=ns:latn",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar"),
- "87,650.00 US$",
- "8,765.00 US$",
- "876.50 US$",
- "87.65 US$",
- "8.76 US$",
- "0.88 US$",
- "0.09 US$",
- "0.01 US$",
- "0.00 US$");
+ "US$ 87,650.00",
+ "US$ 8,765.00",
+ "US$ 876.50",
+ "US$ 87.65",
+ "US$ 8.76",
+ "US$ 0.88",
+ "US$ 0.09",
+ "US$ 0.01",
+ "US$ 0.00");
assertFormatDescending(
"Math Numbering System with French Data",
12345.67,
"\u1041\u1042,\u1043\u1044\u1045.\u1046\u1047");
+ // NOTE: Locale ar puts ¤ after the number in NS arab but before the number in NS latn.
+
+ assertFormatSingle(
+ "Currency symbol should precede number in ar with NS latn",
+ "",
+ NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
+ new ULocale("ar"),
+ 12345.67,
+ "US$ 12,345.67");
+
+ assertFormatSingle(
+ "Currency symbol should precede number in ar@numbers=latn",
+ "",
+ NumberFormatter.with().unit(USD),
+ new ULocale("ar@numbers=latn"),
+ 12345.67,
+ "US$ 12,345.67");
+
+ assertFormatSingle(
+ "Currency symbol should follow number in ar with NS arab",
+ "",
+ NumberFormatter.with().unit(USD),
+ new ULocale("ar"),
+ 12345.67,
+ "١٢٬٣٤٥٫٦٧ US$");
+
+ assertFormatSingle(
+ "Currency symbol should follow number in ar@numbers=arab",
+ "",
+ NumberFormatter.with().unit(USD),
+ new ULocale("ar@numbers=arab"),
+ 12345.67,
+ "١٢٬٣٤٥٫٦٧ US$");
+
DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(new ULocale("de-CH"));
UnlocalizedNumberFormatter f = NumberFormatter.with().symbols(symbols);
symbols.setGroupingSeparatorString("!");
ULocale.ENGLISH,
-444444,
"($444,444.00)");
+
+ assertFormatSingle(
+ "Sign Accounting Negative Hidden",
+ "$USD unit-width=HIDDEN sign=ACCOUNTING",
+ NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.HIDDEN),
+ ULocale.ENGLISH,
+ -444444,
+ "(444,444.00)");
}
@Test
assertFormatDescending(
"Decimal Default",
"decimal=AUTO",
- NumberFormatter.with().decimal(DecimalMarkDisplay.AUTO),
+ NumberFormatter.with().decimal(DecimalSeparatorDisplay.AUTO),
ULocale.ENGLISH,
"87,650",
"8,765",
assertFormatDescending(
"Decimal Always Shown",
"decimal=ALWAYS",
- NumberFormatter.with().decimal(DecimalMarkDisplay.ALWAYS),
+ NumberFormatter.with().decimal(DecimalSeparatorDisplay.ALWAYS),
ULocale.ENGLISH,
"87,650.",
"8,765.",
UnlocalizedNumberFormatter f,
ULocale locale,
String... expected) {
- assert expected.length == 9;
- assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
final double[] inputs = new double[] { 87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0 };
+ assertFormatDescending(message, skeleton, f, locale, inputs, expected);
+ }
+
+ private static void assertFormatDescendingBig(
+ String message,
+ String skeleton,
+ UnlocalizedNumberFormatter f,
+ ULocale locale,
+ String... expected) {
+ final double[] inputs = new double[] { 87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0 };
+ assertFormatDescending(message, skeleton, f, locale, inputs, expected);
+ }
+
+ private static void assertFormatDescending(
+ String message,
+ String skeleton,
+ UnlocalizedNumberFormatter f,
+ ULocale locale,
+ double[] inputs,
+ String... expected) {
+ assert expected.length == 9;
+ // TODO: Add a check for skeleton.
+ // assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
for (int i = 0; i < 9; i++) {
double d = inputs[i];
String actual1 = l1.format(d).toString();
- assertEquals(message + ": L1: " + d, expected[i], actual1);
+ assertEquals(message + ": Unsafe Path: " + d, expected[i], actual1);
String actual2 = l2.format(d).toString();
- assertEquals(message + ": L2: " + d, expected[i], actual2);
+ assertEquals(message + ": Safe Path: " + d, expected[i], actual2);
}
}
ULocale locale,
Number input,
String expected) {
- assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
+ // TODO: Add a check for skeleton.
+ // assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
String actual1 = l1.format(input).toString();
ULocale locale,
Measure input,
String expected) {
- assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
+ // TODO: Add a check for skeleton.
+ // assertEquals(message + ": Skeleton:", skeleton, f.toSkeleton());
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
String actual1 = l1.format(input).toString();
import com.ibm.icu.impl.number.Parse.GroupingMode;
import com.ibm.icu.impl.number.Parse.ParseMode;
import com.ibm.icu.impl.number.PatternStringParser;
+import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.text.CompactDecimalFormat.CompactStyle;
import com.ibm.icu.text.CurrencyPluralInfo;
import com.ibm.icu.text.MeasureFormat.FormatWidth;
import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.ULocale;
-import newapi.impl.Padder.PadPosition;
-
public class PropertiesTest {
@Test
copyFrom(other);
}
+ @Override
+ public DecimalQuantity createCopy() {
+ return new DecimalQuantity_64BitBCD(this);
+ }
+
@Override
protected byte getDigitPos(int position) {
if (position < 0 || position >= 16) return 0;
copyFrom(other);
}
+ @Override
+ public DecimalQuantity createCopy() {
+ return new DecimalQuantity_ByteArrayBCD(this);
+ }
+
@Override
protected byte getDigitPos(int position) {
if (position < 0 || position > precision) return 0;