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