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