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