From: Shane Carr Date: Thu, 24 Aug 2017 08:18:22 +0000 (+0000) Subject: ICU-13177 Removes old (ICU 59) number formatting middleware since it has been wholly... X-Git-Tag: release-60-rc~98^2~30 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=388288b8198169c614b830bbef31b9663e004060;p=icu ICU-13177 Removes old (ICU 59) number formatting middleware since it has been wholly replaced by the property mapper function. This change also removes the IProperties polymorphism and makes all remaining classes reference Properties directly. X-SVN-Rev: 40352 --- diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java index 2a79cc56de0..c49ad5ecdf5 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixPatternUtils.java @@ -2,7 +2,6 @@ // License & terms of use: http://www.unicode.org/copyright.html#License package com.ibm.icu.impl.number; -import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.NumberFormat; /** @@ -229,75 +228,6 @@ public class AffixPatternUtils { return sb.toString(); } - /** - * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", and "‰" - * with their localized equivalents. Replaces "¤", "¤¤", and "¤¤¤" with the three argument - * strings. - * - *

Example input: "'-'¤x"; example output: "-$x" - * - * @param affixPattern The original string to be unescaped. - * @param symbols An instance of {@link DecimalFormatSymbols} for the locale of interest. - * @param currency1 The string to replace "¤". - * @param currency2 The string to replace "¤¤". - * @param currency3 The string to replace "¤¤¤". - * @param minusSign The string to replace "-". If null, symbols.getMinusSignString() is used. - * @param output The {@link NumberStringBuilder} to which the result will be appended. - */ - public static void unescape( - CharSequence affixPattern, - DecimalFormatSymbols symbols, - String currency1, - String currency2, - String currency3, - String minusSign, - NumberStringBuilder output) { - if (affixPattern == null || affixPattern.length() == 0) return; - if (minusSign == null) minusSign = symbols.getMinusSignString(); - long tag = 0L; - while (hasNext(tag, affixPattern)) { - tag = nextToken(tag, affixPattern); - int typeOrCp = getTypeOrCp(tag); - NumberFormat.Field field = (typeOrCp < 0) ? getFieldForType(typeOrCp) : null; - switch (typeOrCp) { - case TYPE_MINUS_SIGN: - output.append(minusSign, field); - break; - case TYPE_PLUS_SIGN: - output.append(symbols.getPlusSignString(), field); - break; - case TYPE_PERCENT: - output.append(symbols.getPercentString(), field); - break; - case TYPE_PERMILLE: - output.append(symbols.getPerMillString(), field); - break; - case TYPE_CURRENCY_SINGLE: - output.append(currency1, field); - break; - case TYPE_CURRENCY_DOUBLE: - output.append(currency2, field); - break; - case TYPE_CURRENCY_TRIPLE: - output.append(currency3, field); - break; - case TYPE_CURRENCY_QUAD: - output.appendCodePoint('\uFFFD', field); - break; - case TYPE_CURRENCY_QUINT: - // TODO: Add support for narrow currency symbols here. - output.appendCodePoint('\uFFFD', field); - break; - case TYPE_CURRENCY_OVERFLOW: - output.appendCodePoint('\uFFFD', field); - break; - default: - output.appendCodePoint(typeOrCp, null); - break; - } - } - } - public static final NumberFormat.Field getFieldForType(int type) { switch (type) { case TYPE_MINUS_SIGN: @@ -329,6 +259,18 @@ public class AffixPatternUtils { public CharSequence getSymbol(int type); } + /** + * Executes the unescape state machine. Replaces the unquoted characters "-", "+", "%", "‰", and + * "¤" with the corresponding symbols provided by the {@link SymbolProvider}, and inserts the + * result into the NumberStringBuilder at the requested location. + * + *

Example input: "'-'¤x"; example output: "-$x" + * + * @param affixPattern The original string to be unescaped. + * @param output The NumberStringBuilder to mutate with the result. + * @param position The index into the NumberStringBuilder to insert the the string. + * @param provider An object to generate locale symbols. + */ public static int unescape( CharSequence affixPattern, NumberStringBuilder output, diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Endpoint.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Endpoint.java deleted file mode 100644 index abf83242537..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Endpoint.java +++ /dev/null @@ -1,298 +0,0 @@ -// © 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 java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import com.ibm.icu.impl.number.Format.BeforeTargetAfterFormat; -import com.ibm.icu.impl.number.Format.SingularFormat; -import com.ibm.icu.impl.number.Format.TargetFormat; -import com.ibm.icu.impl.number.formatters.BigDecimalMultiplier; -import com.ibm.icu.impl.number.formatters.CompactDecimalFormat; -import com.ibm.icu.impl.number.formatters.CurrencyFormat; -import com.ibm.icu.impl.number.formatters.MagnitudeMultiplier; -import com.ibm.icu.impl.number.formatters.MeasureFormat; -import com.ibm.icu.impl.number.formatters.PaddingFormat; -import com.ibm.icu.impl.number.formatters.PositiveDecimalFormat; -import com.ibm.icu.impl.number.formatters.PositiveNegativeAffixFormat; -import com.ibm.icu.impl.number.formatters.RoundingFormat; -import com.ibm.icu.impl.number.formatters.ScientificFormat; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.PluralRules; -import com.ibm.icu.util.ULocale; - -public class Endpoint { - // public static Format from(DecimalFormatSymbols symbols, Properties properties) - // throws ParseException { - // Format format = new PositiveIntegerFormat(symbols, properties); - // // TODO: integer-only format - // format = new PositiveDecimalFormat((SelfContainedFormat) format, symbols, properties); - // if (properties.useCompactDecimalFormat()) { - // format = CompactDecimalFormat.getInstance((SingularFormat) format, symbols, properties); - // } else { - // format = - // PositiveNegativeAffixFormat.getInstance((SingularFormat) format, symbols, properties); - // } - // if (properties.useRoundingInterval()) { - // format = new IntervalRoundingFormat((SingularFormat) format, properties); - // } else if (properties.useSignificantDigits()) { - // format = new SignificantDigitsFormat((SingularFormat) format, properties); - // } else if (properties.useFractionFormat()) { - // format = new RoundingFormat((SingularFormat) format, properties); - // } - // return format; - // } - - public static interface IProperties { - static PluralRules DEFAULT_PLURAL_RULES = null; - - public PluralRules getPluralRules(); - - public IProperties setPluralRules(PluralRules pluralRules); - } - - public static Format fromBTA(Properties properties) { - return fromBTA(properties, getSymbols()); - } - - public static SingularFormat fromBTA(Properties properties, Locale locale) { - return fromBTA(properties, getSymbols(locale)); - } - - public static SingularFormat fromBTA(Properties properties, ULocale uLocale) { - return fromBTA(properties, getSymbols(uLocale)); - } - - public static SingularFormat fromBTA(String pattern) { - return fromBTA(getProperties(pattern), getSymbols()); - } - - public static SingularFormat fromBTA(String pattern, Locale locale) { - return fromBTA(getProperties(pattern), getSymbols(locale)); - } - - public static SingularFormat fromBTA(String pattern, ULocale uLocale) { - return fromBTA(getProperties(pattern), getSymbols(uLocale)); - } - - public static SingularFormat fromBTA(String pattern, DecimalFormatSymbols symbols) { - return fromBTA(getProperties(pattern), symbols); - } - - public static SingularFormat fromBTA(Properties properties, DecimalFormatSymbols symbols) { - - if (symbols == null) throw new IllegalArgumentException("symbols must not be null"); - - // TODO: This fast track results in an improvement of about 10ns during formatting. See if - // there is a way to implement it more elegantly. - boolean canUseFastTrack = true; - PluralRules rules = getPluralRules(symbols.getULocale(), properties); - BeforeTargetAfterFormat format = new Format.BeforeTargetAfterFormat(rules); - TargetFormat target = new PositiveDecimalFormat(symbols, properties); - format.setTargetFormat(target); - // TODO: integer-only format? - if (MagnitudeMultiplier.useMagnitudeMultiplier(properties)) { - canUseFastTrack = false; - format.addBeforeFormat(MagnitudeMultiplier.getInstance(properties)); - } - if (BigDecimalMultiplier.useMultiplier(properties)) { - canUseFastTrack = false; - format.addBeforeFormat(BigDecimalMultiplier.getInstance(properties)); - } - if (MeasureFormat.useMeasureFormat(properties)) { - canUseFastTrack = false; - format.addBeforeFormat(MeasureFormat.getInstance(symbols, properties)); - } - if (CurrencyFormat.useCurrency(properties)) { - canUseFastTrack = false; - if (CompactDecimalFormat.useCompactDecimalFormat(properties)) { - format.addBeforeFormat(CompactDecimalFormat.getInstance(symbols, properties)); - } else if (ScientificFormat.useScientificNotation(properties)) { - // TODO: Should the currency rounder or scientific rounder be used in this case? - // For now, default to using the scientific rounder. - format.addBeforeFormat(PositiveNegativeAffixFormat.getInstance(symbols, properties)); - format.addBeforeFormat(ScientificFormat.getInstance(symbols, properties)); - } else { - format.addBeforeFormat(CurrencyFormat.getCurrencyRounder(symbols, properties)); - format.addBeforeFormat(CurrencyFormat.getCurrencyModifier(symbols, properties)); - } - } else { - if (CompactDecimalFormat.useCompactDecimalFormat(properties)) { - canUseFastTrack = false; - format.addBeforeFormat(CompactDecimalFormat.getInstance(symbols, properties)); - } else if (ScientificFormat.useScientificNotation(properties)) { - canUseFastTrack = false; - format.addBeforeFormat(PositiveNegativeAffixFormat.getInstance(symbols, properties)); - format.addBeforeFormat(ScientificFormat.getInstance(symbols, properties)); - } else { - format.addBeforeFormat(PositiveNegativeAffixFormat.getInstance(symbols, properties)); - format.addBeforeFormat(RoundingFormat.getDefaultOrNoRounder(properties)); - } - } - if (PaddingFormat.usePadding(properties)) { - canUseFastTrack = false; - format.addAfterFormat(PaddingFormat.getInstance(properties)); - } - if (canUseFastTrack) { - return new Format.PositiveNegativeRounderTargetFormat( - PositiveNegativeAffixFormat.getInstance(symbols, properties), - RoundingFormat.getDefaultOrNoRounder(properties), - target); - } else { - return format; - } - } - - public static String staticFormat(FormatQuantity input, Properties properties) { - return staticFormat(input, properties, getSymbols()); - } - - public static String staticFormat(FormatQuantity input, Properties properties, Locale locale) { - return staticFormat(input, properties, getSymbols(locale)); - } - - public static String staticFormat(FormatQuantity input, Properties properties, ULocale uLocale) { - return staticFormat(input, properties, getSymbols(uLocale)); - } - - public static String staticFormat(FormatQuantity input, String pattern) { - return staticFormat(input, getProperties(pattern), getSymbols()); - } - - public static String staticFormat(FormatQuantity input, String pattern, Locale locale) { - return staticFormat(input, getProperties(pattern), getSymbols(locale)); - } - - public static String staticFormat(FormatQuantity input, String pattern, ULocale uLocale) { - return staticFormat(input, getProperties(pattern), getSymbols(uLocale)); - } - - public static String staticFormat( - FormatQuantity input, String pattern, DecimalFormatSymbols symbols) { - return staticFormat(input, getProperties(pattern), symbols); - } - - public static String staticFormat( - FormatQuantity input, Properties properties, DecimalFormatSymbols symbols) { - PluralRules rules = null; - ModifierHolder mods = Format.threadLocalModifierHolder.get().clear(); - NumberStringBuilder sb = Format.threadLocalStringBuilder.get().clear(); - int length = 0; - - // Pre-processing - if (!input.isNaN()) { - if (MagnitudeMultiplier.useMagnitudeMultiplier(properties)) { - MagnitudeMultiplier.getInstance(properties).before(input, mods, rules); - } - if (BigDecimalMultiplier.useMultiplier(properties)) { - BigDecimalMultiplier.getInstance(properties).before(input, mods, rules); - } - if (MeasureFormat.useMeasureFormat(properties)) { - rules = (rules != null) ? rules : getPluralRules(symbols.getULocale(), properties); - MeasureFormat.getInstance(symbols, properties).before(input, mods, rules); - } - if (CompactDecimalFormat.useCompactDecimalFormat(properties)) { - rules = (rules != null) ? rules : getPluralRules(symbols.getULocale(), properties); - CompactDecimalFormat.apply(input, mods, rules, symbols, properties); - } else if (CurrencyFormat.useCurrency(properties)) { - rules = (rules != null) ? rules : getPluralRules(symbols.getULocale(), properties); - CurrencyFormat.getCurrencyRounder(symbols, properties).before(input, mods, rules); - CurrencyFormat.getCurrencyModifier(symbols, properties).before(input, mods, rules); - } else if (ScientificFormat.useScientificNotation(properties)) { - // TODO: Is it possible to combine significant digits with currency? - PositiveNegativeAffixFormat.getInstance(symbols, properties).before(input, mods, rules); - ScientificFormat.getInstance(symbols, properties).before(input, mods, rules); - } else { - PositiveNegativeAffixFormat.apply(input, mods, symbols, properties); - RoundingFormat.getDefaultOrNoRounder(properties).before(input, mods, rules); - } - } - - // Primary format step - length += new PositiveDecimalFormat(symbols, properties).target(input, sb, 0); - length += mods.applyStrong(sb, 0, length); - - // Post-processing - if (PaddingFormat.usePadding(properties)) { - length += PaddingFormat.getInstance(properties).after(mods, sb, 0, length); - } - length += mods.applyAll(sb, 0, length); - - return sb.toString(); - } - - private static final ThreadLocal> threadLocalSymbolsCache = - new ThreadLocal>() { - @Override - protected Map initialValue() { - return new HashMap(); - } - }; - - private static DecimalFormatSymbols getSymbols() { - ULocale uLocale = ULocale.getDefault(); - return getSymbols(uLocale); - } - - private static DecimalFormatSymbols getSymbols(Locale locale) { - ULocale uLocale = ULocale.forLocale(locale); - return getSymbols(uLocale); - } - - private static DecimalFormatSymbols getSymbols(ULocale uLocale) { - if (uLocale == null) uLocale = ULocale.getDefault(); - DecimalFormatSymbols symbols = threadLocalSymbolsCache.get().get(uLocale); - if (symbols == null) { - symbols = DecimalFormatSymbols.getInstance(uLocale); - threadLocalSymbolsCache.get().put(uLocale, symbols); - } - return symbols; - } - - private static final ThreadLocal> threadLocalPropertiesCache = - new ThreadLocal>() { - @Override - protected Map initialValue() { - return new HashMap(); - } - }; - - private static Properties getProperties(String pattern) { - if (pattern == null) pattern = "#"; - Properties properties = threadLocalPropertiesCache.get().get(pattern); - if (properties == null) { - properties = PatternString.parseToProperties(pattern); - threadLocalPropertiesCache.get().put(pattern.intern(), properties); - } - return properties; - } - - private static final ThreadLocal> threadLocalRulesCache = - new ThreadLocal>() { - @Override - protected Map initialValue() { - return new HashMap(); - } - }; - - private static PluralRules getPluralRules(ULocale uLocale, Properties properties) { - if (properties.getPluralRules() != null) { - return properties.getPluralRules(); - } - - // Backwards compatibility: CurrencyPluralInfo wraps its own copy of PluralRules - if (properties.getCurrencyPluralInfo() != null) { - return properties.getCurrencyPluralInfo().getPluralRules(); - } - - if (uLocale == null) uLocale = ULocale.getDefault(); - PluralRules rules = threadLocalRulesCache.get().get(uLocale); - if (rules == null) { - rules = PluralRules.forLocale(uLocale); - threadLocalRulesCache.get().put(uLocale, rules); - } - return rules; - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Exportable.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Exportable.java deleted file mode 100644 index 515667d9156..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Exportable.java +++ /dev/null @@ -1,15 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number; - -/** - * This is a small interface I made to assist with converting from a formatter pipeline object to a - * pattern string. It allows classes to "export" themselves to a property bag, which in turn can be - * passed to {@link PatternString#propertiesToString(Properties)} to generate the pattern string. - * - *

Depending on the new API we expose, this process might not be necessary if we persist the - * property bag in the current DecimalFormat shim. - */ -public interface Exportable { - public void export(Properties properties); -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Format.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Format.java deleted file mode 100644 index c19e9246183..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Format.java +++ /dev/null @@ -1,277 +0,0 @@ -// © 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 java.text.AttributedCharacterIterator; -import java.text.FieldPosition; -import java.util.ArrayDeque; -import java.util.Arrays; -import java.util.Deque; - -import com.ibm.icu.text.PluralRules; - -// TODO: Get a better name for this base class. -public abstract class Format { - - protected static final ThreadLocal threadLocalStringBuilder = - new ThreadLocal() { - @Override - protected NumberStringBuilder initialValue() { - return new NumberStringBuilder(); - } - }; - - protected static final ThreadLocal threadLocalModifierHolder = - new ThreadLocal() { - @Override - protected ModifierHolder initialValue() { - return new ModifierHolder(); - } - }; - - public String format(FormatQuantity... inputs) { - // Setup - Deque inputDeque = new ArrayDeque(); - inputDeque.addAll(Arrays.asList(inputs)); - ModifierHolder modDeque = threadLocalModifierHolder.get().clear(); - NumberStringBuilder sb = threadLocalStringBuilder.get().clear(); - - // Primary "recursion" step, calling the implementation's process method - int length = process(inputDeque, modDeque, sb, 0); - - // Resolve remaining affixes - modDeque.applyAll(sb, 0, length); - return sb.toString(); - } - - /** A Format that works on only one number. */ - public abstract static class SingularFormat extends Format implements Exportable { - - public String format(FormatQuantity input) { - NumberStringBuilder sb = formatToStringBuilder(input); - return sb.toString(); - } - - public void format(FormatQuantity input, StringBuffer output) { - NumberStringBuilder sb = formatToStringBuilder(input); - output.append(sb); - } - - public String format(FormatQuantity input, FieldPosition fp) { - NumberStringBuilder sb = formatToStringBuilder(input); - sb.populateFieldPosition(fp, 0); - return sb.toString(); - } - - public void format(FormatQuantity input, StringBuffer output, FieldPosition fp) { - NumberStringBuilder sb = formatToStringBuilder(input); - sb.populateFieldPosition(fp, output.length()); - output.append(sb); - } - - public AttributedCharacterIterator formatToCharacterIterator(FormatQuantity input) { - NumberStringBuilder sb = formatToStringBuilder(input); - return sb.getIterator(); - } - - private NumberStringBuilder formatToStringBuilder(FormatQuantity input) { - // Setup - ModifierHolder modDeque = threadLocalModifierHolder.get().clear(); - NumberStringBuilder sb = threadLocalStringBuilder.get().clear(); - - // Primary "recursion" step, calling the implementation's process method - int length = process(input, modDeque, sb, 0); - - // Resolve remaining affixes - length += modDeque.applyAll(sb, 0, length); - return sb; - } - - @Override - public int process( - Deque input, - ModifierHolder mods, - NumberStringBuilder string, - int startIndex) { - return process(input.removeFirst(), mods, string, startIndex); - } - - public abstract int process( - FormatQuantity input, ModifierHolder mods, NumberStringBuilder string, int startIndex); - } - - public static class BeforeTargetAfterFormat extends SingularFormat { - // The formatters are kept as individual fields to avoid extra object creation overhead. - private BeforeFormat before1 = null; - private BeforeFormat before2 = null; - private BeforeFormat before3 = null; - private TargetFormat target = null; - private AfterFormat after1 = null; - private AfterFormat after2 = null; - private AfterFormat after3 = null; - private final PluralRules rules; - - public BeforeTargetAfterFormat(PluralRules rules) { - this.rules = rules; - } - - public void addBeforeFormat(BeforeFormat before) { - if (before1 == null) { - before1 = before; - } else if (before2 == null) { - before2 = before; - } else if (before3 == null) { - before3 = before; - } else { - throw new IllegalArgumentException("Only three BeforeFormats are allowed at a time"); - } - } - - public void setTargetFormat(TargetFormat target) { - this.target = target; - } - - public void addAfterFormat(AfterFormat after) { - if (after1 == null) { - after1 = after; - } else if (after2 == null) { - after2 = after; - } else if (after3 == null) { - after3 = after; - } else { - throw new IllegalArgumentException("Only three AfterFormats are allowed at a time"); - } - } - - @Override - public String format(FormatQuantity input) { - ModifierHolder mods = threadLocalModifierHolder.get().clear(); - NumberStringBuilder sb = threadLocalStringBuilder.get().clear(); - int length = process(input, mods, sb, 0); - mods.applyAll(sb, 0, length); - return sb.toString(); - } - - @Override - public int process( - FormatQuantity input, ModifierHolder mods, NumberStringBuilder string, int startIndex) { - // Special case: modifiers are skipped for NaN - int length = 0; - if (!input.isNaN()) { - if (before1 != null) { - before1.before(input, mods, rules); - } - if (before2 != null) { - before2.before(input, mods, rules); - } - if (before3 != null) { - before3.before(input, mods, rules); - } - } - length = target.target(input, string, startIndex); - length += mods.applyStrong(string, startIndex, startIndex + length); - if (after1 != null) { - length += after1.after(mods, string, startIndex, startIndex + length); - } - if (after2 != null) { - length += after2.after(mods, string, startIndex, startIndex + length); - } - if (after3 != null) { - length += after3.after(mods, string, startIndex, startIndex + length); - } - return length; - } - - @Override - public void export(Properties properties) { - if (before1 != null) { - before1.export(properties); - } - if (before2 != null) { - before2.export(properties); - } - if (before3 != null) { - before3.export(properties); - } - target.export(properties); - if (after1 != null) { - after1.export(properties); - } - if (after2 != null) { - after2.export(properties); - } - if (after3 != null) { - after3.export(properties); - } - } - } - - public static class PositiveNegativeRounderTargetFormat extends SingularFormat { - private final Modifier.PositiveNegativeModifier positiveNegative; - private final Rounder rounder; - private final TargetFormat target; - - public PositiveNegativeRounderTargetFormat( - Modifier.PositiveNegativeModifier positiveNegative, Rounder rounder, TargetFormat target) { - this.positiveNegative = positiveNegative; - this.rounder = rounder; - this.target = target; - } - - @Override - public String format(FormatQuantity input) { - NumberStringBuilder sb = threadLocalStringBuilder.get().clear(); - process(input, null, sb, 0); - return sb.toString(); - } - - @Override - public int process( - FormatQuantity input, ModifierHolder mods, NumberStringBuilder string, int startIndex) { - // Special case: modifiers are skipped for NaN - Modifier mod = null; - rounder.apply(input); - if (!input.isNaN() && positiveNegative != null) { - mod = positiveNegative.getModifier(input.isNegative()); - } - int length = target.target(input, string, startIndex); - if (mod != null) { - length += mod.apply(string, 0, length); - } - return length; - } - - @Override - public void export(Properties properties) { - rounder.export(properties); - positiveNegative.export(properties); - target.export(properties); - } - } - - public abstract static class BeforeFormat implements Exportable { - protected abstract void before(FormatQuantity input, ModifierHolder mods); - - @SuppressWarnings("unused") - public void before(FormatQuantity input, ModifierHolder mods, PluralRules rules) { - before(input, mods); - } - } - - public static interface TargetFormat extends Exportable { - public abstract int target(FormatQuantity input, NumberStringBuilder string, int startIndex); - } - - public static interface AfterFormat extends Exportable { - public abstract int after( - ModifierHolder mods, NumberStringBuilder string, int leftIndex, int rightIndex); - } - - // Instead of Dequeue, it could be Deque where - // we control the API of Quantity - public abstract int process( - Deque inputs, - ModifierHolder outputMods, - NumberStringBuilder outputString, - int startIndex); -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity.java index aa80a422948..c0f869bbf70 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity.java @@ -4,9 +4,11 @@ package com.ibm.icu.impl.number; import java.math.BigDecimal; import java.math.MathContext; +import java.text.FieldPosition; import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.text.PluralRules; +import com.ibm.icu.text.UFieldPosition; /** * An interface representing a number to be processed by the decimal formatting pipeline. Includes @@ -186,4 +188,13 @@ public interface FormatQuantity extends PluralRules.IFixedDecimal { /** This method is for internal testing only. */ public long getPositionFingerprint(); + + /** + * If the given {@link FieldPosition} is a {@link UFieldPosition}, populates it with the fraction + * length and fraction long value. If the argument is not a {@link UFieldPosition}, nothing + * happens. + * + * @param fp The {@link UFieldPosition} to populate. + */ + public void populateUFieldPosition(FieldPosition fp); } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity1.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity1.java index d6820b202cd..e68cf99bbfe 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity1.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantity1.java @@ -5,10 +5,12 @@ package com.ibm.icu.impl.number; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; +import java.text.FieldPosition; import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.text.PluralRules; import com.ibm.icu.text.PluralRules.Operand; +import com.ibm.icu.text.UFieldPosition; /** * This is an older implementation of FormatQuantity. A newer, faster implementation is @@ -862,4 +864,12 @@ public class FormatQuantity1 implements FormatQuantity { return i; } } + + @Override + public void populateUFieldPosition(FieldPosition fp) { + if (fp instanceof UFieldPosition) { + ((UFieldPosition) fp) + .setFractionDigits((int) getPluralOperand(Operand.v), (long) getPluralOperand(Operand.f)); + } + } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantityBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantityBCD.java index 0d6e82f87ac..90416bfa137 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantityBCD.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantityBCD.java @@ -243,13 +243,7 @@ public abstract class FormatQuantityBCD implements FormatQuantity { } } - /** - * If the given {@link FieldPosition} is a {@link UFieldPosition}, populates it with the fraction - * length and fraction long value. If the argument is not a {@link UFieldPosition}, nothing - * happens. - * - * @param fp The {@link UFieldPosition} to populate. - */ + @Override public void populateUFieldPosition(FieldPosition fp) { if (fp instanceof UFieldPosition) { ((UFieldPosition) fp) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantitySelector.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantitySelector.java deleted file mode 100644 index 95a11ba8041..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/FormatQuantitySelector.java +++ /dev/null @@ -1,52 +0,0 @@ -// © 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 java.math.BigDecimal; -import java.math.BigInteger; - -/** @author sffc */ -public class FormatQuantitySelector { - public static FormatQuantityBCD from(int input) { - return new FormatQuantity4(input); - } - - public static FormatQuantityBCD from(long input) { - return new FormatQuantity4(input); - } - - public static FormatQuantityBCD from(double input) { - return new FormatQuantity4(input); - } - - public static FormatQuantityBCD from(BigInteger input) { - return new FormatQuantity4(input); - } - - public static FormatQuantityBCD from(BigDecimal input) { - return new FormatQuantity4(input); - } - - public static FormatQuantityBCD from(com.ibm.icu.math.BigDecimal input) { - return from(input.toBigDecimal()); - } - - public static FormatQuantityBCD from(Number number) { - if (number instanceof Long) { - return from(number.longValue()); - } else if (number instanceof Integer) { - return from(number.intValue()); - } else if (number instanceof Double) { - return from(number.doubleValue()); - } else if (number instanceof BigInteger) { - return from((BigInteger) number); - } else if (number instanceof BigDecimal) { - return from((BigDecimal) number); - } else if (number instanceof com.ibm.icu.math.BigDecimal) { - return from((com.ibm.icu.math.BigDecimal) number); - } else { - throw new IllegalArgumentException( - "Number is of an unsupported type: " + number.getClass().getName()); - } - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LdmlPatternInfo.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LdmlPatternInfo.java index ef0781ab4b0..745b4d3344c 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LdmlPatternInfo.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/LdmlPatternInfo.java @@ -2,7 +2,7 @@ // License & terms of use: http://www.unicode.org/copyright.html#License package com.ibm.icu.impl.number; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import newapi.impl.AffixPatternProvider; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java index c05d7a110b6..10b5e05ca00 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Modifier.java @@ -69,7 +69,7 @@ public interface Modifier { * class implementing {@link PositiveNegativeModifier} is not necessarily a {@link Modifier} * itself. Rather, it returns a {@link Modifier} when {@link #getModifier} is called. */ - public static interface PositiveNegativeModifier extends Exportable { + public static interface PositiveNegativeModifier { /** * Converts this {@link PositiveNegativeModifier} to a {@link Modifier} given the negative sign. * @@ -86,7 +86,7 @@ public interface Modifier { * necessarily a {@link Modifier} itself. Rather, it returns a {@link Modifier} when {@link * #getModifier} is called. */ - public static interface PositiveNegativePluralModifier extends Exportable { + public static interface PositiveNegativePluralModifier { /** * Converts this {@link PositiveNegativePluralModifier} to a {@link Modifier} given the negative * sign and the standard plural. @@ -111,13 +111,7 @@ public interface Modifier { *

Implements {@link PositiveNegativeModifier} only so that instances of this class can be used * when a {@link PositiveNegativeModifier} is required. */ - public abstract static class BaseModifier extends Format.BeforeFormat - implements Modifier, PositiveNegativeModifier { - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - mods.add(this); - } + public abstract static class BaseModifier implements Modifier, PositiveNegativeModifier { @Override public Modifier getModifier(boolean isNegative) { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierHolder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierHolder.java deleted file mode 100644 index a35647c3bb5..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ModifierHolder.java +++ /dev/null @@ -1,98 +0,0 @@ -// © 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 java.util.ArrayDeque; - -public class ModifierHolder { - private ArrayDeque mods = new ArrayDeque(); - - // Using five separate fields instead of the ArrayDeque saves about 10ns at the expense of - // worse code. - // TODO: Decide which implementation to use. - - // private Modifier mod1 = null; - // private Modifier mod2 = null; - // private Modifier mod3 = null; - // private Modifier mod4 = null; - // private Modifier mod5 = null; - - public ModifierHolder createCopy() { - ModifierHolder copy = new ModifierHolder(); - copy.mods.addAll(mods); - return copy; - } - - public ModifierHolder clear() { - // mod1 = null; - // mod2 = null; - // mod3 = null; - // mod4 = null; - // mod5 = null; - mods.clear(); - return this; - } - - public void add(Modifier modifier) { - // if (mod1 == null) { - // mod1 = modifier; - // } else if (mod2 == null) { - // mod2 = modifier; - // } else if (mod3 == null) { - // mod3 = modifier; - // } else if (mod4 == null) { - // mod4 = modifier; - // } else if (mod5 == null) { - // mod5 = modifier; - // } else { - // throw new IndexOutOfBoundsException(); - // } - if (modifier != null) mods.addFirst(modifier); - } - - public Modifier peekLast() { - return mods.peekLast(); - } - - public Modifier removeLast() { - return mods.removeLast(); - } - - public int applyAll(NumberStringBuilder string, int leftIndex, int rightIndex) { - int addedLength = 0; - // if (mod5 != null) { - // addedLength += mod5.apply(string, leftIndex, rightIndex + addedLength); - // mod5 = null; - // } - // if (mod4 != null) { - // addedLength += mod4.apply(string, leftIndex, rightIndex + addedLength); - // mod4 = null; - // } - // if (mod3 != null) { - // addedLength += mod3.apply(string, leftIndex, rightIndex + addedLength); - // mod3 = null; - // } - // if (mod2 != null) { - // addedLength += mod2.apply(string, leftIndex, rightIndex + addedLength); - // mod2 = null; - // } - // if (mod1 != null) { - // addedLength += mod1.apply(string, leftIndex, rightIndex + addedLength); - // mod1 = null; - // } - while (!mods.isEmpty()) { - Modifier mod = mods.removeFirst(); - addedLength += mod.apply(string, leftIndex, rightIndex + addedLength); - } - return addedLength; - } - - public int applyStrong(NumberStringBuilder string, int leftIndex, int rightIndex) { - int addedLength = 0; - while (!mods.isEmpty() && mods.peekFirst().isStrong()) { - Modifier mod = mods.removeFirst(); - addedLength += mod.apply(string, leftIndex, rightIndex + addedLength); - } - return addedLength; - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PNAffixGenerator.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PNAffixGenerator.java deleted file mode 100644 index f50704b981e..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PNAffixGenerator.java +++ /dev/null @@ -1,281 +0,0 @@ -// © 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.number.Modifier.AffixModifier; -import com.ibm.icu.impl.number.formatters.CompactDecimalFormat; -import com.ibm.icu.impl.number.formatters.PositiveNegativeAffixFormat; -import com.ibm.icu.impl.number.formatters.PositiveNegativeAffixFormat.IProperties; -import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier; -import com.ibm.icu.impl.number.modifiers.ConstantMultiFieldModifier; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.NumberFormat.Field; - -/** - * A class to convert from a bag of prefix/suffix properties into a positive and negative {@link - * Modifier}. This is a standard implementation used by {@link PositiveNegativeAffixFormat}, {@link - * CompactDecimalFormat}, {@link Parse}, and others. - * - *

This class is is intended to be an efficient generator for instances of Modifier by a single - * thread during construction of a formatter or during static formatting. It uses internal caching - * to avoid creating new Modifier objects when possible. It is NOT THREAD SAFE and NOT IMMUTABLE. - * - *

The thread-local instance of this class provided by {@link #getThreadLocalInstance} should be - * used in most cases instead of constructing a new instance of the object. - * - *

This class also handles the logic of assigning positive signs, negative signs, and currency - * signs according to the LDML specification. - */ -public class PNAffixGenerator { - public static class Result { - public AffixModifier positive = null; - public AffixModifier negative = null; - } - - protected static final ThreadLocal threadLocalInstance = - new ThreadLocal() { - @Override - protected PNAffixGenerator initialValue() { - return new PNAffixGenerator(); - } - }; - - public static PNAffixGenerator getThreadLocalInstance() { - return threadLocalInstance.get(); - } - - // These instances are used internally and cached to avoid object creation. The resultInstance - // also serves as a 1-element cache to avoid creating objects when subsequent calls have - // identical prefixes and suffixes. This happens, for example, when consuming CDF data. - private Result resultInstance = new Result(); - private NumberStringBuilder sb1 = new NumberStringBuilder(); - private NumberStringBuilder sb2 = new NumberStringBuilder(); - private NumberStringBuilder sb3 = new NumberStringBuilder(); - private NumberStringBuilder sb4 = new NumberStringBuilder(); - private NumberStringBuilder sb5 = new NumberStringBuilder(); - private NumberStringBuilder sb6 = new NumberStringBuilder(); - - /** - * Generates modifiers using default currency symbols. - * - * @param symbols The symbols to interpolate for minus, plus, percent, permille, and currency. - * @param properties The bag of properties to convert. - * @return The positive and negative {@link Modifier}. - */ - public Result getModifiers( - DecimalFormatSymbols symbols, PositiveNegativeAffixFormat.IProperties properties) { - // If this method is used, the user doesn't care about currencies. Default the currency symbols - // to the information we can get from the DecimalFormatSymbols instance. - return getModifiers( - symbols, - symbols.getCurrencySymbol(), - symbols.getInternationalCurrencySymbol(), - symbols.getCurrencySymbol(), - properties); - } - - /** - * Generates modifiers using the specified currency symbol for all three lengths of currency - * placeholders in the pattern string. - * - * @param symbols The symbols to interpolate for minus, plus, percent, and permille. - * @param currencySymbol The currency symbol. - * @param properties The bag of properties to convert. - * @return The positive and negative {@link Modifier}. - */ - public Result getModifiers( - DecimalFormatSymbols symbols, - String currencySymbol, - PositiveNegativeAffixFormat.IProperties properties) { - // If this method is used, the user doesn't cares about currencies but doesn't care about - // supporting all three sizes of currency placeholders. Use the one provided string for all - // three sizes of placeholders. - return getModifiers(symbols, currencySymbol, currencySymbol, currencySymbol, properties); - } - - /** - * Generates modifiers using the three specified strings to replace the three lengths of currency - * placeholders: "¤", "¤¤", and "¤¤¤". - * - * @param symbols The symbols to interpolate for minus, plus, percent, and permille. - * @param curr1 The string to replace "¤". - * @param curr2 The string to replace "¤¤". - * @param curr3 The string to replace "¤¤¤". - * @param properties The bag of properties to convert. - * @return The positive and negative {@link Modifier}. - */ - public Result getModifiers( - DecimalFormatSymbols symbols, - String curr1, - String curr2, - String curr3, - PositiveNegativeAffixFormat.IProperties properties) { - - // Use a different code path for handling affixes with "always show plus sign" - if (properties.getSignAlwaysShown()) { - return getModifiersWithPlusSign(symbols, curr1, curr2, curr3, properties); - } - - String ppp = properties.getPositivePrefixPattern(); - String psp = properties.getPositiveSuffixPattern(); - String npp = properties.getNegativePrefixPattern(); - String nsp = properties.getNegativeSuffixPattern(); - - // Set sb1/sb2 to the positive prefix/suffix. - sb1.clear(); - sb2.clear(); - AffixPatternUtils.unescape(ppp, symbols, curr1, curr2, curr3, null, sb1); - AffixPatternUtils.unescape(psp, symbols, curr1, curr2, curr3, null, sb2); - setPositiveResult(sb1, sb2, properties); - - // Set sb1/sb2 to the negative prefix/suffix. - if (npp == null && nsp == null) { - // Negative prefix defaults to positive prefix prepended with the minus sign. - // Negative suffix defaults to positive suffix. - sb1.insert(0, symbols.getMinusSignString(), Field.SIGN); - } else { - sb1.clear(); - sb2.clear(); - AffixPatternUtils.unescape(npp, symbols, curr1, curr2, curr3, null, sb1); - AffixPatternUtils.unescape(nsp, symbols, curr1, curr2, curr3, null, sb2); - } - setNegativeResult(sb1, sb2, properties); - - return resultInstance; - } - - private Result getModifiersWithPlusSign( - DecimalFormatSymbols symbols, - String curr1, - String curr2, - String curr3, - IProperties properties) { - - String ppp = properties.getPositivePrefixPattern(); - String psp = properties.getPositiveSuffixPattern(); - String npp = properties.getNegativePrefixPattern(); - String nsp = properties.getNegativeSuffixPattern(); - - // There are three cases, listed below with their expected outcomes. - // TODO: Should we handle the cases when the positive subpattern has a '+' already? - // - // 1) No negative subpattern. - // Positive => Positive subpattern prepended with '+' - // Negative => Positive subpattern prepended with '-' - // 2) Negative subpattern does not have '-'. - // Positive => Positive subpattern prepended with '+' - // Negative => Negative subpattern - // 3) Negative subpattern has '-'. - // Positive => Negative subpattern with '+' substituted for '-' - // Negative => Negative subpattern - - if (npp != null || nsp != null) { - // Case 2 or Case 3 - sb1.clear(); - sb2.clear(); - sb3.clear(); - sb4.clear(); - AffixPatternUtils.unescape(npp, symbols, curr1, curr2, curr3, null, sb1); - AffixPatternUtils.unescape(nsp, symbols, curr1, curr2, curr3, null, sb2); - AffixPatternUtils.unescape( - npp, symbols, curr1, curr2, curr3, symbols.getPlusSignString(), sb3); - AffixPatternUtils.unescape( - nsp, symbols, curr1, curr2, curr3, symbols.getPlusSignString(), sb4); - if (!charSequenceEquals(sb1, sb3) || !charSequenceEquals(sb2, sb4)) { - // Case 3. The plus sign substitution was successful. - setPositiveResult(sb3, sb4, properties); - setNegativeResult(sb1, sb2, properties); - return resultInstance; - } else { - // Case 2. There was no minus sign. Set the negative result and fall through. - setNegativeResult(sb1, sb2, properties); - } - } - - // Case 1 or 2. Set sb1/sb2 to the positive prefix/suffix. - sb1.clear(); - sb2.clear(); - AffixPatternUtils.unescape(ppp, symbols, curr1, curr2, curr3, null, sb1); - AffixPatternUtils.unescape(psp, symbols, curr1, curr2, curr3, null, sb2); - - if (npp == null && nsp == null) { - // Case 1. Compute the negative result from the positive subpattern. - sb3.clear(); - sb3.append(symbols.getMinusSignString(), Field.SIGN); - sb3.append(sb1); - setNegativeResult(sb3, sb2, properties); - } - - // Case 1 or 2. Prepend a '+' sign to the positive prefix. - sb1.insert(0, symbols.getPlusSignString(), Field.SIGN); - setPositiveResult(sb1, sb2, properties); - - return resultInstance; - } - - private void setPositiveResult( - NumberStringBuilder prefix, NumberStringBuilder suffix, IProperties properties) { - // Override with custom affixes. We need to put these into NumberStringBuilders so that they - // have the same datatype as the incoming prefix and suffix (important when testing for field - // equality in contentEquals). - // TODO: It is a little inefficient that we copy String -> NumberStringBuilder -> Modifier. - // Consider re-working the logic so that fewer copies are required. - String _prefix = properties.getPositivePrefix(); - String _suffix = properties.getPositiveSuffix(); - if (_prefix != null) { - prefix = sb5.clear(); - prefix.append(_prefix, null); - } - if (_suffix != null) { - suffix = sb6.clear(); - suffix.append(_suffix, null); - } - if (prefix.length() == 0 && suffix.length() == 0) { - resultInstance.positive = ConstantAffixModifier.EMPTY; - return; - } - if (resultInstance.positive != null - && (resultInstance.positive instanceof ConstantMultiFieldModifier) - && ((ConstantMultiFieldModifier) resultInstance.positive).contentEquals(prefix, suffix)) { - // Use the cached modifier - return; - } - resultInstance.positive = new ConstantMultiFieldModifier(prefix, suffix, false); - } - - private void setNegativeResult( - NumberStringBuilder prefix, NumberStringBuilder suffix, IProperties properties) { - String _prefix = properties.getNegativePrefix(); - String _suffix = properties.getNegativeSuffix(); - if (_prefix != null) { - prefix = sb5.clear(); - prefix.append(_prefix, null); - } - if (_suffix != null) { - suffix = sb6.clear(); - suffix.append(_suffix, null); - } - if (prefix.length() == 0 && suffix.length() == 0) { - resultInstance.negative = ConstantAffixModifier.EMPTY; - return; - } - if (resultInstance.negative != null - && (resultInstance.negative instanceof ConstantMultiFieldModifier) - && ((ConstantMultiFieldModifier) resultInstance.negative).contentEquals(prefix, suffix)) { - // Use the cached modifier - return; - } - resultInstance.negative = new ConstantMultiFieldModifier(prefix, suffix, false); - } - - /** A null-safe equals method for CharSequences. */ - private static boolean charSequenceEquals(CharSequence a, CharSequence b) { - if (a == b) return true; - if (a == null || b == null) return false; - if (a.length() != b.length()) return false; - for (int i = 0; i < a.length(); i++) { - if (a.charAt(i) != b.charAt(i)) return false; - } - return true; - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Parse.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Parse.java index 1cd6134434c..e921a2eedeb 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Parse.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Parse.java @@ -13,13 +13,6 @@ import java.util.concurrent.ConcurrentHashMap; import com.ibm.icu.impl.StandardPlural; import com.ibm.icu.impl.TextTrieMap; -import com.ibm.icu.impl.number.formatters.BigDecimalMultiplier; -import com.ibm.icu.impl.number.formatters.CurrencyFormat; -import com.ibm.icu.impl.number.formatters.MagnitudeMultiplier; -import com.ibm.icu.impl.number.formatters.PaddingFormat; -import com.ibm.icu.impl.number.formatters.PositiveDecimalFormat; -import com.ibm.icu.impl.number.formatters.PositiveNegativeAffixFormat; -import com.ibm.icu.impl.number.formatters.ScientificFormat; import com.ibm.icu.lang.UCharacter; import com.ibm.icu.text.CurrencyPluralInfo; import com.ibm.icu.text.DecimalFormatSymbols; @@ -95,139 +88,6 @@ public class Parse { FAST, } - /** The set of properties required for {@link Parse}. Accepts a {@link Properties} object. */ - public static interface IProperties - extends PositiveNegativeAffixFormat.IProperties, - PaddingFormat.IProperties, - CurrencyFormat.ICurrencyProperties, - BigDecimalMultiplier.IProperties, - MagnitudeMultiplier.IProperties, - PositiveDecimalFormat.IProperties, - ScientificFormat.IProperties { - - boolean DEFAULT_PARSE_INTEGER_ONLY = false; - - /** @see #setParseIntegerOnly */ - public boolean getParseIntegerOnly(); - - /** - * Whether to ignore the fractional part of numbers. For example, parses "123.4" to "123" - * instead of "123.4". - * - * @param parseIntegerOnly true to parse integers only; false to parse integers with their - * fraction parts - * @return The property bag, for chaining. - */ - public IProperties setParseIntegerOnly(boolean parseIntegerOnly); - - boolean DEFAULT_PARSE_NO_EXPONENT = false; - - /** @see #setParseNoExponent */ - public boolean getParseNoExponent(); - - /** - * Whether to ignore the exponential part of numbers. For example, parses "123E4" to "123" - * instead of "1230000". - * - * @param parseIgnoreExponent true to ignore exponents; false to parse them. - * @return The property bag, for chaining. - */ - public IProperties setParseNoExponent(boolean parseIgnoreExponent); - - boolean DEFAULT_DECIMAL_PATTERN_MATCH_REQUIRED = false; - - /** @see #setDecimalPatternMatchRequired */ - public boolean getDecimalPatternMatchRequired(); - - /** - * Whether to require that the presence of decimal point matches the pattern. If a decimal point - * is not present, but the pattern contained a decimal point, parse will not succeed: null will - * be returned from parse(), and an error index will be set in the {@link - * ParsePosition}. - * - * @param decimalPatternMatchRequired true to set an error if decimal is not present - * @return The property bag, for chaining. - */ - public IProperties setDecimalPatternMatchRequired(boolean decimalPatternMatchRequired); - - ParseMode DEFAULT_PARSE_MODE = null; - - /** @see #setParseMode */ - public ParseMode getParseMode(); - - /** - * Controls certain rules for how strict this parser is when reading strings. See {@link - * ParseMode#LENIENT} and {@link ParseMode#STRICT}. - * - * @param parseMode Either {@link ParseMode#LENIENT} or {@link ParseMode#STRICT}. - * @return The property bag, for chaining. - */ - public IProperties setParseMode(ParseMode parseMode); - - boolean DEFAULT_PARSE_TO_BIG_DECIMAL = false; - - /** @see #setParseToBigDecimal */ - public boolean getParseToBigDecimal(); - - /** - * Whether to always return a BigDecimal from {@link Parse#parse} and all other parse methods. - * By default, a Long or a BigInteger are returned when possible. - * - * @param parseToBigDecimal true to always return a BigDecimal; false to return a Long or a - * BigInteger when possible. - * @return The property bag, for chaining. - */ - public IProperties setParseToBigDecimal(boolean parseToBigDecimal); - - boolean DEFAULT_PARSE_CASE_SENSITIVE = false; - - /** @see #setParseCaseSensitive */ - public boolean getParseCaseSensitive(); - - /** - * Whether to require cases to match when parsing strings; default is true. Case sensitivity - * applies to prefixes, suffixes, the exponent separator, the symbol "NaN", and the infinity - * symbol. Grouping separators, decimal separators, and padding are always case-sensitive. - * Currencies are always case-insensitive. - * - *

This setting is ignored in fast mode. In fast mode, strings are always compared in a - * case-sensitive way. - * - * @param parseCaseSensitive true to be case-sensitive when parsing; false to allow any case. - * @return The property bag, for chaining. - */ - public IProperties setParseCaseSensitive(boolean parseCaseSensitive); - - GroupingMode DEFAULT_PARSE_GROUPING_MODE = null; - - /** @see #setParseGroupingMode */ - public GroupingMode getParseGroupingMode(); - - /** - * Sets the strategy used during parsing when a code point needs to be interpreted as either a - * decimal separator or a grouping separator. - * - *

The comma, period, space, and apostrophe have different meanings in different locales. For - * example, in en-US and most American locales, the period is used as a decimal - * separator, but in es-PY and most European locales, it is used as a grouping - * separator. - * - *

Suppose you are in fr-FR the parser encounters the string "1.234". In - * fr-FR, the grouping is a space and the decimal is a comma. The grouping - * mode is a mechanism to let you specify whether to accept the string as 1234 - * (GroupingMode.DEFAULT) or whether to reject it since the separators don't match - * (GroupingMode.RESTRICTED). - * - *

When resolving grouping separators, it is the equivalence class of separators - * that is considered. For example, a period is seen as equal to a fixed set of other - * period-like characters. - * - * @param parseGroupingMode The {@link GroupingMode} to use; either DEFAULT or RESTRICTED. - * @return The property bag, for chaining. - */ - public IProperties setParseGroupingMode(GroupingMode parseGroupingMode); - } - /** * An enum containing the choices for strategy in parsing when choosing between grouping and * decimal separators. @@ -253,7 +113,7 @@ public class Parse { } /** - * @see Parse#parse(String, ParsePosition, ParseMode, boolean, boolean, IProperties, + * @see Parse#parse(String, ParsePosition, ParseMode, boolean, boolean, Properties, * DecimalFormatSymbols) */ private static enum StateName { @@ -540,7 +400,7 @@ public class Parse { * * @return The Number. Never null. */ - Number toNumber(IProperties properties) { + Number toNumber(Properties properties) { // Check for NaN, infinity, and -0.0 if (sawNaN) { return Double.NaN; @@ -610,7 +470,7 @@ public class Parse { * * @return The CurrencyAmount. Never null. */ - public CurrencyAmount toCurrencyAmount(IProperties properties) { + public CurrencyAmount toCurrencyAmount(Properties properties) { assert isoCode != null; Number number = toNumber(properties); Currency currency = Currency.getInstance(isoCode); @@ -679,7 +539,7 @@ public class Parse { int prevLength; // Properties and Symbols memory: - IProperties properties; + Properties properties; DecimalFormatSymbols symbols; ParseMode mode; boolean caseSensitive; @@ -785,25 +645,25 @@ public class Parse { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(""); - return sb.toString(); + StringBuilder sb = new StringBuilder(); + sb.append(""); + return sb.toString(); } } @@ -820,7 +680,7 @@ public class Parse { static final AffixHolder EMPTY_POSITIVE = new AffixHolder("", "", true, false); static final AffixHolder EMPTY_NEGATIVE = new AffixHolder("", "", true, true); - static void addToState(ParserState state, IProperties properties) { + static void addToState(ParserState state, Properties properties) { AffixHolder pp = fromPropertiesPositivePattern(properties); AffixHolder np = fromPropertiesNegativePattern(properties); AffixHolder ps = fromPropertiesPositiveString(properties); @@ -831,11 +691,11 @@ public class Parse { if (ns != null) state.affixHolders.add(ns); } - static AffixHolder fromPropertiesPositivePattern(IProperties properties) { + static AffixHolder fromPropertiesPositivePattern(Properties properties) { String ppp = properties.getPositivePrefixPattern(); String psp = properties.getPositiveSuffixPattern(); if (properties.getSignAlwaysShown()) { - // TODO: This logic is somewhat duplicated from PNAffixGenerator. + // TODO: This logic is somewhat duplicated from MurkyModifier. boolean foundSign = false; String npp = properties.getNegativePrefixPattern(); String nsp = properties.getNegativeSuffixPattern(); @@ -854,7 +714,7 @@ public class Parse { return getInstance(ppp, psp, false, false); } - static AffixHolder fromPropertiesNegativePattern(IProperties properties) { + static AffixHolder fromPropertiesNegativePattern(Properties properties) { String npp = properties.getNegativePrefixPattern(); String nsp = properties.getNegativeSuffixPattern(); if (npp == null && nsp == null) { @@ -869,14 +729,14 @@ public class Parse { return getInstance(npp, nsp, false, true); } - static AffixHolder fromPropertiesPositiveString(IProperties properties) { + static AffixHolder fromPropertiesPositiveString(Properties properties) { String pp = properties.getPositivePrefix(); String ps = properties.getPositiveSuffix(); if (pp == null && ps == null) return null; return getInstance(pp, ps, true, false); } - static AffixHolder fromPropertiesNegativeString(IProperties properties) { + static AffixHolder fromPropertiesNegativeString(Properties properties) { String np = properties.getNegativePrefix(); String ns = properties.getNegativeSuffix(); if (np == null && ns == null) return null; @@ -1052,7 +912,7 @@ public class Parse { 0xFE63, 0xFE63, 0xFF0D, 0xFF0D) .freeze(); - public static Number parse(String input, IProperties properties, DecimalFormatSymbols symbols) { + public static Number parse(String input, Properties properties, DecimalFormatSymbols symbols) { ParsePosition ppos = threadLocalParsePosition.get(); ppos.setIndex(0); return parse(input, ppos, properties, symbols); @@ -1080,19 +940,19 @@ public class Parse { public static Number parse( CharSequence input, ParsePosition ppos, - IProperties properties, + Properties properties, DecimalFormatSymbols symbols) { StateItem best = _parse(input, ppos, false, properties, symbols); return (best == null) ? null : best.toNumber(properties); } public static CurrencyAmount parseCurrency( - String input, IProperties properties, DecimalFormatSymbols symbols) throws ParseException { + String input, Properties properties, DecimalFormatSymbols symbols) throws ParseException { return parseCurrency(input, null, properties, symbols); } public static CurrencyAmount parseCurrency( - CharSequence input, ParsePosition ppos, IProperties properties, DecimalFormatSymbols symbols) + CharSequence input, ParsePosition ppos, Properties properties, DecimalFormatSymbols symbols) throws ParseException { if (ppos == null) { ppos = threadLocalParsePosition.get(); @@ -1107,7 +967,7 @@ public class Parse { CharSequence input, ParsePosition ppos, boolean parseCurrency, - IProperties properties, + Properties properties, DecimalFormatSymbols symbols) { if (input == null || ppos == null || properties == null || symbols == null) { @@ -1481,7 +1341,9 @@ public class Parse { // Optionally require that the presence of a decimal point matches the pattern. if (properties.getDecimalPatternMatchRequired() - && item.sawDecimalPoint != PositiveDecimalFormat.allowsDecimalPoint(properties)) { + && item.sawDecimalPoint + != (properties.getDecimalSeparatorAlwaysShown() + || properties.getMaximumFractionDigits() != 0)) { if (DEBUGGING) System.out.println("-> rejected due to decimal point violation"); continue; } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java index 1a1b83b1198..6b08f9b3685 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/PatternString.java @@ -4,8 +4,7 @@ package com.ibm.icu.impl.number; import java.math.BigDecimal; -import com.ibm.icu.impl.number.formatters.PaddingFormat; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.text.DecimalFormatSymbols; import newapi.impl.AffixPatternProvider; @@ -105,17 +104,17 @@ public class PatternString { // Figure out the grouping sizes. int grouping1, grouping2, grouping; - if (groupingSize != Math.min(dosMax, Properties.DEFAULT_SECONDARY_GROUPING_SIZE) - && firstGroupingSize != Math.min(dosMax, Properties.DEFAULT_GROUPING_SIZE) + if (groupingSize != Math.min(dosMax, -1) + && firstGroupingSize != Math.min(dosMax, -1) && groupingSize != firstGroupingSize) { grouping = groupingSize; grouping1 = groupingSize; grouping2 = firstGroupingSize; - } else if (groupingSize != Math.min(dosMax, Properties.DEFAULT_SECONDARY_GROUPING_SIZE)) { + } else if (groupingSize != Math.min(dosMax, -1)) { grouping = groupingSize; grouping1 = 0; grouping2 = groupingSize; - } else if (firstGroupingSize != Math.min(dosMax, Properties.DEFAULT_GROUPING_SIZE)) { + } else if (firstGroupingSize != Math.min(dosMax, -1)) { grouping = groupingSize; grouping1 = 0; grouping2 = firstGroupingSize; @@ -130,7 +129,7 @@ public class PatternString { BigDecimal roundingInterval = properties.getRoundingIncrement(); StringBuilder digitsString = new StringBuilder(); int digitsStringScale = 0; - if (maxSig != Math.min(dosMax, Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS)) { + if (maxSig != Math.min(dosMax, -1)) { // Significant Digits. while (digitsString.length() < minSig) { digitsString.append('@'); @@ -138,7 +137,7 @@ public class PatternString { while (digitsString.length() < maxSig) { digitsString.append('#'); } - } else if (roundingInterval != Properties.DEFAULT_ROUNDING_INCREMENT) { + } else if (roundingInterval != null) { // Rounding Interval. digitsStringScale = -roundingInterval.scale(); // TODO: Check for DoS here? @@ -179,7 +178,7 @@ public class PatternString { } // Exponential notation - if (exponentDigits != Math.min(dosMax, Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS)) { + if (exponentDigits != Math.min(dosMax, -1)) { sb.append('E'); if (exponentShowPlusSign) { sb.append('+'); @@ -195,7 +194,7 @@ public class PatternString { AffixPatternUtils.escape(ps, sb); // Resolve Padding - if (paddingWidth != Properties.DEFAULT_FORMAT_WIDTH) { + if (paddingWidth != -1) { while (paddingWidth - sb.length() > 0) { sb.insert(afterPrefixPos, '#'); beforeSuffixPos++; @@ -246,7 +245,7 @@ public class PatternString { /** @return The number of chars inserted. */ private static int escapePaddingString(CharSequence input, StringBuilder output, int startIndex) { - if (input == null || input.length() == 0) input = PaddingFormat.FALLBACK_PADDING_STRING; + if (input == null || input.length() == 0) input = ThingsNeedingNewHome.FALLBACK_PADDING_STRING; int startLength = output.length(); if (input.length() == 1) { if (input.equals("'")) { @@ -474,12 +473,12 @@ public class PatternString { if (grouping2 != -1) { properties.setGroupingSize(grouping1); } else { - properties.setGroupingSize(Properties.DEFAULT_GROUPING_SIZE); + properties.setGroupingSize(-1); } if (grouping3 != -1) { properties.setSecondaryGroupingSize(grouping2); } else { - properties.setSecondaryGroupingSize(Properties.DEFAULT_SECONDARY_GROUPING_SIZE); + properties.setSecondaryGroupingSize(-1); } // For backwards compatibility, require that the pattern emit at least one min digit. @@ -500,9 +499,9 @@ public class PatternString { // Rounding settings // Don't set basic rounding when there is a currency sign; defer to CurrencyUsage if (positive.minimumSignificantDigits > 0) { - properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACTION_DIGITS); - properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACTION_DIGITS); - properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMENT); + properties.setMinimumFractionDigits(-1); + properties.setMaximumFractionDigits(-1); + properties.setRoundingIncrement(null); properties.setMinimumSignificantDigits(positive.minimumSignificantDigits); properties.setMaximumSignificantDigits(positive.maximumSignificantDigits); } else if (positive.rounding != null) { @@ -512,24 +511,24 @@ public class PatternString { properties.setRoundingIncrement( positive.rounding.toBigDecimal().setScale(positive.minimumFractionDigits)); } else { - properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACTION_DIGITS); - properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACTION_DIGITS); - properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMENT); + properties.setMinimumFractionDigits(-1); + properties.setMaximumFractionDigits(-1); + properties.setRoundingIncrement(null); } - properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS); - properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS); + properties.setMinimumSignificantDigits(-1); + properties.setMaximumSignificantDigits(-1); } else { if (!ignoreRounding) { properties.setMinimumFractionDigits(minFrac); properties.setMaximumFractionDigits(positive.maximumFractionDigits); - properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMENT); + properties.setRoundingIncrement(null); } else { - properties.setMinimumFractionDigits(Properties.DEFAULT_MINIMUM_FRACTION_DIGITS); - properties.setMaximumFractionDigits(Properties.DEFAULT_MAXIMUM_FRACTION_DIGITS); - properties.setRoundingIncrement(Properties.DEFAULT_ROUNDING_INCREMENT); + properties.setMinimumFractionDigits(-1); + properties.setMaximumFractionDigits(-1); + properties.setRoundingIncrement(null); } - properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS); - properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS); + properties.setMinimumSignificantDigits(-1); + properties.setMaximumSignificantDigits(-1); } // If the pattern ends with a '.' then force the decimal point. @@ -550,13 +549,13 @@ public class PatternString { } else { // patterns with '@' cannot define max integer digits properties.setMinimumIntegerDigits(1); - properties.setMaximumIntegerDigits(Properties.DEFAULT_MAXIMUM_INTEGER_DIGITS); + properties.setMaximumIntegerDigits(-1); } } else { - properties.setExponentSignAlwaysShown(Properties.DEFAULT_EXPONENT_SIGN_ALWAYS_SHOWN); - properties.setMinimumExponentDigits(Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS); + properties.setExponentSignAlwaysShown(false); + properties.setMinimumExponentDigits(-1); properties.setMinimumIntegerDigits(minInt); - properties.setMaximumIntegerDigits(Properties.DEFAULT_MAXIMUM_INTEGER_DIGITS); + properties.setMaximumIntegerDigits(-1); } // Compute the affix patterns (required for both padding and affixes) @@ -586,9 +585,9 @@ public class PatternString { assert positive.paddingLocation != null; properties.setPadPosition(positive.paddingLocation); } else { - properties.setFormatWidth(Properties.DEFAULT_FORMAT_WIDTH); - properties.setPadString(Properties.DEFAULT_PAD_STRING); - properties.setPadPosition(Properties.DEFAULT_PAD_POSITION); + properties.setFormatWidth(-1); + properties.setPadString(null); + properties.setPadPosition(null); } // Set the affixes @@ -613,7 +612,7 @@ public class PatternString { } else if (positive.hasPerMilleSign) { properties.setMagnitudeMultiplier(3); } else { - properties.setMagnitudeMultiplier(Properties.DEFAULT_MAGNITUDE_MULTIPLIER); + properties.setMagnitudeMultiplier(0); } } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Properties.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Properties.java index bb1092cb22a..d86c66945aa 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Properties.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Properties.java @@ -11,50 +11,20 @@ import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; +import java.text.ParsePosition; import java.util.ArrayList; import java.util.Map; import com.ibm.icu.impl.number.Parse.GroupingMode; import com.ibm.icu.impl.number.Parse.ParseMode; -import com.ibm.icu.impl.number.formatters.BigDecimalMultiplier; -import com.ibm.icu.impl.number.formatters.CompactDecimalFormat; -import com.ibm.icu.impl.number.formatters.CurrencyFormat; -import com.ibm.icu.impl.number.formatters.CurrencyFormat.CurrencyStyle; -import com.ibm.icu.impl.number.formatters.MagnitudeMultiplier; -import com.ibm.icu.impl.number.formatters.MeasureFormat; -import com.ibm.icu.impl.number.formatters.PaddingFormat; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; -import com.ibm.icu.impl.number.formatters.PositiveDecimalFormat; -import com.ibm.icu.impl.number.formatters.PositiveNegativeAffixFormat; -import com.ibm.icu.impl.number.formatters.ScientificFormat; -import com.ibm.icu.impl.number.rounders.IncrementRounder; -import com.ibm.icu.impl.number.rounders.MagnitudeRounder; -import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.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.text.PluralRules; import com.ibm.icu.util.Currency; import com.ibm.icu.util.Currency.CurrencyUsage; -import com.ibm.icu.util.MeasureUnit; - -public class Properties - implements Cloneable, - Serializable, - PositiveDecimalFormat.IProperties, - PositiveNegativeAffixFormat.IProperties, - MagnitudeMultiplier.IProperties, - ScientificFormat.IProperties, - MeasureFormat.IProperties, - CompactDecimalFormat.IProperties, - PaddingFormat.IProperties, - BigDecimalMultiplier.IProperties, - CurrencyFormat.IProperties, - Parse.IProperties, - IncrementRounder.IProperties, - MagnitudeRounder.IProperties, - SignificantDigitsRounder.IProperties, - Endpoint.IProperties { + +public class Properties implements Cloneable, Serializable { private static final Properties DEFAULT = new Properties(); @@ -79,7 +49,6 @@ public class Properties private transient CompactStyle compactStyle; private transient Currency currency; private transient CurrencyPluralInfo currencyPluralInfo; - private transient CurrencyStyle currencyStyle; private transient CurrencyUsage currencyUsage; private transient boolean decimalPatternMatchRequired; private transient boolean decimalSeparatorAlwaysShown; @@ -91,8 +60,6 @@ public class Properties private transient int maximumFractionDigits; private transient int maximumIntegerDigits; private transient int maximumSignificantDigits; - private transient FormatWidth measureFormatWidth; - private transient MeasureUnit measureUnit; private transient int minimumExponentDigits; private transient int minimumFractionDigits; private transient int minimumGroupingDigits; @@ -134,52 +101,61 @@ public class Properties clear(); } + /** + * Sets all properties to their defaults (unset). + * + *

All integers default to -1 EXCEPT FOR MAGNITUDE MULTIPLIER which has a default of 0 (since + * negative numbers are important). + * + *

All booleans default to false. + * + *

All non-primitive types default to null. + * + * @return The property bag, for chaining. + */ private Properties _clear() { - compactCustomData = DEFAULT_COMPACT_CUSTOM_DATA; - compactStyle = DEFAULT_COMPACT_STYLE; - currency = DEFAULT_CURRENCY; - currencyPluralInfo = DEFAULT_CURRENCY_PLURAL_INFO; - currencyStyle = DEFAULT_CURRENCY_STYLE; - currencyUsage = DEFAULT_CURRENCY_USAGE; - decimalPatternMatchRequired = DEFAULT_DECIMAL_PATTERN_MATCH_REQUIRED; - decimalSeparatorAlwaysShown = DEFAULT_DECIMAL_SEPARATOR_ALWAYS_SHOWN; - exponentSignAlwaysShown = DEFAULT_EXPONENT_SIGN_ALWAYS_SHOWN; - formatWidth = DEFAULT_FORMAT_WIDTH; - groupingSize = DEFAULT_GROUPING_SIZE; - magnitudeMultiplier = DEFAULT_MAGNITUDE_MULTIPLIER; - mathContext = DEFAULT_MATH_CONTEXT; - maximumFractionDigits = DEFAULT_MAXIMUM_FRACTION_DIGITS; - maximumIntegerDigits = DEFAULT_MAXIMUM_INTEGER_DIGITS; - maximumSignificantDigits = DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS; - measureFormatWidth = DEFAULT_MEASURE_FORMAT_WIDTH; - measureUnit = DEFAULT_MEASURE_UNIT; - minimumExponentDigits = DEFAULT_MINIMUM_EXPONENT_DIGITS; - minimumFractionDigits = DEFAULT_MINIMUM_FRACTION_DIGITS; - minimumGroupingDigits = DEFAULT_MINIMUM_GROUPING_DIGITS; - minimumIntegerDigits = DEFAULT_MINIMUM_INTEGER_DIGITS; - minimumSignificantDigits = DEFAULT_MINIMUM_SIGNIFICANT_DIGITS; - multiplier = DEFAULT_MULTIPLIER; - negativePrefix = DEFAULT_NEGATIVE_PREFIX; - negativePrefixPattern = DEFAULT_NEGATIVE_PREFIX_PATTERN; - negativeSuffix = DEFAULT_NEGATIVE_SUFFIX; - negativeSuffixPattern = DEFAULT_NEGATIVE_SUFFIX_PATTERN; - padPosition = DEFAULT_PAD_POSITION; - padString = DEFAULT_PAD_STRING; - parseCaseSensitive = DEFAULT_PARSE_CASE_SENSITIVE; - parseGroupingMode = DEFAULT_PARSE_GROUPING_MODE; - parseIntegerOnly = DEFAULT_PARSE_INTEGER_ONLY; - parseMode = DEFAULT_PARSE_MODE; - parseNoExponent = DEFAULT_PARSE_NO_EXPONENT; - parseToBigDecimal = DEFAULT_PARSE_TO_BIG_DECIMAL; - pluralRules = DEFAULT_PLURAL_RULES; - positivePrefix = DEFAULT_POSITIVE_PREFIX; - positivePrefixPattern = DEFAULT_POSITIVE_PREFIX_PATTERN; - positiveSuffix = DEFAULT_POSITIVE_SUFFIX; - positiveSuffixPattern = DEFAULT_POSITIVE_SUFFIX_PATTERN; - roundingIncrement = DEFAULT_ROUNDING_INCREMENT; - roundingMode = DEFAULT_ROUNDING_MODE; - secondaryGroupingSize = DEFAULT_SECONDARY_GROUPING_SIZE; - signAlwaysShown = DEFAULT_SIGN_ALWAYS_SHOWN; + compactCustomData = null; + compactStyle = null; + currency = null; + currencyPluralInfo = null; + currencyUsage = null; + decimalPatternMatchRequired = false; + decimalSeparatorAlwaysShown = false; + exponentSignAlwaysShown = false; + formatWidth = -1; + groupingSize = -1; + magnitudeMultiplier = 0; + mathContext = null; + maximumFractionDigits = -1; + maximumIntegerDigits = -1; + maximumSignificantDigits = -1; + minimumExponentDigits = -1; + minimumFractionDigits = -1; + minimumGroupingDigits = -1; + minimumIntegerDigits = -1; + minimumSignificantDigits = -1; + multiplier = null; + negativePrefix = null; + negativePrefixPattern = null; + negativeSuffix = null; + negativeSuffixPattern = null; + padPosition = null; + padString = null; + parseCaseSensitive = false; + parseGroupingMode = null; + parseIntegerOnly = false; + parseMode = null; + parseNoExponent = false; + parseToBigDecimal = false; + pluralRules = null; + positivePrefix = null; + positivePrefixPattern = null; + positiveSuffix = null; + positiveSuffixPattern = null; + roundingIncrement = null; + roundingMode = null; + secondaryGroupingSize = -1; + signAlwaysShown = false; return this; } @@ -188,7 +164,6 @@ public class Properties compactStyle = other.compactStyle; currency = other.currency; currencyPluralInfo = other.currencyPluralInfo; - currencyStyle = other.currencyStyle; currencyUsage = other.currencyUsage; decimalPatternMatchRequired = other.decimalPatternMatchRequired; decimalSeparatorAlwaysShown = other.decimalSeparatorAlwaysShown; @@ -200,8 +175,6 @@ public class Properties maximumFractionDigits = other.maximumFractionDigits; maximumIntegerDigits = other.maximumIntegerDigits; maximumSignificantDigits = other.maximumSignificantDigits; - measureFormatWidth = other.measureFormatWidth; - measureUnit = other.measureUnit; minimumExponentDigits = other.minimumExponentDigits; minimumFractionDigits = other.minimumFractionDigits; minimumGroupingDigits = other.minimumGroupingDigits; @@ -238,7 +211,6 @@ public class Properties eq = eq && _equalsHelper(compactStyle, other.compactStyle); eq = eq && _equalsHelper(currency, other.currency); eq = eq && _equalsHelper(currencyPluralInfo, other.currencyPluralInfo); - eq = eq && _equalsHelper(currencyStyle, other.currencyStyle); eq = eq && _equalsHelper(currencyUsage, other.currencyUsage); eq = eq && _equalsHelper(decimalPatternMatchRequired, other.decimalPatternMatchRequired); eq = eq && _equalsHelper(decimalSeparatorAlwaysShown, other.decimalSeparatorAlwaysShown); @@ -250,8 +222,6 @@ public class Properties eq = eq && _equalsHelper(maximumFractionDigits, other.maximumFractionDigits); eq = eq && _equalsHelper(maximumIntegerDigits, other.maximumIntegerDigits); eq = eq && _equalsHelper(maximumSignificantDigits, other.maximumSignificantDigits); - eq = eq && _equalsHelper(measureFormatWidth, other.measureFormatWidth); - eq = eq && _equalsHelper(measureUnit, other.measureUnit); eq = eq && _equalsHelper(minimumExponentDigits, other.minimumExponentDigits); eq = eq && _equalsHelper(minimumFractionDigits, other.minimumFractionDigits); eq = eq && _equalsHelper(minimumGroupingDigits, other.minimumGroupingDigits); @@ -302,7 +272,6 @@ public class Properties hashCode ^= _hashCodeHelper(compactStyle); hashCode ^= _hashCodeHelper(currency); hashCode ^= _hashCodeHelper(currencyPluralInfo); - hashCode ^= _hashCodeHelper(currencyStyle); hashCode ^= _hashCodeHelper(currencyUsage); hashCode ^= _hashCodeHelper(decimalPatternMatchRequired); hashCode ^= _hashCodeHelper(decimalSeparatorAlwaysShown); @@ -314,8 +283,6 @@ public class Properties hashCode ^= _hashCodeHelper(maximumFractionDigits); hashCode ^= _hashCodeHelper(maximumIntegerDigits); hashCode ^= _hashCodeHelper(maximumSignificantDigits); - hashCode ^= _hashCodeHelper(measureFormatWidth); - hashCode ^= _hashCodeHelper(measureUnit); hashCode ^= _hashCodeHelper(minimumExponentDigits); hashCode ^= _hashCodeHelper(minimumFractionDigits); hashCode ^= _hashCodeHelper(minimumGroupingDigits); @@ -395,228 +362,170 @@ public class Properties /// BEGIN GETTERS/SETTERS /// - @Override public Map> getCompactCustomData() { return compactCustomData; } - @Override public CompactStyle getCompactStyle() { return compactStyle; } - @Override public Currency getCurrency() { return currency; } - @Override - @Deprecated public CurrencyPluralInfo getCurrencyPluralInfo() { return currencyPluralInfo; } - @Override - public CurrencyStyle getCurrencyStyle() { - return currencyStyle; - } - - @Override public CurrencyUsage getCurrencyUsage() { return currencyUsage; } - @Override public boolean getDecimalPatternMatchRequired() { return decimalPatternMatchRequired; } - @Override public boolean getDecimalSeparatorAlwaysShown() { return decimalSeparatorAlwaysShown; } - @Override public boolean getExponentSignAlwaysShown() { return exponentSignAlwaysShown; } - @Override public int getFormatWidth() { return formatWidth; } - @Override public int getGroupingSize() { return groupingSize; } - @Override public int getMagnitudeMultiplier() { return magnitudeMultiplier; } - @Override public MathContext getMathContext() { return mathContext; } - @Override public int getMaximumFractionDigits() { return maximumFractionDigits; } - @Override public int getMaximumIntegerDigits() { return maximumIntegerDigits; } - @Override public int getMaximumSignificantDigits() { return maximumSignificantDigits; } - @Override - public FormatWidth getMeasureFormatWidth() { - return measureFormatWidth; - } - - @Override - public MeasureUnit getMeasureUnit() { - return measureUnit; - } - - @Override public int getMinimumExponentDigits() { return minimumExponentDigits; } - @Override public int getMinimumFractionDigits() { return minimumFractionDigits; } - @Override public int getMinimumGroupingDigits() { return minimumGroupingDigits; } - @Override public int getMinimumIntegerDigits() { return minimumIntegerDigits; } - @Override public int getMinimumSignificantDigits() { return minimumSignificantDigits; } - @Override public BigDecimal getMultiplier() { return multiplier; } - @Override public String getNegativePrefix() { return negativePrefix; } - @Override public String getNegativePrefixPattern() { return negativePrefixPattern; } - @Override public String getNegativeSuffix() { return negativeSuffix; } - @Override public String getNegativeSuffixPattern() { return negativeSuffixPattern; } - @Override public PadPosition getPadPosition() { return padPosition; } - @Override public String getPadString() { return padString; } - @Override public boolean getParseCaseSensitive() { return parseCaseSensitive; } - @Override public GroupingMode getParseGroupingMode() { return parseGroupingMode; } - @Override public boolean getParseIntegerOnly() { return parseIntegerOnly; } - @Override public ParseMode getParseMode() { return parseMode; } - @Override public boolean getParseNoExponent() { return parseNoExponent; } - @Override public boolean getParseToBigDecimal() { return parseToBigDecimal; } - @Override public PluralRules getPluralRules() { return pluralRules; } - @Override public String getPositivePrefix() { return positivePrefix; } - @Override public String getPositivePrefixPattern() { return positivePrefixPattern; } - @Override public String getPositiveSuffix() { return positiveSuffix; } - @Override public String getPositiveSuffixPattern() { return positiveSuffixPattern; } - @Override public BigDecimal getRoundingIncrement() { return roundingIncrement; } - @Override public RoundingMode getRoundingMode() { return roundingMode; } - @Override public int getSecondaryGroupingSize() { return secondaryGroupingSize; } - @Override public boolean getSignAlwaysShown() { return signAlwaysShown; } @@ -672,27 +581,65 @@ public class Properties } } - @Override + /** + * Specifies custom data to be used instead of CLDR data when constructing a CompactDecimalFormat. + * The argument should be a map with the following structure: + * + *

+   * {
+   *   "1000": {
+   *     "one": "0 thousand",
+   *     "other": "0 thousand"
+   *   },
+   *   "10000": {
+   *     "one": "00 thousand",
+   *     "other": "00 thousand"
+   *   },
+   *   // ...
+   * }
+   * 
+ * + * This API endpoint is used by the CLDR Survey Tool. + * + * @param compactCustomData A map with the above structure. + * @return The property bag, for chaining. + */ public Properties setCompactCustomData(Map> compactCustomData) { // TODO: compactCustomData is not immutable. this.compactCustomData = compactCustomData; return this; } - @Override + /** + * Use compact decimal formatting with the specified {@link CompactStyle}. CompactStyle.SHORT + * produces output like "10K" in locale en-US, whereas CompactStyle.LONG produces output + * like "10 thousand" in that locale. + * + * @param compactStyle The style of prefixes/suffixes to append. + * @return The property bag, for chaining. + */ public Properties setCompactStyle(CompactStyle compactStyle) { this.compactStyle = compactStyle; return this; } - @Override + /** + * Use the specified currency to substitute currency placeholders ('¤') in the pattern string. + * + * @param currency The currency. + * @return The property bag, for chaining. + */ public Properties setCurrency(Currency currency) { this.currency = currency; return this; } - @Override - @Deprecated + /** + * Use the specified {@link CurrencyPluralInfo} instance when formatting currency long names. + * + * @param currencyPluralInfo The currency plural info object. + * @return The property bag, for chaining. + */ public Properties setCurrencyPluralInfo(CurrencyPluralInfo currencyPluralInfo) { // TODO: In order to maintain immutability, we have to perform a clone here. // It would be better to just retire CurrencyPluralInfo entirely. @@ -703,247 +650,610 @@ public class Properties return this; } - @Override - public Properties setCurrencyStyle(CurrencyStyle currencyStyle) { - this.currencyStyle = currencyStyle; - return this; - } - - @Override + /** + * Use the specified {@link CurrencyUsage} instance, which provides default rounding rules for the + * currency in two styles, CurrencyUsage.CASH and CurrencyUsage.STANDARD. + * + *

The CurrencyUsage specified here will not be used unless there is a currency placeholder in + * the pattern. + * + * @param currencyUsage The currency usage. Defaults to CurrencyUsage.STANDARD. + * @return The property bag, for chaining. + */ public Properties setCurrencyUsage(CurrencyUsage currencyUsage) { this.currencyUsage = currencyUsage; return this; } - @Override + /** + * PARSING: Whether to require that the presence of decimal point matches the pattern. If a + * decimal point is not present, but the pattern contained a decimal point, parse will not + * succeed: null will be returned from parse(), and an error index will be set in the + * {@link ParsePosition}. + * + * @param decimalPatternMatchRequired true to set an error if decimal is not present + * @return The property bag, for chaining. + */ public Properties setDecimalPatternMatchRequired(boolean decimalPatternMatchRequired) { this.decimalPatternMatchRequired = decimalPatternMatchRequired; return this; } - @Override + /** + * Sets whether to always show the decimal point, even if the number doesn't require one. For + * example, if always show decimal is true, the number 123 would be formatted as "123." in locale + * en-US. + * + * @param decimalSeparatorAlwaysShown Whether to show the decimal point when it is optional. + * @return The property bag, for chaining. + */ public Properties setDecimalSeparatorAlwaysShown(boolean alwaysShowDecimal) { this.decimalSeparatorAlwaysShown = alwaysShowDecimal; return this; } - @Override + /** + * Sets whether to show the plus sign in the exponent part of numbers with a zero or positive + * exponent. For example, the number "1200" with the pattern "0.0E0" would be formatted as + * "1.2E+3" instead of "1.2E3" in en-US. + * + * @param exponentSignAlwaysShown Whether to show the plus sign in positive exponents. + * @return The property bag, for chaining. + */ public Properties setExponentSignAlwaysShown(boolean exponentSignAlwaysShown) { this.exponentSignAlwaysShown = exponentSignAlwaysShown; return this; } - @Override + /** + * Sets the minimum width of the string output by the formatting pipeline. For example, if padding + * is enabled and paddingWidth is set to 6, formatting the number "3.14159" with the pattern + * "0.00" will result in "··3.14" if '·' is your padding string. + * + *

If the number is longer than your padding width, the number will display as if no padding + * width had been specified, which may result in strings longer than the padding width. + * + *

Width is counted in UTF-16 code units. + * + * @param formatWidth The output width. + * @return The property bag, for chaining. + * @see #setPadPosition + * @see #setPadString + */ public Properties setFormatWidth(int paddingWidth) { this.formatWidth = paddingWidth; return this; } - @Override + /** + * Sets the number of digits between grouping separators. For example, the en-US locale + * uses a grouping size of 3, so the number 1234567 would be formatted as "1,234,567". For locales + * whose grouping sizes vary with magnitude, see {@link #setSecondaryGroupingSize(int)}. + * + * @param groupingSize The primary grouping size. + * @return The property bag, for chaining. + */ public Properties setGroupingSize(int groupingSize) { this.groupingSize = groupingSize; return this; } - @Override + /** + * Multiply all numbers by this power of ten before formatting. Negative multipliers reduce the + * magnitude and make numbers smaller (closer to zero). + * + * @param magnitudeMultiplier The number of powers of ten to scale. + * @return The property bag, for chaining. + * @see #setMultiplier + */ public Properties setMagnitudeMultiplier(int magnitudeMultiplier) { this.magnitudeMultiplier = magnitudeMultiplier; return this; } - @Override + /** + * Sets the {@link MathContext} to be used during math and rounding operations. A MathContext + * encapsulates a RoundingMode and the number of significant digits in the output. + * + * @param mathContext The math context to use when rounding is required. + * @return The property bag, for chaining. + * @see MathContext + * @see #setRoundingMode + */ public Properties setMathContext(MathContext mathContext) { this.mathContext = mathContext; return this; } - @Override + /** + * Sets the maximum number of digits to display after the decimal point. If the number has fewer + * than this number of digits, the number will be rounded off using the rounding mode specified by + * {@link #setRoundingMode(RoundingMode)}. The pattern "#00.0#", for example, corresponds to 2 + * maximum fraction digits, and the number 456.789 would be formatted as "456.79" in locale + * en-US with the default rounding mode. Note that the number 456.999 would be formatted + * as "457.0" given the same configurations. + * + * @param maximumFractionDigits The maximum number of fraction digits to output. + * @return The property bag, for chaining. + */ public Properties setMaximumFractionDigits(int maximumFractionDigits) { this.maximumFractionDigits = maximumFractionDigits; return this; } - @Override + /** + * Sets the maximum number of digits to display before the decimal point. If the number has more + * than this number of digits, the extra digits will be truncated. For example, if maximum integer + * digits is 2, and you attempt to format the number 1970, you will get "70" in locale + * en-US. It is not possible to specify the maximum integer digits using a pattern + * string, except in the special case of a scientific format pattern. + * + * @param maximumIntegerDigits The maximum number of integer digits to output. + * @return The property bag, for chaining. + */ public Properties setMaximumIntegerDigits(int maximumIntegerDigits) { this.maximumIntegerDigits = maximumIntegerDigits; return this; } - @Override + /** + * Sets the maximum number of significant digits to display. The number of significant digits is + * equal to the number of digits counted from the leftmost nonzero digit through the rightmost + * nonzero digit; for example, the number "2010" has 3 significant digits. If the number has more + * significant digits than specified here, the extra significant digits will be rounded off using + * the rounding mode specified by {@link #setRoundingMode(RoundingMode)}. For example, if maximum + * significant digits is 3, the number 1234.56 will be formatted as "1230" in locale + * en-US with the default rounding mode. + * + *

If both maximum significant digits and maximum integer/fraction digits are set at the same + * time, the behavior is undefined. + * + *

The number of significant digits can be specified in a pattern string using the '@' + * character. For example, the pattern "@@#" corresponds to a minimum of 2 and a maximum of 3 + * significant digits. + * + * @param maximumSignificantDigits The maximum number of significant digits to display. + * @return The property bag, for chaining. + */ public Properties setMaximumSignificantDigits(int maximumSignificantDigits) { this.maximumSignificantDigits = maximumSignificantDigits; return this; } - @Override - public Properties setMeasureFormatWidth(FormatWidth measureFormatWidth) { - this.measureFormatWidth = measureFormatWidth; - return this; - } - - @Override - public Properties setMeasureUnit(MeasureUnit measureUnit) { - this.measureUnit = measureUnit; - return this; - } - - @Override + /** + * Sets the minimum number of digits to display in the exponent. For example, the number "1200" + * with the pattern "0.0E00", which has 2 exponent digits, would be formatted as "1.2E03" in + * en-US. + * + * @param minimumExponentDigits The minimum number of digits to display in the exponent field. + * @return The property bag, for chaining. + */ public Properties setMinimumExponentDigits(int exponentDigits) { this.minimumExponentDigits = exponentDigits; return this; } - @Override + /** + * Sets the minimum number of digits to display after the decimal point. If the number has fewer + * than this number of digits, the number will be padded with zeros. The pattern "#00.0#", for + * example, corresponds to 1 minimum fraction digit, and the number 456 would be formatted as + * "456.0" in locale en-US. + * + * @param minimumFractionDigits The minimum number of fraction digits to output. + * @return The property bag, for chaining. + */ public Properties setMinimumFractionDigits(int minimumFractionDigits) { this.minimumFractionDigits = minimumFractionDigits; return this; } - @Override + /** + * Sets the minimum number of digits required to be beyond the first grouping separator in order + * to enable grouping. For example, if the minimum grouping digits is 2, then 1234 would be + * formatted as "1234" but 12345 would be formatted as "12,345" in en-US. Note that + * 1234567 would still be formatted as "1,234,567", not "1234,567". + * + * @param minimumGroupingDigits How many digits must appear before a grouping separator before + * enabling grouping. + * @return The property bag, for chaining. + */ public Properties setMinimumGroupingDigits(int minimumGroupingDigits) { this.minimumGroupingDigits = minimumGroupingDigits; return this; } - @Override + /** + * Sets the minimum number of digits to display before the decimal point. If the number has fewer + * than this number of digits, the number will be padded with zeros. The pattern "#00.0#", for + * example, corresponds to 2 minimum integer digits, and the number 5.3 would be formatted as + * "05.3" in locale en-US. + * + * @param minimumIntegerDigits The minimum number of integer digits to output. + * @return The property bag, for chaining. + */ public Properties setMinimumIntegerDigits(int minimumIntegerDigits) { this.minimumIntegerDigits = minimumIntegerDigits; return this; } - @Override + /** + * Sets the minimum number of significant digits to display. If, after rounding to the number of + * significant digits specified by {@link #setMaximumSignificantDigits}, the number of remaining + * significant digits is less than the minimum, the number will be padded with zeros. For example, + * if minimum significant digits is 3, the number 5.8 will be formatted as "5.80" in locale + * en-US. Note that minimum significant digits is relevant only when numbers have digits + * after the decimal point. + * + *

If both minimum significant digits and minimum integer/fraction digits are set at the same + * time, both values will be respected, and the one that results in the greater number of padding + * zeros will be used. For example, formatting the number 73 with 3 minimum significant digits and + * 2 minimum fraction digits will produce "73.00". + * + *

The number of significant digits can be specified in a pattern string using the '@' + * character. For example, the pattern "@@#" corresponds to a minimum of 2 and a maximum of 3 + * significant digits. + * + * @param minimumSignificantDigits The minimum number of significant digits to display. + * @return The property bag, for chaining. + */ public Properties setMinimumSignificantDigits(int minimumSignificantDigits) { this.minimumSignificantDigits = minimumSignificantDigits; return this; } - @Override + /** + * Multiply all numbers by this amount before formatting. + * + * @param multiplier The amount to multiply by. + * @return The property bag, for chaining. + * @see #setMagnitudeMultiplier + */ public Properties setMultiplier(BigDecimal multiplier) { this.multiplier = multiplier; return this; } - @Override + /** + * Sets the prefix to prepend to negative numbers. The prefix will be interpreted literally. For + * example, if you set a negative prefix of n, then the number -123 will be formatted + * as "n123" in the locale en-US. Note that if the negative prefix is left unset, the + * locale's minus sign is used. + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param negativePrefix The CharSequence to prepend to negative numbers. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setNegativePrefixPattern + */ public Properties setNegativePrefix(String negativePrefix) { this.negativePrefix = negativePrefix; return this; } - @Override + /** + * Sets the prefix to prepend to negative numbers. Locale-specific symbols will be substituted + * into the string according to Unicode Technical Standard #35 (LDML). + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param negativePrefixPattern The CharSequence to prepend to negative numbers after locale + * symbol substitutions take place. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setNegativePrefix + */ public Properties setNegativePrefixPattern(String negativePrefixPattern) { this.negativePrefixPattern = negativePrefixPattern; return this; } - @Override + /** + * Sets the suffix to append to negative numbers. The suffix will be interpreted literally. For + * example, if you set a suffix prefix of n, then the number -123 will be formatted + * as "-123n" in the locale en-US. Note that the minus sign is prepended by default + * unless otherwise specified in either the pattern string or in one of the {@link + * #setNegativePrefix} methods. + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param negativeSuffix The CharSequence to append to negative numbers. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setNegativeSuffixPattern + */ public Properties setNegativeSuffix(String negativeSuffix) { this.negativeSuffix = negativeSuffix; return this; } - @Override + /** + * Sets the suffix to append to negative numbers. Locale-specific symbols will be substituted into + * the string according to Unicode Technical Standard #35 (LDML). + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param negativeSuffixPattern The CharSequence to append to negative numbers after locale symbol + * substitutions take place. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setNegativeSuffix + */ public Properties setNegativeSuffixPattern(String negativeSuffixPattern) { this.negativeSuffixPattern = negativeSuffixPattern; return this; } - @Override + /** + * Sets the location where the padding string is to be inserted to maintain the padding width: one + * of BEFORE_PREFIX, AFTER_PREFIX, BEFORE_SUFFIX, or AFTER_SUFFIX. + * + *

Must be used in conjunction with {@link #setFormatWidth}. + * + * @param padPosition The output width. + * @return The property bag, for chaining. + * @see #setFormatWidth + */ public Properties setPadPosition(PadPosition paddingLocation) { this.padPosition = paddingLocation; return this; } - @Override + /** + * Sets the string used for padding. The string should contain a single character or grapheme + * cluster. + * + *

Must be used in conjunction with {@link #setFormatWidth}. + * + * @param paddingString The padding string. Defaults to an ASCII space (U+0020). + * @return The property bag, for chaining. + * @see #setFormatWidth + */ public Properties setPadString(String paddingString) { this.padString = paddingString; return this; } - @Override + /** + * Whether to require cases to match when parsing strings; default is true. Case sensitivity + * applies to prefixes, suffixes, the exponent separator, the symbol "NaN", and the infinity + * symbol. Grouping separators, decimal separators, and padding are always case-sensitive. + * Currencies are always case-insensitive. + * + *

This setting is ignored in fast mode. In fast mode, strings are always compared in a + * case-sensitive way. + * + * @param parseCaseSensitive true to be case-sensitive when parsing; false to allow any case. + * @return The property bag, for chaining. + */ public Properties setParseCaseSensitive(boolean parseCaseSensitive) { this.parseCaseSensitive = parseCaseSensitive; return this; } - @Override + /** + * Sets the strategy used during parsing when a code point needs to be interpreted as either a + * decimal separator or a grouping separator. + * + *

The comma, period, space, and apostrophe have different meanings in different locales. For + * example, in en-US and most American locales, the period is used as a decimal + * separator, but in es-PY and most European locales, it is used as a grouping separator. + * + *

Suppose you are in fr-FR the parser encounters the string "1.234". In + * fr-FR, the grouping is a space and the decimal is a comma. The grouping mode + * is a mechanism to let you specify whether to accept the string as 1234 (GroupingMode.DEFAULT) + * or whether to reject it since the separators don't match (GroupingMode.RESTRICTED). + * + *

When resolving grouping separators, it is the equivalence class of separators that + * is considered. For example, a period is seen as equal to a fixed set of other period-like + * characters. + * + * @param parseGroupingMode The {@link GroupingMode} to use; either DEFAULT or RESTRICTED. + * @return The property bag, for chaining. + */ public Properties setParseGroupingMode(GroupingMode parseGroupingMode) { this.parseGroupingMode = parseGroupingMode; return this; } - @Override + /** + * Whether to ignore the fractional part of numbers. For example, parses "123.4" to "123" instead + * of "123.4". + * + * @param parseIntegerOnly true to parse integers only; false to parse integers with their + * fraction parts + * @return The property bag, for chaining. + */ public Properties setParseIntegerOnly(boolean parseIntegerOnly) { this.parseIntegerOnly = parseIntegerOnly; return this; } - @Override + /** + * Controls certain rules for how strict this parser is when reading strings. See {@link + * ParseMode#LENIENT} and {@link ParseMode#STRICT}. + * + * @param parseMode Either {@link ParseMode#LENIENT} or {@link ParseMode#STRICT}. + * @return The property bag, for chaining. + */ public Properties setParseMode(ParseMode parseMode) { this.parseMode = parseMode; return this; } - @Override + /** + * Whether to ignore the exponential part of numbers. For example, parses "123E4" to "123" instead + * of "1230000". + * + * @param parseIgnoreExponent true to ignore exponents; false to parse them. + * @return The property bag, for chaining. + */ public Properties setParseNoExponent(boolean parseNoExponent) { this.parseNoExponent = parseNoExponent; return this; } - @Override + /** + * Whether to always return a BigDecimal from {@link Parse#parse} and all other parse methods. By + * default, a Long or a BigInteger are returned when possible. + * + * @param parseToBigDecimal true to always return a BigDecimal; false to return a Long or a + * BigInteger when possible. + * @return The property bag, for chaining. + */ public Properties setParseToBigDecimal(boolean parseToBigDecimal) { this.parseToBigDecimal = parseToBigDecimal; return this; } - @Override + /** + * Sets the PluralRules object to use instead of the default for the locale. + * + * @param pluralRules The object to reference. + * @return The property bag, for chaining. + */ public Properties setPluralRules(PluralRules pluralRules) { this.pluralRules = pluralRules; return this; } - @Override + /** + * Sets the prefix to prepend to positive numbers. The prefix will be interpreted literally. For + * example, if you set a positive prefix of p, then the number 123 will be formatted + * as "p123" in the locale en-US. + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param positivePrefix The CharSequence to prepend to positive numbers. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setPositivePrefixPattern + */ public Properties setPositivePrefix(String positivePrefix) { this.positivePrefix = positivePrefix; return this; } - @Override + /** + * Sets the prefix to prepend to positive numbers. Locale-specific symbols will be substituted + * into the string according to Unicode Technical Standard #35 (LDML). + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param positivePrefixPattern The CharSequence to prepend to positive numbers after locale + * symbol substitutions take place. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setPositivePrefix + */ public Properties setPositivePrefixPattern(String positivePrefixPattern) { this.positivePrefixPattern = positivePrefixPattern; return this; } - @Override + /** + * Sets the suffix to append to positive numbers. The suffix will be interpreted literally. For + * example, if you set a positive suffix of p, then the number 123 will be formatted + * as "123p" in the locale en-US. + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param positiveSuffix The CharSequence to append to positive numbers. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setPositiveSuffixPattern + */ public Properties setPositiveSuffix(String positiveSuffix) { this.positiveSuffix = positiveSuffix; return this; } - @Override + /** + * Sets the suffix to append to positive numbers. Locale-specific symbols will be substituted into + * the string according to Unicode Technical Standard #35 (LDML). + * + *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. + * + * @param positiveSuffixPattern The CharSequence to append to positive numbers after locale symbol + * substitutions take place. + * @return The property bag, for chaining. + * @see PositiveNegativeAffixFormat + * @see #setPositiveSuffix + */ public Properties setPositiveSuffixPattern(String positiveSuffixPattern) { this.positiveSuffixPattern = positiveSuffixPattern; return this; } - @Override + /** + * Sets the increment to which to round numbers. For example, with a rounding interval of 0.05, + * the number 11.17 would be formatted as "11.15" in locale en-US with the default + * rounding mode. + * + *

You can use either a rounding increment or significant digits, but not both at the same + * time. + * + *

The rounding increment can be specified in a pattern string. For example, the pattern + * "#,##0.05" corresponds to a rounding interval of 0.05 with 1 minimum integer digit and a + * grouping size of 3. + * + * @param roundingIncrement The interval to which to round. + * @return The property bag, for chaining. + */ public Properties setRoundingIncrement(BigDecimal roundingIncrement) { this.roundingIncrement = roundingIncrement; return this; } - @Override + /** + * Sets the rounding mode, which determines under which conditions extra decimal places are + * rounded either up or down. See {@link RoundingMode} for details on the choices of rounding + * mode. The default if not set explicitly is {@link RoundingMode#HALF_EVEN}. + * + *

This setting is ignored if {@link #setMathContext} is used. + * + * @param roundingMode The rounding mode to use when rounding is required. + * @return The property bag, for chaining. + * @see RoundingMode + * @see #setMathContext + */ public Properties setRoundingMode(RoundingMode roundingMode) { this.roundingMode = roundingMode; return this; } - @Override + /** + * Sets the number of digits between grouping separators higher than the least-significant + * grouping separator. For example, the locale hi uses a primary grouping size of 3 and a + * secondary grouping size of 2, so the number 1234567 would be formatted as "12,34,567". + * + *

The two levels of grouping separators can be specified in the pattern string. For example, + * the hi locale's default decimal format pattern is "#,##,##0.###". + * + * @param secondaryGroupingSize The secondary grouping size. + * @return The property bag, for chaining. + */ public Properties setSecondaryGroupingSize(int secondaryGroupingSize) { this.secondaryGroupingSize = secondaryGroupingSize; return this; } - @Override + /** + * Sets whether to always display of a plus sign on positive numbers. + * + *

If the location of the negative sign is specified by the decimal format pattern (or by the + * negative prefix/suffix pattern methods), a plus sign is substituted into that location, in + * accordance with Unicode Technical Standard #35 (LDML) section 3.2.1. Otherwise, the plus sign + * is prepended to the number. For example, if the decimal format pattern #;#- is + * used, then formatting 123 would result in "123+" in the locale en-US. + * + *

This method should be used instead of setting the positive prefix/suffix. The + * behavior is undefined if alwaysShowPlusSign is set but the positive prefix/suffix already + * contains a plus sign. + * + * @param plusSignAlwaysShown Whether positive numbers should display a plus sign. + * @return The property bag, for chaining. + */ public Properties setSignAlwaysShown(boolean signAlwaysShown) { this.signAlwaysShown = signAlwaysShown; return this; diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Rounder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Rounder.java deleted file mode 100644 index f13b46292bd..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/Rounder.java +++ /dev/null @@ -1,252 +0,0 @@ -// © 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 java.math.MathContext; -import java.math.RoundingMode; - -import com.ibm.icu.impl.number.formatters.CompactDecimalFormat; -import com.ibm.icu.impl.number.formatters.ScientificFormat; - -/** - * The base class for a Rounder used by ICU Decimal Format. - * - *

A Rounder must implement the method {@link #apply}. An implementation must: - * - *

    - *
  1. Either have the code applyDefaults(input); in its apply function, or otherwise - * ensure that minFrac, maxFrac, minInt, and maxInt are obeyed, paying special attention to - * the case when the input is zero. - *
  2. Call one of {@link FormatQuantity#roundToIncrement}, {@link - * FormatQuantity#roundToMagnitude}, or {@link FormatQuantity#roundToInfinity} on the input. - *
- * - *

In order to be used by {@link CompactDecimalFormat} and {@link ScientificFormat}, among - * others, your rounder must be stable upon decreasing the magnitude of the input number. - * For example, if your rounder converts "999" to "1000", it must also convert "99.9" to "100" and - * "0.999" to "1". (The opposite does not need to be the case: you can round "0.999" to "1" but keep - * "999" as "999".) - * - * @see com.ibm.icu.impl.number.rounders.MagnitudeRounder - * @see com.ibm.icu.impl.number.rounders.IncrementRounder - * @see com.ibm.icu.impl.number.rounders.SignificantDigitsRounder - * @see com.ibm.icu.impl.number.rounders.NoRounder - */ -public abstract class Rounder extends Format.BeforeFormat { - - public static interface IBasicRoundingProperties { - - static int DEFAULT_MINIMUM_INTEGER_DIGITS = -1; - - /** @see #setMinimumIntegerDigits */ - public int getMinimumIntegerDigits(); - - /** - * Sets the minimum number of digits to display before the decimal point. If the number has - * fewer than this number of digits, the number will be padded with zeros. The pattern "#00.0#", - * for example, corresponds to 2 minimum integer digits, and the number 5.3 would be formatted - * as "05.3" in locale en-US. - * - * @param minimumIntegerDigits The minimum number of integer digits to output. - * @return The property bag, for chaining. - */ - public IBasicRoundingProperties setMinimumIntegerDigits(int minimumIntegerDigits); - - static int DEFAULT_MAXIMUM_INTEGER_DIGITS = -1; - - /** @see #setMaximumIntegerDigits */ - public int getMaximumIntegerDigits(); - - /** - * Sets the maximum number of digits to display before the decimal point. If the number has more - * than this number of digits, the extra digits will be truncated. For example, if maximum - * integer digits is 2, and you attempt to format the number 1970, you will get "70" in locale - * en-US. It is not possible to specify the maximum integer digits using a pattern - * string, except in the special case of a scientific format pattern. - * - * @param maximumIntegerDigits The maximum number of integer digits to output. - * @return The property bag, for chaining. - */ - public IBasicRoundingProperties setMaximumIntegerDigits(int maximumIntegerDigits); - - static int DEFAULT_MINIMUM_FRACTION_DIGITS = -1; - - /** @see #setMinimumFractionDigits */ - public int getMinimumFractionDigits(); - - /** - * Sets the minimum number of digits to display after the decimal point. If the number has fewer - * than this number of digits, the number will be padded with zeros. The pattern "#00.0#", for - * example, corresponds to 1 minimum fraction digit, and the number 456 would be formatted as - * "456.0" in locale en-US. - * - * @param minimumFractionDigits The minimum number of fraction digits to output. - * @return The property bag, for chaining. - */ - public IBasicRoundingProperties setMinimumFractionDigits(int minimumFractionDigits); - - static int DEFAULT_MAXIMUM_FRACTION_DIGITS = -1; - - /** @see #setMaximumFractionDigits */ - public int getMaximumFractionDigits(); - - /** - * Sets the maximum number of digits to display after the decimal point. If the number has fewer - * than this number of digits, the number will be rounded off using the rounding mode specified - * by {@link #setRoundingMode(RoundingMode)}. The pattern "#00.0#", for example, corresponds to - * 2 maximum fraction digits, and the number 456.789 would be formatted as "456.79" in locale - * en-US with the default rounding mode. Note that the number 456.999 would be - * formatted as "457.0" given the same configurations. - * - * @param maximumFractionDigits The maximum number of fraction digits to output. - * @return The property bag, for chaining. - */ - public IBasicRoundingProperties setMaximumFractionDigits(int maximumFractionDigits); - - static RoundingMode DEFAULT_ROUNDING_MODE = null; - - /** @see #setRoundingMode */ - public RoundingMode getRoundingMode(); - - /** - * Sets the rounding mode, which determines under which conditions extra decimal places are - * rounded either up or down. See {@link RoundingMode} for details on the choices of rounding - * mode. The default if not set explicitly is {@link RoundingMode#HALF_EVEN}. - * - *

This setting is ignored if {@link #setMathContext} is used. - * - * @param roundingMode The rounding mode to use when rounding is required. - * @return The property bag, for chaining. - * @see RoundingMode - * @see #setMathContext - */ - public IBasicRoundingProperties setRoundingMode(RoundingMode roundingMode); - - static MathContext DEFAULT_MATH_CONTEXT = null; - - /** @see #setMathContext */ - public MathContext getMathContext(); - - /** - * Sets the {@link MathContext} to be used during math and rounding operations. A MathContext - * encapsulates a RoundingMode and the number of significant digits in the output. - * - * @param mathContext The math context to use when rounding is required. - * @return The property bag, for chaining. - * @see MathContext - * @see #setRoundingMode - */ - public IBasicRoundingProperties setMathContext(MathContext mathContext); - } - - public static interface MultiplierGenerator { - public int getMultiplier(int magnitude); - } - - // Properties available to all rounding strategies - protected final MathContext mathContext; - protected final int minInt; - protected final int maxInt; - protected final int minFrac; - protected final int maxFrac; - - /** - * Constructor that uses integer and fraction digit lengths from IBasicRoundingProperties. - * - * @param properties - */ - protected Rounder(IBasicRoundingProperties properties) { - mathContext = RoundingUtils.getMathContextOrUnlimited(properties); - - int _maxInt = properties.getMaximumIntegerDigits(); - int _minInt = properties.getMinimumIntegerDigits(); - int _maxFrac = properties.getMaximumFractionDigits(); - int _minFrac = properties.getMinimumFractionDigits(); - - // Validate min/max int/frac. - // For backwards compatibility, minimum overrides maximum if the two conflict. - // The following logic ensures that there is always a minimum of at least one digit. - if (_minInt == 0 && _maxFrac != 0) { - // Force a digit after the decimal point. - minFrac = _minFrac <= 0 ? 1 : _minFrac; - maxFrac = _maxFrac < 0 ? Integer.MAX_VALUE : _maxFrac < minFrac ? minFrac : _maxFrac; - minInt = 0; - maxInt = _maxInt < 0 ? Integer.MAX_VALUE : _maxInt; - } else { - // Force a digit before the decimal point. - minFrac = _minFrac < 0 ? 0 : _minFrac; - maxFrac = _maxFrac < 0 ? Integer.MAX_VALUE : _maxFrac < minFrac ? minFrac : _maxFrac; - minInt = _minInt <= 0 ? 1 : _minInt; - maxInt = _maxInt < 0 ? Integer.MAX_VALUE : _maxInt < minInt ? minInt : _maxInt; - } - } - - /** - * Perform rounding and specification of integer and fraction digit lengths on the input quantity. - * Calling this method will change the state of the FormatQuantity. - * - * @param input The {@link FormatQuantity} to be modified and rounded. - */ - public abstract void apply(FormatQuantity input); - - /** - * Rounding can affect the magnitude. First we attempt to adjust according to the original - * magnitude, and if the magnitude changes, we adjust according to a magnitude one greater. Note - * that this algorithm assumes that increasing the multiplier never increases the number of digits - * that can be displayed. - * - * @param input The quantity to be rounded. - * @param mg The implementation that returns magnitude adjustment based on a given starting - * magnitude. - * @return The multiplier that was chosen to best fit the input. - */ - public int chooseMultiplierAndApply(FormatQuantity input, MultiplierGenerator mg) { - // TODO: Avoid the object creation here. - FormatQuantity copy = input.createCopy(); - - assert !input.isZero(); - int magnitude = input.getMagnitude(); - int multiplier = mg.getMultiplier(magnitude); - input.adjustMagnitude(multiplier); - apply(input); - - // If the number turned to zero when rounding, do not re-attempt the rounding. - if (!input.isZero() && input.getMagnitude() == magnitude + multiplier + 1) { - magnitude += 1; - input.copyFrom(copy); - multiplier = mg.getMultiplier(magnitude); - input.adjustMagnitude(multiplier); - assert input.getMagnitude() == magnitude + multiplier - 1; - apply(input); - assert input.getMagnitude() == magnitude + multiplier; - } - - return multiplier; - } - - /** - * Implementations can call this method to perform default logic for min/max digits. This method - * performs logic for handling of a zero input. - * - * @param input The digits being formatted. - */ - protected void applyDefaults(FormatQuantity input) { - input.setIntegerLength(minInt, maxInt); - input.setFractionLength(minFrac, maxFrac); - } - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - apply(input); - } - - @Override - public void export(Properties properties) { - properties.setMathContext(mathContext); - properties.setRoundingMode(mathContext.getRoundingMode()); - properties.setMinimumFractionDigits(minFrac); - properties.setMinimumIntegerDigits(minInt); - properties.setMaximumFractionDigits(maxFrac); - properties.setMaximumIntegerDigits(maxInt); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java index 6594c5ee390..7aa15eb5f42 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/RoundingUtils.java @@ -6,8 +6,6 @@ import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; -import com.ibm.icu.impl.number.Rounder.IBasicRoundingProperties; - /** @author sffc */ public class RoundingUtils { @@ -135,7 +133,7 @@ public class RoundingUtils { * @param properties The property bag. * @return A {@link MathContext}. Never null. */ - public static MathContext getMathContextOrUnlimited(IBasicRoundingProperties properties) { + public static MathContext getMathContextOrUnlimited(Properties properties) { MathContext mathContext = properties.getMathContext(); if (mathContext == null) { RoundingMode roundingMode = properties.getRoundingMode(); @@ -153,7 +151,7 @@ public class RoundingUtils { * @param properties The property bag. * @return A {@link MathContext}. Never null. */ - public static MathContext getMathContextOr34Digits(IBasicRoundingProperties properties) { + public static MathContext getMathContextOr34Digits(Properties properties) { MathContext mathContext = properties.getMathContext(); if (mathContext == null) { RoundingMode roundingMode = properties.getRoundingMode(); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ThingsNeedingNewHome.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ThingsNeedingNewHome.java new file mode 100644 index 00000000000..e322c87bdb0 --- /dev/null +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/ThingsNeedingNewHome.java @@ -0,0 +1,59 @@ +// © 2017 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html#License +package com.ibm.icu.impl.number; + +/** @author sffc */ +public class ThingsNeedingNewHome { + public static final String FALLBACK_PADDING_STRING = "\u0020"; // i.e. a space + + public enum PadPosition { + BEFORE_PREFIX, + AFTER_PREFIX, + BEFORE_SUFFIX, + AFTER_SUFFIX; + + public static PadPosition fromOld(int old) { + switch (old) { + case com.ibm.icu.text.DecimalFormat.PAD_BEFORE_PREFIX: + return PadPosition.BEFORE_PREFIX; + case com.ibm.icu.text.DecimalFormat.PAD_AFTER_PREFIX: + return PadPosition.AFTER_PREFIX; + case com.ibm.icu.text.DecimalFormat.PAD_BEFORE_SUFFIX: + return PadPosition.BEFORE_SUFFIX; + case com.ibm.icu.text.DecimalFormat.PAD_AFTER_SUFFIX: + return PadPosition.AFTER_SUFFIX; + default: + throw new IllegalArgumentException("Don't know how to map " + old); + } + } + + public int toOld() { + switch (this) { + case BEFORE_PREFIX: + return com.ibm.icu.text.DecimalFormat.PAD_BEFORE_PREFIX; + case AFTER_PREFIX: + return com.ibm.icu.text.DecimalFormat.PAD_AFTER_PREFIX; + case BEFORE_SUFFIX: + return com.ibm.icu.text.DecimalFormat.PAD_BEFORE_SUFFIX; + case AFTER_SUFFIX: + return com.ibm.icu.text.DecimalFormat.PAD_AFTER_SUFFIX; + default: + return -1; // silence compiler errors + } + } + } + + /** + * Returns true if the currency is set in The property bag or if currency symbols are present in + * the prefix/suffix pattern. + */ + public static boolean useCurrency(Properties properties) { + return ((properties.getCurrency() != null) + || properties.getCurrencyPluralInfo() != null + || properties.getCurrencyUsage() != null + || AffixPatternUtils.hasCurrencySymbols(properties.getPositivePrefixPattern()) + || AffixPatternUtils.hasCurrencySymbols(properties.getPositiveSuffixPattern()) + || AffixPatternUtils.hasCurrencySymbols(properties.getNegativePrefixPattern()) + || AffixPatternUtils.hasCurrencySymbols(properties.getNegativeSuffixPattern())); + } +} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/BigDecimalMultiplier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/BigDecimalMultiplier.java deleted file mode 100644 index a6a008a2ccc..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/BigDecimalMultiplier.java +++ /dev/null @@ -1,57 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import java.math.BigDecimal; - -import com.ibm.icu.impl.number.Format.BeforeFormat; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.Properties; - -public class BigDecimalMultiplier extends BeforeFormat { - public static interface IProperties { - - static BigDecimal DEFAULT_MULTIPLIER = null; - - /** @see #setMultiplier */ - public BigDecimal getMultiplier(); - - /** - * Multiply all numbers by this amount before formatting. - * - * @param multiplier The amount to multiply by. - * @return The property bag, for chaining. - * @see MagnitudeMultiplier - */ - public IProperties setMultiplier(BigDecimal multiplier); - } - - public static boolean useMultiplier(IProperties properties) { - return properties.getMultiplier() != IProperties.DEFAULT_MULTIPLIER; - } - - private final BigDecimal multiplier; - - public static BigDecimalMultiplier getInstance(IProperties properties) { - if (properties.getMultiplier() == null) { - throw new IllegalArgumentException("The multiplier must be present for MultiplierFormat"); - } - // TODO: Intelligently fall back to a MagnitudeMultiplier if the multiplier is a power of ten? - return new BigDecimalMultiplier(properties); - } - - private BigDecimalMultiplier(IProperties properties) { - this.multiplier = properties.getMultiplier(); - } - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - input.multiplyBy(multiplier); - } - - @Override - public void export(Properties properties) { - properties.setMultiplier(multiplier); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CompactDecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CompactDecimalFormat.java deleted file mode 100644 index 2caf40f20e0..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CompactDecimalFormat.java +++ /dev/null @@ -1,553 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import java.util.HashMap; -import java.util.Map; -import java.util.MissingResourceException; - -import com.ibm.icu.impl.ICUData; -import com.ibm.icu.impl.ICUResourceBundle; -import com.ibm.icu.impl.StandardPlural; -import com.ibm.icu.impl.UResource; -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.Modifier; -import com.ibm.icu.impl.number.Modifier.PositiveNegativeModifier; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.PNAffixGenerator; -import com.ibm.icu.impl.number.PatternString; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.Rounder; -import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier; -import com.ibm.icu.impl.number.modifiers.PositiveNegativeAffixModifier; -import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder; -import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; -import com.ibm.icu.text.CompactDecimalFormat.CompactType; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.NumberFormat; -import com.ibm.icu.text.NumberingSystem; -import com.ibm.icu.text.PluralRules; -import com.ibm.icu.util.ULocale; -import com.ibm.icu.util.UResourceBundle; - -public class CompactDecimalFormat extends Format.BeforeFormat { - public static interface IProperties - extends RoundingFormat.IProperties, CurrencyFormat.ICurrencyProperties { - - static CompactStyle DEFAULT_COMPACT_STYLE = null; - - /** @see #setCompactStyle */ - public CompactStyle getCompactStyle(); - - /** - * Use compact decimal formatting with the specified {@link CompactStyle}. CompactStyle.SHORT - * produces output like "10K" in locale en-US, whereas CompactStyle.LONG produces - * output like "10 thousand" in that locale. - * - * @param compactStyle The style of prefixes/suffixes to append. - * @return The property bag, for chaining. - */ - public IProperties setCompactStyle(CompactStyle compactStyle); - - static Map> DEFAULT_COMPACT_CUSTOM_DATA = null; - - /** @see #setCompactCustomData */ - public Map> getCompactCustomData(); - - /** - * Specifies custom data to be used instead of CLDR data when constructing a - * CompactDecimalFormat. The argument should be a map with the following structure: - * - *

-     * {
-     *   "1000": {
-     *     "one": "0 thousand",
-     *     "other": "0 thousand"
-     *   },
-     *   "10000": {
-     *     "one": "00 thousand",
-     *     "other": "00 thousand"
-     *   },
-     *   // ...
-     * }
-     * 
- * - * This API endpoint is used by the CLDR Survey Tool. - * - * @param compactCustomData A map with the above structure. - * @return The property bag, for chaining. - * @internal - * @deprecated This API is ICU internal only. - */ - @Deprecated - public IProperties setCompactCustomData(Map> compactCustomData); - } - - public static boolean useCompactDecimalFormat(IProperties properties) { - return properties.getCompactStyle() != IProperties.DEFAULT_COMPACT_STYLE; - } - - static final int MAX_DIGITS = 15; - - // Properties - private final CompactDecimalData data; - private final Rounder rounder; - private final PositiveNegativeModifier defaultMod; - private final CompactStyle style; // retained for exporting only - - public static CompactDecimalFormat getInstance( - DecimalFormatSymbols symbols, IProperties properties) { - return new CompactDecimalFormat(symbols, properties); - } - - private static final int DEFAULT_MIN_SIG = 1; - private static final int DEFAULT_MAX_SIG = 2; - - private static final ThreadLocal threadLocalProperties = - new ThreadLocal() { - @Override - protected Properties initialValue() { - return new Properties(); - } - }; - - private static Rounder getRounder(IProperties properties) { - // Use rounding settings if they were specified, or else use the default CDF rounder. - // TODO: Detecting and overriding significant digits here is a bit of a hack, since detection - // is also performed in the "RoundingFormat.getDefaultOrNull" method. - // It would be more elegant to call some sort of "fallback" copy method. - Rounder rounder = null; - if (!SignificantDigitsRounder.useSignificantDigits(properties)) { - rounder = RoundingFormat.getDefaultOrNull(properties); - } - if (rounder == null) { - int _minSig = properties.getMinimumSignificantDigits(); - int _maxSig = properties.getMaximumSignificantDigits(); - Properties rprops = threadLocalProperties.get().clear(); - // Settings needing possible override: - rprops.setMinimumSignificantDigits(_minSig > 0 ? _minSig : DEFAULT_MIN_SIG); - rprops.setMaximumSignificantDigits(_maxSig > 0 ? _maxSig : DEFAULT_MAX_SIG); - // TODO: Should copyFrom() be used instead? It requires a cast. - // Settings to copy verbatim: - rprops.setRoundingMode(properties.getRoundingMode()); - rprops.setMinimumFractionDigits(properties.getMinimumFractionDigits()); - rprops.setMaximumFractionDigits(properties.getMaximumFractionDigits()); - rprops.setMinimumIntegerDigits(properties.getMinimumIntegerDigits()); - rprops.setMaximumIntegerDigits(properties.getMaximumIntegerDigits()); - rounder = SignificantDigitsRounder.getInstance(rprops); - } - return rounder; - } - - protected static final ThreadLocal> - threadLocalDataCache = - new ThreadLocal>() { - @Override - protected Map initialValue() { - return new HashMap(); - } - }; - - private static CompactDecimalData getData( - DecimalFormatSymbols symbols, CompactDecimalFingerprint fingerprint) { - // See if we already have a data object based on the fingerprint - CompactDecimalData data = threadLocalDataCache.get().get(fingerprint); - if (data != null) return data; - - // Make data bundle object - data = new CompactDecimalData(); - ULocale ulocale = symbols.getULocale(); - CompactDecimalDataSink sink = new CompactDecimalDataSink(data, symbols, fingerprint); - String nsName = NumberingSystem.getInstance(ulocale).getName(); - ICUResourceBundle rb = - (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, ulocale); - internalPopulateData(nsName, rb, sink, data); - if (data.isEmpty() && fingerprint.compactStyle == CompactStyle.LONG) { - // No long data is available; load short data instead - sink.compactStyle = CompactStyle.SHORT; - internalPopulateData(nsName, rb, sink, data); - } - threadLocalDataCache.get().put(fingerprint, data); - return data; - } - - private static void internalPopulateData( - String nsName, ICUResourceBundle rb, CompactDecimalDataSink sink, CompactDecimalData data) { - try { - rb.getAllItemsWithFallback("NumberElements/" + nsName, sink); - } catch (MissingResourceException e) { - // Fall back to latn - } - if (data.isEmpty() && !nsName.equals("latn")) { - rb.getAllItemsWithFallback("NumberElements/latn", sink); - } - if (sink.exception != null) { - throw sink.exception; - } - } - - private static PositiveNegativeModifier getDefaultMod( - DecimalFormatSymbols symbols, CompactDecimalFingerprint fingerprint) { - ULocale uloc = symbols.getULocale(); - String pattern; - if (fingerprint.compactType == CompactType.CURRENCY) { - pattern = NumberFormat.getPatternForStyle(uloc, NumberFormat.CURRENCYSTYLE); - } else { - pattern = NumberFormat.getPatternForStyle(uloc, NumberFormat.NUMBERSTYLE); - } - // TODO: Clean this up; avoid the extra object creations. - // TODO: Currency may also need to override grouping settings, not just affixes. - Properties properties = PatternString.parseToProperties(pattern); - PNAffixGenerator pnag = PNAffixGenerator.getThreadLocalInstance(); - PNAffixGenerator.Result result = - pnag.getModifiers(symbols, fingerprint.currencySymbol, properties); - return new PositiveNegativeAffixModifier(result.positive, result.negative); - } - - private CompactDecimalFormat(DecimalFormatSymbols symbols, IProperties properties) { - CompactDecimalFingerprint fingerprint = new CompactDecimalFingerprint(symbols, properties); - this.rounder = getRounder(properties); - // Short-circuit and use custom data if provided - if (properties.getCompactCustomData() != null) { - this.data = createDataFromCustom(symbols, fingerprint, properties.getCompactCustomData()); - } else { - this.data = getData(symbols, fingerprint); - } - this.defaultMod = getDefaultMod(symbols, fingerprint); - this.style = properties.getCompactStyle(); // for exporting only - } - - @Override - public void before(FormatQuantity input, ModifierHolder mods, PluralRules rules) { - apply(input, mods, rules, rounder, data, defaultMod); - } - - @Override - protected void before(FormatQuantity input, ModifierHolder mods) { - throw new UnsupportedOperationException(); - } - - public static void apply( - FormatQuantity input, - ModifierHolder mods, - PluralRules rules, - DecimalFormatSymbols symbols, - IProperties properties) { - CompactDecimalFingerprint fingerprint = new CompactDecimalFingerprint(symbols, properties); - Rounder rounder = getRounder(properties); - CompactDecimalData data = getData(symbols, fingerprint); - PositiveNegativeModifier defaultMod = getDefaultMod(symbols, fingerprint); - apply(input, mods, rules, rounder, data, defaultMod); - } - - private static void apply( - FormatQuantity input, - ModifierHolder mods, - PluralRules rules, - Rounder rounder, - CompactDecimalData data, - PositiveNegativeModifier defaultMod) { - - // Treat zero as if it had magnitude 0 - int magnitude; - if (input.isZero()) { - magnitude = 0; - rounder.apply(input); - } else { - // TODO: Revisit chooseMultiplierAndApply - int multiplier = rounder.chooseMultiplierAndApply(input, data); - magnitude = input.isZero() ? 0 : input.getMagnitude(); - magnitude -= multiplier; - } - - StandardPlural plural = input.getStandardPlural(rules); - boolean isNegative = input.isNegative(); - Modifier mod = data.getModifier(magnitude, plural, isNegative); - if (mod == null) { - // Use the default (non-compact) modifier. - mod = defaultMod.getModifier(isNegative); - } - mods.add(mod); - } - - @Override - public void export(Properties properties) { - properties.setCompactStyle(style); - rounder.export(properties); - } - - static class CompactDecimalData implements Rounder.MultiplierGenerator { - - // A dummy object used when a "0" compact decimal entry is encountered. This is necessary - // in order to prevent falling back to root. - private static final Modifier USE_FALLBACK = new ConstantAffixModifier(); - - final Modifier[] mods; - final byte[] multipliers; - boolean isEmpty; - int largestMagnitude; - - CompactDecimalData() { - mods = new Modifier[(MAX_DIGITS + 1) * StandardPlural.COUNT * 2]; - multipliers = new byte[MAX_DIGITS + 1]; - isEmpty = true; - largestMagnitude = -1; - } - - boolean isEmpty() { - return isEmpty; - } - - @Override - public int getMultiplier(int magnitude) { - if (magnitude < 0) { - return 0; - } - if (magnitude > largestMagnitude) { - magnitude = largestMagnitude; - } - return multipliers[magnitude]; - } - - int setOrGetMultiplier(int magnitude, byte multiplier) { - if (multipliers[magnitude] != 0) { - return multipliers[magnitude]; - } - multipliers[magnitude] = multiplier; - isEmpty = false; - if (magnitude > largestMagnitude) largestMagnitude = magnitude; - return multiplier; - } - - Modifier getModifier(int magnitude, StandardPlural plural, boolean isNegative) { - if (magnitude < 0) { - return null; - } - if (magnitude > largestMagnitude) { - magnitude = largestMagnitude; - } - Modifier mod = mods[modIndex(magnitude, plural, isNegative)]; - if (mod == null && plural != StandardPlural.OTHER) { - // Fall back to "other" plural variant - mod = mods[modIndex(magnitude, StandardPlural.OTHER, isNegative)]; - } - if (mod == USE_FALLBACK) { - // Return null if USE_FALLBACK is present - mod = null; - } - return mod; - } - - public boolean has(int magnitude, StandardPlural plural) { - // Return true if USE_FALLBACK is present - return mods[modIndex(magnitude, plural, false)] != null; - } - - void setModifiers(Modifier positive, Modifier negative, int magnitude, StandardPlural plural) { - mods[modIndex(magnitude, plural, false)] = positive; - mods[modIndex(magnitude, plural, true)] = negative; - isEmpty = false; - if (magnitude > largestMagnitude) largestMagnitude = magnitude; - } - - void setNoFallback(int magnitude, StandardPlural plural) { - setModifiers(USE_FALLBACK, USE_FALLBACK, magnitude, plural); - } - - private static final int modIndex(int magnitude, StandardPlural plural, boolean isNegative) { - return magnitude * StandardPlural.COUNT * 2 + plural.ordinal() * 2 + (isNegative ? 1 : 0); - } - } - - static class CompactDecimalFingerprint { - // TODO: Add more stuff to the fingerprint, like the symbols used by PNAffixGenerator - final CompactStyle compactStyle; - final CompactType compactType; - final ULocale uloc; - final String currencySymbol; - - CompactDecimalFingerprint(DecimalFormatSymbols symbols, IProperties properties) { - // CompactDecimalFormat does not need to worry about the same constraints as non-compact - // currency formatting needs to consider, like the currency rounding mode and the currency - // long names with plural forms. - if (properties.getCurrency() != CurrencyFormat.ICurrencyProperties.DEFAULT_CURRENCY) { - compactType = CompactType.CURRENCY; - currencySymbol = CurrencyFormat.getCurrencySymbol(symbols, properties); - } else { - compactType = CompactType.DECIMAL; - currencySymbol = ""; // fallback; should remain unused - } - compactStyle = properties.getCompactStyle(); - uloc = symbols.getULocale(); - } - - @Override - public boolean equals(Object _other) { - if (_other == null) return false; - CompactDecimalFingerprint other = (CompactDecimalFingerprint) _other; - if (this == other) return true; - if (compactStyle != other.compactStyle) return false; - if (compactType != other.compactType) return false; - if (currencySymbol != other.currencySymbol) { - // String comparison with null handling - if (currencySymbol == null || other.currencySymbol == null) return false; - if (!currencySymbol.equals(other.currencySymbol)) return false; - } - if (!uloc.equals(other.uloc)) return false; - return true; - } - - @Override - public int hashCode() { - int hashCode = 0; - if (compactStyle != null) hashCode ^= compactStyle.hashCode(); - if (compactType != null) hashCode ^= compactType.hashCode(); - if (uloc != null) hashCode ^= uloc.hashCode(); - if (currencySymbol != null) hashCode ^= currencySymbol.hashCode(); - return hashCode; - } - } - - private static final class CompactDecimalDataSink extends UResource.Sink { - - CompactDecimalData data; - DecimalFormatSymbols symbols; - CompactStyle compactStyle; - CompactType compactType; - String currencySymbol; - PNAffixGenerator pnag; - IllegalArgumentException exception; - - /* - * NumberElements{ <-- top (numbering system table) - * latn{ <-- patternsTable (one per numbering system) - * patternsLong{ <-- formatsTable (one per pattern) - * decimalFormat{ <-- powersOfTenTable (one per format) - * 1000{ <-- pluralVariantsTable (one per power of ten) - * one{"0 thousand"} <-- plural variant and template - */ - - public CompactDecimalDataSink( - CompactDecimalData data, - DecimalFormatSymbols symbols, - CompactDecimalFingerprint fingerprint) { - this.data = data; - this.symbols = symbols; - compactType = fingerprint.compactType; - currencySymbol = fingerprint.currencySymbol; - compactStyle = fingerprint.compactStyle; - pnag = PNAffixGenerator.getThreadLocalInstance(); - } - - @Override - public void put(UResource.Key key, UResource.Value value, boolean isRoot) { - UResource.Table patternsTable = value.getTable(); - for (int i1 = 0; patternsTable.getKeyAndValue(i1, key, value); ++i1) { - if (key.contentEquals("patternsShort") && compactStyle == CompactStyle.SHORT) { - } else if (key.contentEquals("patternsLong") && compactStyle == CompactStyle.LONG) { - } else { - continue; - } - - // traverse into the table of formats - UResource.Table formatsTable = value.getTable(); - for (int i2 = 0; formatsTable.getKeyAndValue(i2, key, value); ++i2) { - if (key.contentEquals("decimalFormat") && compactType == CompactType.DECIMAL) { - } else if (key.contentEquals("currencyFormat") && compactType == CompactType.CURRENCY) { - } else { - continue; - } - - // traverse into the table of powers of ten - UResource.Table powersOfTenTable = value.getTable(); - for (int i3 = 0; powersOfTenTable.getKeyAndValue(i3, key, value); ++i3) { - try { - - // Assumes that the keys are always of the form "10000" where the magnitude is the - // length of the key minus one - byte magnitude = (byte) (key.length() - 1); - - // Silently ignore divisors that are too big. - if (magnitude >= MAX_DIGITS) continue; - - // Iterate over the plural variants ("one", "other", etc) - UResource.Table pluralVariantsTable = value.getTable(); - for (int i4 = 0; pluralVariantsTable.getKeyAndValue(i4, key, value); ++i4) { - - // Skip this magnitude/plural if we already have it from a child locale. - StandardPlural plural = StandardPlural.fromString(key.toString()); - if (data.has(magnitude, plural)) { - continue; - } - - // The value "0" means that we need to use the default pattern and not fall back - // to parent locales. Example locale where this is relevant: 'it'. - String patternString = value.toString(); - if (patternString.equals("0")) { - data.setNoFallback(magnitude, plural); - continue; - } - - // The magnitude multiplier is the difference between the magnitude and the number - // of zeros in the pattern, getMinimumIntegerDigits. - Properties properties = PatternString.parseToProperties(patternString); - byte _multiplier = (byte) -(magnitude - properties.getMinimumIntegerDigits() + 1); - if (_multiplier != data.setOrGetMultiplier(magnitude, _multiplier)) { - throw new IllegalArgumentException( - String.format( - "Different number of zeros for same power of ten in compact decimal format data for locale '%s', style '%s', type '%s'", - symbols.getULocale().toString(), - compactStyle.toString(), - compactType.toString())); - } - - PNAffixGenerator.Result result = - pnag.getModifiers(symbols, currencySymbol, properties); - data.setModifiers(result.positive, result.negative, magnitude, plural); - } - - } catch (IllegalArgumentException e) { - exception = e; - continue; - } - } - - // We want only one table of compact decimal formats, so if we get here, stop consuming. - // The data.isEmpty() check will prevent further bundles from being traversed. - return; - } - } - } - } - - /** - * Uses data from the custom powersToPluralsToPatterns map instead of an ICUResourceBundle to - * populate an instance of CompactDecimalData. - */ - static CompactDecimalData createDataFromCustom( - DecimalFormatSymbols symbols, - CompactDecimalFingerprint fingerprint, - Map> powersToPluralsToPatterns) { - CompactDecimalData data = new CompactDecimalData(); - PNAffixGenerator pnag = PNAffixGenerator.getThreadLocalInstance(); - for (Map.Entry> magnitudeEntry : - powersToPluralsToPatterns.entrySet()) { - byte magnitude = (byte) (magnitudeEntry.getKey().length() - 1); - for (Map.Entry pluralEntry : magnitudeEntry.getValue().entrySet()) { - StandardPlural plural = StandardPlural.fromString(pluralEntry.getKey().toString()); - String patternString = pluralEntry.getValue().toString(); - Properties properties = PatternString.parseToProperties(patternString); - byte _multiplier = (byte) -(magnitude - properties.getMinimumIntegerDigits() + 1); - if (_multiplier != data.setOrGetMultiplier(magnitude, _multiplier)) { - throw new IllegalArgumentException( - "Different number of zeros for same power of ten in custom compact decimal format data"); - } - PNAffixGenerator.Result result = - pnag.getModifiers(symbols, fingerprint.currencySymbol, properties); - data.setModifiers(result.positive, result.negative, magnitude, plural); - } - } - return data; - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CurrencyFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CurrencyFormat.java deleted file mode 100644 index 41e9d3f2a02..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/CurrencyFormat.java +++ /dev/null @@ -1,311 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import java.math.BigDecimal; - -import com.ibm.icu.impl.StandardPlural; -import com.ibm.icu.impl.number.AffixPatternUtils; -import com.ibm.icu.impl.number.PNAffixGenerator; -import com.ibm.icu.impl.number.PatternString; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.Rounder; -import com.ibm.icu.impl.number.modifiers.GeneralPluralModifier; -import com.ibm.icu.impl.number.rounders.IncrementRounder; -import com.ibm.icu.impl.number.rounders.MagnitudeRounder; -import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder; -import com.ibm.icu.text.CurrencyPluralInfo; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.util.Currency; -import com.ibm.icu.util.Currency.CurrencyUsage; - -public class CurrencyFormat { - - public enum CurrencyStyle { - SYMBOL, - ISO_CODE; - } - - public static interface ICurrencyProperties { - static Currency DEFAULT_CURRENCY = null; - - /** @see #setCurrency */ - public Currency getCurrency(); - - /** - * Use the specified currency to substitute currency placeholders ('¤') in the pattern string. - * - * @param currency The currency. - * @return The property bag, for chaining. - */ - public IProperties setCurrency(Currency currency); - - static CurrencyStyle DEFAULT_CURRENCY_STYLE = null; - - /** @see #setCurrencyStyle */ - public CurrencyStyle getCurrencyStyle(); - - /** - * Use the specified {@link CurrencyStyle} to replace currency placeholders ('¤'). - * CurrencyStyle.SYMBOL will use the short currency symbol, like "$" or "€", whereas - * CurrencyStyle.ISO_CODE will use the ISO 4217 currency code, like "USD" or "EUR". - * - *

For long currency names, use {@link MeasureFormat.IProperties#setMeasureUnit}. - * - * @param currencyStyle The currency style. Defaults to CurrencyStyle.SYMBOL. - * @return The property bag, for chaining. - */ - public IProperties setCurrencyStyle(CurrencyStyle currencyStyle); - - /** - * An old enum that specifies how currencies should be rounded. It contains a subset of the - * functionality supported by RoundingInterval. - */ - static Currency.CurrencyUsage DEFAULT_CURRENCY_USAGE = null; - - /** @see #setCurrencyUsage */ - public Currency.CurrencyUsage getCurrencyUsage(); - - /** - * Use the specified {@link CurrencyUsage} instance, which provides default rounding rules for - * the currency in two styles, CurrencyUsage.CASH and CurrencyUsage.STANDARD. - * - *

The CurrencyUsage specified here will not be used unless there is a currency placeholder - * in the pattern. - * - * @param currencyUsage The currency usage. Defaults to CurrencyUsage.STANDARD. - * @return The property bag, for chaining. - */ - public IProperties setCurrencyUsage(Currency.CurrencyUsage currencyUsage); - - static CurrencyPluralInfo DEFAULT_CURRENCY_PLURAL_INFO = null; - - /** @see #setCurrencyPluralInfo */ - @Deprecated - public CurrencyPluralInfo getCurrencyPluralInfo(); - - /** - * Use the specified {@link CurrencyPluralInfo} instance when formatting currency long names. - * - * @param currencyPluralInfo The currency plural info object. - * @return The property bag, for chaining. - * @deprecated Use {@link MeasureFormat.IProperties#setMeasureUnit} with a Currency instead. - */ - @Deprecated - public IProperties setCurrencyPluralInfo(CurrencyPluralInfo currencyPluralInfo); - } - - public static interface IProperties - extends ICurrencyProperties, - RoundingFormat.IProperties, - PositiveNegativeAffixFormat.IProperties {} - - /** - * Returns true if the currency is set in The property bag or if currency symbols are present in - * the prefix/suffix pattern. - */ - public static boolean useCurrency(IProperties properties) { - return ((properties.getCurrency() != null) - || properties.getCurrencyPluralInfo() != null - || properties.getCurrencyUsage() != null - || AffixPatternUtils.hasCurrencySymbols(properties.getPositivePrefixPattern()) - || AffixPatternUtils.hasCurrencySymbols(properties.getPositiveSuffixPattern()) - || AffixPatternUtils.hasCurrencySymbols(properties.getNegativePrefixPattern()) - || AffixPatternUtils.hasCurrencySymbols(properties.getNegativeSuffixPattern())); - } - - /** - * Returns the effective currency symbol based on the input. If {@link - * ICurrencyProperties#setCurrencyStyle} was set to {@link CurrencyStyle#ISO_CODE}, the ISO Code - * will be returned; otherwise, the currency symbol, like "$", will be returned. - * - * @param symbols The current {@link DecimalFormatSymbols} instance - * @param properties The current property bag - * @return The currency symbol string, e.g., to substitute '¤' in a decimal pattern string. - */ - public static String getCurrencySymbol( - DecimalFormatSymbols symbols, ICurrencyProperties properties) { - // If the user asked for ISO Code, return the ISO Code instead of the symbol - CurrencyStyle style = properties.getCurrencyStyle(); - if (style == CurrencyStyle.ISO_CODE) { - return getCurrencyIsoCode(symbols, properties); - } - - // Get the currency symbol - Currency currency = properties.getCurrency(); - if (currency == null) { - return symbols.getCurrencySymbol(); - } else if (currency.equals(symbols.getCurrency())) { - // The user may have set a custom currency symbol in DecimalFormatSymbols. - return symbols.getCurrencySymbol(); - } else { - // Use the canonical symbol. - return currency.getName(symbols.getULocale(), Currency.SYMBOL_NAME, null); - } - } - - /** - * Returns the currency ISO code based on the input, like "USD". - * - * @param symbols The current {@link DecimalFormatSymbols} instance - * @param properties The current property bag - * @return The currency ISO code string, e.g., to substitute '¤¤' in a decimal pattern string. - */ - public static String getCurrencyIsoCode( - DecimalFormatSymbols symbols, ICurrencyProperties properties) { - Currency currency = properties.getCurrency(); - if (currency == null) { - // If a currency object was not provided, use the string from symbols - // Note: symbols.getCurrency().getCurrencyCode() won't work here because - // DecimalFormatSymbols#setInternationalCurrencySymbol() does not update the - // immutable internal currency instance. - return symbols.getInternationalCurrencySymbol(); - } else if (currency.equals(symbols.getCurrency())) { - // The user may have set a custom currency symbol in DecimalFormatSymbols. - return symbols.getInternationalCurrencySymbol(); - } else { - // Use the canonical currency code. - return currency.getCurrencyCode(); - } - } - - /** - * Returns the currency long name on the input, like "US dollars". - * - * @param symbols The current {@link DecimalFormatSymbols} instance - * @param properties The current property bag - * @param plural The plural form - * @return The currency long name string, e.g., to substitute '¤¤¤' in a decimal pattern string. - */ - public static String getCurrencyLongName( - DecimalFormatSymbols symbols, ICurrencyProperties properties, StandardPlural plural) { - // Attempt to get a currency object first from properties then from symbols - Currency currency = properties.getCurrency(); - if (currency == null) { - currency = symbols.getCurrency(); - } - - // If no currency object is available, fall back to the currency symbol - if (currency == null) { - return getCurrencySymbol(symbols, properties); - } - - // Get the long name - return currency.getName( - symbols.getULocale(), Currency.PLURAL_LONG_NAME, plural.getKeyword(), null); - } - - public static GeneralPluralModifier getCurrencyModifier( - DecimalFormatSymbols symbols, IProperties properties) { - - PNAffixGenerator pnag = PNAffixGenerator.getThreadLocalInstance(); - String sym = getCurrencySymbol(symbols, properties); - String iso = getCurrencyIsoCode(symbols, properties); - - // Previously, the user was also able to specify '¤¤' and '¤¤¤' directly into the prefix or - // suffix, which is how the user specified whether they wanted the ISO code or long name. - // For backwards compatibility support, that feature is implemented here. - - CurrencyPluralInfo info = properties.getCurrencyPluralInfo(); - GeneralPluralModifier mod = new GeneralPluralModifier(); - Properties temp = new Properties(); - for (StandardPlural plural : StandardPlural.VALUES) { - String longName = getCurrencyLongName(symbols, properties, plural); - - PNAffixGenerator.Result result; - if (info == null) { - // CurrencyPluralInfo is not available. - result = pnag.getModifiers(symbols, sym, iso, longName, properties); - } else { - // CurrencyPluralInfo is available. Use it to generate affixes for long name support. - String pluralPattern = info.getCurrencyPluralPattern(plural.getKeyword()); - PatternString.parseToExistingProperties( - pluralPattern, temp, PatternString.IGNORE_ROUNDING_ALWAYS); - result = pnag.getModifiers(symbols, sym, iso, longName, temp); - } - mod.put(plural, result.positive, result.negative); - } - return mod; - } - - private static final Currency DEFAULT_CURRENCY = Currency.getInstance("XXX"); - - public static void populateCurrencyRounderProperties( - Properties destination, DecimalFormatSymbols symbols, IProperties properties) { - - Currency currency = properties.getCurrency(); - if (currency == null) { - // Fall back to the DecimalFormatSymbols currency instance. - currency = symbols.getCurrency(); - } - if (currency == null) { - // There is a currency symbol in the pattern, but we have no currency available to use. - // Use the default currency instead so that we can still apply currency usage rules. - currency = DEFAULT_CURRENCY; - } - - CurrencyUsage _currencyUsage = properties.getCurrencyUsage(); - int _minFrac = properties.getMinimumFractionDigits(); - int _maxFrac = properties.getMaximumFractionDigits(); - - CurrencyUsage effectiveCurrencyUsage = - (_currencyUsage != null) ? _currencyUsage : CurrencyUsage.STANDARD; - double incrementDouble = currency.getRoundingIncrement(effectiveCurrencyUsage); - int fractionDigits = currency.getDefaultFractionDigits(effectiveCurrencyUsage); - - destination.setRoundingMode(properties.getRoundingMode()); - destination.setMinimumIntegerDigits(properties.getMinimumIntegerDigits()); - destination.setMaximumIntegerDigits(properties.getMaximumIntegerDigits()); - - if (_currencyUsage == null && (_minFrac >= 0 || _maxFrac >= 0)) { - // User override of fraction length - if (_minFrac < 0) { - destination.setMinimumFractionDigits(fractionDigits < _maxFrac ? fractionDigits : _maxFrac); - destination.setMaximumFractionDigits(_maxFrac); - } else if (_maxFrac < 0) { - destination.setMinimumFractionDigits(_minFrac); - destination.setMaximumFractionDigits(fractionDigits > _minFrac ? fractionDigits : _minFrac); - } else { - destination.setMinimumFractionDigits(_minFrac); - destination.setMaximumFractionDigits(_maxFrac); - } - } else { - // Currency rounding - destination.setMinimumFractionDigits(fractionDigits); - destination.setMaximumFractionDigits(fractionDigits); - } - - if (incrementDouble > 0.0) { - BigDecimal incrementBigDecimal; - BigDecimal _roundingIncrement = properties.getRoundingIncrement(); - if (_roundingIncrement != null) { - incrementBigDecimal = _roundingIncrement; - } else { - incrementBigDecimal = BigDecimal.valueOf(incrementDouble); - } - destination.setRoundingIncrement(incrementBigDecimal); - } else { - } - } - - private static final ThreadLocal threadLocalProperties = - new ThreadLocal() { - @Override - protected Properties initialValue() { - return new Properties(); - } - }; - - public static Rounder getCurrencyRounder(DecimalFormatSymbols symbols, IProperties properties) { - if (SignificantDigitsRounder.useSignificantDigits(properties)) { - return SignificantDigitsRounder.getInstance(properties); - } - Properties cprops = threadLocalProperties.get().clear(); - populateCurrencyRounderProperties(cprops, symbols, properties); - if (cprops.getRoundingIncrement() != null) { - return IncrementRounder.getInstance(cprops); - } else { - return MagnitudeRounder.getInstance(cprops); - } - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/MagnitudeMultiplier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/MagnitudeMultiplier.java deleted file mode 100644 index 6e19c7d3684..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/MagnitudeMultiplier.java +++ /dev/null @@ -1,59 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.Format.BeforeFormat; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.Properties; - -public class MagnitudeMultiplier extends Format.BeforeFormat { - private static final MagnitudeMultiplier DEFAULT = new MagnitudeMultiplier(0); - - public static interface IProperties { - - static int DEFAULT_MAGNITUDE_MULTIPLIER = 0; - - /** @see #setMagnitudeMultiplier */ - public int getMagnitudeMultiplier(); - - /** - * Multiply all numbers by this power of ten before formatting. Negative multipliers reduce the - * magnitude and make numbers smaller (closer to zero). - * - * @param magnitudeMultiplier The number of powers of ten to scale. - * @return The property bag, for chaining. - * @see BigDecimalMultiplier - */ - public IProperties setMagnitudeMultiplier(int magnitudeMultiplier); - } - - public static boolean useMagnitudeMultiplier(IProperties properties) { - return properties.getMagnitudeMultiplier() != IProperties.DEFAULT_MAGNITUDE_MULTIPLIER; - } - - // Properties - final int delta; - - public static BeforeFormat getInstance(Properties properties) { - if (properties.getMagnitudeMultiplier() == 0) { - return DEFAULT; - } - return new MagnitudeMultiplier(properties.getMagnitudeMultiplier()); - } - - private MagnitudeMultiplier(int delta) { - this.delta = delta; - } - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - input.adjustMagnitude(delta); - } - - @Override - public void export(Properties properties) { - properties.setMagnitudeMultiplier(delta); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/MeasureFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/MeasureFormat.java deleted file mode 100644 index 752dc0af988..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/MeasureFormat.java +++ /dev/null @@ -1,73 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.StandardPlural; -import com.ibm.icu.impl.number.modifiers.GeneralPluralModifier; -import com.ibm.icu.impl.number.modifiers.SimpleModifier; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.MeasureFormat.FormatWidth; -import com.ibm.icu.util.MeasureUnit; -import com.ibm.icu.util.ULocale; - -public class MeasureFormat { - - public static interface IProperties { - - static MeasureUnit DEFAULT_MEASURE_UNIT = null; - - /** @see #setMeasureUnit */ - public MeasureUnit getMeasureUnit(); - - /** - * Apply prefixes and suffixes for the specified {@link MeasureUnit} to the formatted number. - * - * @param measureUnit The measure unit. - * @return The property bag, for chaining. - */ - public IProperties setMeasureUnit(MeasureUnit measureUnit); - - static FormatWidth DEFAULT_MEASURE_FORMAT_WIDTH = null; - - /** @see #setMeasureFormatWidth */ - public FormatWidth getMeasureFormatWidth(); - - /** - * Use the specified {@link FormatWidth} when choosing the style of measure unit prefix/suffix. - * - *

Must be used in conjunction with {@link #setMeasureUnit}. - * - * @param measureFormatWidth The width style. Defaults to FormatWidth.WIDE. - * @return The property bag, for chaining. - */ - public IProperties setMeasureFormatWidth(FormatWidth measureFormatWidth); - } - - public static boolean useMeasureFormat(IProperties properties) { - return properties.getMeasureUnit() != IProperties.DEFAULT_MEASURE_UNIT; - } - - public static GeneralPluralModifier getInstance(DecimalFormatSymbols symbols, IProperties properties) { - ULocale uloc = symbols.getULocale(); - MeasureUnit unit = properties.getMeasureUnit(); - FormatWidth width = properties.getMeasureFormatWidth(); - - if (unit == null) { - throw new IllegalArgumentException("A measure unit is required for MeasureFormat"); - } - if (width == null) { - width = FormatWidth.WIDE; - } - - // Temporarily, create a MeasureFormat instance for its data loading capability - // TODO: Move data loading directly into this class file - com.ibm.icu.text.MeasureFormat mf = com.ibm.icu.text.MeasureFormat.getInstance(uloc, width); - GeneralPluralModifier mod = new GeneralPluralModifier(); - for (StandardPlural plural : StandardPlural.VALUES) { - String formatString = null; - mf.getPluralFormatter(unit, width, plural.ordinal()); - mod.put(plural, new SimpleModifier(formatString, null, false)); - } - return mod; - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PaddingFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PaddingFormat.java deleted file mode 100644 index 77eb6dc1c62..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PaddingFormat.java +++ /dev/null @@ -1,173 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.number.Format.AfterFormat; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; - -public class PaddingFormat implements AfterFormat { - public enum PadPosition { - BEFORE_PREFIX, - AFTER_PREFIX, - BEFORE_SUFFIX, - AFTER_SUFFIX; - - public static PadPosition fromOld(int old) { - switch (old) { - case com.ibm.icu.text.DecimalFormat.PAD_BEFORE_PREFIX: - return PadPosition.BEFORE_PREFIX; - case com.ibm.icu.text.DecimalFormat.PAD_AFTER_PREFIX: - return PadPosition.AFTER_PREFIX; - case com.ibm.icu.text.DecimalFormat.PAD_BEFORE_SUFFIX: - return PadPosition.BEFORE_SUFFIX; - case com.ibm.icu.text.DecimalFormat.PAD_AFTER_SUFFIX: - return PadPosition.AFTER_SUFFIX; - default: - throw new IllegalArgumentException("Don't know how to map " + old); - } - } - - public int toOld() { - switch (this) { - case BEFORE_PREFIX: - return com.ibm.icu.text.DecimalFormat.PAD_BEFORE_PREFIX; - case AFTER_PREFIX: - return com.ibm.icu.text.DecimalFormat.PAD_AFTER_PREFIX; - case BEFORE_SUFFIX: - return com.ibm.icu.text.DecimalFormat.PAD_BEFORE_SUFFIX; - case AFTER_SUFFIX: - return com.ibm.icu.text.DecimalFormat.PAD_AFTER_SUFFIX; - default: - return -1; // silence compiler errors - } - } - } - - public static interface IProperties { - - static int DEFAULT_FORMAT_WIDTH = 0; - - /** @see #setFormatWidth */ - public int getFormatWidth(); - - /** - * Sets the minimum width of the string output by the formatting pipeline. For example, if - * padding is enabled and paddingWidth is set to 6, formatting the number "3.14159" with the - * pattern "0.00" will result in "··3.14" if '·' is your padding string. - * - *

If the number is longer than your padding width, the number will display as if no padding - * width had been specified, which may result in strings longer than the padding width. - * - *

Width is counted in UTF-16 code units. - * - * @param formatWidth The output width. - * @return The property bag, for chaining. - * @see #setPadPosition - * @see #setPadString - */ - public IProperties setFormatWidth(int formatWidth); - - static String DEFAULT_PAD_STRING = null; - - /** @see #setPadString */ - public String getPadString(); - - /** - * Sets the string used for padding. The string should contain a single character or grapheme - * cluster. - * - *

Must be used in conjunction with {@link #setFormatWidth}. - * - * @param paddingString The padding string. Defaults to an ASCII space (U+0020). - * @return The property bag, for chaining. - * @see #setFormatWidth - */ - public IProperties setPadString(String paddingString); - - static PadPosition DEFAULT_PAD_POSITION = null; - - /** @see #setPadPosition */ - public PadPosition getPadPosition(); - - /** - * Sets the location where the padding string is to be inserted to maintain the padding width: - * one of BEFORE_PREFIX, AFTER_PREFIX, BEFORE_SUFFIX, or AFTER_SUFFIX. - * - *

Must be used in conjunction with {@link #setFormatWidth}. - * - * @param padPosition The output width. - * @return The property bag, for chaining. - * @see #setFormatWidth - */ - public IProperties setPadPosition(PadPosition padPosition); - } - - public static final String FALLBACK_PADDING_STRING = "\u0020"; // i.e. a space - - public static boolean usePadding(IProperties properties) { - return properties.getFormatWidth() != IProperties.DEFAULT_FORMAT_WIDTH; - } - - public static AfterFormat getInstance(IProperties properties) { - return new PaddingFormat( - properties.getFormatWidth(), properties.getPadString(), properties.getPadPosition()); - } - - // Properties - private final int paddingWidth; - private final String paddingString; - private final PadPosition paddingLocation; - - private PaddingFormat(int paddingWidth, String paddingString, PadPosition paddingLocation) { - this.paddingWidth = paddingWidth > 0 ? paddingWidth : 10; // TODO: Is this a sensible default? - this.paddingString = paddingString != null ? paddingString : FALLBACK_PADDING_STRING; - this.paddingLocation = paddingLocation != null ? paddingLocation : PadPosition.BEFORE_PREFIX; - } - - @Override - public int after(ModifierHolder mods, NumberStringBuilder string, int leftIndex, int rightIndex) { - - // TODO: Count code points instead of code units? - // TODO: Make this more efficient (less copying) - NumberStringBuilder copy1 = new NumberStringBuilder(string); - ModifierHolder copy2 = mods.createCopy(); - copy2.applyAll(copy1, leftIndex, rightIndex); - int requiredPadding = paddingWidth - copy1.length(); - - if (requiredPadding <= 0) { - // Skip padding, but still apply modifiers to be consistent - return mods.applyAll(string, leftIndex, rightIndex); - } - - int length = 0; - if (paddingLocation == PadPosition.AFTER_PREFIX) { - length += addPadding(requiredPadding, string, leftIndex); - } else if (paddingLocation == PadPosition.BEFORE_SUFFIX) { - length += addPadding(requiredPadding, string, rightIndex); - } - length += mods.applyAll(string, leftIndex, rightIndex + length); - if (paddingLocation == PadPosition.BEFORE_PREFIX) { - length += addPadding(requiredPadding, string, leftIndex); - } else if (paddingLocation == PadPosition.AFTER_SUFFIX) { - length += addPadding(requiredPadding, string, rightIndex + length); - } - - return length; - } - - private int addPadding(int requiredPadding, NumberStringBuilder string, int index) { - for (int i = 0; i < requiredPadding; i++) { - string.insert(index, paddingString, null); - } - return paddingString.length() * requiredPadding; - } - - @Override - public void export(Properties properties) { - properties.setFormatWidth(paddingWidth); - properties.setPadString(paddingString); - properties.setPadPosition(paddingLocation); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PositiveDecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PositiveDecimalFormat.java deleted file mode 100644 index fdb26062f4c..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PositiveDecimalFormat.java +++ /dev/null @@ -1,222 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.NumberFormat; -import com.ibm.icu.text.NumberFormat.Field; - -public class PositiveDecimalFormat implements Format.TargetFormat { - - public static interface IProperties extends CurrencyFormat.IProperties { - - static int DEFAULT_GROUPING_SIZE = -1; - - /** @see #setGroupingSize */ - public int getGroupingSize(); - - /** - * Sets the number of digits between grouping separators. For example, the en-US locale - * uses a grouping size of 3, so the number 1234567 would be formatted as "1,234,567". For - * locales whose grouping sizes vary with magnitude, see {@link #setSecondaryGroupingSize(int)}. - * - * @param groupingSize The primary grouping size. - * @return The property bag, for chaining. - */ - public IProperties setGroupingSize(int groupingSize); - - static int DEFAULT_SECONDARY_GROUPING_SIZE = -1; - - /** @see #setSecondaryGroupingSize */ - public int getSecondaryGroupingSize(); - - /** - * Sets the number of digits between grouping separators higher than the least-significant - * grouping separator. For example, the locale hi uses a primary grouping size of 3 and - * a secondary grouping size of 2, so the number 1234567 would be formatted as "12,34,567". - * - *

The two levels of grouping separators can be specified in the pattern string. For example, - * the hi locale's default decimal format pattern is "#,##,##0.###". - * - * @param secondaryGroupingSize The secondary grouping size. - * @return The property bag, for chaining. - */ - public IProperties setSecondaryGroupingSize(int secondaryGroupingSize); - - static boolean DEFAULT_DECIMAL_SEPARATOR_ALWAYS_SHOWN = false; - - /** @see #setDecimalSeparatorAlwaysShown */ - public boolean getDecimalSeparatorAlwaysShown(); - - /** - * Sets whether to always show the decimal point, even if the number doesn't require one. For - * example, if always show decimal is true, the number 123 would be formatted as "123." in - * locale en-US. - * - * @param decimalSeparatorAlwaysShown Whether to show the decimal point when it is optional. - * @return The property bag, for chaining. - */ - public IProperties setDecimalSeparatorAlwaysShown(boolean decimalSeparatorAlwaysShown); - - static int DEFAULT_MINIMUM_GROUPING_DIGITS = 1; - - /** @see #setMinimumGroupingDigits */ - public int getMinimumGroupingDigits(); - - /** - * Sets the minimum number of digits required to be beyond the first grouping separator in order - * to enable grouping. For example, if the minimum grouping digits is 2, then 1234 would be - * formatted as "1234" but 12345 would be formatted as "12,345" in en-US. Note that - * 1234567 would still be formatted as "1,234,567", not "1234,567". - * - * @param minimumGroupingDigits How many digits must appear before a grouping separator before - * enabling grouping. - * @return The property bag, for chaining. - */ - public IProperties setMinimumGroupingDigits(int minimumGroupingDigits); - } - - public static boolean useGrouping(IProperties properties) { - return properties.getGroupingSize() != IProperties.DEFAULT_GROUPING_SIZE - || properties.getSecondaryGroupingSize() != IProperties.DEFAULT_SECONDARY_GROUPING_SIZE; - } - - public static boolean allowsDecimalPoint(IProperties properties) { - return properties.getDecimalSeparatorAlwaysShown() - || properties.getMaximumFractionDigits() != 0; - } - - // Properties - private final boolean alwaysShowDecimal; - private final int primaryGroupingSize; - private final int secondaryGroupingSize; - private final int minimumGroupingDigits; - - // Symbols - private final String infinityString; - private final String nanString; - private final String groupingSeparator; - private final String decimalSeparator; - private final String[] digitStrings; - private final int codePointZero; - - public PositiveDecimalFormat(DecimalFormatSymbols symbols, IProperties properties) { - int _primary = properties.getGroupingSize(); - int _secondary = properties.getSecondaryGroupingSize(); - primaryGroupingSize = _primary > 0 ? _primary : _secondary > 0 ? _secondary : 0; - secondaryGroupingSize = _secondary > 0 ? _secondary : primaryGroupingSize; - - minimumGroupingDigits = properties.getMinimumGroupingDigits(); - alwaysShowDecimal = properties.getDecimalSeparatorAlwaysShown(); - infinityString = symbols.getInfinity(); - nanString = symbols.getNaN(); - - if (CurrencyFormat.useCurrency(properties)) { - groupingSeparator = symbols.getMonetaryGroupingSeparatorString(); - decimalSeparator = symbols.getMonetaryDecimalSeparatorString(); - } else { - groupingSeparator = symbols.getGroupingSeparatorString(); - decimalSeparator = symbols.getDecimalSeparatorString(); - } - - // Check to see if we can use code points instead of strings - int _codePointZero = symbols.getCodePointZero(); - if (_codePointZero != -1) { - // Fast Path (~9% faster than slow path when formatting long strings) - digitStrings = null; - codePointZero = _codePointZero; - } else { - // Slow Path - digitStrings = symbols.getDigitStrings(); // makes a copy - codePointZero = -1; - } - } - - @Override - public int target(FormatQuantity input, NumberStringBuilder string, int startIndex) { - int length = 0; - - if (input.isInfinite()) { - length += string.insert(startIndex, infinityString, NumberFormat.Field.INTEGER); - - } else if (input.isNaN()) { - length += string.insert(startIndex, nanString, NumberFormat.Field.INTEGER); - - } else { - // Add the integer digits - length += addIntegerDigits(input, string, startIndex); - - // Add the decimal point - if (input.getLowerDisplayMagnitude() < 0 || alwaysShowDecimal) { - length += - string.insert( - startIndex + length, decimalSeparator, NumberFormat.Field.DECIMAL_SEPARATOR); - } - - // Add the fraction digits - length += addFractionDigits(input, string, startIndex + length); - } - - return length; - } - - private int addIntegerDigits(FormatQuantity input, NumberStringBuilder string, int startIndex) { - int length = 0; - int integerCount = input.getUpperDisplayMagnitude() + 1; - for (int i = 0; i < integerCount; i++) { - // Add grouping separator - if (primaryGroupingSize > 0 - && i == primaryGroupingSize - && integerCount - i >= minimumGroupingDigits) { - length += - string.insert(startIndex, groupingSeparator, NumberFormat.Field.GROUPING_SEPARATOR); - } else if (secondaryGroupingSize > 0 - && i > primaryGroupingSize - && (i - primaryGroupingSize) % secondaryGroupingSize == 0) { - length += - string.insert(startIndex, groupingSeparator, NumberFormat.Field.GROUPING_SEPARATOR); - } - - // Get and append the next digit value - byte nextDigit = input.getDigit(i); - length += addDigit(nextDigit, string, startIndex, NumberFormat.Field.INTEGER); - } - - return length; - } - - private int addFractionDigits(FormatQuantity input, NumberStringBuilder string, int index) { - int length = 0; - int fractionCount = -input.getLowerDisplayMagnitude(); - for (int i = 0; i < fractionCount; i++) { - // Get and append the next digit value - byte nextDigit = input.getDigit(-i - 1); - length += addDigit(nextDigit, string, index + length, NumberFormat.Field.FRACTION); - } - return length; - } - - private int addDigit(byte digit, NumberStringBuilder outputString, int index, Field field) { - if (codePointZero != -1) { - return outputString.insertCodePoint(index, codePointZero + digit, field); - } else { - return outputString.insert(index, digitStrings[digit], field); - } - } - - @Override - public void export(Properties properties) { - // For backwards compatibility, export 0 as secondary grouping if primary and secondary are the same - int effectiveSecondaryGroupingSize = - secondaryGroupingSize == primaryGroupingSize ? 0 : secondaryGroupingSize; - - properties.setDecimalSeparatorAlwaysShown(alwaysShowDecimal); - properties.setGroupingSize(primaryGroupingSize); - properties.setSecondaryGroupingSize(effectiveSecondaryGroupingSize); - properties.setMinimumGroupingDigits(minimumGroupingDigits); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PositiveNegativeAffixFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PositiveNegativeAffixFormat.java deleted file mode 100644 index 6036eb0d002..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/PositiveNegativeAffixFormat.java +++ /dev/null @@ -1,256 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.PNAffixGenerator; -import com.ibm.icu.impl.number.modifiers.PositiveNegativeAffixModifier; -import com.ibm.icu.text.DecimalFormatSymbols; - -/** - * The implementation of this class is a thin wrapper around {@link PNAffixGenerator}, a utility - * used by this and other classes, including {@link CompactDecimalFormat} and {@link Parse}, to - * efficiently convert from the abstract properties in the property bag to actual prefix and suffix - * strings. - */ - -/** - * This class is responsible for adding the positive/negative prefixes and suffixes from the decimal - * format pattern. Properties are set using the following methods: - * - *

    - *
  • {@link IProperties#setPositivePrefix(String)} - *
  • {@link IProperties#setPositiveSuffix(String)} - *
  • {@link IProperties#setNegativePrefix(String)} - *
  • {@link IProperties#setNegativeSuffix(String)} - *
  • {@link IProperties#setPositivePrefixPattern(String)} - *
  • {@link IProperties#setPositiveSuffixPattern(String)} - *
  • {@link IProperties#setNegativePrefixPattern(String)} - *
  • {@link IProperties#setNegativeSuffixPattern(String)} - *
- * - * If one of the first four methods is used (those of the form setXxxYyy), the value - * will be interpreted literally. If one of the second four methods is used (those of the form - * setXxxYyyPattern), locale-specific symbols for the plus sign, minus sign, percent - * sign, permille sign, and currency sign will be substituted into the string, according to Unicode - * Technical Standard #35 (LDML) section 3.2. - * - *

Literal characters can be used in the setXxxYyyPattern methods by using quotes; - * for example, to display a literal "%" sign, you can set the pattern '%'. To display - * a literal quote, use two quotes in a row, like ''. - * - *

If a value is set in both a setXxxYyy method and in the corresponding - * setXxxYyyPattern method, the one set in setXxxYyy takes precedence. - * - *

For more information on formatting currencies, see {@link CurrencyFormat}. - * - *

The parameter is taken by reference by these methods into the property bag, meaning that if a - * mutable object like StringBuilder is passed, changes to the StringBuilder will be reflected in - * the property bag. However, upon creation of a finalized formatter object, all prefixes and - * suffixes will be converted to strings and will stop reflecting changes in the property bag. - */ -public class PositiveNegativeAffixFormat { - - public static interface IProperties { - - static String DEFAULT_POSITIVE_PREFIX = null; - - /** @see #setPositivePrefix */ - public String getPositivePrefix(); - - /** - * Sets the prefix to prepend to positive numbers. The prefix will be interpreted literally. For - * example, if you set a positive prefix of p, then the number 123 will be - * formatted as "p123" in the locale en-US. - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param positivePrefix The CharSequence to prepend to positive numbers. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setPositivePrefixPattern - */ - public IProperties setPositivePrefix(String positivePrefix); - - static String DEFAULT_POSITIVE_SUFFIX = null; - - /** @see #setPositiveSuffix */ - public String getPositiveSuffix(); - - /** - * Sets the suffix to append to positive numbers. The suffix will be interpreted literally. For - * example, if you set a positive suffix of p, then the number 123 will be - * formatted as "123p" in the locale en-US. - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param positiveSuffix The CharSequence to append to positive numbers. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setPositiveSuffixPattern - */ - public IProperties setPositiveSuffix(String positiveSuffix); - - static String DEFAULT_NEGATIVE_PREFIX = null; - - /** @see #setNegativePrefix */ - public String getNegativePrefix(); - - /** - * Sets the prefix to prepend to negative numbers. The prefix will be interpreted literally. For - * example, if you set a negative prefix of n, then the number -123 will be - * formatted as "n123" in the locale en-US. Note that if the negative prefix is left unset, - * the locale's minus sign is used. - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param negativePrefix The CharSequence to prepend to negative numbers. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setNegativePrefixPattern - */ - public IProperties setNegativePrefix(String negativePrefix); - - static String DEFAULT_NEGATIVE_SUFFIX = null; - - /** @see #setNegativeSuffix */ - public String getNegativeSuffix(); - - /** - * Sets the suffix to append to negative numbers. The suffix will be interpreted literally. For - * example, if you set a suffix prefix of n, then the number -123 will be formatted - * as "-123n" in the locale en-US. Note that the minus sign is prepended by default unless - * otherwise specified in either the pattern string or in one of the {@link #setNegativePrefix} - * methods. - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param negativeSuffix The CharSequence to append to negative numbers. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setNegativeSuffixPattern - */ - public IProperties setNegativeSuffix(String negativeSuffix); - - static String DEFAULT_POSITIVE_PREFIX_PATTERN = null; - - /** @see #setPositivePrefixPattern */ - public String getPositivePrefixPattern(); - - /** - * Sets the prefix to prepend to positive numbers. Locale-specific symbols will be substituted - * into the string according to Unicode Technical Standard #35 (LDML). - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param positivePrefixPattern The CharSequence to prepend to positive numbers after locale - * symbol substitutions take place. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setPositivePrefix - */ - public IProperties setPositivePrefixPattern(String positivePrefixPattern); - - static String DEFAULT_POSITIVE_SUFFIX_PATTERN = null; - - /** @see #setPositiveSuffixPattern */ - public String getPositiveSuffixPattern(); - - /** - * Sets the suffix to append to positive numbers. Locale-specific symbols will be substituted - * into the string according to Unicode Technical Standard #35 (LDML). - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param positiveSuffixPattern The CharSequence to append to positive numbers after locale - * symbol substitutions take place. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setPositiveSuffix - */ - public IProperties setPositiveSuffixPattern(String positiveSuffixPattern); - - static String DEFAULT_NEGATIVE_PREFIX_PATTERN = null; - - /** @see #setNegativePrefixPattern */ - public String getNegativePrefixPattern(); - - /** - * Sets the prefix to prepend to negative numbers. Locale-specific symbols will be substituted - * into the string according to Unicode Technical Standard #35 (LDML). - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param negativePrefixPattern The CharSequence to prepend to negative numbers after locale - * symbol substitutions take place. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setNegativePrefix - */ - public IProperties setNegativePrefixPattern(String negativePrefixPattern); - - static String DEFAULT_NEGATIVE_SUFFIX_PATTERN = null; - - /** @see #setNegativeSuffixPattern */ - public String getNegativeSuffixPattern(); - - /** - * Sets the suffix to append to negative numbers. Locale-specific symbols will be substituted - * into the string according to Unicode Technical Standard #35 (LDML). - * - *

For more information on prefixes and suffixes, see {@link PositiveNegativeAffixFormat}. - * - * @param negativeSuffixPattern The CharSequence to append to negative numbers after locale - * symbol substitutions take place. - * @return The property bag, for chaining. - * @see PositiveNegativeAffixFormat - * @see #setNegativeSuffix - */ - public IProperties setNegativeSuffixPattern(String negativeSuffixPattern); - - static boolean DEFAULT_SIGN_ALWAYS_SHOWN = false; - - /** @see #setSignAlwaysShown */ - public boolean getSignAlwaysShown(); - - /** - * Sets whether to always display of a plus sign on positive numbers. - * - *

If the location of the negative sign is specified by the decimal format pattern (or by the - * negative prefix/suffix pattern methods), a plus sign is substituted into that location, in - * accordance with Unicode Technical Standard #35 (LDML) section 3.2.1. Otherwise, the plus sign - * is prepended to the number. For example, if the decimal format pattern #;#- is - * used, then formatting 123 would result in "123+" in the locale en-US. - * - *

This method should be used instead of setting the positive prefix/suffix. The - * behavior is undefined if alwaysShowPlusSign is set but the positive prefix/suffix already - * contains a plus sign. - * - * @param plusSignAlwaysShown Whether positive numbers should display a plus sign. - * @return The property bag, for chaining. - */ - public IProperties setSignAlwaysShown(boolean plusSignAlwaysShown); - } - - public static PositiveNegativeAffixModifier getInstance(DecimalFormatSymbols symbols, IProperties properties) { - PNAffixGenerator pnag = PNAffixGenerator.getThreadLocalInstance(); - PNAffixGenerator.Result result = pnag.getModifiers(symbols, properties); - return new PositiveNegativeAffixModifier(result.positive, result.negative); - } - - // TODO: Investigate static interface methods (Java 8 only?) - public static void apply( - FormatQuantity input, - ModifierHolder mods, - DecimalFormatSymbols symbols, - IProperties properties) { - PNAffixGenerator pnag = PNAffixGenerator.getThreadLocalInstance(); - PNAffixGenerator.Result result = pnag.getModifiers(symbols, properties); - if (input.isNegative()) { - mods.add(result.negative); - } else { - mods.add(result.positive); - } - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/RangeFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/RangeFormat.java deleted file mode 100644 index 7c72e626372..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/RangeFormat.java +++ /dev/null @@ -1,58 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -// THIS CLASS IS A PROOF OF CONCEPT ONLY. -// IT REQUIRES ADDITIONAL DISCUSION ABOUT ITS DESIGN AND IMPLEMENTATION. - -package com.ibm.icu.impl.number.formatters; - -import java.util.Deque; - -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.NumberStringBuilder; - -public class RangeFormat extends Format { - // Primary settings - private final String separator; - - // Child formatters - private final Format left; - private final Format right; - - public RangeFormat(Format left, Format right, String separator) { - this.separator = separator; // TODO: This would be loaded from locale data. - this.left = left; - this.right = right; - - if (left == null || right == null) { - throw new IllegalArgumentException("Both child formatters are required for RangeFormat"); - } - } - - @Override - public int process( - Deque inputs, - ModifierHolder mods, - NumberStringBuilder string, - int startIndex) { - ModifierHolder lMods = new ModifierHolder(); - ModifierHolder rMods = new ModifierHolder(); - int lLen = left.process(inputs, lMods, string, startIndex); - int rLen = right.process(inputs, rMods, string, startIndex + lLen); - - // Bubble up any modifiers that are shared between the two sides - while (lMods.peekLast() != null && lMods.peekLast() == rMods.peekLast()) { - mods.add(lMods.removeLast()); - rMods.removeLast(); - } - - // Apply the remaining modifiers - lLen += lMods.applyAll(string, startIndex, startIndex + lLen); - rLen += rMods.applyAll(string, startIndex + lLen, startIndex + lLen + rLen); - - int sLen = string.insert(startIndex + lLen, separator, null); - - return lLen + sLen + rLen; - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/RoundingFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/RoundingFormat.java deleted file mode 100644 index a57caf4c06e..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/RoundingFormat.java +++ /dev/null @@ -1,41 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.number.Rounder; -import com.ibm.icu.impl.number.Rounder.IBasicRoundingProperties; -import com.ibm.icu.impl.number.rounders.IncrementRounder; -import com.ibm.icu.impl.number.rounders.MagnitudeRounder; -import com.ibm.icu.impl.number.rounders.NoRounder; -import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder; - -// TODO: Figure out a better place to put these methods. - -public class RoundingFormat { - - public static interface IProperties - extends IBasicRoundingProperties, - IncrementRounder.IProperties, - MagnitudeRounder.IProperties, - SignificantDigitsRounder.IProperties {} - - public static Rounder getDefaultOrNoRounder(IProperties properties) { - Rounder candidate = getDefaultOrNull(properties); - if (candidate == null) { - candidate = NoRounder.getInstance(properties); - } - return candidate; - } - - public static Rounder getDefaultOrNull(IProperties properties) { - if (SignificantDigitsRounder.useSignificantDigits(properties)) { - return SignificantDigitsRounder.getInstance(properties); - } else if (IncrementRounder.useRoundingIncrement(properties)) { - return IncrementRounder.getInstance(properties); - } else if (MagnitudeRounder.useFractionFormat(properties)) { - return MagnitudeRounder.getInstance(properties); - } else { - return null; - } - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java deleted file mode 100644 index 9ef9fdb3f64..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/ScientificFormat.java +++ /dev/null @@ -1,240 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.FormatQuantitySelector; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.Rounder; -import com.ibm.icu.impl.number.modifiers.ConstantAffixModifier; -import com.ibm.icu.impl.number.modifiers.PositiveNegativeAffixModifier; -import com.ibm.icu.impl.number.rounders.IncrementRounder; -import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder; -import com.ibm.icu.text.DecimalFormatSymbols; -import com.ibm.icu.text.NumberFormat; - -public class ScientificFormat extends Format.BeforeFormat implements Rounder.MultiplierGenerator { - - public static interface IProperties - extends RoundingFormat.IProperties, CurrencyFormat.IProperties { - - static boolean DEFAULT_EXPONENT_SIGN_ALWAYS_SHOWN = false; - - /** @see #setExponentSignAlwaysShown */ - public boolean getExponentSignAlwaysShown(); - - /** - * Sets whether to show the plus sign in the exponent part of numbers with a zero or positive - * exponent. For example, the number "1200" with the pattern "0.0E0" would be formatted as - * "1.2E+3" instead of "1.2E3" in en-US. - * - * @param exponentSignAlwaysShown Whether to show the plus sign in positive exponents. - * @return The property bag, for chaining. - */ - public IProperties setExponentSignAlwaysShown(boolean exponentSignAlwaysShown); - - static int DEFAULT_MINIMUM_EXPONENT_DIGITS = -1; - - /** @see #setMinimumExponentDigits */ - public int getMinimumExponentDigits(); - - /** - * Sets the minimum number of digits to display in the exponent. For example, the number "1200" - * with the pattern "0.0E00", which has 2 exponent digits, would be formatted as "1.2E03" in - * en-US. - * - * @param minimumExponentDigits The minimum number of digits to display in the exponent field. - * @return The property bag, for chaining. - */ - public IProperties setMinimumExponentDigits(int minimumExponentDigits); - } - - public static boolean useScientificNotation(IProperties properties) { - return properties.getMinimumExponentDigits() != IProperties.DEFAULT_MINIMUM_EXPONENT_DIGITS; - } - - private static final ThreadLocal threadLocalProperties = - new ThreadLocal() { - @Override - protected Properties initialValue() { - return new Properties(); - } - }; - - public static ScientificFormat getInstance(DecimalFormatSymbols symbols, IProperties properties) { - // If significant digits or rounding interval are specified through normal means, we use those. - // Otherwise, we use the special significant digit rules for scientific notation. - Rounder rounder; - if (IncrementRounder.useRoundingIncrement(properties)) { - rounder = IncrementRounder.getInstance(properties); - } else if (SignificantDigitsRounder.useSignificantDigits(properties)) { - rounder = SignificantDigitsRounder.getInstance(properties); - } else { - Properties rprops = threadLocalProperties.get().clear(); - - int minInt = properties.getMinimumIntegerDigits(); - int maxInt = properties.getMaximumIntegerDigits(); - int minFrac = properties.getMinimumFractionDigits(); - int maxFrac = properties.getMaximumFractionDigits(); - - // If currency is in use, pull information from CurrencyUsage. - if (CurrencyFormat.useCurrency(properties)) { - // Use rprops as the vehicle (it is still clean) - CurrencyFormat.populateCurrencyRounderProperties(rprops, symbols, properties); - minFrac = rprops.getMinimumFractionDigits(); - maxFrac = rprops.getMaximumFractionDigits(); - rprops.clear(); - } - - // TODO: Mark/Andy, take a look at this logic and see if it makes sense to you. - // I fiddled with the settings and fallbacks to make the unit tests pass, but I - // don't feel that it's the "right way" to do things. - - if (minInt < 0) minInt = 0; - if (maxInt < minInt) maxInt = minInt; - if (minFrac < 0) minFrac = 0; - if (maxFrac < minFrac) maxFrac = minFrac; - - rprops.setRoundingMode(properties.getRoundingMode()); - - if (minInt == 0 && maxFrac == 0) { - // Special case for the pattern "#E0" with no significant digits specified. - rprops.setMinimumSignificantDigits(1); - rprops.setMaximumSignificantDigits(Integer.MAX_VALUE); - } else if (minInt == 0 && minFrac == 0) { - // Special case for patterns like "#.##E0" with no significant digits specified. - rprops.setMinimumSignificantDigits(1); - rprops.setMaximumSignificantDigits(1 + maxFrac); - } else { - rprops.setMinimumSignificantDigits(minInt + minFrac); - rprops.setMaximumSignificantDigits(minInt + maxFrac); - } - rprops.setMinimumIntegerDigits(maxInt == 0 ? 0 : Math.max(1, minInt + minFrac - maxFrac)); - rprops.setMaximumIntegerDigits(maxInt); - rprops.setMinimumFractionDigits(Math.max(0, minFrac + minInt - maxInt)); - rprops.setMaximumFractionDigits(maxFrac); - rounder = SignificantDigitsRounder.getInstance(rprops); - } - - return new ScientificFormat(symbols, properties, rounder); - } - - public static ScientificFormat getInstance( - DecimalFormatSymbols symbols, IProperties properties, Rounder rounder) { - return new ScientificFormat(symbols, properties, rounder); - } - - // Properties - private final boolean exponentShowPlusSign; - private final int exponentDigits; - private final int minInt; - private final int maxInt; - private final int interval; - private final Rounder rounder; - private final ConstantAffixModifier separatorMod; - private final PositiveNegativeAffixModifier signMod; - - // Symbols - private final String[] digitStrings; - - private ScientificFormat(DecimalFormatSymbols symbols, IProperties properties, Rounder rounder) { - exponentShowPlusSign = properties.getExponentSignAlwaysShown(); - exponentDigits = Math.max(1, properties.getMinimumExponentDigits()); - - // Calculate minInt/maxInt for the purposes of engineering notation: - // 0 <= minInt <= maxInt < 8 - // The values are validated separately for rounding. This scheme needs to prevent OOM issues - // (see #13118). Note that the bound 8 on integer digits is historic. - int _maxInt = properties.getMaximumIntegerDigits(); - int _minInt = properties.getMinimumIntegerDigits(); - // Bug #13289: if maxInt > minInt > 1, then minInt should be 1 for the - // purposes of engineering notatation. - if (_maxInt > _minInt && _minInt > 1) { - _minInt = 1; - } - minInt = _minInt < 0 ? 0 : _minInt >= 8 ? 1 : _minInt; - maxInt = _maxInt < _minInt ? _minInt : _maxInt >= 8 ? _minInt : _maxInt; - assert 0 <= minInt && minInt <= maxInt && maxInt < 8; - - interval = maxInt < 1 ? 1 : maxInt; - this.rounder = rounder; - digitStrings = symbols.getDigitStrings(); // makes a copy - - separatorMod = - new ConstantAffixModifier( - "", symbols.getExponentSeparator(), NumberFormat.Field.EXPONENT_SYMBOL, true); - signMod = - new PositiveNegativeAffixModifier( - new ConstantAffixModifier( - "", - exponentShowPlusSign ? symbols.getPlusSignString() : "", - NumberFormat.Field.EXPONENT_SIGN, - true), - new ConstantAffixModifier( - "", symbols.getMinusSignString(), NumberFormat.Field.EXPONENT_SIGN, true)); - } - - private static final ThreadLocal threadLocalStringBuilder = - new ThreadLocal() { - @Override - protected StringBuilder initialValue() { - return new StringBuilder(); - } - }; - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - - // Treat zero as if it had magnitude 0 - int exponent; - if (input.isZero()) { - rounder.apply(input); - exponent = 0; - } else { - // TODO: Revisit chooseMultiplierAndApply - exponent = -rounder.chooseMultiplierAndApply(input, this); - } - - // Format the exponent part of the scientific format. - // Insert digits starting from the left so that append can be used. - // TODO: Use thread locals here. - FormatQuantity exponentQ = FormatQuantitySelector.from(exponent); - StringBuilder exponentSB = threadLocalStringBuilder.get(); - exponentSB.setLength(0); - exponentQ.setIntegerLength(exponentDigits, Integer.MAX_VALUE); - exponentQ.setFractionLength(0, 0); - for (int i = exponentQ.getUpperDisplayMagnitude(); i >= 0; i--) { - exponentSB.append(digitStrings[exponentQ.getDigit(i)]); - } - - // Add modifiers from the outside in. - mods.add( - new ConstantAffixModifier("", exponentSB.toString(), NumberFormat.Field.EXPONENT, true)); - mods.add(signMod.getModifier(exponent < 0)); - mods.add(separatorMod); - } - - @Override - public int getMultiplier(int magnitude) { - int digitsShown = ((magnitude % interval + interval) % interval) + 1; - if (digitsShown < minInt) { - digitsShown = minInt; - } else if (digitsShown > maxInt) { - digitsShown = maxInt; - } - int retval = digitsShown - magnitude - 1; - return retval; - } - - @Override - public void export(Properties properties) { - properties.setMinimumExponentDigits(exponentDigits); - properties.setExponentSignAlwaysShown(exponentShowPlusSign); - - // Set the transformed object into the property bag. This may result in a pattern string that - // uses different syntax from the original, but it will be functionally equivalent. - rounder.export(properties); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/StrongAffixFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/StrongAffixFormat.java deleted file mode 100644 index c70352d6e7a..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/formatters/StrongAffixFormat.java +++ /dev/null @@ -1,48 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.formatters; - -import java.util.Deque; - -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; - -// TODO: This class isn't currently being used anywhere. Consider removing it. - -/** Attaches all prefixes and suffixes at this point in the render tree without bubbling up. */ -public class StrongAffixFormat extends Format implements Format.AfterFormat { - private final Format child; - - public StrongAffixFormat(Format child) { - this.child = child; - - if (child == null) { - throw new IllegalArgumentException("A child formatter is required for StrongAffixFormat"); - } - } - - @Override - public int process( - Deque inputs, - ModifierHolder mods, - NumberStringBuilder string, - int startIndex) { - int length = child.process(inputs, mods, string, startIndex); - length += mods.applyAll(string, startIndex, startIndex + length); - return length; - } - - @Override - public int after( - ModifierHolder mods, NumberStringBuilder string, int leftIndex, int rightIndex) { - return mods.applyAll(string, leftIndex, rightIndex); - } - - @Override - public void export(Properties properties) { - // Nothing to do. - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java index ba46aa9d0cb..1b2d88d3245 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantAffixModifier.java @@ -5,7 +5,6 @@ package com.ibm.icu.impl.number.modifiers; import com.ibm.icu.impl.number.Modifier; import com.ibm.icu.impl.number.Modifier.AffixModifier; import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; import com.ibm.icu.text.NumberFormat.Field; /** The canonical implementation of {@link Modifier}, containing a prefix and suffix string. */ @@ -89,9 +88,4 @@ public class ConstantAffixModifier extends Modifier.BaseModifier implements Affi public String toString() { return String.format("", prefix, suffix); } - - @Override - public void export(Properties properties) { - throw new UnsupportedOperationException(); - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java index 499f6d7ee04..c6c4d880501 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/ConstantMultiFieldModifier.java @@ -5,7 +5,6 @@ package com.ibm.icu.impl.number.modifiers; import com.ibm.icu.impl.number.Modifier; import com.ibm.icu.impl.number.Modifier.AffixModifier; import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; import com.ibm.icu.text.NumberFormat.Field; /** @@ -79,9 +78,4 @@ public class ConstantMultiFieldModifier extends Modifier.BaseModifier implements public String toString() { return String.format("", prefix, suffix); } - - @Override - public void export(Properties properties) { - throw new UnsupportedOperationException(); - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/GeneralPluralModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/GeneralPluralModifier.java index 3dfeefa4059..15ba186916a 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/GeneralPluralModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/GeneralPluralModifier.java @@ -3,22 +3,18 @@ package com.ibm.icu.impl.number.modifiers; import com.ibm.icu.impl.StandardPlural; -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; import com.ibm.icu.impl.number.Modifier; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.text.PluralRules; // TODO: Is it okay that this class is not completely immutable? Right now it is internal-only. // Freezable or Builder could be used if necessary. +// TODO: This class is currently unused. Probably should be deleted. + /** * A basic implementation of {@link com.ibm.icu.impl.number.Modifier.PositiveNegativePluralModifier} * that is built on the fly using its put methods. */ -public class GeneralPluralModifier extends Format.BeforeFormat - implements Modifier.PositiveNegativePluralModifier { +public class GeneralPluralModifier implements Modifier.PositiveNegativePluralModifier { /** * A single array for modifiers. Even elements are positive; odd elements are negative. The * elements 2i and 2i+1 belong to the StandardPlural with ordinal i. @@ -55,22 +51,4 @@ public class GeneralPluralModifier extends Format.BeforeFormat } return mod; } - - @Override - public void before(FormatQuantity input, ModifierHolder mods, PluralRules rules) { - mods.add(getModifier(input.getStandardPlural(rules), input.isNegative())); - } - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - throw new UnsupportedOperationException(); - } - - @Override - public void export(Properties properties) { - // Since we can export only one affix pair, do the one for "other". - Modifier positive = getModifier(StandardPlural.OTHER, false); - Modifier negative = getModifier(StandardPlural.OTHER, true); - PositiveNegativeAffixModifier.exportPositiveNegative(properties, positive, negative); - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/PositiveNegativeAffixModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/PositiveNegativeAffixModifier.java index 1384b7bbdf0..6ccd243f8c6 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/PositiveNegativeAffixModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/PositiveNegativeAffixModifier.java @@ -2,16 +2,13 @@ // License & terms of use: http://www.unicode.org/copyright.html#License package com.ibm.icu.impl.number.modifiers; -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; import com.ibm.icu.impl.number.Modifier; import com.ibm.icu.impl.number.Modifier.AffixModifier; -import com.ibm.icu.impl.number.ModifierHolder; -import com.ibm.icu.impl.number.Properties; + +// TODO: This class is currently unused. Should probably be deleted. /** A class containing a positive form and a negative form of {@link ConstantAffixModifier}. */ -public class PositiveNegativeAffixModifier extends Format.BeforeFormat - implements Modifier.PositiveNegativeModifier { +public class PositiveNegativeAffixModifier implements Modifier.PositiveNegativeModifier { private final AffixModifier positive; private final AffixModifier negative; @@ -31,23 +28,4 @@ public class PositiveNegativeAffixModifier extends Format.BeforeFormat public Modifier getModifier(boolean isNegative) { return isNegative ? negative : positive; } - - @Override - public void before(FormatQuantity input, ModifierHolder mods) { - Modifier mod = getModifier(input.isNegative()); - mods.add(mod); - } - - @Override - public void export(Properties properties) { - exportPositiveNegative(properties, positive, negative); - } - - /** Internal method used to export a positive and negative modifier to a property bag. */ - static void exportPositiveNegative(Properties properties, Modifier positive, Modifier negative) { - properties.setPositivePrefix(positive.getPrefix().isEmpty() ? null : positive.getPrefix()); - properties.setPositiveSuffix(positive.getSuffix().isEmpty() ? null : positive.getSuffix()); - properties.setNegativePrefix(negative.getPrefix().isEmpty() ? null : negative.getPrefix()); - properties.setNegativeSuffix(negative.getSuffix().isEmpty() ? null : negative.getSuffix()); - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java index 98385a9dc56..c1ceb221d64 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/modifiers/SimpleModifier.java @@ -5,7 +5,6 @@ package com.ibm.icu.impl.number.modifiers; import com.ibm.icu.impl.SimpleFormatterImpl; import com.ibm.icu.impl.number.Modifier; import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; import com.ibm.icu.text.NumberFormat.Field; /** @@ -128,9 +127,4 @@ public class SimpleModifier extends Modifier.BaseModifier { } } } - - @Override - public void export(Properties properties) { - throw new UnsupportedOperationException(); - } } diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/IncrementRounder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/IncrementRounder.java deleted file mode 100644 index 01ba69ce75b..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/IncrementRounder.java +++ /dev/null @@ -1,67 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.rounders; - -import java.math.BigDecimal; - -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.Rounder; - -public class IncrementRounder extends Rounder { - - public static interface IProperties extends IBasicRoundingProperties { - - static BigDecimal DEFAULT_ROUNDING_INCREMENT = null; - - /** @see #setRoundingIncrement */ - public BigDecimal getRoundingIncrement(); - - /** - * Sets the increment to which to round numbers. For example, with a rounding interval of 0.05, - * the number 11.17 would be formatted as "11.15" in locale en-US with the default - * rounding mode. - * - *

You can use either a rounding increment or significant digits, but not both at the same - * time. - * - *

The rounding increment can be specified in a pattern string. For example, the pattern - * "#,##0.05" corresponds to a rounding interval of 0.05 with 1 minimum integer digit and a - * grouping size of 3. - * - * @param roundingIncrement The interval to which to round. - * @return The property bag, for chaining. - */ - public IProperties setRoundingIncrement(BigDecimal roundingIncrement); - } - - public static boolean useRoundingIncrement(IProperties properties) { - return properties.getRoundingIncrement() != IProperties.DEFAULT_ROUNDING_INCREMENT; - } - - private final BigDecimal roundingIncrement; - - public static IncrementRounder getInstance(IProperties properties) { - return new IncrementRounder(properties); - } - - private IncrementRounder(IProperties properties) { - super(properties); - if (properties.getRoundingIncrement().compareTo(BigDecimal.ZERO) <= 0) { - throw new IllegalArgumentException("Rounding interval must be greater than zero"); - } - roundingIncrement = properties.getRoundingIncrement(); - } - - @Override - public void apply(FormatQuantity input) { - input.roundToIncrement(roundingIncrement, mathContext); - applyDefaults(input); - } - - @Override - public void export(Properties properties) { - super.export(properties); - properties.setRoundingIncrement(roundingIncrement); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/MagnitudeRounder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/MagnitudeRounder.java deleted file mode 100644 index d53f966fa09..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/MagnitudeRounder.java +++ /dev/null @@ -1,30 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.rounders; - -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.Rounder; - -public class MagnitudeRounder extends Rounder { - - public static interface IProperties extends IBasicRoundingProperties {} - - public static boolean useFractionFormat(IProperties properties) { - return properties.getMinimumFractionDigits() != IProperties.DEFAULT_MINIMUM_FRACTION_DIGITS - || properties.getMaximumFractionDigits() != IProperties.DEFAULT_MAXIMUM_FRACTION_DIGITS; - } - - public static MagnitudeRounder getInstance(IBasicRoundingProperties properties) { - return new MagnitudeRounder(properties); - } - - private MagnitudeRounder(IBasicRoundingProperties properties) { - super(properties); - } - - @Override - public void apply(FormatQuantity input) { - input.roundToMagnitude(-maxFrac, mathContext); - applyDefaults(input); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/NoRounder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/NoRounder.java deleted file mode 100644 index 814e11e997f..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/NoRounder.java +++ /dev/null @@ -1,24 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.rounders; - -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.Rounder; - -/** Sets the integer and fraction length based on the properties, but does not perform rounding. */ -public final class NoRounder extends Rounder { - - public static NoRounder getInstance(IBasicRoundingProperties properties) { - return new NoRounder(properties); - } - - private NoRounder(IBasicRoundingProperties properties) { - super(properties); - } - - @Override - public void apply(FormatQuantity input) { - applyDefaults(input); - input.roundToInfinity(); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/SignificantDigitsRounder.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/SignificantDigitsRounder.java deleted file mode 100644 index 23977c384b7..00000000000 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/rounders/SignificantDigitsRounder.java +++ /dev/null @@ -1,131 +0,0 @@ -// © 2017 and later: Unicode, Inc. and others. -// License & terms of use: http://www.unicode.org/copyright.html#License -package com.ibm.icu.impl.number.rounders; - -import java.math.RoundingMode; - -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.Rounder; - -public class SignificantDigitsRounder extends Rounder { - - public static interface IProperties extends Rounder.IBasicRoundingProperties { - - static int DEFAULT_MINIMUM_SIGNIFICANT_DIGITS = -1; - - /** @see #setMinimumSignificantDigits */ - public int getMinimumSignificantDigits(); - - /** - * Sets the minimum number of significant digits to display. If, after rounding to the number of - * significant digits specified by {@link #setMaximumSignificantDigits}, the number of remaining - * significant digits is less than the minimum, the number will be padded with zeros. For - * example, if minimum significant digits is 3, the number 5.8 will be formatted as "5.80" in - * locale en-US. Note that minimum significant digits is relevant only when numbers - * have digits after the decimal point. - * - *

If both minimum significant digits and minimum integer/fraction digits are set at the same - * time, both values will be respected, and the one that results in the greater number of - * padding zeros will be used. For example, formatting the number 73 with 3 minimum significant - * digits and 2 minimum fraction digits will produce "73.00". - * - *

The number of significant digits can be specified in a pattern string using the '@' - * character. For example, the pattern "@@#" corresponds to a minimum of 2 and a maximum of 3 - * significant digits. - * - * @param minimumSignificantDigits The minimum number of significant digits to display. - * @return The property bag, for chaining. - */ - public IProperties setMinimumSignificantDigits(int minimumSignificantDigits); - - static int DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS = -1; - - /** @see #setMaximumSignificantDigits */ - public int getMaximumSignificantDigits(); - - /** - * Sets the maximum number of significant digits to display. The number of significant digits is - * equal to the number of digits counted from the leftmost nonzero digit through the rightmost - * nonzero digit; for example, the number "2010" has 3 significant digits. If the number has - * more significant digits than specified here, the extra significant digits will be rounded off - * using the rounding mode specified by {@link #setRoundingMode(RoundingMode)}. For example, if - * maximum significant digits is 3, the number 1234.56 will be formatted as "1230" in locale - * en-US with the default rounding mode. - * - *

If both maximum significant digits and maximum integer/fraction digits are set at the same - * time, the behavior is undefined. - * - *

The number of significant digits can be specified in a pattern string using the '@' - * character. For example, the pattern "@@#" corresponds to a minimum of 2 and a maximum of 3 - * significant digits. - * - * @param maximumSignificantDigits The maximum number of significant digits to display. - * @return The property bag, for chaining. - */ - public IProperties setMaximumSignificantDigits(int maximumSignificantDigits); - } - - public static boolean useSignificantDigits(IProperties properties) { - return properties.getMinimumSignificantDigits() - != IProperties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS - || properties.getMaximumSignificantDigits() - != IProperties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS; - } - - public static SignificantDigitsRounder getInstance(IProperties properties) { - return new SignificantDigitsRounder(properties); - } - - private final int minSig; - private final int maxSig; - - private SignificantDigitsRounder(IProperties properties) { - super(properties); - int _minSig = properties.getMinimumSignificantDigits(); - int _maxSig = properties.getMaximumSignificantDigits(); - minSig = _minSig < 1 ? 1 : _minSig > 1000 ? 1000 : _minSig; - maxSig = _maxSig < 0 ? 1000 : _maxSig < minSig ? minSig : _maxSig > 1000 ? 1000 : _maxSig; - } - - @Override - public void apply(FormatQuantity input) { - - int magnitude, effectiveMag, magMinSig, magMaxSig; - - if (input.isZero()) { - // Treat zero as if magnitude corresponded to the minimum number of zeros - magnitude = minInt - 1; - } else { - magnitude = input.getMagnitude(); - } - effectiveMag = Math.min(magnitude + 1, maxInt); - magMinSig = effectiveMag - minSig; - magMaxSig = effectiveMag - maxSig; - - // Step 1: pick the rounding magnitude and apply. - int roundingMagnitude = magMaxSig; - input.roundToMagnitude(roundingMagnitude, mathContext); - - // In case magnitude changed: - if (input.isZero()) { - magnitude = minInt - 1; - } else { - magnitude = input.getMagnitude(); - } - effectiveMag = Math.min(magnitude + 1, maxInt); - magMinSig = effectiveMag - minSig; - magMaxSig = effectiveMag - maxSig; - - // Step 2: pick the number of visible digits. - input.setIntegerLength(minInt, maxInt); - input.setFractionLength(Math.max(minFrac, -magMinSig), Integer.MAX_VALUE); - } - - @Override - public void export(Properties properties) { - super.export(properties); - properties.setMinimumSignificantDigits(minSig); - properties.setMaximumSignificantDigits(maxSig); - } -} diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java index 7fb96a2eb4d..3efa8fa15b8 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java @@ -16,10 +16,8 @@ import java.text.ParsePosition; import com.ibm.icu.impl.number.Parse; import com.ibm.icu.impl.number.PatternString; import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; -import com.ibm.icu.impl.number.formatters.PositiveDecimalFormat; -import com.ibm.icu.impl.number.formatters.ScientificFormat; -import com.ibm.icu.impl.number.rounders.SignificantDigitsRounder; +import com.ibm.icu.impl.number.ThingsNeedingNewHome; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.lang.UCharacter; import com.ibm.icu.math.BigDecimal; import com.ibm.icu.math.MathContext; @@ -1472,7 +1470,8 @@ public class DecimalFormat extends NumberFormat { * @stable ICU 3.0 */ public synchronized boolean areSignificantDigitsUsed() { - return SignificantDigitsRounder.useSignificantDigits(properties); + return properties.getMinimumSignificantDigits() != -1 + || properties.getMaximumSignificantDigits() != -1; } /** @@ -1496,8 +1495,8 @@ public class DecimalFormat extends NumberFormat { properties.setMinimumSignificantDigits(1); properties.setMaximumSignificantDigits(6); } else { - properties.setMinimumSignificantDigits(Properties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS); - properties.setMaximumSignificantDigits(Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS); + properties.setMinimumSignificantDigits(-1); + properties.setMaximumSignificantDigits(-1); } refreshFormatter(); } @@ -1691,7 +1690,7 @@ public class DecimalFormat extends NumberFormat { * @stable ICU 2.0 */ public synchronized boolean isScientificNotation() { - return ScientificFormat.useScientificNotation(properties); + return properties.getMinimumExponentDigits() != -1; } /** @@ -1712,7 +1711,7 @@ public class DecimalFormat extends NumberFormat { if (useScientific) { properties.setMinimumExponentDigits(1); } else { - properties.setMinimumExponentDigits(Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS); + properties.setMinimumExponentDigits(-1); } refreshFormatter(); } @@ -1783,7 +1782,7 @@ public class DecimalFormat extends NumberFormat { */ @Override public synchronized boolean isGroupingUsed() { - return PositiveDecimalFormat.useGrouping(properties); + return properties.getGroupingSize() != -1 || properties.getSecondaryGroupingSize() != -1; } /** @@ -1809,8 +1808,8 @@ public class DecimalFormat extends NumberFormat { // Set to a reasonable default value properties.setGroupingSize(3); } else { - properties.setGroupingSize(Properties.DEFAULT_GROUPING_SIZE); - properties.setSecondaryGroupingSize(Properties.DEFAULT_SECONDARY_GROUPING_SIZE); + properties.setGroupingSize(-1); + properties.setSecondaryGroupingSize(-1); } refreshFormatter(); } @@ -1894,7 +1893,12 @@ public class DecimalFormat extends NumberFormat { */ @Deprecated public synchronized int getMinimumGroupingDigits() { - return properties.getMinimumGroupingDigits(); + // Only 1 and 2 are supported right now. + if (properties.getMinimumGroupingDigits() == 2) { + return 2; + } else { + return 1; + } } /** @@ -2374,7 +2378,7 @@ public class DecimalFormat extends NumberFormat { // so that CurrencyUsage is reflected properly. // TODO: Consider putting this logic in PatternString.java instead. Properties tprops = threadLocalProperties.get().copyFrom(properties); - if (com.ibm.icu.impl.number.formatters.CurrencyFormat.useCurrency(properties)) { + if (ThingsNeedingNewHome.useCurrency(properties)) { tprops.setMinimumFractionDigits(exportedProperties.getMinimumFractionDigits()); tprops.setMaximumFractionDigits(exportedProperties.getMaximumFractionDigits()); tprops.setRoundingIncrement(exportedProperties.getRoundingIncrement()); diff --git a/icu4j/main/classes/core/src/newapi/NumberFormatter.java b/icu4j/main/classes/core/src/newapi/NumberFormatter.java index 8d6c975e26e..80f7138f54c 100644 --- a/icu4j/main/classes/core/src/newapi/NumberFormatter.java +++ b/icu4j/main/classes/core/src/newapi/NumberFormatter.java @@ -11,9 +11,9 @@ import java.text.FieldPosition; import java.util.Arrays; import java.util.Locale; -import com.ibm.icu.impl.number.FormatQuantityBCD; +import com.ibm.icu.impl.number.FormatQuantity; import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.MeasureFormat.FormatWidth; @@ -236,7 +236,7 @@ public final class NumberFormatter { public static class NumberFormatterResult { NumberStringBuilder nsb; - FormatQuantityBCD fq; + FormatQuantity fq; MicroProps micros; /** @@ -244,7 +244,7 @@ public final class NumberFormatter { * @deprecated This API is ICU internal only. */ @Deprecated - public NumberFormatterResult(NumberStringBuilder nsb, FormatQuantityBCD fq, MicroProps micros) { + public NumberFormatterResult(NumberStringBuilder nsb, FormatQuantity fq, MicroProps micros) { this.nsb = nsb; this.fq = fq; this.micros = micros; diff --git a/icu4j/main/classes/core/src/newapi/impl/NumberFormatterImpl.java b/icu4j/main/classes/core/src/newapi/impl/NumberFormatterImpl.java index 173031e884a..f772ed2492d 100644 --- a/icu4j/main/classes/core/src/newapi/impl/NumberFormatterImpl.java +++ b/icu4j/main/classes/core/src/newapi/impl/NumberFormatterImpl.java @@ -6,8 +6,8 @@ import java.util.Locale; import java.util.Objects; import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import com.ibm.icu.impl.number.FormatQuantity; import com.ibm.icu.impl.number.FormatQuantity4; -import com.ibm.icu.impl.number.FormatQuantityBCD; import com.ibm.icu.impl.number.NumberStringBuilder; import com.ibm.icu.impl.number.PatternString; import com.ibm.icu.impl.number.Properties; @@ -216,7 +216,7 @@ public class NumberFormatterImpl extends NumberFormatter.LocalizedNumberFormatte * @param fq The quantity to be formatted. * @return The formatted number result. */ - private NumberFormatterResult format(FormatQuantityBCD fq) { + public NumberFormatterResult format(FormatQuantity fq) { MacroProps macros = resolve(); NumberStringBuilder string = new NumberStringBuilder(); long currentCount = callCount.incrementAndGet(this); diff --git a/icu4j/main/classes/core/src/newapi/impl/NumberPropertyMapper.java b/icu4j/main/classes/core/src/newapi/impl/NumberPropertyMapper.java index 74769532538..81cf952960a 100644 --- a/icu4j/main/classes/core/src/newapi/impl/NumberPropertyMapper.java +++ b/icu4j/main/classes/core/src/newapi/impl/NumberPropertyMapper.java @@ -33,6 +33,13 @@ import newapi.impl.RoundingImpl.RoundingImplSignificant; /** @author sffc */ public final class NumberPropertyMapper { + /** Convenience method to create a NumberFormatterImpl directly. */ + public static NumberFormatterImpl create( + Properties properties, DecimalFormatSymbols symbols, ULocale uloc) { + MacroProps macros = oldToNew(properties, symbols, null); + return NumberFormatterImpl.fromMacros(macros).locale(uloc); + } + /** * Creates a new {@link MacroProps} object based on the content of a {@link Properties} object. In * other words, maps Properties to MacroProps. This function is used by the JDK-compatibility API @@ -96,7 +103,7 @@ public final class NumberPropertyMapper { || affixProvider.hasCurrencySign()); Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols); CurrencyUsage currencyUsage = properties.getCurrencyUsage(); - boolean explicitCurrencyUsage = currencyUsage != Properties.DEFAULT_CURRENCY_USAGE; + boolean explicitCurrencyUsage = currencyUsage != null; if (!explicitCurrencyUsage) { currencyUsage = CurrencyUsage.STANDARD; } @@ -116,12 +123,8 @@ public final class NumberPropertyMapper { int maxSig = properties.getMaximumSignificantDigits(); BigDecimal roundingIncrement = properties.getRoundingIncrement(); MathContext mathContext = RoundingUtils.getMathContextOrUnlimited(properties); - boolean explicitMinMaxFrac = - minFrac != Properties.DEFAULT_MINIMUM_FRACTION_DIGITS - || maxFrac != Properties.DEFAULT_MAXIMUM_FRACTION_DIGITS; - boolean explicitMinMaxSig = - minSig != Properties.DEFAULT_MINIMUM_SIGNIFICANT_DIGITS - || maxSig != Properties.DEFAULT_MAXIMUM_SIGNIFICANT_DIGITS; + boolean explicitMinMaxFrac = minFrac != -1 || maxFrac != -1; + boolean explicitMinMaxSig = minSig != -1 || maxSig != -1; // Validate min/max int/frac. // For backwards compatibility, minimum overrides maximum if the two conflict. // The following logic ensures that there is always a minimum of at least one digit. @@ -181,7 +184,7 @@ public final class NumberPropertyMapper { // PADDING // ///////////// - if (properties.getFormatWidth() != Properties.DEFAULT_FORMAT_WIDTH) { + if (properties.getFormatWidth() != -1) { macros.padding = PaddingImpl.getInstance( properties.getPadString(), properties.getFormatWidth(), properties.getPadPosition()); @@ -206,13 +209,18 @@ public final class NumberPropertyMapper { // SCIENTIFIC NOTATION // ///////////////////////// - if (properties.getMinimumExponentDigits() != Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS) { + if (properties.getMinimumExponentDigits() != -1) { // Scientific notation is required. // The mapping from property bag to scientific notation is nontrivial due to LDML rules. // The maximum of 8 engineering digits has unknown origins and is not in the spec. int engineering = (maxInt != Integer.MAX_VALUE) ? maxInt : properties.getMaximumIntegerDigits(); engineering = (engineering < 0) ? 0 : (engineering > 8) ? minInt : engineering; + // Bug #13289: if maxInt > minInt > 1, then minInt should be 1. + // Clear out IntegerWidth to prevent padding extra zeros. + if (maxInt > minInt && minInt > 1) { + macros.integerWidth = null; + } macros.notation = new NotationScientificImpl( // Engineering interval: @@ -222,9 +230,7 @@ public final class NumberPropertyMapper { // Minimum exponent digits: properties.getMinimumExponentDigits(), // Exponent sign always shown: - properties.getExponentSignAlwaysShown() - ? SignDisplay.ALWAYS - : SignDisplay.AUTO); + properties.getExponentSignAlwaysShown() ? SignDisplay.ALWAYS : SignDisplay.AUTO); // Scientific notation also involves overriding the rounding mode. if (macros.rounding instanceof RoundingImplFraction) { int minInt_ = properties.getMinimumIntegerDigits(); @@ -249,7 +255,7 @@ public final class NumberPropertyMapper { // COMPACT NOTATION // ////////////////////// - if (properties.getCompactStyle() != Properties.DEFAULT_COMPACT_STYLE) { + if (properties.getCompactStyle() != null) { if (properties.getCompactCustomData() != null) { macros.notation = new NotationImpl.NotationCompactImpl(properties.getCompactCustomData()); } else if (properties.getCompactStyle() == CompactStyle.LONG) { @@ -265,9 +271,9 @@ public final class NumberPropertyMapper { // MULTIPLIERS // ///////////////// - if (properties.getMagnitudeMultiplier() != Properties.DEFAULT_MAGNITUDE_MULTIPLIER) { + if (properties.getMagnitudeMultiplier() != 0) { macros.multiplier = new MultiplierImpl(properties.getMagnitudeMultiplier()); - } else if (properties.getMultiplier() != Properties.DEFAULT_MULTIPLIER) { + } else if (properties.getMultiplier() != null) { macros.multiplier = new MultiplierImpl(properties.getMultiplier()); } diff --git a/icu4j/main/classes/core/src/newapi/impl/PaddingImpl.java b/icu4j/main/classes/core/src/newapi/impl/PaddingImpl.java index f747ebb0e62..6700d871820 100644 --- a/icu4j/main/classes/core/src/newapi/impl/PaddingImpl.java +++ b/icu4j/main/classes/core/src/newapi/impl/PaddingImpl.java @@ -3,7 +3,7 @@ package newapi.impl; import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import newapi.NumberFormatter.Padding; diff --git a/icu4j/main/classes/core/src/newapi/impl/PositiveDecimalImpl.java b/icu4j/main/classes/core/src/newapi/impl/PositiveDecimalImpl.java index 65f3cb11668..01df495c477 100644 --- a/icu4j/main/classes/core/src/newapi/impl/PositiveDecimalImpl.java +++ b/icu4j/main/classes/core/src/newapi/impl/PositiveDecimalImpl.java @@ -3,28 +3,14 @@ package newapi.impl; // License & terms of use: http://www.unicode.org/copyright.html#License -import com.ibm.icu.impl.number.Format; import com.ibm.icu.impl.number.FormatQuantity; import com.ibm.icu.impl.number.NumberStringBuilder; -import com.ibm.icu.impl.number.Properties; import com.ibm.icu.text.NumberFormat; import newapi.NumberFormatter.DecimalMarkDisplay; -public class PositiveDecimalImpl implements Format.TargetFormat { +public class PositiveDecimalImpl { - @Override - public int target(FormatQuantity input, NumberStringBuilder string, int startIndex) { - // FIXME - throw new UnsupportedOperationException(); - } - - /** - * @param micros - * @param fq - * @param output - * @return - */ public static int apply(MicroProps micros, FormatQuantity input, NumberStringBuilder string) { int length = 0; if (input.isInfinite()) { @@ -106,9 +92,4 @@ public class PositiveDecimalImpl implements Format.TargetFormat { } return length; } - - @Override - public void export(Properties properties) { - throw new UnsupportedOperationException(); - } } diff --git a/icu4j/main/classes/core/src/newapi/impl/SkeletonBuilder.java b/icu4j/main/classes/core/src/newapi/impl/SkeletonBuilder.java index 97160d5462d..8663f715483 100644 --- a/icu4j/main/classes/core/src/newapi/impl/SkeletonBuilder.java +++ b/icu4j/main/classes/core/src/newapi/impl/SkeletonBuilder.java @@ -6,7 +6,7 @@ import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.MeasureFormat.FormatWidth; @@ -461,9 +461,9 @@ public final class SkeletonBuilder { if (c1 == ',') { char c2 = safeCharAt(skeleton, offset++); char c3 = safeCharAt(skeleton, offset++); - result = GroupingImpl.getInstance((short) (c0 - '0'), (short) (c2 - '0'), c3 == '&'); + result = GroupingImpl.getInstance((byte) (c0 - '0'), (byte) (c2 - '0'), c3 == '&'); } else { - result = GroupingImpl.getInstance((short) (c0 - '0'), (short) (c0 - '0'), c1 == '&'); + result = GroupingImpl.getInstance((byte) (c0 - '0'), (byte) (c0 - '0'), c1 == '&'); } } else { StringBuilder sb = new StringBuilder(); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt index 38b5f3acbf5..31955450cf6 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt @@ -364,8 +364,7 @@ minIntegerDigits maxIntegerDigits minFractionDigits maxFractionDigits output bre // Treat max int digits > 8 as being the same as min int digits. // This behavior is not spelled out in the specification. // JDK fails here because it tries to use 9 + 6 = 15 sig digits. -// C and J get 29.979246E7 -2 9 1 6 2.9979246E8 CJK +2 9 1 6 29.979246E7 K test significant digits scientific set locale en diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java index 8772d332c76..1c8dd8277ed 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java @@ -4,26 +4,15 @@ package com.ibm.icu.dev.test.format; import java.math.BigDecimal; import java.math.RoundingMode; -import java.text.ParseException; import java.text.ParsePosition; import org.junit.Test; import com.ibm.icu.dev.test.TestUtil; -import com.ibm.icu.impl.number.Endpoint; -import com.ibm.icu.impl.number.Format; -import com.ibm.icu.impl.number.FormatQuantity; -import com.ibm.icu.impl.number.FormatQuantity1; -import com.ibm.icu.impl.number.FormatQuantity2; -import com.ibm.icu.impl.number.FormatQuantity3; -import com.ibm.icu.impl.number.FormatQuantity4; -import com.ibm.icu.impl.number.Parse; import com.ibm.icu.impl.number.Parse.ParseMode; import com.ibm.icu.impl.number.PatternString; import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; -import com.ibm.icu.text.DecimalFormat; -import com.ibm.icu.text.DecimalFormat.PropertySetter; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.DecimalFormat_ICU58; import com.ibm.icu.util.CurrencyAmount; @@ -436,244 +425,6 @@ public class NumberFormatDataDrivenTest { } }; - private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU59 = - new DataDrivenNumberFormatTestUtility.CodeUnderTest() { - - @Override - public Character Id() { - return 'S'; - } - - /** - * Runs a single formatting test. On success, returns null. On failure, returns the error. - * This implementation just returns null. Subclasses should override. - * - * @param tuple contains the parameters of the format test. - */ - @Override - public String format(DataDrivenNumberFormatTestData tuple) { - String pattern = (tuple.pattern == null) ? "0" : tuple.pattern; - ULocale locale = (tuple.locale == null) ? ULocale.ENGLISH : tuple.locale; - Properties properties = - PatternString.parseToProperties( - pattern, - tuple.currency != null - ? PatternString.IGNORE_ROUNDING_ALWAYS - : PatternString.IGNORE_ROUNDING_NEVER); - propertiesFromTuple(tuple, properties); - Format fmt = Endpoint.fromBTA(properties, locale); - FormatQuantity q1, q2, q3; - if (tuple.format.equals("NaN")) { - q1 = q2 = new FormatQuantity1(Double.NaN); - q3 = new FormatQuantity2(Double.NaN); - } else if (tuple.format.equals("-Inf")) { - q1 = q2 = new FormatQuantity1(Double.NEGATIVE_INFINITY); - q3 = new FormatQuantity1(Double.NEGATIVE_INFINITY); - } else if (tuple.format.equals("Inf")) { - q1 = q2 = new FormatQuantity1(Double.POSITIVE_INFINITY); - q3 = new FormatQuantity1(Double.POSITIVE_INFINITY); - } else { - BigDecimal d = new BigDecimal(tuple.format); - if (d.precision() <= 16) { - q1 = new FormatQuantity1(d); - q2 = new FormatQuantity1(Double.parseDouble(tuple.format)); - q3 = new FormatQuantity4(d); - } else { - q1 = new FormatQuantity1(d); - q2 = new FormatQuantity3(d); - q3 = new FormatQuantity4(d); // duplicate values so no null - } - } - String expected = tuple.output; - String actual1 = fmt.format(q1); - if (!expected.equals(actual1)) { - return "Expected \"" - + expected - + "\", got \"" - + actual1 - + "\" on FormatQuantity1 BigDecimal"; - } - String actual2 = fmt.format(q2); - if (!expected.equals(actual2)) { - return "Expected \"" - + expected - + "\", got \"" - + actual2 - + "\" on FormatQuantity1 double"; - } - String actual3 = fmt.format(q3); - if (!expected.equals(actual3)) { - return "Expected \"" - + expected - + "\", got \"" - + actual3 - + "\" on FormatQuantity4 BigDecimal"; - } - return null; - } - - /** - * Runs a single toPattern test. On success, returns null. On failure, returns the error. - * This implementation just returns null. Subclasses should override. - * - * @param tuple contains the parameters of the format test. - */ - @Override - public String toPattern(DataDrivenNumberFormatTestData tuple) { - String pattern = (tuple.pattern == null) ? "0" : tuple.pattern; - final Properties properties; - DecimalFormat df; - try { - properties = - PatternString.parseToProperties( - pattern, - tuple.currency != null - ? PatternString.IGNORE_ROUNDING_ALWAYS - : PatternString.IGNORE_ROUNDING_NEVER); - propertiesFromTuple(tuple, properties); - // TODO: Use PatternString.propertiesToString() directly. (How to deal with CurrencyUsage?) - df = new DecimalFormat(); - df.setProperties( - new PropertySetter() { - @Override - public void set(Properties props) { - props.copyFrom(properties); - } - }); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - return e.getLocalizedMessage(); - } - - if (tuple.toPattern != null) { - String expected = tuple.toPattern; - String actual = df.toPattern(); - if (!expected.equals(actual)) { - return "Expected toPattern='" + expected + "'; got '" + actual + "'"; - } - } - if (tuple.toLocalizedPattern != null) { - String expected = tuple.toLocalizedPattern; - String actual = PatternString.propertiesToString(properties); - if (!expected.equals(actual)) { - return "Expected toLocalizedPattern='" + expected + "'; got '" + actual + "'"; - } - } - return null; - } - - /** - * Runs a single parse test. On success, returns null. On failure, returns the error. This - * implementation just returns null. Subclasses should override. - * - * @param tuple contains the parameters of the format test. - */ - @Override - public String parse(DataDrivenNumberFormatTestData tuple) { - String pattern = (tuple.pattern == null) ? "0" : tuple.pattern; - Properties properties; - ParsePosition ppos = new ParsePosition(0); - Number actual; - try { - properties = - PatternString.parseToProperties( - pattern, - tuple.currency != null - ? PatternString.IGNORE_ROUNDING_ALWAYS - : PatternString.IGNORE_ROUNDING_NEVER); - propertiesFromTuple(tuple, properties); - actual = - Parse.parse( - tuple.parse, ppos, properties, DecimalFormatSymbols.getInstance(tuple.locale)); - } catch (IllegalArgumentException e) { - return "parse exception: " + e.getMessage(); - } - if (actual == null && ppos.getIndex() != 0) { - throw new AssertionError("Error: value is null but parse position is not zero"); - } - if (ppos.getIndex() == 0) { - return "Parse failed; got " + actual + ", but expected " + tuple.output; - } - if (tuple.output.equals("NaN")) { - if (!Double.isNaN(actual.doubleValue())) { - return "Expected NaN, but got: " + actual; - } - return null; - } else if (tuple.output.equals("Inf")) { - if (!Double.isInfinite(actual.doubleValue()) - || Double.compare(actual.doubleValue(), 0.0) < 0) { - return "Expected Inf, but got: " + actual; - } - return null; - } else if (tuple.output.equals("-Inf")) { - if (!Double.isInfinite(actual.doubleValue()) - || Double.compare(actual.doubleValue(), 0.0) > 0) { - return "Expected -Inf, but got: " + actual; - } - return null; - } else if (tuple.output.equals("fail")) { - return null; - } else if (new BigDecimal(tuple.output).compareTo(new BigDecimal(actual.toString())) - != 0) { - return "Expected: " + tuple.output + ", got: " + actual; - } else { - return null; - } - } - - /** - * Runs a single parse currency test. On success, returns null. On failure, returns the - * error. This implementation just returns null. Subclasses should override. - * - * @param tuple contains the parameters of the format test. - */ - @Override - public String parseCurrency(DataDrivenNumberFormatTestData tuple) { - String pattern = (tuple.pattern == null) ? "0" : tuple.pattern; - Properties properties; - ParsePosition ppos = new ParsePosition(0); - CurrencyAmount actual; - try { - properties = - PatternString.parseToProperties( - pattern, - tuple.currency != null - ? PatternString.IGNORE_ROUNDING_ALWAYS - : PatternString.IGNORE_ROUNDING_NEVER); - propertiesFromTuple(tuple, properties); - actual = - Parse.parseCurrency( - tuple.parse, ppos, properties, DecimalFormatSymbols.getInstance(tuple.locale)); - } catch (ParseException e) { - e.printStackTrace(); - return "parse exception: " + e.getMessage(); - } - if (ppos.getIndex() == 0 || actual.getCurrency().getCurrencyCode().equals("XXX")) { - return "Parse failed; got " + actual + ", but expected " + tuple.output; - } - BigDecimal expectedNumber = new BigDecimal(tuple.output); - if (expectedNumber.compareTo(new BigDecimal(actual.getNumber().toString())) != 0) { - return "Wrong number: Expected: " + expectedNumber + ", got: " + actual; - } - String expectedCurrency = tuple.outputCurrency; - if (!expectedCurrency.equals(actual.getCurrency().toString())) { - return "Wrong currency: Expected: " + expectedCurrency + ", got: " + actual; - } - return null; - } - - /** - * Runs a single select test. On success, returns null. On failure, returns the error. This - * implementation just returns null. Subclasses should override. - * - * @param tuple contains the parameters of the format test. - */ - @Override - public String select(DataDrivenNumberFormatTestData tuple) { - return null; - } - }; - static void propertiesFromTuple(DataDrivenNumberFormatTestData tuple, Properties properties) { if (tuple.minIntegerDigits != null) { properties.setMinimumIntegerDigits(tuple.minIntegerDigits); @@ -720,7 +471,7 @@ public class NumberFormatDataDrivenTest { } if (tuple.useScientific != null) { properties.setMinimumExponentDigits( - tuple.useScientific != 0 ? 1 : Properties.DEFAULT_MINIMUM_EXPONENT_DIGITS); + tuple.useScientific != 0 ? 1 : -1); } if (tuple.grouping != null) { properties.setGroupingSize(tuple.grouping); @@ -836,12 +587,6 @@ public class NumberFormatDataDrivenTest { "numberformattestspecification.txt", JDK); } - @Test - public void TestDataDrivenICU59() { - DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures( - "numberformattestspecification.txt", ICU59); - } - @Test public void TestDataDrivenICU60() { DataDrivenNumberFormatTestUtility.runFormatSuiteIncludingKnownFailures( diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java index 4f5e6a7f815..32bcc71e04f 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/AffixPatternUtilsTest.java @@ -79,10 +79,6 @@ public class AffixPatternUtilsTest { {"#0#@#*#;#", false, 9, "#0#@#*#;#"} }; - // ar_SA has an interesting percent sign and various Arabic letter marks - DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(new ULocale("ar_SA")); - NumberStringBuilder sb = new NumberStringBuilder(); - for (Object[] cas : cases) { String input = (String) cas[0]; boolean curr = (Boolean) cas[1]; @@ -93,9 +89,8 @@ public class AffixPatternUtilsTest { "Currency on <" + input + ">", curr, AffixPatternUtils.hasCurrencySymbols(input)); assertEquals("Length on <" + input + ">", length, AffixPatternUtils.unescapedLength(input)); - sb.clear(); - AffixPatternUtils.unescape(input, symbols, "$", "XXX", "long name", "−", sb); - assertEquals("Output on <" + input + ">", output, sb.toString()); + String actual = unescapeWithDefaults(input); + assertEquals("Output on <" + input + ">", output, actual); } } @@ -130,8 +125,6 @@ public class AffixPatternUtilsTest { @Test public void testInvalid() { String[] invalidExamples = {"'", "x'", "'x", "'x''", "''x'"}; - DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(new ULocale("en_US")); - NumberStringBuilder sb = new NumberStringBuilder(); for (String str : invalidExamples) { try { @@ -147,7 +140,7 @@ public class AffixPatternUtilsTest { // OK } try { - AffixPatternUtils.unescape(str, symbols, "$", "XXX", "long name", "−", sb); + unescapeWithDefaults(str); fail("No exception was thrown on an invalid string"); } catch (IllegalArgumentException e) { // OK @@ -189,4 +182,46 @@ public class AffixPatternUtilsTest { AffixPatternUtils.unescape("-+%", sb, 4, provider); assertEquals("Symbol provider into middle", "abcd123efg", sb.toString()); } + + private static final SymbolProvider DEFAULT_SYMBOL_PROVIDER = + new SymbolProvider() { + // ar_SA has an interesting percent sign and various Arabic letter marks + private final DecimalFormatSymbols SYMBOLS = + DecimalFormatSymbols.getInstance(new ULocale("ar_SA")); + + @Override + public CharSequence getSymbol(int type) { + switch (type) { + case AffixPatternUtils.TYPE_MINUS_SIGN: + return "−"; + case AffixPatternUtils.TYPE_PLUS_SIGN: + return SYMBOLS.getPlusSignString(); + case AffixPatternUtils.TYPE_PERCENT: + return SYMBOLS.getPercentString(); + case AffixPatternUtils.TYPE_PERMILLE: + return SYMBOLS.getPerMillString(); + case AffixPatternUtils.TYPE_CURRENCY_SINGLE: + return "$"; + case AffixPatternUtils.TYPE_CURRENCY_DOUBLE: + return "XXX"; + case AffixPatternUtils.TYPE_CURRENCY_TRIPLE: + return "long name"; + case AffixPatternUtils.TYPE_CURRENCY_QUAD: + return "\uFFFD"; + case AffixPatternUtils.TYPE_CURRENCY_QUINT: + // TODO: Add support for narrow currency symbols here. + return "\uFFFD"; + case AffixPatternUtils.TYPE_CURRENCY_OVERFLOW: + return "\uFFFD"; + default: + throw new AssertionError(); + } + } + }; + + private static String unescapeWithDefaults(String input) { + NumberStringBuilder nsb = new NumberStringBuilder(); + AffixPatternUtils.unescape(input, nsb, 0, DEFAULT_SYMBOL_PROVIDER); + return nsb.toString(); + } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/FormatQuantityTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/FormatQuantityTest.java index 45bc3b5c346..c92905b261f 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/FormatQuantityTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/FormatQuantityTest.java @@ -13,8 +13,6 @@ import java.util.Random; import org.junit.Test; import com.ibm.icu.dev.test.TestFmwk; -import com.ibm.icu.impl.number.Endpoint; -import com.ibm.icu.impl.number.Format; import com.ibm.icu.impl.number.FormatQuantity; import com.ibm.icu.impl.number.FormatQuantity1; import com.ibm.icu.impl.number.FormatQuantity2; @@ -22,6 +20,10 @@ import com.ibm.icu.impl.number.FormatQuantity3; import com.ibm.icu.impl.number.FormatQuantity4; import com.ibm.icu.impl.number.Properties; import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; +import com.ibm.icu.util.ULocale; + +import newapi.impl.NumberFormatterImpl; +import newapi.impl.NumberPropertyMapper; /** TODO: This is a temporary name for this class. Suggestions for a better name? */ public class FormatQuantityTest extends TestFmwk { @@ -30,31 +32,27 @@ public class FormatQuantityTest extends TestFmwk { public void testBehavior() throws ParseException { // Make a list of several formatters to test the behavior of FormatQuantity. - List formats = new ArrayList(); + List formats = new ArrayList(); Properties properties = new Properties(); - Format ndf = Endpoint.fromBTA(properties); - formats.add(ndf); + formats.add(NumberPropertyMapper.create(properties, null, ULocale.ENGLISH)); properties = new Properties() .setMinimumSignificantDigits(3) .setMaximumSignificantDigits(3) .setCompactStyle(CompactStyle.LONG); - Format cdf = Endpoint.fromBTA(properties); - formats.add(cdf); + formats.add(NumberPropertyMapper.create(properties, null, ULocale.ENGLISH)); properties = new Properties() .setMinimumExponentDigits(1) .setMaximumIntegerDigits(3) .setMaximumFractionDigits(1); - Format exf = Endpoint.fromBTA(properties); - formats.add(exf); + formats.add(NumberPropertyMapper.create(properties, null, ULocale.ENGLISH)); properties = new Properties().setRoundingIncrement(new BigDecimal("0.5")); - Format rif = Endpoint.fromBTA(properties); - formats.add(rif); + formats.add(NumberPropertyMapper.create(properties, null, ULocale.ENGLISH)); String[] cases = { "1.0", @@ -112,7 +110,7 @@ public class FormatQuantityTest extends TestFmwk { } } - static void testFormatQuantity(int t, String str, List formats, int mode) { + static void testFormatQuantity(int t, String str, List formats, int mode) { if (mode == 2) { assertEquals("Double is not valid", Double.toString(Double.parseDouble(str)), str); } @@ -230,12 +228,12 @@ public class FormatQuantityTest extends TestFmwk { } private static void testFormatQuantityWithFormats( - FormatQuantity rq0, FormatQuantity rq1, List formats) { - for (Format format : formats) { + FormatQuantity rq0, FormatQuantity rq1, List formats) { + for (NumberFormatterImpl format : formats) { FormatQuantity q0 = rq0.createCopy(); FormatQuantity q1 = rq1.createCopy(); - String s1 = format.format(q0); - String s2 = format.format(q1); + String s1 = format.format(q0).toString(); + String s2 = format.format(q1).toString(); assertEquals("Different output from formatter (" + q0 + ", " + q1 + ")", s1, s2); } } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterTest.java index bcdd4d31d23..89cb4ac6497 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterTest.java @@ -12,7 +12,7 @@ import java.util.Locale; import org.junit.Ignore; import org.junit.Test; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.text.DecimalFormatSymbols; import com.ibm.icu.text.MeasureFormat.FormatWidth; import com.ibm.icu.text.NumberingSystem; diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/PropertiesTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/PropertiesTest.java index 7f64bf03a6a..d8e786a9a94 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/PropertiesTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/PropertiesTest.java @@ -33,8 +33,7 @@ import com.ibm.icu.impl.number.Parse.GroupingMode; import com.ibm.icu.impl.number.Parse.ParseMode; import com.ibm.icu.impl.number.PatternString; import com.ibm.icu.impl.number.Properties; -import com.ibm.icu.impl.number.formatters.CurrencyFormat.CurrencyStyle; -import com.ibm.icu.impl.number.formatters.PaddingFormat.PadPosition; +import com.ibm.icu.impl.number.ThingsNeedingNewHome.PadPosition; import com.ibm.icu.text.CompactDecimalFormat.CompactStyle; import com.ibm.icu.text.CurrencyPluralInfo; import com.ibm.icu.text.MeasureFormat.FormatWidth; @@ -241,11 +240,6 @@ public class PropertiesTest { ULocale[] locales = ULocale.getAvailableLocales(); return CurrencyPluralInfo.getInstance(locales[seed % locales.length]); - } else if (type == CurrencyStyle.class) { - if (seed == 0) return null; - CurrencyStyle[] values = CurrencyStyle.values(); - return values[seed % values.length]; - } else if (type == CurrencyUsage.class) { if (seed == 0) return null; CurrencyUsage[] values = CurrencyUsage.values();