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