• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GENERATED SOURCE. DO NOT MODIFY. */
2 // © 2017 and later: Unicode, Inc. and others.
3 // License & terms of use: http://www.unicode.org/copyright.html#License
4 package ohos.global.icu.number;
5 
6 import java.math.BigDecimal;
7 import java.math.MathContext;
8 
9 import ohos.global.icu.impl.number.AffixPatternProvider;
10 import ohos.global.icu.impl.number.CustomSymbolCurrency;
11 import ohos.global.icu.impl.number.DecimalFormatProperties;
12 import ohos.global.icu.impl.number.Grouper;
13 import ohos.global.icu.impl.number.MacroProps;
14 import ohos.global.icu.impl.number.Padder;
15 import ohos.global.icu.impl.number.PatternStringParser;
16 import ohos.global.icu.impl.number.PatternStringUtils;
17 import ohos.global.icu.impl.number.PropertiesAffixPatternProvider;
18 import ohos.global.icu.impl.number.RoundingUtils;
19 import ohos.global.icu.number.NumberFormatter.DecimalSeparatorDisplay;
20 import ohos.global.icu.number.NumberFormatter.SignDisplay;
21 import ohos.global.icu.number.Precision.FractionRounderImpl;
22 import ohos.global.icu.number.Precision.IncrementRounderImpl;
23 import ohos.global.icu.number.Precision.SignificantRounderImpl;
24 import ohos.global.icu.text.CompactDecimalFormat.CompactStyle;
25 import ohos.global.icu.text.DecimalFormatSymbols;
26 import ohos.global.icu.text.PluralRules;
27 import ohos.global.icu.util.Currency;
28 import ohos.global.icu.util.Currency.CurrencyUsage;
29 import ohos.global.icu.util.ULocale;
30 
31 /**
32  * <p>
33  * This class, as well as NumberFormatterImpl, could go into the impl package, but they depend on too
34  * many package-private members of the public APIs.
35  */
36 final class NumberPropertyMapper {
37 
38     /** Convenience method to create a NumberFormatter directly from Properties. */
create( DecimalFormatProperties properties, DecimalFormatSymbols symbols)39     public static UnlocalizedNumberFormatter create(
40             DecimalFormatProperties properties,
41             DecimalFormatSymbols symbols) {
42         MacroProps macros = oldToNew(properties, symbols, null);
43         return NumberFormatter.with().macros(macros);
44     }
45 
46     /** Convenience method to create a NumberFormatter directly from Properties. */
create( DecimalFormatProperties properties, DecimalFormatSymbols symbols, DecimalFormatProperties exportedProperties)47     public static UnlocalizedNumberFormatter create(
48             DecimalFormatProperties properties,
49             DecimalFormatSymbols symbols,
50             DecimalFormatProperties exportedProperties) {
51         MacroProps macros = oldToNew(properties, symbols, exportedProperties);
52         return NumberFormatter.with().macros(macros);
53     }
54 
55     /**
56      * Convenience method to create a NumberFormatter directly from a pattern string. Something like this
57      * could become public API if there is demand.
58      *
59      * NOTE: This appears to be dead code.
60      */
create(String pattern, DecimalFormatSymbols symbols)61     public static UnlocalizedNumberFormatter create(String pattern, DecimalFormatSymbols symbols) {
62         DecimalFormatProperties properties = PatternStringParser.parseToProperties(pattern);
63         return create(properties, symbols);
64     }
65 
66     /**
67      * Creates a new {@link MacroProps} object based on the content of a {@link DecimalFormatProperties}
68      * object. In other words, maps Properties to MacroProps. This function is used by the
69      * JDK-compatibility API to call into the ICU 60 fluent number formatting pipeline.
70      *
71      * @param properties
72      *            The property bag to be mapped.
73      * @param symbols
74      *            The symbols associated with the property bag.
75      * @param exportedProperties
76      *            A property bag in which to store validated properties. Used by some DecimalFormat
77      *            getters.
78      * @return A new MacroProps containing all of the information in the Properties.
79      */
oldToNew( DecimalFormatProperties properties, DecimalFormatSymbols symbols, DecimalFormatProperties exportedProperties)80     public static MacroProps oldToNew(
81             DecimalFormatProperties properties,
82             DecimalFormatSymbols symbols,
83             DecimalFormatProperties exportedProperties) {
84         MacroProps macros = new MacroProps();
85         ULocale locale = symbols.getULocale();
86 
87         /////////////
88         // SYMBOLS //
89         /////////////
90 
91         macros.symbols = symbols;
92 
93         //////////////////
94         // PLURAL RULES //
95         //////////////////
96 
97         PluralRules rules = properties.getPluralRules();
98         if (rules == null && properties.getCurrencyPluralInfo() != null) {
99             rules = properties.getCurrencyPluralInfo().getPluralRules();
100         }
101         macros.rules = rules;
102 
103         /////////////
104         // AFFIXES //
105         /////////////
106 
107         AffixPatternProvider affixProvider = PropertiesAffixPatternProvider.forProperties(properties);
108         macros.affixProvider = affixProvider;
109 
110         ///////////
111         // UNITS //
112         ///////////
113 
114         boolean useCurrency = ((properties.getCurrency() != null)
115                 || properties.getCurrencyPluralInfo() != null
116                 || properties.getCurrencyUsage() != null
117                 || affixProvider.hasCurrencySign());
118         Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
119         CurrencyUsage currencyUsage = properties.getCurrencyUsage();
120         boolean explicitCurrencyUsage = currencyUsage != null;
121         if (!explicitCurrencyUsage) {
122             currencyUsage = CurrencyUsage.STANDARD;
123         }
124         if (useCurrency) {
125             macros.unit = currency;
126         }
127 
128         ///////////////////////
129         // ROUNDING STRATEGY //
130         ///////////////////////
131 
132         int maxInt = properties.getMaximumIntegerDigits();
133         int minInt = properties.getMinimumIntegerDigits();
134         int maxFrac = properties.getMaximumFractionDigits();
135         int minFrac = properties.getMinimumFractionDigits();
136         int minSig = properties.getMinimumSignificantDigits();
137         int maxSig = properties.getMaximumSignificantDigits();
138         BigDecimal roundingIncrement = properties.getRoundingIncrement();
139         MathContext mathContext = RoundingUtils.getMathContextOrUnlimited(properties);
140         boolean explicitMinMaxFrac = minFrac != -1 || maxFrac != -1;
141         boolean explicitMinMaxSig = minSig != -1 || maxSig != -1;
142         // Resolve min/max frac for currencies, required for the validation logic and for when minFrac or
143         // maxFrac was set (but not both) on a currency instance.
144         // NOTE: Increments are handled in "Rounder.constructCurrency()".
145         if (useCurrency) {
146             if (minFrac == -1 && maxFrac == -1) {
147                 minFrac = currency.getDefaultFractionDigits(currencyUsage);
148                 maxFrac = currency.getDefaultFractionDigits(currencyUsage);
149             } else if (minFrac == -1) {
150                 minFrac = Math.min(maxFrac, currency.getDefaultFractionDigits(currencyUsage));
151             } else if (maxFrac == -1) {
152                 maxFrac = Math.max(minFrac, currency.getDefaultFractionDigits(currencyUsage));
153             } else {
154                 // No-op: user override for both minFrac and maxFrac
155             }
156         }
157         // Validate min/max int/frac.
158         // For backwards compatibility, minimum overrides maximum if the two conflict.
159         if (minInt == 0 && maxFrac != 0) {
160             minFrac = (minFrac < 0 || (minFrac == 0 && maxInt == 0)) ? 1 : minFrac;
161             maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
162             minInt = 0;
163             maxInt = maxInt < 0 ? -1 : maxInt > RoundingUtils.MAX_INT_FRAC_SIG ? -1 : maxInt;
164         } else {
165             // Force a digit before the decimal point.
166             minFrac = minFrac < 0 ? 0 : minFrac;
167             maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac;
168             minInt = minInt <= 0 ? 1 : minInt > RoundingUtils.MAX_INT_FRAC_SIG ? 1 : minInt;
169             maxInt = maxInt < 0 ? -1
170                     : maxInt < minInt ? minInt : maxInt > RoundingUtils.MAX_INT_FRAC_SIG ? -1 : maxInt;
171         }
172         Precision rounding = null;
173         if (explicitCurrencyUsage) {
174             rounding = Precision.constructCurrency(currencyUsage).withCurrency(currency);
175         } else if (roundingIncrement != null) {
176             if (PatternStringUtils.ignoreRoundingIncrement(roundingIncrement, maxFrac)) {
177                 rounding = Precision.constructFraction(minFrac, maxFrac);
178             } else {
179                 rounding = Precision.constructIncrement(roundingIncrement);
180             }
181         } else if (explicitMinMaxSig) {
182             minSig = minSig < 1 ? 1
183                     : minSig > RoundingUtils.MAX_INT_FRAC_SIG ? RoundingUtils.MAX_INT_FRAC_SIG : minSig;
184             maxSig = maxSig < 0 ? RoundingUtils.MAX_INT_FRAC_SIG
185                     : maxSig < minSig ? minSig
186                             : maxSig > RoundingUtils.MAX_INT_FRAC_SIG ? RoundingUtils.MAX_INT_FRAC_SIG
187                                     : maxSig;
188             rounding = Precision.constructSignificant(minSig, maxSig);
189         } else if (explicitMinMaxFrac) {
190             rounding = Precision.constructFraction(minFrac, maxFrac);
191         } else if (useCurrency) {
192             rounding = Precision.constructCurrency(currencyUsage);
193         }
194         if (rounding != null) {
195             rounding = rounding.withMode(mathContext);
196             macros.precision = rounding;
197         }
198 
199         ///////////////////
200         // INTEGER WIDTH //
201         ///////////////////
202 
203         macros.integerWidth = IntegerWidth.zeroFillTo(minInt).truncateAt(maxInt);
204 
205         ///////////////////////
206         // GROUPING STRATEGY //
207         ///////////////////////
208 
209         macros.grouping = Grouper.forProperties(properties);
210 
211         /////////////
212         // PADDING //
213         /////////////
214 
215         if (properties.getFormatWidth() > 0) {
216             macros.padder = Padder.forProperties(properties);
217         }
218 
219         ///////////////////////////////
220         // DECIMAL MARK ALWAYS SHOWN //
221         ///////////////////////////////
222 
223         macros.decimal = properties.getDecimalSeparatorAlwaysShown() ? DecimalSeparatorDisplay.ALWAYS
224                 : DecimalSeparatorDisplay.AUTO;
225 
226         ///////////////////////
227         // SIGN ALWAYS SHOWN //
228         ///////////////////////
229 
230         macros.sign = properties.getSignAlwaysShown() ? SignDisplay.ALWAYS : SignDisplay.AUTO;
231 
232         /////////////////////////
233         // SCIENTIFIC NOTATION //
234         /////////////////////////
235 
236         if (properties.getMinimumExponentDigits() != -1) {
237             // Scientific notation is required.
238             // This whole section feels like a hack, but it is needed for regression tests.
239             // The mapping from property bag to scientific notation is nontrivial due to LDML rules.
240             if (maxInt > 8) {
241                 // But #13110: The maximum of 8 digits has unknown origins and is not in the spec.
242                 // If maxInt is greater than 8, it is set to minInt, even if minInt is greater than 8.
243                 maxInt = minInt;
244                 macros.integerWidth = IntegerWidth.zeroFillTo(minInt).truncateAt(maxInt);
245             } else if (maxInt > minInt && minInt > 1) {
246                 // Bug #13289: if maxInt > minInt > 1, then minInt should be 1.
247                 minInt = 1;
248                 macros.integerWidth = IntegerWidth.zeroFillTo(minInt).truncateAt(maxInt);
249             }
250             int engineering = maxInt < 0 ? -1 : maxInt;
251             macros.notation = new ScientificNotation(
252                     // Engineering interval:
253                     engineering,
254                     // Enforce minimum integer digits (for patterns like "000.00E0"):
255                     (engineering == minInt),
256                     // Minimum exponent digits:
257                     properties.getMinimumExponentDigits(),
258                     // Exponent sign always shown:
259                     properties.getExponentSignAlwaysShown() ? SignDisplay.ALWAYS : SignDisplay.AUTO);
260             // Scientific notation also involves overriding the rounding mode.
261             // TODO: Overriding here is a bit of a hack. Should this logic go earlier?
262             if (macros.precision instanceof FractionPrecision) {
263                 // For the purposes of rounding, get the original min/max int/frac, since the local
264                 // variables have been manipulated for display purposes.
265                 int maxInt_ = properties.getMaximumIntegerDigits();
266                 int minInt_ = properties.getMinimumIntegerDigits();
267                 int minFrac_ = properties.getMinimumFractionDigits();
268                 int maxFrac_ = properties.getMaximumFractionDigits();
269                 if (minInt_ == 0 && maxFrac_ == 0) {
270                     // Patterns like "#E0" and "##E0", which mean no rounding!
271                     macros.precision = Precision.constructInfinite().withMode(mathContext);
272                 } else if (minInt_ == 0 && minFrac_ == 0) {
273                     // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1
274                     macros.precision = Precision.constructSignificant(1, maxFrac_ + 1).withMode(mathContext);
275                 } else {
276                     int maxSig_ = minInt_ + maxFrac_;
277                     // Bug #20058: if maxInt_ > minInt_ > 1, then minInt_ should be 1.
278                     if (maxInt_ > minInt_ && minInt_ > 1) {
279                         minInt_ = 1;
280                     }
281                     int minSig_ = minInt_ + minFrac_;
282                     // To avoid regression, maxSig is not reset when minInt_ set to 1.
283                     // TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec.
284                     macros.precision = Precision.constructSignificant(minSig_, maxSig_)
285                             .withMode(mathContext);
286                 }
287             }
288         }
289 
290         //////////////////////
291         // COMPACT NOTATION //
292         //////////////////////
293 
294         if (properties.getCompactStyle() != null) {
295             if (properties.getCompactCustomData() != null) {
296                 macros.notation = new CompactNotation(properties.getCompactCustomData());
297             } else if (properties.getCompactStyle() == CompactStyle.LONG) {
298                 macros.notation = Notation.compactLong();
299             } else {
300                 macros.notation = Notation.compactShort();
301             }
302             // Do not forward the affix provider.
303             macros.affixProvider = null;
304         }
305 
306         /////////////////
307         // MULTIPLIERS //
308         /////////////////
309 
310         macros.scale = RoundingUtils.scaleFromProperties(properties);
311 
312         //////////////////////
313         // PROPERTY EXPORTS //
314         //////////////////////
315 
316         if (exportedProperties != null) {
317 
318             exportedProperties.setCurrency(currency);
319             exportedProperties.setMathContext(mathContext);
320             exportedProperties.setRoundingMode(mathContext.getRoundingMode());
321             exportedProperties.setMinimumIntegerDigits(minInt);
322             exportedProperties.setMaximumIntegerDigits(maxInt == -1 ? Integer.MAX_VALUE : maxInt);
323 
324             Precision rounding_;
325             if (rounding instanceof CurrencyPrecision) {
326                 rounding_ = ((CurrencyPrecision) rounding).withCurrency(currency);
327             } else {
328                 rounding_ = rounding;
329             }
330             int minFrac_ = minFrac;
331             int maxFrac_ = maxFrac;
332             int minSig_ = minSig;
333             int maxSig_ = maxSig;
334             BigDecimal increment_ = null;
335             if (rounding_ instanceof FractionRounderImpl) {
336                 minFrac_ = ((FractionRounderImpl) rounding_).minFrac;
337                 maxFrac_ = ((FractionRounderImpl) rounding_).maxFrac;
338             } else if (rounding_ instanceof IncrementRounderImpl) {
339                 increment_ = ((IncrementRounderImpl) rounding_).increment;
340                 minFrac_ = increment_.scale();
341                 maxFrac_ = increment_.scale();
342             } else if (rounding_ instanceof SignificantRounderImpl) {
343                 minSig_ = ((SignificantRounderImpl) rounding_).minSig;
344                 maxSig_ = ((SignificantRounderImpl) rounding_).maxSig;
345             }
346 
347             exportedProperties.setMinimumFractionDigits(minFrac_);
348             exportedProperties.setMaximumFractionDigits(maxFrac_);
349             exportedProperties.setMinimumSignificantDigits(minSig_);
350             exportedProperties.setMaximumSignificantDigits(maxSig_);
351             exportedProperties.setRoundingIncrement(increment_);
352         }
353 
354         return macros;
355     }
356 }
357