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