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