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