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