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