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.BigInteger; 8 import java.math.MathContext; 9 10 import ohos.global.icu.impl.number.DecimalQuantity; 11 import ohos.global.icu.impl.number.MultiplierProducer; 12 import ohos.global.icu.impl.number.RoundingUtils; 13 import ohos.global.icu.util.Currency; 14 import ohos.global.icu.util.Currency.CurrencyUsage; 15 16 /** 17 * A class that defines the rounding precision to be used when formatting numbers in NumberFormatter. 18 * 19 * <p> 20 * To create a Precision, use one of the factory methods. 21 * 22 * @see NumberFormatter 23 * @hide exposed on OHOS 24 */ 25 public abstract class Precision { 26 27 /* package-private final */ MathContext mathContext; 28 Precision()29 /* package-private */ Precision() { 30 mathContext = RoundingUtils.DEFAULT_MATH_CONTEXT_UNLIMITED; 31 } 32 33 /** 34 * Show all available digits to full precision. 35 * 36 * <p> 37 * <strong>NOTE:</strong> When formatting a <em>double</em>, this method, along with 38 * {@link #minFraction} and {@link #minSignificantDigits}, will trigger complex algorithm similar to 39 * <em>Dragon4</em> to determine the low-order digits and the number of digits to display based on 40 * the value of the double. If the number of fraction places or significant digits can be bounded, 41 * consider using {@link #maxFraction} or {@link #maxSignificantDigits} instead to maximize performance. 42 * For more information, read the following blog post. 43 * 44 * <p> 45 * http://www.serpentine.com/blog/2011/06/29/here-be-dragons-advances-in-problems-you-didnt-even-know-you-had/ 46 * 47 * @return A Precision for chaining or passing to the NumberFormatter precision() setter. 48 * @see NumberFormatter 49 */ unlimited()50 public static Precision unlimited() { 51 return constructInfinite(); 52 } 53 54 /** 55 * Show numbers rounded if necessary to the nearest integer. 56 * 57 * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. 58 * @see NumberFormatter 59 */ integer()60 public static FractionPrecision integer() { 61 return constructFraction(0, 0); 62 } 63 64 /** 65 * Show numbers rounded if necessary to a certain number of fraction places (numerals after the 66 * decimal separator). Additionally, pad with zeros to ensure that this number of places are always 67 * shown. 68 * 69 * <p> 70 * Example output with minMaxFractionPlaces = 3: 71 * 72 * <p> 73 * 87,650.000<br> 74 * 8,765.000<br> 75 * 876.500<br> 76 * 87.650<br> 77 * 8.765<br> 78 * 0.876<br> 79 * 0.088<br> 80 * 0.009<br> 81 * 0.000 (zero) 82 * 83 * <p> 84 * This method is equivalent to {@link #minMaxFraction} with both arguments equal. 85 * 86 * @param minMaxFractionPlaces 87 * The minimum and maximum number of numerals to display after the decimal separator 88 * (rounding if too long or padding with zeros if too short). 89 * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. 90 * @throws IllegalArgumentException if the input number is too big or smaller than 0. 91 * @see NumberFormatter 92 */ fixedFraction(int minMaxFractionPlaces)93 public static FractionPrecision fixedFraction(int minMaxFractionPlaces) { 94 if (minMaxFractionPlaces >= 0 && minMaxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) { 95 return constructFraction(minMaxFractionPlaces, minMaxFractionPlaces); 96 } else { 97 throw new IllegalArgumentException("Fraction length must be between 0 and " 98 + RoundingUtils.MAX_INT_FRAC_SIG 99 + " (inclusive)"); 100 } 101 } 102 103 /** 104 * Always show at least a certain number of fraction places after the decimal separator, padding with 105 * zeros if necessary. Do not perform rounding (display numbers to their full precision). 106 * 107 * <p> 108 * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in 109 * {@link #unlimited}. 110 * 111 * @param minFractionPlaces 112 * The minimum number of numerals to display after the decimal separator (padding with 113 * zeros if necessary). 114 * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. 115 * @throws IllegalArgumentException if the input number is too big or smaller than 0. 116 * @see NumberFormatter 117 */ minFraction(int minFractionPlaces)118 public static FractionPrecision minFraction(int minFractionPlaces) { 119 if (minFractionPlaces >= 0 && minFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) { 120 return constructFraction(minFractionPlaces, -1); 121 } else { 122 throw new IllegalArgumentException("Fraction length must be between 0 and " 123 + RoundingUtils.MAX_INT_FRAC_SIG 124 + " (inclusive)"); 125 } 126 } 127 128 /** 129 * Show numbers rounded if necessary to a certain number of fraction places (numerals after the 130 * decimal separator). Unlike the other fraction rounding strategies, this strategy does <em>not</em> 131 * pad zeros to the end of the number. 132 * 133 * @param maxFractionPlaces 134 * The maximum number of numerals to display after the decimal mark (rounding if 135 * necessary). 136 * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. 137 * @throws IllegalArgumentException if the input number is too big or smaller than 0. 138 * @see NumberFormatter 139 */ maxFraction(int maxFractionPlaces)140 public static FractionPrecision maxFraction(int maxFractionPlaces) { 141 if (maxFractionPlaces >= 0 && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG) { 142 return constructFraction(0, maxFractionPlaces); 143 } else { 144 throw new IllegalArgumentException("Fraction length must be between 0 and " 145 + RoundingUtils.MAX_INT_FRAC_SIG 146 + " (inclusive)"); 147 } 148 } 149 150 /** 151 * Show numbers rounded if necessary to a certain number of fraction places (numerals after the 152 * decimal separator); in addition, always show at least a certain number of places after the decimal 153 * separator, padding with zeros if necessary. 154 * 155 * @param minFractionPlaces 156 * The minimum number of numerals to display after the decimal separator (padding with 157 * zeros if necessary). 158 * @param maxFractionPlaces 159 * The maximum number of numerals to display after the decimal separator (rounding if 160 * necessary). 161 * @return A FractionPrecision for chaining or passing to the NumberFormatter precision() setter. 162 * @throws IllegalArgumentException if the input number is too big or smaller than 0. 163 * @see NumberFormatter 164 */ minMaxFraction(int minFractionPlaces, int maxFractionPlaces)165 public static FractionPrecision minMaxFraction(int minFractionPlaces, int maxFractionPlaces) { 166 if (minFractionPlaces >= 0 167 && maxFractionPlaces <= RoundingUtils.MAX_INT_FRAC_SIG 168 && minFractionPlaces <= maxFractionPlaces) { 169 return constructFraction(minFractionPlaces, maxFractionPlaces); 170 } else { 171 throw new IllegalArgumentException("Fraction length must be between 0 and " 172 + RoundingUtils.MAX_INT_FRAC_SIG 173 + " (inclusive)"); 174 } 175 } 176 177 /** 178 * Show numbers rounded if necessary to a certain number of significant digits or significant 179 * figures. Additionally, pad with zeros to ensure that this number of significant digits/figures are 180 * always shown. 181 * 182 * <p> 183 * This method is equivalent to {@link #minMaxSignificantDigits} with both arguments equal. 184 * 185 * @param minMaxSignificantDigits 186 * The minimum and maximum number of significant digits to display (rounding if too long 187 * or padding with zeros if too short). 188 * @return A Precision for chaining or passing to the NumberFormatter precision() setter. 189 * @throws IllegalArgumentException if the input number is too big or smaller than 1. 190 * @see NumberFormatter 191 */ fixedSignificantDigits(int minMaxSignificantDigits)192 public static Precision fixedSignificantDigits(int minMaxSignificantDigits) { 193 if (minMaxSignificantDigits >= 1 && minMaxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) { 194 return constructSignificant(minMaxSignificantDigits, minMaxSignificantDigits); 195 } else { 196 throw new IllegalArgumentException("Significant digits must be between 1 and " 197 + RoundingUtils.MAX_INT_FRAC_SIG 198 + " (inclusive)"); 199 } 200 } 201 202 /** 203 * Always show at least a certain number of significant digits/figures, padding with zeros if 204 * necessary. Do not perform rounding (display numbers to their full precision). 205 * 206 * <p> 207 * <strong>NOTE:</strong> If you are formatting <em>doubles</em>, see the performance note in 208 * {@link #unlimited}. 209 * 210 * @param minSignificantDigits 211 * The minimum number of significant digits to display (padding with zeros if too short). 212 * @return A Precision for chaining or passing to the NumberFormatter precision() setter. 213 * @throws IllegalArgumentException if the input number is too big or smaller than 1. 214 * @see NumberFormatter 215 */ minSignificantDigits(int minSignificantDigits)216 public static Precision minSignificantDigits(int minSignificantDigits) { 217 if (minSignificantDigits >= 1 && minSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) { 218 return constructSignificant(minSignificantDigits, -1); 219 } else { 220 throw new IllegalArgumentException("Significant digits must be between 1 and " 221 + RoundingUtils.MAX_INT_FRAC_SIG 222 + " (inclusive)"); 223 } 224 } 225 226 /** 227 * Show numbers rounded if necessary to a certain number of significant digits/figures. 228 * 229 * @param maxSignificantDigits 230 * The maximum number of significant digits to display (rounding if too long). 231 * @return A Precision for chaining or passing to the NumberFormatter precision() setter. 232 * @throws IllegalArgumentException if the input number is too big or smaller than 1. 233 * @see NumberFormatter 234 */ maxSignificantDigits(int maxSignificantDigits)235 public static Precision maxSignificantDigits(int maxSignificantDigits) { 236 if (maxSignificantDigits >= 1 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG) { 237 return constructSignificant(1, maxSignificantDigits); 238 } else { 239 throw new IllegalArgumentException("Significant digits must be between 1 and " 240 + RoundingUtils.MAX_INT_FRAC_SIG 241 + " (inclusive)"); 242 } 243 } 244 245 /** 246 * Show numbers rounded if necessary to a certain number of significant digits/figures; in addition, 247 * always show at least a certain number of significant digits, padding with zeros if necessary. 248 * 249 * @param minSignificantDigits 250 * The minimum number of significant digits to display (padding with zeros if necessary). 251 * @param maxSignificantDigits 252 * The maximum number of significant digits to display (rounding if necessary). 253 * @return A Precision for chaining or passing to the NumberFormatter precision() setter. 254 * @throws IllegalArgumentException if the input number is too big or smaller than 1. 255 * @see NumberFormatter 256 */ minMaxSignificantDigits(int minSignificantDigits, int maxSignificantDigits)257 public static Precision minMaxSignificantDigits(int minSignificantDigits, int maxSignificantDigits) { 258 if (minSignificantDigits >= 1 259 && maxSignificantDigits <= RoundingUtils.MAX_INT_FRAC_SIG 260 && minSignificantDigits <= maxSignificantDigits) { 261 return constructSignificant(minSignificantDigits, maxSignificantDigits); 262 } else { 263 throw new IllegalArgumentException("Significant digits must be between 1 and " 264 + RoundingUtils.MAX_INT_FRAC_SIG 265 + " (inclusive)"); 266 } 267 } 268 269 /** 270 * Show numbers rounded if necessary to the closest multiple of a certain rounding increment. For 271 * example, if the rounding increment is 0.5, then round 1.2 to 1 and round 1.3 to 1.5. 272 * 273 * <p> 274 * In order to ensure that numbers are padded to the appropriate number of fraction places, set the 275 * scale on the rounding increment BigDecimal. For example, to round to the nearest 0.5 and always 276 * display 2 numerals after the decimal separator (to display 1.2 as "1.00" and 1.3 as "1.50"), you 277 * can run: 278 * 279 * <pre> 280 * Precision.increment(new BigDecimal("0.50")) 281 * </pre> 282 * 283 * <p> 284 * For more information on the scale of Java BigDecimal, see {@link java.math.BigDecimal#scale()}. 285 * 286 * @param roundingIncrement 287 * The increment to which to round numbers. 288 * @return A Precision for chaining or passing to the NumberFormatter precision() setter. 289 * @throws IllegalArgumentException if the rounding increment is null or non-positive. 290 * @see NumberFormatter 291 */ increment(BigDecimal roundingIncrement)292 public static Precision increment(BigDecimal roundingIncrement) { 293 if (roundingIncrement != null && roundingIncrement.compareTo(BigDecimal.ZERO) > 0) { 294 return constructIncrement(roundingIncrement); 295 } else { 296 throw new IllegalArgumentException("Rounding increment must be positive and non-null"); 297 } 298 } 299 300 /** 301 * Show numbers rounded and padded according to the rules for the currency unit. The most common 302 * rounding precision settings for currencies include <code>Precision.fixedFraction(2)</code>, 303 * <code>Precision.integer()</code>, and <code>Precision.increment(0.05)</code> for cash transactions 304 * ("nickel rounding"). 305 * 306 * <p> 307 * The exact rounding details will be resolved at runtime based on the currency unit specified in the 308 * NumberFormatter chain. To round according to the rules for one currency while displaying the 309 * symbol for another currency, the withCurrency() method can be called on the return value of this 310 * method. 311 * 312 * @param currencyUsage 313 * Either STANDARD (for digital transactions) or CASH (for transactions where the rounding 314 * increment may be limited by the available denominations of cash or coins). 315 * @return A CurrencyPrecision for chaining or passing to the NumberFormatter precision() setter. 316 * @throws IllegalArgumentException if currencyUsage is null. 317 * @see NumberFormatter 318 */ currency(CurrencyUsage currencyUsage)319 public static CurrencyPrecision currency(CurrencyUsage currencyUsage) { 320 if (currencyUsage != null) { 321 return constructCurrency(currencyUsage); 322 } else { 323 throw new IllegalArgumentException("CurrencyUsage must be non-null"); 324 } 325 } 326 327 /** 328 * Sets a MathContext to use on this Precision. 329 * 330 * @deprecated This API is ICU internal only. 331 * @hide draft / provisional / internal are hidden on OHOS 332 */ 333 @Deprecated withMode(MathContext mathContext)334 public Precision withMode(MathContext mathContext) { 335 if (this.mathContext.equals(mathContext)) { 336 return this; 337 } 338 Precision other = createCopy(); 339 other.mathContext = mathContext; 340 return other; 341 } 342 343 /** Package-private clone method */ createCopy()344 abstract Precision createCopy(); 345 346 /** 347 * @deprecated ICU 60 This API is ICU internal only. 348 * @hide draft / provisional / internal are hidden on OHOS 349 */ 350 @Deprecated apply(DecimalQuantity value)351 public abstract void apply(DecimalQuantity value); 352 353 ////////////////////////// 354 // PACKAGE-PRIVATE APIS // 355 ////////////////////////// 356 357 static final InfiniteRounderImpl NONE = new InfiniteRounderImpl(); 358 359 static final FractionRounderImpl FIXED_FRAC_0 = new FractionRounderImpl(0, 0); 360 static final FractionRounderImpl FIXED_FRAC_2 = new FractionRounderImpl(2, 2); 361 static final FractionRounderImpl DEFAULT_MAX_FRAC_6 = new FractionRounderImpl(0, 6); 362 363 static final SignificantRounderImpl FIXED_SIG_2 = new SignificantRounderImpl(2, 2); 364 static final SignificantRounderImpl FIXED_SIG_3 = new SignificantRounderImpl(3, 3); 365 static final SignificantRounderImpl RANGE_SIG_2_3 = new SignificantRounderImpl(2, 3); 366 367 static final FracSigRounderImpl COMPACT_STRATEGY = new FracSigRounderImpl(0, 0, 2, -1); 368 369 static final IncrementFiveRounderImpl NICKEL = new IncrementFiveRounderImpl(new BigDecimal("0.05"), 2, 2); 370 371 static final CurrencyRounderImpl MONETARY_STANDARD = new CurrencyRounderImpl(CurrencyUsage.STANDARD); 372 static final CurrencyRounderImpl MONETARY_CASH = new CurrencyRounderImpl(CurrencyUsage.CASH); 373 constructInfinite()374 static Precision constructInfinite() { 375 return NONE; 376 } 377 constructFraction(int minFrac, int maxFrac)378 static FractionPrecision constructFraction(int minFrac, int maxFrac) { 379 if (minFrac == 0 && maxFrac == 0) { 380 return FIXED_FRAC_0; 381 } else if (minFrac == 2 && maxFrac == 2) { 382 return FIXED_FRAC_2; 383 } else if (minFrac == 0 && maxFrac == 6) { 384 return DEFAULT_MAX_FRAC_6; 385 } else { 386 return new FractionRounderImpl(minFrac, maxFrac); 387 } 388 } 389 390 /** Assumes that minSig <= maxSig. */ constructSignificant(int minSig, int maxSig)391 static Precision constructSignificant(int minSig, int maxSig) { 392 if (minSig == 2 && maxSig == 2) { 393 return FIXED_SIG_2; 394 } else if (minSig == 3 && maxSig == 3) { 395 return FIXED_SIG_3; 396 } else if (minSig == 2 && maxSig == 3) { 397 return RANGE_SIG_2_3; 398 } else { 399 return new SignificantRounderImpl(minSig, maxSig); 400 } 401 } 402 constructFractionSignificant(FractionPrecision base_, int minSig, int maxSig)403 static Precision constructFractionSignificant(FractionPrecision base_, int minSig, int maxSig) { 404 assert base_ instanceof FractionRounderImpl; 405 FractionRounderImpl base = (FractionRounderImpl) base_; 406 Precision returnValue; 407 if (base.minFrac == 0 && base.maxFrac == 0 && minSig == 2 /* && maxSig == -1 */) { 408 returnValue = COMPACT_STRATEGY; 409 } else { 410 returnValue = new FracSigRounderImpl(base.minFrac, base.maxFrac, minSig, maxSig); 411 } 412 return returnValue.withMode(base.mathContext); 413 } 414 constructIncrement(BigDecimal increment)415 static Precision constructIncrement(BigDecimal increment) { 416 // NOTE: .equals() is what we want, not .compareTo() 417 if (increment.equals(NICKEL.increment)) { 418 return NICKEL; 419 } 420 // Note: For number formatting, the BigDecimal increment is used for IncrementRounderImpl 421 // but not mIncrementOneRounderImpl or IncrementFiveRounderImpl. However, fIncrement is 422 // used in all three when constructing a skeleton. 423 BigDecimal reduced = increment.stripTrailingZeros(); 424 if (reduced.precision() == 1) { 425 int minFrac = increment.scale(); 426 int maxFrac = reduced.scale(); 427 BigInteger digit = reduced.unscaledValue(); 428 if (digit.intValue() == 1) { 429 return new IncrementOneRounderImpl(increment, minFrac, maxFrac); 430 } else if (digit.intValue() == 5) { 431 return new IncrementFiveRounderImpl(increment, minFrac, maxFrac); 432 } 433 } 434 return new IncrementRounderImpl(increment); 435 } 436 constructCurrency(CurrencyUsage usage)437 static CurrencyPrecision constructCurrency(CurrencyUsage usage) { 438 if (usage == CurrencyUsage.STANDARD) { 439 return MONETARY_STANDARD; 440 } else if (usage == CurrencyUsage.CASH) { 441 return MONETARY_CASH; 442 } else { 443 throw new AssertionError(); 444 } 445 } 446 constructFromCurrency(CurrencyPrecision base_, Currency currency)447 static Precision constructFromCurrency(CurrencyPrecision base_, Currency currency) { 448 assert base_ instanceof CurrencyRounderImpl; 449 CurrencyRounderImpl base = (CurrencyRounderImpl) base_; 450 double incrementDouble = currency.getRoundingIncrement(base.usage); 451 Precision returnValue; 452 if (incrementDouble != 0.0) { 453 BigDecimal increment = BigDecimal.valueOf(incrementDouble); 454 returnValue = constructIncrement(increment); 455 } else { 456 int minMaxFrac = currency.getDefaultFractionDigits(base.usage); 457 returnValue = constructFraction(minMaxFrac, minMaxFrac); 458 } 459 return returnValue.withMode(base.mathContext); 460 } 461 462 /** 463 * Returns a valid working Rounder. If the Rounder is a CurrencyRounder, applies the given currency. 464 * Otherwise, simply passes through the argument. 465 * 466 * @param currency 467 * A currency object to use in case the input object needs it. 468 * @return A Rounder object ready for use. 469 */ withLocaleData(Currency currency)470 Precision withLocaleData(Currency currency) { 471 if (this instanceof CurrencyPrecision) { 472 return ((CurrencyPrecision) this).withCurrency(currency); 473 } else { 474 return this; 475 } 476 } 477 478 /** 479 * Rounding endpoint used by Engineering and Compact notation. Chooses the most appropriate 480 * multiplier (magnitude adjustment), applies the adjustment, rounds, and returns the chosen 481 * multiplier. 482 * 483 * <p> 484 * In most cases, this is simple. However, when rounding the number causes it to cross a multiplier 485 * boundary, we need to re-do the rounding. For example, to display 999,999 in Engineering notation 486 * with 2 sigfigs, first you guess the multiplier to be -3. However, then you end up getting 1000E3, 487 * which is not the correct output. You then change your multiplier to be -6, and you get 1.0E6, 488 * which is correct. 489 * 490 * @param input 491 * The quantity to process. 492 * @param producer 493 * Function to call to return a multiplier based on a magnitude. 494 * @return The number of orders of magnitude the input was adjusted by this method. 495 */ chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer)496 int chooseMultiplierAndApply(DecimalQuantity input, MultiplierProducer producer) { 497 // Do not call this method with zero, NaN, or infinity. 498 assert !input.isZeroish(); 499 500 // Perform the first attempt at rounding. 501 int magnitude = input.getMagnitude(); 502 int multiplier = producer.getMultiplier(magnitude); 503 input.adjustMagnitude(multiplier); 504 apply(input); 505 506 // If the number rounded to zero, exit. 507 if (input.isZeroish()) { 508 return multiplier; 509 } 510 511 // If the new magnitude after rounding is the same as it was before rounding, then we are done. 512 // This case applies to most numbers. 513 if (input.getMagnitude() == magnitude + multiplier) { 514 return multiplier; 515 } 516 517 // If the above case DIDN'T apply, then we have a case like 99.9 -> 100 or 999.9 -> 1000: 518 // The number rounded up to the next magnitude. Check if the multiplier changes; if it doesn't, 519 // we do not need to make any more adjustments. 520 int _multiplier = producer.getMultiplier(magnitude + 1); 521 if (multiplier == _multiplier) { 522 return multiplier; 523 } 524 525 // We have a case like 999.9 -> 1000, where the correct output is "1K", not "1000". 526 // Fix the magnitude and re-apply the rounding strategy. 527 input.adjustMagnitude(_multiplier - multiplier); 528 apply(input); 529 return _multiplier; 530 } 531 532 /////////////// 533 // INTERNALS // 534 /////////////// 535 536 static class InfiniteRounderImpl extends Precision { 537 InfiniteRounderImpl()538 public InfiniteRounderImpl() { 539 } 540 541 @Override apply(DecimalQuantity value)542 public void apply(DecimalQuantity value) { 543 value.roundToInfinity(); 544 value.setMinFraction(0); 545 } 546 547 @Override createCopy()548 InfiniteRounderImpl createCopy() { 549 InfiniteRounderImpl copy = new InfiniteRounderImpl(); 550 copy.mathContext = mathContext; 551 return copy; 552 } 553 } 554 555 static class FractionRounderImpl extends FractionPrecision { 556 final int minFrac; 557 final int maxFrac; 558 FractionRounderImpl(int minFrac, int maxFrac)559 public FractionRounderImpl(int minFrac, int maxFrac) { 560 this.minFrac = minFrac; 561 this.maxFrac = maxFrac; 562 } 563 564 @Override apply(DecimalQuantity value)565 public void apply(DecimalQuantity value) { 566 value.roundToMagnitude(getRoundingMagnitudeFraction(maxFrac), mathContext); 567 value.setMinFraction(Math.max(0, -getDisplayMagnitudeFraction(minFrac))); 568 } 569 570 @Override createCopy()571 FractionRounderImpl createCopy() { 572 FractionRounderImpl copy = new FractionRounderImpl(minFrac, maxFrac); 573 copy.mathContext = mathContext; 574 return copy; 575 } 576 } 577 578 static class SignificantRounderImpl extends Precision { 579 final int minSig; 580 final int maxSig; 581 SignificantRounderImpl(int minSig, int maxSig)582 public SignificantRounderImpl(int minSig, int maxSig) { 583 this.minSig = minSig; 584 this.maxSig = maxSig; 585 } 586 587 @Override apply(DecimalQuantity value)588 public void apply(DecimalQuantity value) { 589 value.roundToMagnitude(getRoundingMagnitudeSignificant(value, maxSig), mathContext); 590 value.setMinFraction(Math.max(0, -getDisplayMagnitudeSignificant(value, minSig))); 591 // Make sure that digits are displayed on zero. 592 if (value.isZeroish() && minSig > 0) { 593 value.setMinInteger(1); 594 } 595 } 596 597 /** 598 * Version of {@link #apply} that obeys minInt constraints. Used for scientific notation 599 * compatibility mode. 600 */ apply(DecimalQuantity quantity, int minInt)601 public void apply(DecimalQuantity quantity, int minInt) { 602 assert quantity.isZeroish(); 603 quantity.setMinFraction(minSig - minInt); 604 } 605 606 @Override createCopy()607 SignificantRounderImpl createCopy() { 608 SignificantRounderImpl copy = new SignificantRounderImpl(minSig, maxSig); 609 copy.mathContext = mathContext; 610 return copy; 611 } 612 } 613 614 static class FracSigRounderImpl extends Precision { 615 final int minFrac; 616 final int maxFrac; 617 final int minSig; 618 final int maxSig; 619 FracSigRounderImpl(int minFrac, int maxFrac, int minSig, int maxSig)620 public FracSigRounderImpl(int minFrac, int maxFrac, int minSig, int maxSig) { 621 this.minFrac = minFrac; 622 this.maxFrac = maxFrac; 623 this.minSig = minSig; 624 this.maxSig = maxSig; 625 } 626 627 @Override apply(DecimalQuantity value)628 public void apply(DecimalQuantity value) { 629 int displayMag = getDisplayMagnitudeFraction(minFrac); 630 int roundingMag = getRoundingMagnitudeFraction(maxFrac); 631 if (minSig == -1) { 632 // Max Sig override 633 int candidate = getRoundingMagnitudeSignificant(value, maxSig); 634 roundingMag = Math.max(roundingMag, candidate); 635 } else { 636 // Min Sig override 637 int candidate = getDisplayMagnitudeSignificant(value, minSig); 638 roundingMag = Math.min(roundingMag, candidate); 639 } 640 value.roundToMagnitude(roundingMag, mathContext); 641 value.setMinFraction(Math.max(0, -displayMag)); 642 } 643 644 @Override createCopy()645 FracSigRounderImpl createCopy() { 646 FracSigRounderImpl copy = new FracSigRounderImpl(minFrac, maxFrac, minSig, maxSig); 647 copy.mathContext = mathContext; 648 return copy; 649 } 650 } 651 652 /** 653 * Used for strange increments like 3.14. 654 */ 655 static class IncrementRounderImpl extends Precision { 656 final BigDecimal increment; 657 IncrementRounderImpl(BigDecimal increment)658 public IncrementRounderImpl(BigDecimal increment) { 659 this.increment = increment; 660 } 661 662 @Override apply(DecimalQuantity value)663 public void apply(DecimalQuantity value) { 664 value.roundToIncrement(increment, mathContext); 665 value.setMinFraction(increment.scale()); 666 } 667 668 @Override createCopy()669 IncrementRounderImpl createCopy() { 670 IncrementRounderImpl copy = new IncrementRounderImpl(increment); 671 copy.mathContext = mathContext; 672 return copy; 673 } 674 } 675 676 /** 677 * Used for increments with 1 as the only digit. This is different than fraction 678 * rounding because it supports having additional trailing zeros. For example, this 679 * class is used to round with the increment 0.010. 680 */ 681 static class IncrementOneRounderImpl extends IncrementRounderImpl { 682 final int minFrac; 683 final int maxFrac; 684 IncrementOneRounderImpl(BigDecimal increment, int minFrac, int maxFrac)685 public IncrementOneRounderImpl(BigDecimal increment, int minFrac, int maxFrac) { 686 super(increment); 687 this.minFrac = minFrac; 688 this.maxFrac = maxFrac; 689 } 690 691 @Override apply(DecimalQuantity value)692 public void apply(DecimalQuantity value) { 693 value.roundToMagnitude(-maxFrac, mathContext); 694 value.setMinFraction(minFrac); 695 } 696 697 @Override createCopy()698 IncrementOneRounderImpl createCopy() { 699 IncrementOneRounderImpl copy = new IncrementOneRounderImpl(increment, minFrac, maxFrac); 700 copy.mathContext = mathContext; 701 return copy; 702 } 703 } 704 705 /** 706 * Used for increments with 5 as the only digit (nickel rounding). 707 */ 708 static class IncrementFiveRounderImpl extends IncrementRounderImpl { 709 final int minFrac; 710 final int maxFrac; 711 IncrementFiveRounderImpl(BigDecimal increment, int minFrac, int maxFrac)712 public IncrementFiveRounderImpl(BigDecimal increment, int minFrac, int maxFrac) { 713 super(increment); 714 this.minFrac = minFrac; 715 this.maxFrac = maxFrac; 716 } 717 718 @Override apply(DecimalQuantity value)719 public void apply(DecimalQuantity value) { 720 value.roundToNickel(-maxFrac, mathContext); 721 value.setMinFraction(minFrac); 722 } 723 724 @Override createCopy()725 IncrementFiveRounderImpl createCopy() { 726 IncrementFiveRounderImpl copy = new IncrementFiveRounderImpl(increment, minFrac, maxFrac); 727 copy.mathContext = mathContext; 728 return copy; 729 } 730 } 731 732 static class CurrencyRounderImpl extends CurrencyPrecision { 733 final CurrencyUsage usage; 734 CurrencyRounderImpl(CurrencyUsage usage)735 public CurrencyRounderImpl(CurrencyUsage usage) { 736 this.usage = usage; 737 } 738 739 @Override apply(DecimalQuantity value)740 public void apply(DecimalQuantity value) { 741 // Call .withCurrency() before .apply()! 742 throw new AssertionError(); 743 } 744 745 @Override createCopy()746 CurrencyRounderImpl createCopy() { 747 CurrencyRounderImpl copy = new CurrencyRounderImpl(usage); 748 copy.mathContext = mathContext; 749 return copy; 750 } 751 } 752 getRoundingMagnitudeFraction(int maxFrac)753 private static int getRoundingMagnitudeFraction(int maxFrac) { 754 if (maxFrac == -1) { 755 return Integer.MIN_VALUE; 756 } 757 return -maxFrac; 758 } 759 getRoundingMagnitudeSignificant(DecimalQuantity value, int maxSig)760 private static int getRoundingMagnitudeSignificant(DecimalQuantity value, int maxSig) { 761 if (maxSig == -1) { 762 return Integer.MIN_VALUE; 763 } 764 int magnitude = value.isZeroish() ? 0 : value.getMagnitude(); 765 return magnitude - maxSig + 1; 766 } 767 getDisplayMagnitudeFraction(int minFrac)768 private static int getDisplayMagnitudeFraction(int minFrac) { 769 if (minFrac == 0) { 770 return Integer.MAX_VALUE; 771 } 772 return -minFrac; 773 } 774 getDisplayMagnitudeSignificant(DecimalQuantity value, int minSig)775 private static int getDisplayMagnitudeSignificant(DecimalQuantity value, int minSig) { 776 int magnitude = value.isZeroish() ? 0 : value.getMagnitude(); 777 return magnitude - minSig + 1; 778 } 779 } 780