1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package java.text; 26 27 import java.io.IOException; 28 import java.io.InvalidObjectException; 29 import java.io.ObjectInputStream; 30 import java.math.BigDecimal; 31 import java.math.BigInteger; 32 import java.math.RoundingMode; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.Objects; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.concurrent.atomic.AtomicLong; 40 41 42 /** 43 * <p> 44 * {@code CompactNumberFormat} is a concrete subclass of {@code NumberFormat} 45 * that formats a decimal number in its compact form. 46 * 47 * The compact number formatting is designed for the environment where the space 48 * is limited, and the formatted string can be displayed in that limited space. 49 * It is defined by LDML's specification for 50 * <a href = "http://unicode.org/reports/tr35/tr35-numbers.html#Compact_Number_Formats"> 51 * Compact Number Formats</a>. A compact number formatting refers 52 * to the representation of a number in a shorter form, based on the patterns 53 * provided for a given locale. 54 * 55 * <p> 56 * For example: 57 * <br>In the {@link java.util.Locale#US US locale}, {@code 1000} can be formatted 58 * as {@code "1K"}, and {@code 1000000} as {@code "1M"}, depending upon the 59 * <a href = "#compact_number_style" >style</a> used. 60 * <br>In the {@code "hi_IN"} locale, {@code 1000} can be formatted as 61 * "1 \u0939\u091C\u093C\u093E\u0930", and {@code 50000000} as "5 \u0915.", 62 * depending upon the <a href = "#compact_number_style" >style</a> used. 63 * 64 * <p> 65 * To obtain a {@code CompactNumberFormat} for a locale, use one 66 * of the factory methods given by {@code NumberFormat} for compact number 67 * formatting. For example, 68 * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}. 69 * 70 * <blockquote><pre> 71 * NumberFormat fmt = NumberFormat.getCompactNumberInstance( 72 * new Locale("hi", "IN"), NumberFormat.Style.SHORT); 73 * String result = fmt.format(1000); 74 * </pre></blockquote> 75 * 76 * <h2><a id="compact_number_style">Style</a></h2> 77 * <p> 78 * A number can be formatted in the compact forms with two different 79 * styles, {@link NumberFormat.Style#SHORT SHORT} 80 * and {@link NumberFormat.Style#LONG LONG}. Use 81 * {@link NumberFormat#getCompactNumberInstance(Locale, Style)} for formatting and 82 * parsing a number in {@link NumberFormat.Style#SHORT SHORT} or 83 * {@link NumberFormat.Style#LONG LONG} compact form, 84 * where the given {@code Style} parameter requests the desired 85 * format. A {@link NumberFormat.Style#SHORT SHORT} style 86 * compact number instance in the {@link java.util.Locale#US US locale} formats 87 * {@code 10000} as {@code "10K"}. However, a 88 * {@link NumberFormat.Style#LONG LONG} style instance in same locale 89 * formats {@code 10000} as {@code "10 thousand"}. 90 * 91 * <h2><a id="compact_number_patterns">Compact Number Patterns</a></h2> 92 * <p> 93 * The compact number patterns are represented in a series of patterns where each 94 * pattern is used to format a range of numbers. An example of 95 * {@link NumberFormat.Style#SHORT SHORT} styled compact number patterns 96 * for the {@link java.util.Locale#US US locale} is {@code {"", "", "", "0K", 97 * "00K", "000K", "0M", "00M", "000M", "0B", "00B", "000B", "0T", "00T", "000T"}}, 98 * ranging from {@code 10}<sup>{@code 0}</sup> to {@code 10}<sup>{@code 14}</sup>. 99 * There can be any number of patterns and they are 100 * strictly index based starting from the range {@code 10}<sup>{@code 0}</sup>. 101 * For example, in the above patterns, pattern at index 3 102 * ({@code "0K"}) is used for formatting {@code number >= 1000 and number < 10000}, 103 * pattern at index 4 ({@code "00K"}) is used for formatting 104 * {@code number >= 10000 and number < 100000} and so on. In most of the locales, 105 * patterns with the range 106 * {@code 10}<sup>{@code 0}</sup>-{@code 10}<sup>{@code 2}</sup> are empty 107 * strings, which implicitly means a special pattern {@code "0"}. 108 * A special pattern {@code "0"} is used for any range which does not contain 109 * a compact pattern. This special pattern can appear explicitly for any specific 110 * range, or considered as a default pattern for an empty string. 111 * <p> 112 * A compact pattern has the following syntax: 113 * <blockquote><pre> 114 * <i>Pattern:</i> 115 * <i>PositivePattern</i> 116 * <i>PositivePattern</i> <i>[; NegativePattern]<sub>optional</sub></i> 117 * <i>PositivePattern:</i> 118 * <i>Prefix<sub>optional</sub></i> <i>MinimumInteger</i> <i>Suffix<sub>optional</sub></i> 119 * <i>NegativePattern:</i> 120 * <i>Prefix<sub>optional</sub></i> <i>MinimumInteger</i> <i>Suffix<sub>optional</sub></i> 121 * <i>Prefix:</i> 122 * Any Unicode characters except \uFFFE, \uFFFF, and 123 * <a href = "DecimalFormat.html#special_pattern_character">special characters</a> 124 * <i>Suffix:</i> 125 * Any Unicode characters except \uFFFE, \uFFFF, and 126 * <a href = "DecimalFormat.html#special_pattern_character">special characters</a> 127 * <i>MinimumInteger:</i> 128 * 0 129 * 0 <i>MinimumInteger</i> 130 * </pre></blockquote> 131 * 132 * A compact pattern contains a positive and negative subpattern 133 * separated by a subpattern boundary character {@code ';' (U+003B)}, 134 * for example, {@code "0K;-0K"}. Each subpattern has a prefix, 135 * minimum integer digits, and suffix. The negative subpattern 136 * is optional, if absent, then the positive subpattern prefixed with the 137 * minus sign ({@code '-' U+002D HYPHEN-MINUS}) is used as the negative 138 * subpattern. That is, {@code "0K"} alone is equivalent to {@code "0K;-0K"}. 139 * If there is an explicit negative subpattern, it serves only to specify 140 * the negative prefix and suffix. The number of minimum integer digits, 141 * and other characteristics are all the same as the positive pattern. 142 * That means that {@code "0K;-00K"} produces precisely the same behavior 143 * as {@code "0K;-0K"}. 144 * 145 * <p> 146 * Many characters in a compact pattern are taken literally, they are matched 147 * during parsing and output unchanged during formatting. 148 * <a href = "DecimalFormat.html#special_pattern_character">Special characters</a>, 149 * on the other hand, stand for other characters, strings, or classes of 150 * characters. They must be quoted, using single quote {@code ' (U+0027)} 151 * unless noted otherwise, if they are to appear in the prefix or suffix 152 * as literals. For example, 0\u0915'.'. 153 * 154 * <h2>Formatting</h2> 155 * The default formatting behavior returns a formatted string with no fractional 156 * digits, however users can use the {@link #setMinimumFractionDigits(int)} 157 * method to include the fractional part. 158 * The number {@code 1000.0} or {@code 1000} is formatted as {@code "1K"} 159 * not {@code "1.00K"} (in the {@link java.util.Locale#US US locale}). For this 160 * reason, the patterns provided for formatting contain only the minimum 161 * integer digits, prefix and/or suffix, but no fractional part. 162 * For example, patterns used are {@code {"", "", "", 0K, 00K, ...}}. If the pattern 163 * selected for formatting a number is {@code "0"} (special pattern), 164 * either explicit or defaulted, then the general number formatting provided by 165 * {@link java.text.DecimalFormat DecimalFormat} 166 * for the specified locale is used. 167 * 168 * <h2>Parsing</h2> 169 * The default parsing behavior does not allow a grouping separator until 170 * grouping used is set to {@code true} by using 171 * {@link #setGroupingUsed(boolean)}. The parsing of the fractional part 172 * depends on the {@link #isParseIntegerOnly()}. For example, if the 173 * parse integer only is set to true, then the fractional part is skipped. 174 * 175 * <h2>Rounding</h2> 176 * {@code CompactNumberFormat} provides rounding modes defined in 177 * {@link java.math.RoundingMode} for formatting. By default, it uses 178 * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}. 179 * 180 * @see CompactNumberFormat.Style 181 * @see NumberFormat 182 * @see DecimalFormat 183 * @since 12 184 */ 185 public final class CompactNumberFormat extends NumberFormat { 186 187 @java.io.Serial 188 private static final long serialVersionUID = 7128367218649234678L; 189 190 /** 191 * The patterns for compact form of numbers for this 192 * {@code CompactNumberFormat}. A possible example is 193 * {@code {"", "", "", "0K", "00K", "000K", "0M", "00M", "000M", "0B", 194 * "00B", "000B", "0T", "00T", "000T"}} ranging from 195 * {@code 10}<sup>{@code 0}</sup>-{@code 10}<sup>{@code 14}</sup>, 196 * where each pattern is used to format a range of numbers. 197 * For example, {@code "0K"} is used for formatting 198 * {@code number >= 1000 and number < 10000}, {@code "00K"} is used for 199 * formatting {@code number >= 10000 and number < 100000} and so on. 200 * This field must not be {@code null}. 201 * 202 * @serial 203 */ 204 private String[] compactPatterns; 205 206 /** 207 * List of positive prefix patterns of this formatter's 208 * compact number patterns. 209 */ 210 private transient List<String> positivePrefixPatterns; 211 212 /** 213 * List of negative prefix patterns of this formatter's 214 * compact number patterns. 215 */ 216 private transient List<String> negativePrefixPatterns; 217 218 /** 219 * List of positive suffix patterns of this formatter's 220 * compact number patterns. 221 */ 222 private transient List<String> positiveSuffixPatterns; 223 224 /** 225 * List of negative suffix patterns of this formatter's 226 * compact number patterns. 227 */ 228 private transient List<String> negativeSuffixPatterns; 229 230 /** 231 * List of divisors of this formatter's compact number patterns. 232 * Divisor can be either Long or BigInteger (if the divisor value goes 233 * beyond long boundary) 234 */ 235 private transient List<Number> divisors; 236 237 /** 238 * The {@code DecimalFormatSymbols} object used by this format. 239 * It contains the symbols used to format numbers. For example, 240 * the grouping separator, decimal separator, and so on. 241 * This field must not be {@code null}. 242 * 243 * @serial 244 * @see DecimalFormatSymbols 245 */ 246 private DecimalFormatSymbols symbols; 247 248 /** 249 * The decimal pattern which is used for formatting the numbers 250 * matching special pattern "0". This field must not be {@code null}. 251 * 252 * @serial 253 * @see DecimalFormat 254 */ 255 private final String decimalPattern; 256 257 /** 258 * A {@code DecimalFormat} used by this format for getting corresponding 259 * general number formatting behavior for compact numbers. 260 * 261 */ 262 private transient DecimalFormat decimalFormat; 263 264 /** 265 * A {@code DecimalFormat} used by this format for getting general number 266 * formatting behavior for the numbers which can't be represented as compact 267 * numbers. For example, number matching the special pattern "0" are 268 * formatted through general number format pattern provided by 269 * {@link java.text.DecimalFormat DecimalFormat} 270 * for the specified locale. 271 * 272 */ 273 private transient DecimalFormat defaultDecimalFormat; 274 275 /** 276 * The number of digits between grouping separators in the integer portion 277 * of a compact number. For the grouping to work while formatting, this 278 * field needs to be greater than 0 with grouping used set as true. 279 * This field must not be negative. 280 * 281 * @serial 282 */ 283 private byte groupingSize = 0; 284 285 /** 286 * Returns whether the {@link #parse(String, ParsePosition)} 287 * method returns {@code BigDecimal}. 288 * 289 * @serial 290 */ 291 private boolean parseBigDecimal = false; 292 293 /** 294 * The {@code RoundingMode} used in this compact number format. 295 * This field must not be {@code null}. 296 * 297 * @serial 298 */ 299 private RoundingMode roundingMode = RoundingMode.HALF_EVEN; 300 301 /** 302 * Special pattern used for compact numbers 303 */ 304 private static final String SPECIAL_PATTERN = "0"; 305 306 /** 307 * Multiplier for compact pattern range. In 308 * the list compact patterns each compact pattern 309 * specify the range with the multiplication factor of 10 310 * of its previous compact pattern range. 311 * For example, 10^0, 10^1, 10^2, 10^3, 10^4... 312 * 313 */ 314 private static final int RANGE_MULTIPLIER = 10; 315 316 /** 317 * Creates a {@code CompactNumberFormat} using the given decimal pattern, 318 * decimal format symbols and compact patterns. 319 * To obtain the instance of {@code CompactNumberFormat} with the standard 320 * compact patterns for a {@code Locale} and {@code Style}, 321 * it is recommended to use the factory methods given by 322 * {@code NumberFormat} for compact number formatting. For example, 323 * {@link NumberFormat#getCompactNumberInstance(Locale, Style)}. 324 * 325 * @param decimalPattern a decimal pattern for general number formatting 326 * @param symbols the set of symbols to be used 327 * @param compactPatterns an array of 328 * <a href = "CompactNumberFormat.html#compact_number_patterns"> 329 * compact number patterns</a> 330 * @throws NullPointerException if any of the given arguments is 331 * {@code null} 332 * @throws IllegalArgumentException if the given {@code decimalPattern} or the 333 * {@code compactPatterns} array contains an invalid pattern 334 * or if a {@code null} appears in the array of compact 335 * patterns 336 * @see DecimalFormat#DecimalFormat(java.lang.String, DecimalFormatSymbols) 337 * @see DecimalFormatSymbols 338 */ 339 public CompactNumberFormat(String decimalPattern, 340 DecimalFormatSymbols symbols, String[] compactPatterns) { 341 342 Objects.requireNonNull(decimalPattern, "decimalPattern"); 343 Objects.requireNonNull(symbols, "symbols"); 344 Objects.requireNonNull(compactPatterns, "compactPatterns"); 345 346 this.symbols = symbols; 347 // Instantiating the DecimalFormat with "0" pattern; this acts just as a 348 // basic pattern; the properties (For example, prefix/suffix) 349 // are later computed based on the compact number formatting process. 350 decimalFormat = new DecimalFormat(SPECIAL_PATTERN, this.symbols); 351 352 // Initializing the super class state with the decimalFormat values 353 // to represent this CompactNumberFormat. 354 // For setting the digits counts, use overridden setXXX methods of this 355 // CompactNumberFormat, as it performs check with the max range allowed 356 // for compact number formatting 357 setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits()); 358 setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits()); 359 setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits()); 360 setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits()); 361 362 super.setGroupingUsed(decimalFormat.isGroupingUsed()); 363 super.setParseIntegerOnly(decimalFormat.isParseIntegerOnly()); 364 365 this.compactPatterns = compactPatterns; 366 367 // DecimalFormat used for formatting numbers with special pattern "0". 368 // Formatting is delegated to the DecimalFormat's number formatting 369 // with no fraction digits 370 this.decimalPattern = decimalPattern; 371 defaultDecimalFormat = new DecimalFormat(this.decimalPattern, 372 this.symbols); 373 defaultDecimalFormat.setMaximumFractionDigits(0); 374 // Process compact patterns to extract the prefixes, suffixes and 375 // divisors 376 processCompactPatterns(); 377 } 378 379 /** 380 * Formats a number to produce a string representing its compact form. 381 * The number can be of any subclass of {@link java.lang.Number}. 382 * @param number the number to format 383 * @param toAppendTo the {@code StringBuffer} to which the formatted 384 * text is to be appended 385 * @param fieldPosition keeps track on the position of the field within 386 * the returned string. For example, for formatting 387 * a number {@code 123456789} in the 388 * {@link java.util.Locale#US US locale}, 389 * if the given {@code fieldPosition} is 390 * {@link NumberFormat#INTEGER_FIELD}, the begin 391 * index and end index of {@code fieldPosition} 392 * will be set to 0 and 3, respectively for the 393 * output string {@code 123M}. Similarly, positions 394 * of the prefix and the suffix fields can be 395 * obtained using {@link NumberFormat.Field#PREFIX} 396 * and {@link NumberFormat.Field#SUFFIX} respectively. 397 * @return the {@code StringBuffer} passed in as {@code toAppendTo} 398 * @throws IllegalArgumentException if {@code number} is 399 * {@code null} or not an instance of {@code Number} 400 * @throws NullPointerException if {@code toAppendTo} or 401 * {@code fieldPosition} is {@code null} 402 * @throws ArithmeticException if rounding is needed with rounding 403 * mode being set to {@code RoundingMode.UNNECESSARY} 404 * @see FieldPosition 405 */ 406 @Override 407 public final StringBuffer format(Object number, 408 StringBuffer toAppendTo, 409 FieldPosition fieldPosition) { 410 411 if (number == null) { 412 throw new IllegalArgumentException("Cannot format null as a number"); 413 } 414 415 if (number instanceof Long || number instanceof Integer 416 || number instanceof Short || number instanceof Byte 417 || number instanceof AtomicInteger 418 || number instanceof AtomicLong 419 || (number instanceof BigInteger 420 && ((BigInteger) number).bitLength() < 64)) { 421 return format(((Number) number).longValue(), toAppendTo, 422 fieldPosition); 423 } else if (number instanceof BigDecimal) { 424 return format((BigDecimal) number, toAppendTo, fieldPosition); 425 } else if (number instanceof BigInteger) { 426 return format((BigInteger) number, toAppendTo, fieldPosition); 427 } else if (number instanceof Number) { 428 return format(((Number) number).doubleValue(), toAppendTo, fieldPosition); 429 } else { 430 throw new IllegalArgumentException("Cannot format " 431 + number.getClass().getName() + " as a number"); 432 } 433 } 434 435 /** 436 * Formats a double to produce a string representing its compact form. 437 * @param number the double number to format 438 * @param result where the text is to be appended 439 * @param fieldPosition keeps track on the position of the field within 440 * the returned string. For example, to format 441 * a number {@code 1234567.89} in the 442 * {@link java.util.Locale#US US locale} 443 * if the given {@code fieldPosition} is 444 * {@link NumberFormat#INTEGER_FIELD}, the begin 445 * index and end index of {@code fieldPosition} 446 * will be set to 0 and 1, respectively for the 447 * output string {@code 1M}. Similarly, positions 448 * of the prefix and the suffix fields can be 449 * obtained using {@link NumberFormat.Field#PREFIX} 450 * and {@link NumberFormat.Field#SUFFIX} respectively. 451 * @return the {@code StringBuffer} passed in as {@code result} 452 * @throws NullPointerException if {@code result} or 453 * {@code fieldPosition} is {@code null} 454 * @throws ArithmeticException if rounding is needed with rounding 455 * mode being set to {@code RoundingMode.UNNECESSARY} 456 * @see FieldPosition 457 */ 458 @Override 459 public StringBuffer format(double number, StringBuffer result, 460 FieldPosition fieldPosition) { 461 462 fieldPosition.setBeginIndex(0); 463 fieldPosition.setEndIndex(0); 464 return format(number, result, fieldPosition.getFieldDelegate()); 465 } 466 467 private StringBuffer format(double number, StringBuffer result, 468 FieldDelegate delegate) { 469 470 boolean nanOrInfinity = decimalFormat.handleNaN(number, result, delegate); 471 if (nanOrInfinity) { 472 return result; 473 } 474 475 boolean isNegative = ((number < 0.0) 476 || (number == 0.0 && 1 / number < 0.0)); 477 478 nanOrInfinity = decimalFormat.handleInfinity(number, result, delegate, isNegative); 479 if (nanOrInfinity) { 480 return result; 481 } 482 483 // Round the double value with min fraction digits, the integer 484 // part of the rounded value is used for matching the compact 485 // number pattern 486 // For example, if roundingMode is HALF_UP with min fraction 487 // digits = 0, the number 999.6 should round up 488 // to 1000 and outputs 1K/thousand in "en_US" locale 489 DigitList dList = new DigitList(); 490 dList.setRoundingMode(getRoundingMode()); 491 number = isNegative ? -number : number; 492 dList.set(isNegative, number, getMinimumFractionDigits()); 493 494 double roundedNumber = dList.getDouble(); 495 int compactDataIndex = selectCompactPattern((long) roundedNumber); 496 if (compactDataIndex != -1) { 497 String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex) 498 : positivePrefixPatterns.get(compactDataIndex); 499 String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex) 500 : positiveSuffixPatterns.get(compactDataIndex); 501 502 if (!prefix.isEmpty() || !suffix.isEmpty()) { 503 appendPrefix(result, prefix, delegate); 504 long divisor = (Long) divisors.get(compactDataIndex); 505 roundedNumber = roundedNumber / divisor; 506 decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits()); 507 decimalFormat.subformatNumber(result, delegate, isNegative, 508 false, getMaximumIntegerDigits(), getMinimumIntegerDigits(), 509 getMaximumFractionDigits(), getMinimumFractionDigits()); 510 appendSuffix(result, suffix, delegate); 511 } else { 512 defaultDecimalFormat.doubleSubformat(number, result, delegate, isNegative); 513 } 514 } else { 515 defaultDecimalFormat.doubleSubformat(number, result, delegate, isNegative); 516 } 517 return result; 518 } 519 520 /** 521 * Formats a long to produce a string representing its compact form. 522 * @param number the long number to format 523 * @param result where the text is to be appended 524 * @param fieldPosition keeps track on the position of the field within 525 * the returned string. For example, to format 526 * a number {@code 123456789} in the 527 * {@link java.util.Locale#US US locale}, 528 * if the given {@code fieldPosition} is 529 * {@link NumberFormat#INTEGER_FIELD}, the begin 530 * index and end index of {@code fieldPosition} 531 * will be set to 0 and 3, respectively for the 532 * output string {@code 123M}. Similarly, positions 533 * of the prefix and the suffix fields can be 534 * obtained using {@link NumberFormat.Field#PREFIX} 535 * and {@link NumberFormat.Field#SUFFIX} respectively. 536 * @return the {@code StringBuffer} passed in as {@code result} 537 * @throws NullPointerException if {@code result} or 538 * {@code fieldPosition} is {@code null} 539 * @throws ArithmeticException if rounding is needed with rounding 540 * mode being set to {@code RoundingMode.UNNECESSARY} 541 * @see FieldPosition 542 */ 543 @Override 544 public StringBuffer format(long number, StringBuffer result, 545 FieldPosition fieldPosition) { 546 547 fieldPosition.setBeginIndex(0); 548 fieldPosition.setEndIndex(0); 549 return format(number, result, fieldPosition.getFieldDelegate()); 550 } 551 552 private StringBuffer format(long number, StringBuffer result, FieldDelegate delegate) { 553 boolean isNegative = (number < 0); 554 if (isNegative) { 555 number = -number; 556 } 557 558 if (number < 0) { // LONG_MIN 559 BigInteger bigIntegerValue = BigInteger.valueOf(number); 560 return format(bigIntegerValue, result, delegate, true); 561 } 562 563 int compactDataIndex = selectCompactPattern(number); 564 if (compactDataIndex != -1) { 565 String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex) 566 : positivePrefixPatterns.get(compactDataIndex); 567 String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex) 568 : positiveSuffixPatterns.get(compactDataIndex); 569 if (!prefix.isEmpty() || !suffix.isEmpty()) { 570 appendPrefix(result, prefix, delegate); 571 long divisor = (Long) divisors.get(compactDataIndex); 572 if ((number % divisor == 0)) { 573 number = number / divisor; 574 decimalFormat.setDigitList(number, isNegative, 0); 575 decimalFormat.subformatNumber(result, delegate, 576 isNegative, true, getMaximumIntegerDigits(), 577 getMinimumIntegerDigits(), getMaximumFractionDigits(), 578 getMinimumFractionDigits()); 579 } else { 580 // To avoid truncation of fractional part store 581 // the value in double and follow double path instead of 582 // long path 583 double dNumber = (double) number / divisor; 584 decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits()); 585 decimalFormat.subformatNumber(result, delegate, 586 isNegative, false, getMaximumIntegerDigits(), 587 getMinimumIntegerDigits(), getMaximumFractionDigits(), 588 getMinimumFractionDigits()); 589 } 590 appendSuffix(result, suffix, delegate); 591 } else { 592 number = isNegative ? -number : number; 593 defaultDecimalFormat.format(number, result, delegate); 594 } 595 } else { 596 number = isNegative ? -number : number; 597 defaultDecimalFormat.format(number, result, delegate); 598 } 599 return result; 600 } 601 602 /** 603 * Formats a BigDecimal to produce a string representing its compact form. 604 * @param number the BigDecimal number to format 605 * @param result where the text is to be appended 606 * @param fieldPosition keeps track on the position of the field within 607 * the returned string. For example, to format 608 * a number {@code 1234567.89} in the 609 * {@link java.util.Locale#US US locale}, 610 * if the given {@code fieldPosition} is 611 * {@link NumberFormat#INTEGER_FIELD}, the begin 612 * index and end index of {@code fieldPosition} 613 * will be set to 0 and 1, respectively for the 614 * output string {@code 1M}. Similarly, positions 615 * of the prefix and the suffix fields can be 616 * obtained using {@link NumberFormat.Field#PREFIX} 617 * and {@link NumberFormat.Field#SUFFIX} respectively. 618 * @return the {@code StringBuffer} passed in as {@code result} 619 * @throws ArithmeticException if rounding is needed with rounding 620 * mode being set to {@code RoundingMode.UNNECESSARY} 621 * @throws NullPointerException if any of the given parameter 622 * is {@code null} 623 * @see FieldPosition 624 */ 625 private StringBuffer format(BigDecimal number, StringBuffer result, 626 FieldPosition fieldPosition) { 627 628 Objects.requireNonNull(number); 629 fieldPosition.setBeginIndex(0); 630 fieldPosition.setEndIndex(0); 631 return format(number, result, fieldPosition.getFieldDelegate()); 632 } 633 634 private StringBuffer format(BigDecimal number, StringBuffer result, 635 FieldDelegate delegate) { 636 637 boolean isNegative = number.signum() == -1; 638 if (isNegative) { 639 number = number.negate(); 640 } 641 642 // Round the value with min fraction digits, the integer 643 // part of the rounded value is used for matching the compact 644 // number pattern 645 // For example, If roundingMode is HALF_UP with min fraction digits = 0, 646 // the number 999.6 should round up 647 // to 1000 and outputs 1K/thousand in "en_US" locale 648 number = number.setScale(getMinimumFractionDigits(), getRoundingMode()); 649 650 int compactDataIndex; 651 if (number.toBigInteger().bitLength() < 64) { 652 compactDataIndex = selectCompactPattern(number.toBigInteger().longValue()); 653 } else { 654 compactDataIndex = selectCompactPattern(number.toBigInteger()); 655 } 656 657 if (compactDataIndex != -1) { 658 String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex) 659 : positivePrefixPatterns.get(compactDataIndex); 660 String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex) 661 : positiveSuffixPatterns.get(compactDataIndex); 662 if (!prefix.isEmpty() || !suffix.isEmpty()) { 663 appendPrefix(result, prefix, delegate); 664 Number divisor = divisors.get(compactDataIndex); 665 number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode()); 666 decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits()); 667 decimalFormat.subformatNumber(result, delegate, isNegative, 668 false, getMaximumIntegerDigits(), getMinimumIntegerDigits(), 669 getMaximumFractionDigits(), getMinimumFractionDigits()); 670 appendSuffix(result, suffix, delegate); 671 } else { 672 number = isNegative ? number.negate() : number; 673 defaultDecimalFormat.format(number, result, delegate); 674 } 675 } else { 676 number = isNegative ? number.negate() : number; 677 defaultDecimalFormat.format(number, result, delegate); 678 } 679 return result; 680 } 681 682 /** 683 * Formats a BigInteger to produce a string representing its compact form. 684 * @param number the BigInteger number to format 685 * @param result where the text is to be appended 686 * @param fieldPosition keeps track on the position of the field within 687 * the returned string. For example, to format 688 * a number {@code 123456789} in the 689 * {@link java.util.Locale#US US locale}, 690 * if the given {@code fieldPosition} is 691 * {@link NumberFormat#INTEGER_FIELD}, the begin index 692 * and end index of {@code fieldPosition} will be set 693 * to 0 and 3, respectively for the output string 694 * {@code 123M}. Similarly, positions of the 695 * prefix and the suffix fields can be obtained 696 * using {@link NumberFormat.Field#PREFIX} and 697 * {@link NumberFormat.Field#SUFFIX} respectively. 698 * @return the {@code StringBuffer} passed in as {@code result} 699 * @throws ArithmeticException if rounding is needed with rounding 700 * mode being set to {@code RoundingMode.UNNECESSARY} 701 * @throws NullPointerException if any of the given parameter 702 * is {@code null} 703 * @see FieldPosition 704 */ 705 private StringBuffer format(BigInteger number, StringBuffer result, 706 FieldPosition fieldPosition) { 707 708 Objects.requireNonNull(number); 709 fieldPosition.setBeginIndex(0); 710 fieldPosition.setEndIndex(0); 711 return format(number, result, fieldPosition.getFieldDelegate(), false); 712 } 713 714 private StringBuffer format(BigInteger number, StringBuffer result, 715 FieldDelegate delegate, boolean formatLong) { 716 717 boolean isNegative = number.signum() == -1; 718 if (isNegative) { 719 number = number.negate(); 720 } 721 722 int compactDataIndex = selectCompactPattern(number); 723 if (compactDataIndex != -1) { 724 String prefix = isNegative ? negativePrefixPatterns.get(compactDataIndex) 725 : positivePrefixPatterns.get(compactDataIndex); 726 String suffix = isNegative ? negativeSuffixPatterns.get(compactDataIndex) 727 : positiveSuffixPatterns.get(compactDataIndex); 728 if (!prefix.isEmpty() || !suffix.isEmpty()) { 729 appendPrefix(result, prefix, delegate); 730 Number divisor = divisors.get(compactDataIndex); 731 if (number.mod(new BigInteger(divisor.toString())) 732 .compareTo(BigInteger.ZERO) == 0) { 733 number = number.divide(new BigInteger(divisor.toString())); 734 735 decimalFormat.setDigitList(number, isNegative, 0); 736 decimalFormat.subformatNumber(result, delegate, 737 isNegative, true, getMaximumIntegerDigits(), 738 getMinimumIntegerDigits(), getMaximumFractionDigits(), 739 getMinimumFractionDigits()); 740 } else { 741 // To avoid truncation of fractional part store the value in 742 // BigDecimal and follow BigDecimal path instead of 743 // BigInteger path 744 BigDecimal nDecimal = new BigDecimal(number) 745 .divide(new BigDecimal(divisor.toString()), getRoundingMode()); 746 decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits()); 747 decimalFormat.subformatNumber(result, delegate, 748 isNegative, false, getMaximumIntegerDigits(), 749 getMinimumIntegerDigits(), getMaximumFractionDigits(), 750 getMinimumFractionDigits()); 751 } 752 appendSuffix(result, suffix, delegate); 753 } else { 754 number = isNegative ? number.negate() : number; 755 defaultDecimalFormat.format(number, result, delegate, formatLong); 756 } 757 } else { 758 number = isNegative ? number.negate() : number; 759 defaultDecimalFormat.format(number, result, delegate, formatLong); 760 } 761 return result; 762 } 763 764 /** 765 * Appends the {@code prefix} to the {@code result} and also set the 766 * {@code NumberFormat.Field.SIGN} and {@code NumberFormat.Field.PREFIX} 767 * field positions. 768 * @param result the resulting string, where the pefix is to be appended 769 * @param prefix prefix to append 770 * @param delegate notified of the locations of 771 * {@code NumberFormat.Field.SIGN} and 772 * {@code NumberFormat.Field.PREFIX} fields 773 */ 774 private void appendPrefix(StringBuffer result, String prefix, 775 FieldDelegate delegate) { 776 append(result, expandAffix(prefix), delegate, 777 getFieldPositions(prefix, NumberFormat.Field.PREFIX)); 778 } 779 780 /** 781 * Appends {@code suffix} to the {@code result} and also set the 782 * {@code NumberFormat.Field.SIGN} and {@code NumberFormat.Field.SUFFIX} 783 * field positions. 784 * @param result the resulting string, where the suffix is to be appended 785 * @param suffix suffix to append 786 * @param delegate notified of the locations of 787 * {@code NumberFormat.Field.SIGN} and 788 * {@code NumberFormat.Field.SUFFIX} fields 789 */ 790 private void appendSuffix(StringBuffer result, String suffix, 791 FieldDelegate delegate) { 792 append(result, expandAffix(suffix), delegate, 793 getFieldPositions(suffix, NumberFormat.Field.SUFFIX)); 794 } 795 796 /** 797 * Appends the {@code string} to the {@code result}. 798 * {@code delegate} is notified of SIGN, PREFIX and/or SUFFIX 799 * field positions. 800 * @param result the resulting string, where the text is to be appended 801 * @param string the text to append 802 * @param delegate notified of the locations of sub fields 803 * @param positions a list of {@code FieldPostion} in the given 804 * string 805 */ 806 private void append(StringBuffer result, String string, 807 FieldDelegate delegate, List<FieldPosition> positions) { 808 if (!string.isEmpty()) { 809 int start = result.length(); 810 result.append(string); 811 for (int counter = 0; counter < positions.size(); counter++) { 812 FieldPosition fp = positions.get(counter); 813 Format.Field attribute = fp.getFieldAttribute(); 814 delegate.formatted(attribute, attribute, 815 start + fp.getBeginIndex(), 816 start + fp.getEndIndex(), result); 817 } 818 } 819 } 820 821 /** 822 * Expands an affix {@code pattern} into a string of literals. 823 * All characters in the pattern are literals unless prefixed by QUOTE. 824 * The character prefixed by QUOTE is replaced with its respective 825 * localized literal. 826 * @param pattern a compact number pattern affix 827 * @return an expanded affix 828 */ 829 private String expandAffix(String pattern) { 830 // Return if no quoted character exists 831 if (pattern.indexOf(QUOTE) < 0) { 832 return pattern; 833 } 834 StringBuilder sb = new StringBuilder(); 835 for (int index = 0; index < pattern.length();) { 836 char ch = pattern.charAt(index++); 837 if (ch == QUOTE) { 838 ch = pattern.charAt(index++); 839 if (ch == MINUS_SIGN) { 840 sb.append(symbols.getMinusSignText()); 841 continue; 842 } 843 } 844 sb.append(ch); 845 } 846 return sb.toString(); 847 } 848 849 /** 850 * Returns a list of {@code FieldPostion} in the given {@code pattern}. 851 * @param pattern the pattern to be parsed for {@code FieldPosition} 852 * @param field whether a PREFIX or SUFFIX field 853 * @return a list of {@code FieldPostion} 854 */ 855 private List<FieldPosition> getFieldPositions(String pattern, Field field) { 856 List<FieldPosition> positions = new ArrayList<>(); 857 StringBuilder affix = new StringBuilder(); 858 int stringIndex = 0; 859 for (int index = 0; index < pattern.length();) { 860 char ch = pattern.charAt(index++); 861 if (ch == QUOTE) { 862 ch = pattern.charAt(index++); 863 if (ch == MINUS_SIGN) { 864 String minusText = symbols.getMinusSignText(); 865 FieldPosition fp = new FieldPosition(NumberFormat.Field.SIGN); 866 fp.setBeginIndex(stringIndex); 867 fp.setEndIndex(stringIndex + minusText.length()); 868 positions.add(fp); 869 stringIndex += minusText.length(); 870 affix.append(minusText); 871 continue; 872 } 873 } 874 stringIndex++; 875 affix.append(ch); 876 } 877 if (affix.length() != 0) { 878 FieldPosition fp = new FieldPosition(field); 879 fp.setBeginIndex(0); 880 fp.setEndIndex(affix.length()); 881 positions.add(fp); 882 } 883 return positions; 884 } 885 886 /** 887 * Select the index of the matched compact number pattern for 888 * the given {@code long} {@code number}. 889 * 890 * @param number number to be formatted 891 * @return index of matched compact pattern; 892 * -1 if no compact patterns specified 893 */ 894 private int selectCompactPattern(long number) { 895 896 if (compactPatterns.length == 0) { 897 return -1; 898 } 899 900 // Minimum index can be "0", max index can be "size - 1" 901 int dataIndex = number <= 1 ? 0 : (int) Math.log10(number); 902 dataIndex = Math.min(dataIndex, compactPatterns.length - 1); 903 return dataIndex; 904 } 905 906 /** 907 * Select the index of the matched compact number 908 * pattern for the given {@code BigInteger} {@code number}. 909 * 910 * @param number number to be formatted 911 * @return index of matched compact pattern; 912 * -1 if no compact patterns specified 913 */ 914 private int selectCompactPattern(BigInteger number) { 915 916 int matchedIndex = -1; 917 if (compactPatterns.length == 0) { 918 return matchedIndex; 919 } 920 921 BigInteger currentValue = BigInteger.ONE; 922 923 // For formatting a number, the greatest type less than 924 // or equal to number is used 925 for (int index = 0; index < compactPatterns.length; index++) { 926 if (number.compareTo(currentValue) > 0) { 927 // Input number is greater than current type; try matching with 928 // the next 929 matchedIndex = index; 930 currentValue = currentValue.multiply(BigInteger.valueOf(RANGE_MULTIPLIER)); 931 continue; 932 } 933 if (number.compareTo(currentValue) < 0) { 934 // Current type is greater than the input number; 935 // take the previous pattern 936 break; 937 } else { 938 // Equal 939 matchedIndex = index; 940 break; 941 } 942 } 943 return matchedIndex; 944 } 945 946 /** 947 * Formats an Object producing an {@code AttributedCharacterIterator}. 948 * The returned {@code AttributedCharacterIterator} can be used 949 * to build the resulting string, as well as to determine information 950 * about the resulting string. 951 * <p> 952 * Each attribute key of the {@code AttributedCharacterIterator} will 953 * be of type {@code NumberFormat.Field}, with the attribute value 954 * being the same as the attribute key. The prefix and the suffix 955 * parts of the returned iterator (if present) are represented by 956 * the attributes {@link NumberFormat.Field#PREFIX} and 957 * {@link NumberFormat.Field#SUFFIX} respectively. 958 * 959 * 960 * @throws NullPointerException if obj is null 961 * @throws IllegalArgumentException when the Format cannot format the 962 * given object 963 * @throws ArithmeticException if rounding is needed with rounding 964 * mode being set to {@code RoundingMode.UNNECESSARY} 965 * @param obj The object to format 966 * @return an {@code AttributedCharacterIterator} describing the 967 * formatted value 968 */ 969 @Override 970 public AttributedCharacterIterator formatToCharacterIterator(Object obj) { 971 CharacterIteratorFieldDelegate delegate 972 = new CharacterIteratorFieldDelegate(); 973 StringBuffer sb = new StringBuffer(); 974 975 if (obj instanceof Double || obj instanceof Float) { 976 format(((Number) obj).doubleValue(), sb, delegate); 977 } else if (obj instanceof Long || obj instanceof Integer 978 || obj instanceof Short || obj instanceof Byte 979 || obj instanceof AtomicInteger || obj instanceof AtomicLong) { 980 format(((Number) obj).longValue(), sb, delegate); 981 } else if (obj instanceof BigDecimal) { 982 format((BigDecimal) obj, sb, delegate); 983 } else if (obj instanceof BigInteger) { 984 format((BigInteger) obj, sb, delegate, false); 985 } else if (obj == null) { 986 throw new NullPointerException( 987 "formatToCharacterIterator must be passed non-null object"); 988 } else { 989 throw new IllegalArgumentException( 990 "Cannot format given Object as a Number"); 991 } 992 return delegate.getIterator(sb.toString()); 993 } 994 995 /** 996 * Computes the divisor using minimum integer digits and 997 * matched pattern index. 998 * @param minIntDigits string of 0s in compact pattern 999 * @param patternIndex index of matched compact pattern 1000 * @return divisor value for the number matching the compact 1001 * pattern at given {@code patternIndex} 1002 */ 1003 private Number computeDivisor(String minIntDigits, int patternIndex) { 1004 int count = minIntDigits.length() - 1; 1005 Number matchedValue; 1006 // The divisor value can go above long range, if the compact patterns 1007 // goes above index 18, divisor may need to be stored as BigInteger, 1008 // since long can't store numbers >= 10^19, 1009 if (patternIndex < 19) { 1010 matchedValue = (long) Math.pow(RANGE_MULTIPLIER, patternIndex); 1011 } else { 1012 matchedValue = BigInteger.valueOf(RANGE_MULTIPLIER).pow(patternIndex); 1013 } 1014 Number divisor = matchedValue; 1015 if (count != 0) { 1016 if (matchedValue instanceof BigInteger) { 1017 BigInteger bigValue = (BigInteger) matchedValue; 1018 if (bigValue.compareTo(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count))) < 0) { 1019 throw new IllegalArgumentException("Invalid Pattern" 1020 + " [" + compactPatterns[patternIndex] 1021 + "]: min integer digits specified exceeds the limit" 1022 + " for the index " + patternIndex); 1023 } 1024 divisor = bigValue.divide(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count))); 1025 } else { 1026 long longValue = (long) matchedValue; 1027 if (longValue < (long) Math.pow(RANGE_MULTIPLIER, count)) { 1028 throw new IllegalArgumentException("Invalid Pattern" 1029 + " [" + compactPatterns[patternIndex] 1030 + "]: min integer digits specified exceeds the limit" 1031 + " for the index " + patternIndex); 1032 } 1033 divisor = longValue / (long) Math.pow(RANGE_MULTIPLIER, count); 1034 } 1035 } 1036 return divisor; 1037 } 1038 1039 /** 1040 * Process the series of compact patterns to compute the 1041 * series of prefixes, suffixes and their respective divisor 1042 * value. 1043 * 1044 */ 1045 private void processCompactPatterns() { 1046 int size = compactPatterns.length; 1047 positivePrefixPatterns = new ArrayList<>(size); 1048 negativePrefixPatterns = new ArrayList<>(size); 1049 positiveSuffixPatterns = new ArrayList<>(size); 1050 negativeSuffixPatterns = new ArrayList<>(size); 1051 divisors = new ArrayList<>(size); 1052 1053 for (int index = 0; index < size; index++) { 1054 applyPattern(compactPatterns[index], index); 1055 } 1056 } 1057 1058 /** 1059 * Process a compact pattern at a specific {@code index} 1060 * @param pattern the compact pattern to be processed 1061 * @param index index in the array of compact patterns 1062 * 1063 */ 1064 private void applyPattern(String pattern, int index) { 1065 1066 if (pattern == null) { 1067 throw new IllegalArgumentException("A null compact pattern" + 1068 " encountered at index: " + index); 1069 } 1070 1071 int start = 0; 1072 boolean gotNegative = false; 1073 1074 String positivePrefix = ""; 1075 String positiveSuffix = ""; 1076 String negativePrefix = ""; 1077 String negativeSuffix = ""; 1078 String zeros = ""; 1079 for (int j = 1; j >= 0 && start < pattern.length(); --j) { 1080 1081 StringBuffer prefix = new StringBuffer(); 1082 StringBuffer suffix = new StringBuffer(); 1083 boolean inQuote = false; 1084 // The phase ranges from 0 to 2. Phase 0 is the prefix. Phase 1 is 1085 // the section of the pattern with digits. Phase 2 is the suffix. 1086 // The separation of the characters into phases is 1087 // strictly enforced; if phase 1 characters are to appear in the 1088 // suffix, for example, they must be quoted. 1089 int phase = 0; 1090 1091 // The affix is either the prefix or the suffix. 1092 StringBuffer affix = prefix; 1093 1094 for (int pos = start; pos < pattern.length(); ++pos) { 1095 char ch = pattern.charAt(pos); 1096 switch (phase) { 1097 case 0: 1098 case 2: 1099 // Process the prefix / suffix characters 1100 if (inQuote) { 1101 // A quote within quotes indicates either the closing 1102 // quote or two quotes, which is a quote literal. That 1103 // is, we have the second quote in 'do' or 'don''t'. 1104 if (ch == QUOTE) { 1105 if ((pos + 1) < pattern.length() 1106 && pattern.charAt(pos + 1) == QUOTE) { 1107 ++pos; 1108 affix.append("''"); // 'don''t' 1109 } else { 1110 inQuote = false; // 'do' 1111 } 1112 continue; 1113 } 1114 } else { 1115 // Process unquoted characters seen in prefix or suffix 1116 // phase. 1117 switch (ch) { 1118 case ZERO_DIGIT: 1119 phase = 1; 1120 --pos; // Reprocess this character 1121 continue; 1122 case QUOTE: 1123 // A quote outside quotes indicates either the 1124 // opening quote or two quotes, which is a quote 1125 // literal. That is, we have the first quote in 'do' 1126 // or o''clock. 1127 if ((pos + 1) < pattern.length() 1128 && pattern.charAt(pos + 1) == QUOTE) { 1129 ++pos; 1130 affix.append("''"); // o''clock 1131 } else { 1132 inQuote = true; // 'do' 1133 } 1134 continue; 1135 case SEPARATOR: 1136 // Don't allow separators before we see digit 1137 // characters of phase 1, and don't allow separators 1138 // in the second pattern (j == 0). 1139 if (phase == 0 || j == 0) { 1140 throw new IllegalArgumentException( 1141 "Unquoted special character '" 1142 + ch + "' in pattern \"" + pattern + "\""); 1143 } 1144 start = pos + 1; 1145 pos = pattern.length(); 1146 continue; 1147 case MINUS_SIGN: 1148 affix.append("'-"); 1149 continue; 1150 case DECIMAL_SEPARATOR: 1151 case GROUPING_SEPARATOR: 1152 case DIGIT: 1153 case PERCENT: 1154 case PER_MILLE: 1155 case CURRENCY_SIGN: 1156 throw new IllegalArgumentException( 1157 "Unquoted special character '" + ch 1158 + "' in pattern \"" + pattern + "\""); 1159 default: 1160 break; 1161 } 1162 } 1163 // Note that if we are within quotes, or if this is an 1164 // unquoted, non-special character, then we usually fall 1165 // through to here. 1166 affix.append(ch); 1167 break; 1168 1169 case 1: 1170 // The negative subpattern (j = 0) serves only to specify the 1171 // negative prefix and suffix, so all the phase 1 characters, 1172 // for example, digits, zeroDigit, groupingSeparator, 1173 // decimalSeparator, exponent are ignored 1174 if (j == 0) { 1175 while (pos < pattern.length()) { 1176 char negPatternChar = pattern.charAt(pos); 1177 if (negPatternChar == ZERO_DIGIT) { 1178 ++pos; 1179 } else { 1180 // Not a phase 1 character, consider it as 1181 // suffix and parse it in phase 2 1182 --pos; //process it again in outer loop 1183 phase = 2; 1184 affix = suffix; 1185 break; 1186 } 1187 } 1188 continue; 1189 } 1190 // Consider only '0' as valid pattern char which can appear 1191 // in number part, rest can be either suffix or prefix 1192 if (ch == ZERO_DIGIT) { 1193 zeros = zeros + "0"; 1194 } else { 1195 phase = 2; 1196 affix = suffix; 1197 --pos; 1198 } 1199 break; 1200 } 1201 } 1202 1203 if (inQuote) { 1204 throw new IllegalArgumentException("Invalid single quote" 1205 + " in pattern \"" + pattern + "\""); 1206 } 1207 1208 if (j == 1) { 1209 positivePrefix = prefix.toString(); 1210 positiveSuffix = suffix.toString(); 1211 negativePrefix = positivePrefix; 1212 negativeSuffix = positiveSuffix; 1213 } else { 1214 negativePrefix = prefix.toString(); 1215 negativeSuffix = suffix.toString(); 1216 gotNegative = true; 1217 } 1218 1219 // If there is no negative pattern, or if the negative pattern is 1220 // identical to the positive pattern, then prepend the minus sign to 1221 // the positive pattern to form the negative pattern. 1222 if (!gotNegative 1223 || (negativePrefix.equals(positivePrefix) 1224 && negativeSuffix.equals(positiveSuffix))) { 1225 negativeSuffix = positiveSuffix; 1226 negativePrefix = "'-" + positivePrefix; 1227 } 1228 } 1229 1230 // If no 0s are specified in a non empty pattern, it is invalid 1231 if (!pattern.isEmpty() && zeros.isEmpty()) { 1232 throw new IllegalArgumentException("Invalid pattern" 1233 + " [" + pattern + "]: all patterns must include digit" 1234 + " placement 0s"); 1235 } 1236 1237 // Only if positive affix exists; else put empty strings 1238 if (!positivePrefix.isEmpty() || !positiveSuffix.isEmpty()) { 1239 positivePrefixPatterns.add(positivePrefix); 1240 negativePrefixPatterns.add(negativePrefix); 1241 positiveSuffixPatterns.add(positiveSuffix); 1242 negativeSuffixPatterns.add(negativeSuffix); 1243 divisors.add(computeDivisor(zeros, index)); 1244 } else { 1245 positivePrefixPatterns.add(""); 1246 negativePrefixPatterns.add(""); 1247 positiveSuffixPatterns.add(""); 1248 negativeSuffixPatterns.add(""); 1249 divisors.add(1L); 1250 } 1251 } 1252 1253 private final transient DigitList digitList = new DigitList(); 1254 private static final int STATUS_INFINITE = 0; 1255 private static final int STATUS_POSITIVE = 1; 1256 private static final int STATUS_LENGTH = 2; 1257 1258 private static final char ZERO_DIGIT = '0'; 1259 private static final char DIGIT = '#'; 1260 private static final char DECIMAL_SEPARATOR = '.'; 1261 private static final char GROUPING_SEPARATOR = ','; 1262 private static final char MINUS_SIGN = '-'; 1263 private static final char PERCENT = '%'; 1264 private static final char PER_MILLE = '\u2030'; 1265 private static final char SEPARATOR = ';'; 1266 private static final char CURRENCY_SIGN = '\u00A4'; 1267 private static final char QUOTE = '\''; 1268 1269 // Expanded form of positive/negative prefix/suffix, 1270 // the expanded form contains special characters in 1271 // its localized form, which are used for matching 1272 // while parsing a string to number 1273 private transient List<String> positivePrefixes; 1274 private transient List<String> negativePrefixes; 1275 private transient List<String> positiveSuffixes; 1276 private transient List<String> negativeSuffixes; 1277 1278 private void expandAffixPatterns() { 1279 positivePrefixes = new ArrayList<>(compactPatterns.length); 1280 negativePrefixes = new ArrayList<>(compactPatterns.length); 1281 positiveSuffixes = new ArrayList<>(compactPatterns.length); 1282 negativeSuffixes = new ArrayList<>(compactPatterns.length); 1283 for (int index = 0; index < compactPatterns.length; index++) { 1284 positivePrefixes.add(expandAffix(positivePrefixPatterns.get(index))); 1285 negativePrefixes.add(expandAffix(negativePrefixPatterns.get(index))); 1286 positiveSuffixes.add(expandAffix(positiveSuffixPatterns.get(index))); 1287 negativeSuffixes.add(expandAffix(negativeSuffixPatterns.get(index))); 1288 } 1289 } 1290 1291 /** 1292 * Parses a compact number from a string to produce a {@code Number}. 1293 * <p> 1294 * The method attempts to parse text starting at the index given by 1295 * {@code pos}. 1296 * If parsing succeeds, then the index of {@code pos} is updated 1297 * to the index after the last character used (parsing does not necessarily 1298 * use all characters up to the end of the string), and the parsed 1299 * number is returned. The updated {@code pos} can be used to 1300 * indicate the starting point for the next call to this method. 1301 * If an error occurs, then the index of {@code pos} is not 1302 * changed, the error index of {@code pos} is set to the index of 1303 * the character where the error occurred, and {@code null} is returned. 1304 * <p> 1305 * The value is the numeric part in the given text multiplied 1306 * by the numeric equivalent of the affix attached 1307 * (For example, "K" = 1000 in {@link java.util.Locale#US US locale}). 1308 * The subclass returned depends on the value of 1309 * {@link #isParseBigDecimal}. 1310 * <ul> 1311 * <li>If {@link #isParseBigDecimal()} is false (the default), 1312 * most integer values are returned as {@code Long} 1313 * objects, no matter how they are written: {@code "17K"} and 1314 * {@code "17.000K"} both parse to {@code Long.valueOf(17000)}. 1315 * If the value cannot fit into {@code Long}, then the result is 1316 * returned as {@code Double}. This includes values with a 1317 * fractional part, infinite values, {@code NaN}, 1318 * and the value -0.0. 1319 * <p> 1320 * Callers may use the {@code Number} methods {@code doubleValue}, 1321 * {@code longValue}, etc., to obtain the type they want. 1322 * 1323 * <li>If {@link #isParseBigDecimal()} is true, values are returned 1324 * as {@code BigDecimal} objects. The special cases negative 1325 * and positive infinity and NaN are returned as {@code Double} 1326 * instances holding the values of the corresponding 1327 * {@code Double} constants. 1328 * </ul> 1329 * <p> 1330 * {@code CompactNumberFormat} parses all Unicode characters that represent 1331 * decimal digits, as defined by {@code Character.digit()}. In 1332 * addition, {@code CompactNumberFormat} also recognizes as digits the ten 1333 * consecutive characters starting with the localized zero digit defined in 1334 * the {@code DecimalFormatSymbols} object. 1335 * <p> 1336 * {@code CompactNumberFormat} parse does not allow parsing scientific 1337 * notations. For example, parsing a string {@code "1.05E4K"} in 1338 * {@link java.util.Locale#US US locale} breaks at character 'E' 1339 * and returns 1.05. 1340 * 1341 * @param text the string to be parsed 1342 * @param pos a {@code ParsePosition} object with index and error 1343 * index information as described above 1344 * @return the parsed value, or {@code null} if the parse fails 1345 * @exception NullPointerException if {@code text} or 1346 * {@code pos} is null 1347 * 1348 */ 1349 @Override 1350 public Number parse(String text, ParsePosition pos) { 1351 1352 Objects.requireNonNull(text); 1353 Objects.requireNonNull(pos); 1354 1355 // Lazily expanding the affix patterns, on the first parse 1356 // call on this instance 1357 // If not initialized, expand and load all affixes 1358 if (positivePrefixes == null) { 1359 expandAffixPatterns(); 1360 } 1361 1362 // The compact number multiplier for parsed string. 1363 // Its value is set on parsing prefix and suffix. For example, 1364 // in the {@link java.util.Locale#US US locale} parsing {@code "1K"} 1365 // sets its value to 1000, as K (thousand) is abbreviated form of 1000. 1366 Number cnfMultiplier = 1L; 1367 1368 // Special case NaN 1369 if (text.regionMatches(pos.index, symbols.getNaN(), 1370 0, symbols.getNaN().length())) { 1371 pos.index = pos.index + symbols.getNaN().length(); 1372 return Double.NaN; 1373 } 1374 1375 int position = pos.index; 1376 int oldStart = pos.index; 1377 boolean gotPositive = false; 1378 boolean gotNegative = false; 1379 int matchedPosIndex = -1; 1380 int matchedNegIndex = -1; 1381 String matchedPosPrefix = ""; 1382 String matchedNegPrefix = ""; 1383 String defaultPosPrefix = defaultDecimalFormat.getPositivePrefix(); 1384 String defaultNegPrefix = defaultDecimalFormat.getNegativePrefix(); 1385 // Prefix matching 1386 for (int compactIndex = 0; compactIndex < compactPatterns.length; compactIndex++) { 1387 String positivePrefix = positivePrefixes.get(compactIndex); 1388 String negativePrefix = negativePrefixes.get(compactIndex); 1389 1390 // Do not break if a match occur; there is a possibility that the 1391 // subsequent affixes may match the longer subsequence in the given 1392 // string. 1393 // For example, matching "Mdx 3" with "M", "Md" as prefix should 1394 // match with "Md" 1395 boolean match = matchAffix(text, position, positivePrefix, 1396 defaultPosPrefix, matchedPosPrefix); 1397 if (match) { 1398 matchedPosIndex = compactIndex; 1399 matchedPosPrefix = positivePrefix; 1400 gotPositive = true; 1401 } 1402 1403 match = matchAffix(text, position, negativePrefix, 1404 defaultNegPrefix, matchedNegPrefix); 1405 if (match) { 1406 matchedNegIndex = compactIndex; 1407 matchedNegPrefix = negativePrefix; 1408 gotNegative = true; 1409 } 1410 } 1411 1412 // Given text does not match the non empty valid compact prefixes 1413 // check with the default prefixes 1414 if (!gotPositive && !gotNegative) { 1415 if (text.regionMatches(pos.index, defaultPosPrefix, 0, 1416 defaultPosPrefix.length())) { 1417 // Matches the default positive prefix 1418 matchedPosPrefix = defaultPosPrefix; 1419 gotPositive = true; 1420 } 1421 if (text.regionMatches(pos.index, defaultNegPrefix, 0, 1422 defaultNegPrefix.length())) { 1423 // Matches the default negative prefix 1424 matchedNegPrefix = defaultNegPrefix; 1425 gotNegative = true; 1426 } 1427 } 1428 1429 // If both match, take the longest one 1430 if (gotPositive && gotNegative) { 1431 if (matchedPosPrefix.length() > matchedNegPrefix.length()) { 1432 gotNegative = false; 1433 } else if (matchedPosPrefix.length() < matchedNegPrefix.length()) { 1434 gotPositive = false; 1435 } 1436 } 1437 1438 // Update the position and take compact multiplier 1439 // only if it matches the compact prefix, not the default 1440 // prefix; else multiplier should be 1 1441 if (gotPositive) { 1442 position += matchedPosPrefix.length(); 1443 cnfMultiplier = matchedPosIndex != -1 1444 ? divisors.get(matchedPosIndex) : 1L; 1445 } else if (gotNegative) { 1446 position += matchedNegPrefix.length(); 1447 cnfMultiplier = matchedNegIndex != -1 1448 ? divisors.get(matchedNegIndex) : 1L; 1449 } 1450 1451 digitList.setRoundingMode(getRoundingMode()); 1452 boolean[] status = new boolean[STATUS_LENGTH]; 1453 1454 // Call DecimalFormat.subparseNumber() method to parse the 1455 // number part of the input text 1456 position = decimalFormat.subparseNumber(text, position, 1457 digitList, false, false, status); 1458 1459 if (position == -1) { 1460 // Unable to parse the number successfully 1461 pos.index = oldStart; 1462 pos.errorIndex = oldStart; 1463 return null; 1464 } 1465 1466 // If parse integer only is true and the parsing is broken at 1467 // decimal point, then pass/ignore all digits and move pointer 1468 // at the start of suffix, to process the suffix part 1469 if (isParseIntegerOnly() 1470 && text.charAt(position) == symbols.getDecimalSeparator()) { 1471 position++; // Pass decimal character 1472 for (; position < text.length(); ++position) { 1473 char ch = text.charAt(position); 1474 int digit = ch - symbols.getZeroDigit(); 1475 if (digit < 0 || digit > 9) { 1476 digit = Character.digit(ch, 10); 1477 // Parse all digit characters 1478 if (!(digit >= 0 && digit <= 9)) { 1479 break; 1480 } 1481 } 1482 } 1483 } 1484 1485 // Number parsed successfully; match prefix and 1486 // suffix to obtain multiplier 1487 pos.index = position; 1488 Number multiplier = computeParseMultiplier(text, pos, 1489 gotPositive ? matchedPosPrefix : matchedNegPrefix, 1490 status, gotPositive, gotNegative); 1491 1492 if (multiplier.longValue() == -1L) { 1493 return null; 1494 } else if (multiplier.longValue() != 1L) { 1495 cnfMultiplier = multiplier; 1496 } 1497 1498 // Special case INFINITY 1499 if (status[STATUS_INFINITE]) { 1500 if (status[STATUS_POSITIVE]) { 1501 return Double.POSITIVE_INFINITY; 1502 } else { 1503 return Double.NEGATIVE_INFINITY; 1504 } 1505 } 1506 1507 if (isParseBigDecimal()) { 1508 BigDecimal bigDecimalResult = digitList.getBigDecimal(); 1509 1510 if (cnfMultiplier.longValue() != 1) { 1511 bigDecimalResult = bigDecimalResult 1512 .multiply(new BigDecimal(cnfMultiplier.toString())); 1513 } 1514 if (!status[STATUS_POSITIVE]) { 1515 bigDecimalResult = bigDecimalResult.negate(); 1516 } 1517 return bigDecimalResult; 1518 } else { 1519 Number cnfResult; 1520 if (digitList.fitsIntoLong(status[STATUS_POSITIVE], isParseIntegerOnly())) { 1521 long longResult = digitList.getLong(); 1522 cnfResult = generateParseResult(longResult, false, 1523 longResult < 0, status, cnfMultiplier); 1524 } else { 1525 cnfResult = generateParseResult(digitList.getDouble(), 1526 true, false, status, cnfMultiplier); 1527 } 1528 return cnfResult; 1529 } 1530 } 1531 1532 /** 1533 * Returns the parsed result by multiplying the parsed number 1534 * with the multiplier representing the prefix and suffix. 1535 * 1536 * @param number parsed number component 1537 * @param gotDouble whether the parsed number contains decimal 1538 * @param gotLongMin whether the parsed number is Long.MIN 1539 * @param status boolean status flags indicating whether the 1540 * value is infinite and whether it is positive 1541 * @param cnfMultiplier compact number multiplier 1542 * @return parsed result 1543 */ 1544 private Number generateParseResult(Number number, boolean gotDouble, 1545 boolean gotLongMin, boolean[] status, Number cnfMultiplier) { 1546 1547 if (gotDouble) { 1548 if (cnfMultiplier.longValue() != 1L) { 1549 double doubleResult = number.doubleValue() * cnfMultiplier.doubleValue(); 1550 doubleResult = (double) convertIfNegative(doubleResult, status, gotLongMin); 1551 // Check if a double can be represeneted as a long 1552 long longResult = (long) doubleResult; 1553 gotDouble = ((doubleResult != (double) longResult) 1554 || (doubleResult == 0.0 && 1 / doubleResult < 0.0)); 1555 return gotDouble ? (Number) doubleResult : (Number) longResult; 1556 } 1557 } else { 1558 if (cnfMultiplier.longValue() != 1L) { 1559 Number result; 1560 if ((cnfMultiplier instanceof Long) && !gotLongMin) { 1561 long longMultiplier = (long) cnfMultiplier; 1562 try { 1563 result = Math.multiplyExact(number.longValue(), 1564 longMultiplier); 1565 } catch (ArithmeticException ex) { 1566 // If number * longMultiplier can not be represented 1567 // as long return as double 1568 result = number.doubleValue() * cnfMultiplier.doubleValue(); 1569 } 1570 } else { 1571 // cnfMultiplier can not be stored into long or the number 1572 // part is Long.MIN, return as double 1573 result = number.doubleValue() * cnfMultiplier.doubleValue(); 1574 } 1575 return convertIfNegative(result, status, gotLongMin); 1576 } 1577 } 1578 1579 // Default number 1580 return convertIfNegative(number, status, gotLongMin); 1581 } 1582 1583 /** 1584 * Negate the parsed value if the positive status flag is false 1585 * and the value is not a Long.MIN 1586 * @param number parsed value 1587 * @param status boolean status flags indicating whether the 1588 * value is infinite and whether it is positive 1589 * @param gotLongMin whether the parsed number is Long.MIN 1590 * @return the resulting value 1591 */ 1592 private Number convertIfNegative(Number number, boolean[] status, 1593 boolean gotLongMin) { 1594 1595 if (!status[STATUS_POSITIVE] && !gotLongMin) { 1596 if (number instanceof Long) { 1597 return -(long) number; 1598 } else { 1599 return -(double) number; 1600 } 1601 } else { 1602 return number; 1603 } 1604 } 1605 1606 /** 1607 * Attempts to match the given {@code affix} in the 1608 * specified {@code text}. 1609 */ 1610 private boolean matchAffix(String text, int position, String affix, 1611 String defaultAffix, String matchedAffix) { 1612 1613 // Check with the compact affixes which are non empty and 1614 // do not match with default affix 1615 if (!affix.isEmpty() && !affix.equals(defaultAffix)) { 1616 // Look ahead only for the longer match than the previous match 1617 if (matchedAffix.length() < affix.length()) { 1618 if (text.regionMatches(position, affix, 0, affix.length())) { 1619 return true; 1620 } 1621 } 1622 } 1623 return false; 1624 } 1625 1626 /** 1627 * Attempts to match given {@code prefix} and {@code suffix} in 1628 * the specified {@code text}. 1629 */ 1630 private boolean matchPrefixAndSuffix(String text, int position, String prefix, 1631 String matchedPrefix, String defaultPrefix, String suffix, 1632 String matchedSuffix, String defaultSuffix) { 1633 1634 // Check the compact pattern suffix only if there is a 1635 // compact prefix match or a default prefix match 1636 // because the compact prefix and suffix should match at the same 1637 // index to obtain the multiplier. 1638 // The prefix match is required because of the possibility of 1639 // same prefix at multiple index, in which case matching the suffix 1640 // is used to obtain the single match 1641 1642 if (prefix.equals(matchedPrefix) 1643 || matchedPrefix.equals(defaultPrefix)) { 1644 return matchAffix(text, position, suffix, defaultSuffix, matchedSuffix); 1645 } 1646 return false; 1647 } 1648 1649 /** 1650 * Computes multiplier by matching the given {@code matchedPrefix} 1651 * and suffix in the specified {@code text} from the lists of 1652 * prefixes and suffixes extracted from compact patterns. 1653 * 1654 * @param text the string to parse 1655 * @param parsePosition the {@code ParsePosition} object representing the 1656 * index and error index of the parse string 1657 * @param matchedPrefix prefix extracted which needs to be matched to 1658 * obtain the multiplier 1659 * @param status upon return contains boolean status flags indicating 1660 * whether the value is positive 1661 * @param gotPositive based on the prefix parsed; whether the number is positive 1662 * @param gotNegative based on the prefix parsed; whether the number is negative 1663 * @return the multiplier matching the prefix and suffix; -1 otherwise 1664 */ 1665 private Number computeParseMultiplier(String text, ParsePosition parsePosition, 1666 String matchedPrefix, boolean[] status, boolean gotPositive, 1667 boolean gotNegative) { 1668 1669 int position = parsePosition.index; 1670 boolean gotPos = false; 1671 boolean gotNeg = false; 1672 int matchedPosIndex = -1; 1673 int matchedNegIndex = -1; 1674 String matchedPosSuffix = ""; 1675 String matchedNegSuffix = ""; 1676 for (int compactIndex = 0; compactIndex < compactPatterns.length; compactIndex++) { 1677 String positivePrefix = positivePrefixes.get(compactIndex); 1678 String negativePrefix = negativePrefixes.get(compactIndex); 1679 String positiveSuffix = positiveSuffixes.get(compactIndex); 1680 String negativeSuffix = negativeSuffixes.get(compactIndex); 1681 1682 // Do not break if a match occur; there is a possibility that the 1683 // subsequent affixes may match the longer subsequence in the given 1684 // string. 1685 // For example, matching "3Mdx" with "M", "Md" should match with "Md" 1686 boolean match = matchPrefixAndSuffix(text, position, positivePrefix, matchedPrefix, 1687 defaultDecimalFormat.getPositivePrefix(), positiveSuffix, 1688 matchedPosSuffix, defaultDecimalFormat.getPositiveSuffix()); 1689 if (match) { 1690 matchedPosIndex = compactIndex; 1691 matchedPosSuffix = positiveSuffix; 1692 gotPos = true; 1693 } 1694 1695 match = matchPrefixAndSuffix(text, position, negativePrefix, matchedPrefix, 1696 defaultDecimalFormat.getNegativePrefix(), negativeSuffix, 1697 matchedNegSuffix, defaultDecimalFormat.getNegativeSuffix()); 1698 if (match) { 1699 matchedNegIndex = compactIndex; 1700 matchedNegSuffix = negativeSuffix; 1701 gotNeg = true; 1702 } 1703 } 1704 1705 // Suffix in the given text does not match with the compact 1706 // patterns suffixes; match with the default suffix 1707 if (!gotPos && !gotNeg) { 1708 String positiveSuffix = defaultDecimalFormat.getPositiveSuffix(); 1709 String negativeSuffix = defaultDecimalFormat.getNegativeSuffix(); 1710 if (text.regionMatches(position, positiveSuffix, 0, 1711 positiveSuffix.length())) { 1712 // Matches the default positive prefix 1713 matchedPosSuffix = positiveSuffix; 1714 gotPos = true; 1715 } 1716 if (text.regionMatches(position, negativeSuffix, 0, 1717 negativeSuffix.length())) { 1718 // Matches the default negative suffix 1719 matchedNegSuffix = negativeSuffix; 1720 gotNeg = true; 1721 } 1722 } 1723 1724 // If both matches, take the longest one 1725 if (gotPos && gotNeg) { 1726 if (matchedPosSuffix.length() > matchedNegSuffix.length()) { 1727 gotNeg = false; 1728 } else if (matchedPosSuffix.length() < matchedNegSuffix.length()) { 1729 gotPos = false; 1730 } else { 1731 // If longest comparison fails; take the positive and negative 1732 // sign of matching prefix 1733 gotPos = gotPositive; 1734 gotNeg = gotNegative; 1735 } 1736 } 1737 1738 // Fail if neither or both 1739 if (gotPos == gotNeg) { 1740 parsePosition.errorIndex = position; 1741 return -1L; 1742 } 1743 1744 Number cnfMultiplier; 1745 // Update the parse position index and take compact multiplier 1746 // only if it matches the compact suffix, not the default 1747 // suffix; else multiplier should be 1 1748 if (gotPos) { 1749 parsePosition.index = position + matchedPosSuffix.length(); 1750 cnfMultiplier = matchedPosIndex != -1 1751 ? divisors.get(matchedPosIndex) : 1L; 1752 } else { 1753 parsePosition.index = position + matchedNegSuffix.length(); 1754 cnfMultiplier = matchedNegIndex != -1 1755 ? divisors.get(matchedNegIndex) : 1L; 1756 } 1757 status[STATUS_POSITIVE] = gotPos; 1758 return cnfMultiplier; 1759 } 1760 1761 /** 1762 * Reconstitutes this {@code CompactNumberFormat} from a stream 1763 * (that is, deserializes it) after performing some validations. 1764 * This method throws InvalidObjectException, if the stream data is invalid 1765 * because of the following reasons, 1766 * <ul> 1767 * <li> If any of the {@code decimalPattern}, {@code compactPatterns}, 1768 * {@code symbols} or {@code roundingMode} is {@code null}. 1769 * <li> If the {@code decimalPattern} or the {@code compactPatterns} array 1770 * contains an invalid pattern or if a {@code null} appears in the array of 1771 * compact patterns. 1772 * <li> If the {@code minimumIntegerDigits} is greater than the 1773 * {@code maximumIntegerDigits} or the {@code minimumFractionDigits} is 1774 * greater than the {@code maximumFractionDigits}. This check is performed 1775 * by superclass's Object. 1776 * <li> If any of the minimum/maximum integer/fraction digit count is 1777 * negative. This check is performed by superclass's readObject. 1778 * <li> If the minimum or maximum integer digit count is larger than 309 or 1779 * if the minimum or maximum fraction digit count is larger than 340. 1780 * <li> If the grouping size is negative or larger than 127. 1781 * </ul> 1782 * 1783 * @param inStream the stream 1784 * @throws IOException if an I/O error occurs 1785 * @throws ClassNotFoundException if the class of a serialized object 1786 * could not be found 1787 */ 1788 @java.io.Serial 1789 private void readObject(ObjectInputStream inStream) throws IOException, 1790 ClassNotFoundException { 1791 1792 inStream.defaultReadObject(); 1793 if (decimalPattern == null || compactPatterns == null 1794 || symbols == null || roundingMode == null) { 1795 throw new InvalidObjectException("One of the 'decimalPattern'," 1796 + " 'compactPatterns', 'symbols' or 'roundingMode'" 1797 + " is null"); 1798 } 1799 1800 // Check only the maximum counts because NumberFormat.readObject has 1801 // already ensured that the maximum is greater than the minimum count. 1802 if (getMaximumIntegerDigits() > DecimalFormat.DOUBLE_INTEGER_DIGITS 1803 || getMaximumFractionDigits() > DecimalFormat.DOUBLE_FRACTION_DIGITS) { 1804 throw new InvalidObjectException("Digit count out of range"); 1805 } 1806 1807 // Check if the grouping size is negative, on an attempt to 1808 // put value > 127, it wraps around, so check just negative value 1809 if (groupingSize < 0) { 1810 throw new InvalidObjectException("Grouping size is negative"); 1811 } 1812 1813 try { 1814 processCompactPatterns(); 1815 } catch (IllegalArgumentException ex) { 1816 throw new InvalidObjectException(ex.getMessage()); 1817 } 1818 1819 decimalFormat = new DecimalFormat(SPECIAL_PATTERN, symbols); 1820 decimalFormat.setMaximumFractionDigits(getMaximumFractionDigits()); 1821 decimalFormat.setMinimumFractionDigits(getMinimumFractionDigits()); 1822 decimalFormat.setMaximumIntegerDigits(getMaximumIntegerDigits()); 1823 decimalFormat.setMinimumIntegerDigits(getMinimumIntegerDigits()); 1824 decimalFormat.setRoundingMode(getRoundingMode()); 1825 decimalFormat.setGroupingSize(getGroupingSize()); 1826 decimalFormat.setGroupingUsed(isGroupingUsed()); 1827 decimalFormat.setParseIntegerOnly(isParseIntegerOnly()); 1828 1829 try { 1830 defaultDecimalFormat = new DecimalFormat(decimalPattern, symbols); 1831 defaultDecimalFormat.setMaximumFractionDigits(0); 1832 } catch (IllegalArgumentException ex) { 1833 throw new InvalidObjectException(ex.getMessage()); 1834 } 1835 1836 } 1837 1838 /** 1839 * Sets the maximum number of digits allowed in the integer portion of a 1840 * number. 1841 * The maximum allowed integer range is 309, if the {@code newValue} > 309, 1842 * then the maximum integer digits count is set to 309. Negative input 1843 * values are replaced with 0. 1844 * 1845 * @param newValue the maximum number of integer digits to be shown 1846 * @see #getMaximumIntegerDigits() 1847 */ 1848 @Override 1849 public void setMaximumIntegerDigits(int newValue) { 1850 // The maximum integer digits is checked with the allowed range before calling 1851 // the DecimalFormat.setMaximumIntegerDigits, which performs the negative check 1852 // on the given newValue while setting it as max integer digits. 1853 // For example, if a negative value is specified, it is replaced with 0 1854 decimalFormat.setMaximumIntegerDigits(Math.min(newValue, 1855 DecimalFormat.DOUBLE_INTEGER_DIGITS)); 1856 super.setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits()); 1857 if (decimalFormat.getMinimumIntegerDigits() > decimalFormat.getMaximumIntegerDigits()) { 1858 decimalFormat.setMinimumIntegerDigits(decimalFormat.getMaximumIntegerDigits()); 1859 super.setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits()); 1860 } 1861 } 1862 1863 /** 1864 * Sets the minimum number of digits allowed in the integer portion of a 1865 * number. 1866 * The maximum allowed integer range is 309, if the {@code newValue} > 309, 1867 * then the minimum integer digits count is set to 309. Negative input 1868 * values are replaced with 0. 1869 * 1870 * @param newValue the minimum number of integer digits to be shown 1871 * @see #getMinimumIntegerDigits() 1872 */ 1873 @Override 1874 public void setMinimumIntegerDigits(int newValue) { 1875 // The minimum integer digits is checked with the allowed range before calling 1876 // the DecimalFormat.setMinimumIntegerDigits, which performs check on the given 1877 // newValue while setting it as min integer digits. For example, if a negative 1878 // value is specified, it is replaced with 0 1879 decimalFormat.setMinimumIntegerDigits(Math.min(newValue, 1880 DecimalFormat.DOUBLE_INTEGER_DIGITS)); 1881 super.setMinimumIntegerDigits(decimalFormat.getMinimumIntegerDigits()); 1882 if (decimalFormat.getMinimumIntegerDigits() > decimalFormat.getMaximumIntegerDigits()) { 1883 decimalFormat.setMaximumIntegerDigits(decimalFormat.getMinimumIntegerDigits()); 1884 super.setMaximumIntegerDigits(decimalFormat.getMaximumIntegerDigits()); 1885 } 1886 } 1887 1888 /** 1889 * Sets the minimum number of digits allowed in the fraction portion of a 1890 * number. 1891 * The maximum allowed fraction range is 340, if the {@code newValue} > 340, 1892 * then the minimum fraction digits count is set to 340. Negative input 1893 * values are replaced with 0. 1894 * 1895 * @param newValue the minimum number of fraction digits to be shown 1896 * @see #getMinimumFractionDigits() 1897 */ 1898 @Override 1899 public void setMinimumFractionDigits(int newValue) { 1900 // The minimum fraction digits is checked with the allowed range before 1901 // calling the DecimalFormat.setMinimumFractionDigits, which performs 1902 // check on the given newValue while setting it as min fraction 1903 // digits. For example, if a negative value is specified, it is 1904 // replaced with 0 1905 decimalFormat.setMinimumFractionDigits(Math.min(newValue, 1906 DecimalFormat.DOUBLE_FRACTION_DIGITS)); 1907 super.setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits()); 1908 if (decimalFormat.getMinimumFractionDigits() > decimalFormat.getMaximumFractionDigits()) { 1909 decimalFormat.setMaximumFractionDigits(decimalFormat.getMinimumFractionDigits()); 1910 super.setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits()); 1911 } 1912 } 1913 1914 /** 1915 * Sets the maximum number of digits allowed in the fraction portion of a 1916 * number. 1917 * The maximum allowed fraction range is 340, if the {@code newValue} > 340, 1918 * then the maximum fraction digits count is set to 340. Negative input 1919 * values are replaced with 0. 1920 * 1921 * @param newValue the maximum number of fraction digits to be shown 1922 * @see #getMaximumFractionDigits() 1923 */ 1924 @Override 1925 public void setMaximumFractionDigits(int newValue) { 1926 // The maximum fraction digits is checked with the allowed range before 1927 // calling the DecimalFormat.setMaximumFractionDigits, which performs 1928 // check on the given newValue while setting it as max fraction digits. 1929 // For example, if a negative value is specified, it is replaced with 0 1930 decimalFormat.setMaximumFractionDigits(Math.min(newValue, 1931 DecimalFormat.DOUBLE_FRACTION_DIGITS)); 1932 super.setMaximumFractionDigits(decimalFormat.getMaximumFractionDigits()); 1933 if (decimalFormat.getMinimumFractionDigits() > decimalFormat.getMaximumFractionDigits()) { 1934 decimalFormat.setMinimumFractionDigits(decimalFormat.getMaximumFractionDigits()); 1935 super.setMinimumFractionDigits(decimalFormat.getMinimumFractionDigits()); 1936 } 1937 } 1938 1939 /** 1940 * Gets the {@link java.math.RoundingMode} used in this 1941 * {@code CompactNumberFormat}. 1942 * 1943 * @return the {@code RoundingMode} used for this 1944 * {@code CompactNumberFormat} 1945 * @see #setRoundingMode(RoundingMode) 1946 */ 1947 @Override 1948 public RoundingMode getRoundingMode() { 1949 return roundingMode; 1950 } 1951 1952 /** 1953 * Sets the {@link java.math.RoundingMode} used in this 1954 * {@code CompactNumberFormat}. 1955 * 1956 * @param roundingMode the {@code RoundingMode} to be used 1957 * @see #getRoundingMode() 1958 * @throws NullPointerException if {@code roundingMode} is {@code null} 1959 */ 1960 @Override 1961 public void setRoundingMode(RoundingMode roundingMode) { 1962 decimalFormat.setRoundingMode(roundingMode); 1963 this.roundingMode = roundingMode; 1964 } 1965 1966 /** 1967 * Returns the grouping size. Grouping size is the number of digits between 1968 * grouping separators in the integer portion of a number. For example, 1969 * in the compact number {@code "12,347 trillion"} for the 1970 * {@link java.util.Locale#US US locale}, the grouping size is 3. 1971 * 1972 * @return the grouping size 1973 * @see #setGroupingSize 1974 * @see java.text.NumberFormat#isGroupingUsed 1975 * @see java.text.DecimalFormatSymbols#getGroupingSeparator 1976 */ 1977 public int getGroupingSize() { 1978 return groupingSize; 1979 } 1980 1981 /** 1982 * Sets the grouping size. Grouping size is the number of digits between 1983 * grouping separators in the integer portion of a number. For example, 1984 * in the compact number {@code "12,347 trillion"} for the 1985 * {@link java.util.Locale#US US locale}, the grouping size is 3. The grouping 1986 * size must be greater than or equal to zero and less than or equal to 127. 1987 * 1988 * @param newValue the new grouping size 1989 * @see #getGroupingSize 1990 * @see java.text.NumberFormat#setGroupingUsed 1991 * @see java.text.DecimalFormatSymbols#setGroupingSeparator 1992 * @throws IllegalArgumentException if {@code newValue} is negative or 1993 * larger than 127 1994 */ 1995 public void setGroupingSize(int newValue) { 1996 if (newValue < 0 || newValue > 127) { 1997 throw new IllegalArgumentException( 1998 "The value passed is negative or larger than 127"); 1999 } 2000 groupingSize = (byte) newValue; 2001 decimalFormat.setGroupingSize(groupingSize); 2002 } 2003 2004 /** 2005 * Returns true if grouping is used in this format. For example, with 2006 * grouping on and grouping size set to 3, the number {@code 12346567890987654} 2007 * can be formatted as {@code "12,347 trillion"} in the 2008 * {@link java.util.Locale#US US locale}. 2009 * The grouping separator is locale dependent. 2010 * 2011 * @return {@code true} if grouping is used; 2012 * {@code false} otherwise 2013 * @see #setGroupingUsed 2014 */ 2015 @Override 2016 public boolean isGroupingUsed() { 2017 return super.isGroupingUsed(); 2018 } 2019 2020 /** 2021 * Sets whether or not grouping will be used in this format. 2022 * 2023 * @param newValue {@code true} if grouping is used; 2024 * {@code false} otherwise 2025 * @see #isGroupingUsed 2026 */ 2027 @Override 2028 public void setGroupingUsed(boolean newValue) { 2029 decimalFormat.setGroupingUsed(newValue); 2030 super.setGroupingUsed(newValue); 2031 } 2032 2033 /** 2034 * Returns true if this format parses only an integer from the number 2035 * component of a compact number. 2036 * Parsing an integer means that only an integer is considered from the 2037 * number component, prefix/suffix is still considered to compute the 2038 * resulting output. 2039 * For example, in the {@link java.util.Locale#US US locale}, if this method 2040 * returns {@code true}, the string {@code "1234.78 thousand"} would be 2041 * parsed as the value {@code 1234000} (1234 (integer part) * 1000 2042 * (thousand)) and the fractional part would be skipped. 2043 * The exact format accepted by the parse operation is locale dependent. 2044 * 2045 * @return {@code true} if compact numbers should be parsed as integers 2046 * only; {@code false} otherwise 2047 */ 2048 @Override 2049 public boolean isParseIntegerOnly() { 2050 return super.isParseIntegerOnly(); 2051 } 2052 2053 /** 2054 * Sets whether or not this format parses only an integer from the number 2055 * component of a compact number. 2056 * 2057 * @param value {@code true} if compact numbers should be parsed as 2058 * integers only; {@code false} otherwise 2059 * @see #isParseIntegerOnly 2060 */ 2061 @Override 2062 public void setParseIntegerOnly(boolean value) { 2063 decimalFormat.setParseIntegerOnly(value); 2064 super.setParseIntegerOnly(value); 2065 } 2066 2067 /** 2068 * Returns whether the {@link #parse(String, ParsePosition)} 2069 * method returns {@code BigDecimal}. The default value is false. 2070 * 2071 * @return {@code true} if the parse method returns BigDecimal; 2072 * {@code false} otherwise 2073 * @see #setParseBigDecimal 2074 * 2075 */ 2076 public boolean isParseBigDecimal() { 2077 return parseBigDecimal; 2078 } 2079 2080 /** 2081 * Sets whether the {@link #parse(String, ParsePosition)} 2082 * method returns {@code BigDecimal}. 2083 * 2084 * @param newValue {@code true} if the parse method returns BigDecimal; 2085 * {@code false} otherwise 2086 * @see #isParseBigDecimal 2087 * 2088 */ 2089 public void setParseBigDecimal(boolean newValue) { 2090 parseBigDecimal = newValue; 2091 } 2092 2093 /** 2094 * Checks if this {@code CompactNumberFormat} is equal to the 2095 * specified {@code obj}. The objects of type {@code CompactNumberFormat} 2096 * are compared, other types return false; obeys the general contract of 2097 * {@link java.lang.Object#equals(java.lang.Object) Object.equals}. 2098 * 2099 * @param obj the object to compare with 2100 * @return true if this is equal to the other {@code CompactNumberFormat} 2101 */ 2102 @Override 2103 public boolean equals(Object obj) { 2104 2105 if (!super.equals(obj)) { 2106 return false; 2107 } 2108 2109 CompactNumberFormat other = (CompactNumberFormat) obj; 2110 return decimalPattern.equals(other.decimalPattern) 2111 && symbols.equals(other.symbols) 2112 && Arrays.equals(compactPatterns, other.compactPatterns) 2113 && roundingMode.equals(other.roundingMode) 2114 && groupingSize == other.groupingSize 2115 && parseBigDecimal == other.parseBigDecimal; 2116 } 2117 2118 /** 2119 * Returns the hash code for this {@code CompactNumberFormat} instance. 2120 * 2121 * @return hash code for this {@code CompactNumberFormat} 2122 */ 2123 @Override 2124 public int hashCode() { 2125 return 31 * super.hashCode() + 2126 Objects.hash(decimalPattern, symbols, roundingMode) 2127 + Arrays.hashCode(compactPatterns) + groupingSize 2128 + Boolean.hashCode(parseBigDecimal); 2129 } 2130 2131 /** 2132 * Creates and returns a copy of this {@code CompactNumberFormat} 2133 * instance. 2134 * 2135 * @return a clone of this instance 2136 */ 2137 @Override 2138 public CompactNumberFormat clone() { 2139 CompactNumberFormat other = (CompactNumberFormat) super.clone(); 2140 other.compactPatterns = compactPatterns.clone(); 2141 other.symbols = (DecimalFormatSymbols) symbols.clone(); 2142 return other; 2143 } 2144 2145 } 2146