changeset 55509:83deaa8f0c8e

8220224: With CLDR provider, NumberFormat.format could not handle locale with number extension correctly. Reviewed-by: darcy
author naoto
date Fri, 22 Mar 2019 09:31:36 -0700
parents 51195881bd3a
children 01d8eae542ff
files src/java.base/share/classes/java/text/CompactNumberFormat.java src/java.base/share/classes/java/text/DecimalFormat.java src/java.base/share/classes/java/text/DecimalFormatSymbols.java test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java
diffstat 4 files changed, 606 insertions(+), 284 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/text/CompactNumberFormat.java	Fri Mar 22 08:18:26 2019 -0700
+++ b/src/java.base/share/classes/java/text/CompactNumberFormat.java	Fri Mar 22 09:31:36 2019 -0700
@@ -836,7 +836,8 @@
             if (ch == QUOTE) {
                 ch = pattern.charAt(index++);
                 if (ch == MINUS_SIGN) {
-                    ch = symbols.getMinusSign();
+                    sb.append(symbols.getMinusSignText());
+                    continue;
                 }
             }
             sb.append(ch);
@@ -859,11 +860,14 @@
             if (ch == QUOTE) {
                 ch = pattern.charAt(index++);
                 if (ch == MINUS_SIGN) {
-                    ch = symbols.getMinusSign();
+                    String minusText = symbols.getMinusSignText();
                     FieldPosition fp = new FieldPosition(NumberFormat.Field.SIGN);
                     fp.setBeginIndex(stringIndex);
-                    fp.setEndIndex(stringIndex + 1);
+                    fp.setEndIndex(stringIndex + minusText.length());
                     positions.add(fp);
+                    stringIndex += minusText.length();
+                    affix.append(minusText);
+                    continue;
                 }
             }
             stringIndex++;
--- a/src/java.base/share/classes/java/text/DecimalFormat.java	Fri Mar 22 08:18:26 2019 -0700
+++ b/src/java.base/share/classes/java/text/DecimalFormat.java	Fri Mar 22 09:31:36 2019 -0700
@@ -54,20 +54,20 @@
 import sun.util.locale.provider.ResourceBundleBasedAdapter;
 
 /**
- * <code>DecimalFormat</code> is a concrete subclass of
- * <code>NumberFormat</code> that formats decimal numbers. It has a variety of
+ * {@code DecimalFormat} is a concrete subclass of
+ * {@code NumberFormat} that formats decimal numbers. It has a variety of
  * features designed to make it possible to parse and format numbers in any
  * locale, including support for Western, Arabic, and Indic digits.  It also
  * supports different kinds of numbers, including integers (123), fixed-point
  * numbers (123.4), scientific notation (1.23E4), percentages (12%), and
  * currency amounts ($123).  All of these can be localized.
  *
- * <p>To obtain a <code>NumberFormat</code> for a specific locale, including the
- * default locale, call one of <code>NumberFormat</code>'s factory methods, such
- * as <code>getInstance()</code>.  In general, do not call the
- * <code>DecimalFormat</code> constructors directly, since the
- * <code>NumberFormat</code> factory methods may return subclasses other than
- * <code>DecimalFormat</code>. If you need to customize the format object, do
+ * <p>To obtain a {@code NumberFormat} for a specific locale, including the
+ * default locale, call one of {@code NumberFormat}'s factory methods, such
+ * as {@code getInstance()}.  In general, do not call the
+ * {@code DecimalFormat} constructors directly, since the
+ * {@code NumberFormat} factory methods may return subclasses other than
+ * {@code DecimalFormat}. If you need to customize the format object, do
  * something like this:
  *
  * <blockquote><pre>
@@ -77,16 +77,16 @@
  * }
  * </pre></blockquote>
  *
- * <p>A <code>DecimalFormat</code> comprises a <em>pattern</em> and a set of
+ * <p>A {@code DecimalFormat} comprises a <em>pattern</em> and a set of
  * <em>symbols</em>.  The pattern may be set directly using
- * <code>applyPattern()</code>, or indirectly using the API methods.  The
- * symbols are stored in a <code>DecimalFormatSymbols</code> object.  When using
- * the <code>NumberFormat</code> factory methods, the pattern and symbols are
- * read from localized <code>ResourceBundle</code>s.
+ * {@code applyPattern()}, or indirectly using the API methods.  The
+ * symbols are stored in a {@code DecimalFormatSymbols} object.  When using
+ * the {@code NumberFormat} factory methods, the pattern and symbols are
+ * read from localized {@code ResourceBundle}s.
  *
  * <h2>Patterns</h2>
  *
- * <code>DecimalFormat</code> patterns have the following syntax:
+ * {@code DecimalFormat} patterns have the following syntax:
  * <blockquote><pre>
  * <i>Pattern:</i>
  *         <i>PositivePattern</i>
@@ -123,26 +123,26 @@
  *         0 <i>MinimumExponent<sub>opt</sub></i>
  * </pre></blockquote>
  *
- * <p>A <code>DecimalFormat</code> pattern contains a positive and negative
- * subpattern, for example, <code>"#,##0.00;(#,##0.00)"</code>.  Each
+ * <p>A {@code DecimalFormat} pattern contains a positive and negative
+ * subpattern, for example, {@code "#,##0.00;(#,##0.00)"}.  Each
  * subpattern has a prefix, numeric part, and suffix. The negative subpattern
  * is optional; if absent, then the positive subpattern prefixed with the
- * localized minus sign (<code>'-'</code> in most locales) is used as the
- * negative subpattern. That is, <code>"0.00"</code> alone is equivalent to
- * <code>"0.00;-0.00"</code>.  If there is an explicit negative subpattern, it
+ * localized minus sign ({@code '-'} in most locales) is used as the
+ * negative subpattern. That is, {@code "0.00"} alone is equivalent to
+ * {@code "0.00;-0.00"}.  If there is an explicit negative subpattern, it
  * serves only to specify the negative prefix and suffix; the number of digits,
  * minimal digits, and other characteristics are all the same as the positive
- * pattern. That means that <code>"#,##0.0#;(#)"</code> produces precisely
- * the same behavior as <code>"#,##0.0#;(#,##0.0#)"</code>.
+ * pattern. That means that {@code "#,##0.0#;(#)"} produces precisely
+ * the same behavior as {@code "#,##0.0#;(#,##0.0#)"}.
  *
  * <p>The prefixes, suffixes, and various symbols used for infinity, digits,
  * thousands separators, decimal separators, etc. may be set to arbitrary
  * values, and they will appear properly during formatting.  However, care must
  * be taken that the symbols and strings do not conflict, or parsing will be
  * unreliable.  For example, either the positive and negative prefixes or the
- * suffixes must be distinct for <code>DecimalFormat.parse()</code> to be able
+ * suffixes must be distinct for {@code DecimalFormat.parse()} to be able
  * to distinguish positive from negative values.  (If they are identical, then
- * <code>DecimalFormat</code> will behave as if no negative subpattern was
+ * {@code DecimalFormat} will behave as if no negative subpattern was
  * specified.)  Another example is that the decimal separator and thousands
  * separator should be distinct characters, or parsing will be impossible.
  *
@@ -151,8 +151,8 @@
  * of digits between the grouping characters, such as 3 for 100,000,000 or 4 for
  * 1,0000,0000.  If you supply a pattern with multiple grouping characters, the
  * interval between the last one and the end of the integer is the one that is
- * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> ==
- * <code>"##,####,####"</code>.
+ * used. So {@code "#,##,###,####"} == {@code "######,####"} ==
+ * {@code "##,####,####"}.
  *
  * <h3><a id="special_pattern_character">Special Pattern Characters</a></h3>
  *
@@ -164,7 +164,7 @@
  *
  * <p>The characters listed here are used in non-localized patterns.  Localized
  * patterns use the corresponding characters taken from this formatter's
- * <code>DecimalFormatSymbols</code> object instead, and these characters lose
+ * {@code DecimalFormatSymbols} object instead, and these characters lose
  * their special status.  Two exceptions are the currency sign and quote, which
  * are not localized.
  *
@@ -180,53 +180,53 @@
  * </thead>
  * <tbody>
  *     <tr style="vertical-align:top">
- *          <th scope="row"><code>0</code>
+ *          <th scope="row">{@code 0}
  *          <td>Number
  *          <td>Yes
  *          <td>Digit
  *     <tr style="vertical-align: top">
- *          <th scope="row"><code>#</code>
+ *          <th scope="row">{@code #}
  *          <td>Number
  *          <td>Yes
  *          <td>Digit, zero shows as absent
  *     <tr style="vertical-align:top">
- *          <th scope="row"><code>.</code>
+ *          <th scope="row">{@code .}
  *          <td>Number
  *          <td>Yes
  *          <td>Decimal separator or monetary decimal separator
  *     <tr style="vertical-align: top">
- *          <th scope="row"><code>-</code>
+ *          <th scope="row">{@code -}
  *          <td>Number
  *          <td>Yes
  *          <td>Minus sign
  *     <tr style="vertical-align:top">
- *          <th scope="row"><code>,</code>
+ *          <th scope="row">{@code ,}
  *          <td>Number
  *          <td>Yes
  *          <td>Grouping separator
  *     <tr style="vertical-align: top">
- *          <th scope="row"><code>E</code>
+ *          <th scope="row">{@code E}
  *          <td>Number
  *          <td>Yes
  *          <td>Separates mantissa and exponent in scientific notation.
  *              <em>Need not be quoted in prefix or suffix.</em>
  *     <tr style="vertical-align:top">
- *          <th scope="row"><code>;</code>
+ *          <th scope="row">{@code ;}
  *          <td>Subpattern boundary
  *          <td>Yes
  *          <td>Separates positive and negative subpatterns
  *     <tr style="vertical-align: top">
- *          <th scope="row"><code>%</code>
+ *          <th scope="row">{@code %}
  *          <td>Prefix or suffix
  *          <td>Yes
  *          <td>Multiply by 100 and show as percentage
  *     <tr style="vertical-align:top">
- *          <th scope="row"><code>&#92;u2030</code>
+ *          <th scope="row">{@code &#92;u2030}
  *          <td>Prefix or suffix
  *          <td>Yes
  *          <td>Multiply by 1000 and show as per mille value
  *     <tr style="vertical-align: top">
- *          <th scope="row"><code>&#164;</code> (<code>&#92;u00A4</code>)
+ *          <th scope="row">{@code &#164;} ({@code &#92;u00A4})
  *          <td>Prefix or suffix
  *          <td>No
  *          <td>Currency sign, replaced by currency symbol.  If
@@ -234,13 +234,13 @@
  *              If present in a pattern, the monetary decimal separator
  *              is used instead of the decimal separator.
  *     <tr style="vertical-align:top">
- *          <th scope="row"><code>'</code>
+ *          <th scope="row">{@code '}
  *          <td>Prefix or suffix
  *          <td>No
  *          <td>Used to quote special characters in a prefix or suffix,
- *              for example, <code>"'#'#"</code> formats 123 to
- *              <code>"#123"</code>.  To create a single quote
- *              itself, use two in a row: <code>"# o''clock"</code>.
+ *              for example, {@code "'#'#"} formats 123 to
+ *              {@code "#123"}.  To create a single quote
+ *              itself, use two in a row: {@code "# o''clock"}.
  * </tbody>
  * </table>
  * </blockquote>
@@ -251,18 +251,18 @@
  * and a power of ten, for example, 1234 can be expressed as 1.234 x 10^3.  The
  * mantissa is often in the range 1.0 &le; x {@literal <} 10.0, but it need not
  * be.
- * <code>DecimalFormat</code> can be instructed to format and parse scientific
+ * {@code DecimalFormat} can be instructed to format and parse scientific
  * notation <em>only via a pattern</em>; there is currently no factory method
  * that creates a scientific notation format.  In a pattern, the exponent
  * character immediately followed by one or more digit characters indicates
- * scientific notation.  Example: <code>"0.###E0"</code> formats the number
- * 1234 as <code>"1.234E3"</code>.
+ * scientific notation.  Example: {@code "0.###E0"} formats the number
+ * 1234 as {@code "1.234E3"}.
  *
  * <ul>
  * <li>The number of digit characters after the exponent character gives the
  * minimum exponent digit count.  There is no maximum.  Negative exponents are
  * formatted using the localized minus sign, <em>not</em> the prefix and suffix
- * from the pattern.  This allows patterns such as <code>"0.###E0 m/s"</code>.
+ * from the pattern.  This allows patterns such as {@code "0.###E0 m/s"}.
  *
  * <li>The minimum and maximum number of integer digits are interpreted
  * together:
@@ -273,19 +273,19 @@
  * number of integer digits, and the minimum number of integer digits to be
  * interpreted as 1.  The most common use of this is to generate
  * <em>engineering notation</em>, in which the exponent is a multiple of three,
- * e.g., <code>"##0.#####E0"</code>. Using this pattern, the number 12345
- * formats to <code>"12.345E3"</code>, and 123456 formats to
- * <code>"123.456E3"</code>.
+ * e.g., {@code "##0.#####E0"}. Using this pattern, the number 12345
+ * formats to {@code "12.345E3"}, and 123456 formats to
+ * {@code "123.456E3"}.
  *
  * <li>Otherwise, the minimum number of integer digits is achieved by adjusting the
- * exponent.  Example: 0.00123 formatted with <code>"00.###E0"</code> yields
- * <code>"12.3E-4"</code>.
+ * exponent.  Example: 0.00123 formatted with {@code "00.###E0"} yields
+ * {@code "12.3E-4"}.
  * </ul>
  *
  * <li>The number of significant digits in the mantissa is the sum of the
  * <em>minimum integer</em> and <em>maximum fraction</em> digits, and is
  * unaffected by the maximum integer digits.  For example, 12345 formatted with
- * <code>"##0.##E0"</code> is <code>"12.3E3"</code>. To show all digits, set
+ * {@code "##0.##E0"} is {@code "12.3E3"}. To show all digits, set
  * the significant digits count to zero.  The number of significant digits
  * does not affect parsing.
  *
@@ -294,38 +294,38 @@
  *
  * <h3>Rounding</h3>
  *
- * <code>DecimalFormat</code> provides rounding modes defined in
+ * {@code DecimalFormat} provides rounding modes defined in
  * {@link java.math.RoundingMode} for formatting.  By default, it uses
  * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}.
  *
  * <h3>Digits</h3>
  *
- * For formatting, <code>DecimalFormat</code> uses the ten consecutive
+ * For formatting, {@code DecimalFormat} uses the ten consecutive
  * characters starting with the localized zero digit defined in the
- * <code>DecimalFormatSymbols</code> object as digits. For parsing, these
+ * {@code DecimalFormatSymbols} object as digits. For parsing, these
  * digits as well as all Unicode decimal digits, as defined by
  * {@link Character#digit Character.digit}, are recognized.
  *
  * <h4>Special Values</h4>
  *
- * <p><code>NaN</code> is formatted as a string, which typically has a single character
- * <code>&#92;uFFFD</code>.  This string is determined by the
- * <code>DecimalFormatSymbols</code> object.  This is the only value for which
+ * <p>{@code NaN} is formatted as a string, which typically has a single character
+ * {@code &#92;uFFFD}.  This string is determined by the
+ * {@code DecimalFormatSymbols} object.  This is the only value for which
  * the prefixes and suffixes are not used.
  *
  * <p>Infinity is formatted as a string, which typically has a single character
- * <code>&#92;u221E</code>, with the positive or negative prefixes and suffixes
+ * {@code &#92;u221E}, with the positive or negative prefixes and suffixes
  * applied.  The infinity string is determined by the
- * <code>DecimalFormatSymbols</code> object.
+ * {@code DecimalFormatSymbols} object.
  *
- * <p>Negative zero (<code>"-0"</code>) parses to
+ * <p>Negative zero ({@code "-0"}) parses to
  * <ul>
- * <li><code>BigDecimal(0)</code> if <code>isParseBigDecimal()</code> is
+ * <li>{@code BigDecimal(0)} if {@code isParseBigDecimal()} is
  * true,
- * <li><code>Long(0)</code> if <code>isParseBigDecimal()</code> is false
- *     and <code>isParseIntegerOnly()</code> is true,
- * <li><code>Double(-0.0)</code> if both <code>isParseBigDecimal()</code>
- * and <code>isParseIntegerOnly()</code> are false.
+ * <li>{@code Long(0)} if {@code isParseBigDecimal()} is false
+ *     and {@code isParseIntegerOnly()} is true,
+ * <li>{@code Double(-0.0)} if both {@code isParseBigDecimal()}
+ * and {@code isParseIntegerOnly()} are false.
  * </ul>
  *
  * <h3><a id="synchronization">Synchronization</a></h3>
@@ -425,7 +425,7 @@
      * locale.
      *
      * @param pattern a non-localized pattern string.
-     * @exception NullPointerException if <code>pattern</code> is null
+     * @exception NullPointerException if {@code pattern} is null
      * @exception IllegalArgumentException if the given pattern is invalid.
      * @see java.text.NumberFormat#getInstance
      * @see java.text.NumberFormat#getNumberInstance
@@ -475,7 +475,7 @@
      * <p>
      * This implementation uses the maximum precision permitted.
      * @param number     the number to format
-     * @param toAppendTo the <code>StringBuffer</code> to which the formatted
+     * @param toAppendTo the {@code StringBuffer} to which the formatted
      *                   text is to be appended
      * @param pos        keeps track on the position of the field within the
      *                   returned string. For example, for formatting a number
@@ -485,11 +485,11 @@
      *                   and end index of {@code fieldPosition} will be set
      *                   to 0 and 9, respectively for the output string
      *                   {@code 1,234,567.89}.
-     * @return           the value passed in as <code>toAppendTo</code>
-     * @exception        IllegalArgumentException if <code>number</code> is
-     *                   null or not an instance of <code>Number</code>.
-     * @exception        NullPointerException if <code>toAppendTo</code> or
-     *                   <code>pos</code> is null
+     * @return           the value passed in as {@code toAppendTo}
+     * @exception        IllegalArgumentException if {@code number} is
+     *                   null or not an instance of {@code Number}.
+     * @exception        NullPointerException if {@code toAppendTo} or
+     *                   {@code pos} is null
      * @exception        ArithmeticException if rounding is needed with rounding
      *                   mode being set to RoundingMode.UNNECESSARY
      * @see              java.text.FieldPosition
@@ -914,13 +914,13 @@
     }
 
     /**
-     * Formats an Object producing an <code>AttributedCharacterIterator</code>.
-     * You can use the returned <code>AttributedCharacterIterator</code>
+     * Formats an Object producing an {@code AttributedCharacterIterator}.
+     * You can use the returned {@code AttributedCharacterIterator}
      * to build the resulting String, as well as to determine information
      * about the resulting String.
      * <p>
      * Each attribute key of the AttributedCharacterIterator will be of type
-     * <code>NumberFormat.Field</code>, with the attribute value being the
+     * {@code NumberFormat.Field}, with the attribute value being the
      * same as the attribute key.
      *
      * @exception NullPointerException if obj is null.
@@ -1916,7 +1916,7 @@
             if (negativeExponent) {
                 exponent = -exponent;
                 fieldStart = result.length();
-                result.append(symbols.getMinusSign());
+                result.append(symbols.getMinusSignText());
                 delegate.formatted(Field.EXPONENT_SIGN, Field.EXPONENT_SIGN,
                         fieldStart, result.length(), result);
             }
@@ -2042,17 +2042,17 @@
     }
 
     /**
-     * Appends the String <code>string</code> to <code>result</code>.
-     * <code>delegate</code> is notified of all  the
-     * <code>FieldPosition</code>s in <code>positions</code>.
+     * Appends the String {@code string} to {@code result}.
+     * {@code delegate} is notified of all  the
+     * {@code FieldPosition}s in {@code positions}.
      * <p>
-     * If one of the <code>FieldPosition</code>s in <code>positions</code>
-     * identifies a <code>SIGN</code> attribute, it is mapped to
-     * <code>signAttribute</code>. This is used
-     * to map the <code>SIGN</code> attribute to the <code>EXPONENT</code>
+     * If one of the {@code FieldPosition}s in {@code positions}
+     * identifies a {@code SIGN} attribute, it is mapped to
+     * {@code signAttribute}. This is used
+     * to map the {@code SIGN} attribute to the {@code EXPONENT}
      * attribute as necessary.
      * <p>
-     * This is used by <code>subformat</code> to add the prefix/suffix.
+     * This is used by {@code subformat} to add the prefix/suffix.
      */
     private void append(StringBuffer result, String string,
                         FieldDelegate delegate,
@@ -2078,60 +2078,60 @@
     }
 
     /**
-     * Parses text from a string to produce a <code>Number</code>.
+     * Parses text from a string to produce a {@code Number}.
      * <p>
      * The method attempts to parse text starting at the index given by
-     * <code>pos</code>.
-     * If parsing succeeds, then the index of <code>pos</code> is updated
+     * {@code pos}.
+     * If parsing succeeds, then the index of {@code pos} is updated
      * to the index after the last character used (parsing does not necessarily
      * use all characters up to the end of the string), and the parsed
-     * number is returned. The updated <code>pos</code> can be used to
+     * number is returned. The updated {@code pos} can be used to
      * indicate the starting point for the next call to this method.
-     * If an error occurs, then the index of <code>pos</code> is not
-     * changed, the error index of <code>pos</code> is set to the index of
+     * If an error occurs, then the index of {@code pos} is not
+     * changed, the error index of {@code pos} is set to the index of
      * the character where the error occurred, and null is returned.
      * <p>
      * The subclass returned depends on the value of {@link #isParseBigDecimal}
      * as well as on the string being parsed.
      * <ul>
-     *   <li>If <code>isParseBigDecimal()</code> is false (the default),
-     *       most integer values are returned as <code>Long</code>
-     *       objects, no matter how they are written: <code>"17"</code> and
-     *       <code>"17.000"</code> both parse to <code>Long(17)</code>.
-     *       Values that cannot fit into a <code>Long</code> are returned as
-     *       <code>Double</code>s. This includes values with a fractional part,
-     *       infinite values, <code>NaN</code>, and the value -0.0.
-     *       <code>DecimalFormat</code> does <em>not</em> decide whether to
-     *       return a <code>Double</code> or a <code>Long</code> based on the
+     *   <li>If {@code isParseBigDecimal()} is false (the default),
+     *       most integer values are returned as {@code Long}
+     *       objects, no matter how they are written: {@code "17"} and
+     *       {@code "17.000"} both parse to {@code Long(17)}.
+     *       Values that cannot fit into a {@code Long} are returned as
+     *       {@code Double}s. This includes values with a fractional part,
+     *       infinite values, {@code NaN}, and the value -0.0.
+     *       {@code DecimalFormat} does <em>not</em> decide whether to
+     *       return a {@code Double} or a {@code Long} based on the
      *       presence of a decimal separator in the source string. Doing so
      *       would prevent integers that overflow the mantissa of a double,
-     *       such as <code>"-9,223,372,036,854,775,808.00"</code>, from being
+     *       such as {@code "-9,223,372,036,854,775,808.00"}, from being
      *       parsed accurately.
      *       <p>
-     *       Callers may use the <code>Number</code> methods
-     *       <code>doubleValue</code>, <code>longValue</code>, etc., to obtain
+     *       Callers may use the {@code Number} methods
+     *       {@code doubleValue}, {@code longValue}, etc., to obtain
      *       the type they want.
-     *   <li>If <code>isParseBigDecimal()</code> is true, values are returned
-     *       as <code>BigDecimal</code> objects. The values are the ones
+     *   <li>If {@code isParseBigDecimal()} is true, values are returned
+     *       as {@code BigDecimal} objects. The values are the ones
      *       constructed by {@link java.math.BigDecimal#BigDecimal(String)}
      *       for corresponding strings in locale-independent format. The
      *       special cases negative and positive infinity and NaN are returned
-     *       as <code>Double</code> instances holding the values of the
-     *       corresponding <code>Double</code> constants.
+     *       as {@code Double} instances holding the values of the
+     *       corresponding {@code Double} constants.
      * </ul>
      * <p>
-     * <code>DecimalFormat</code> parses all Unicode characters that represent
-     * decimal digits, as defined by <code>Character.digit()</code>. In
-     * addition, <code>DecimalFormat</code> also recognizes as digits the ten
+     * {@code DecimalFormat} parses all Unicode characters that represent
+     * decimal digits, as defined by {@code Character.digit()}. In
+     * addition, {@code DecimalFormat} also recognizes as digits the ten
      * consecutive characters starting with the localized zero digit defined in
-     * the <code>DecimalFormatSymbols</code> object.
+     * the {@code DecimalFormatSymbols} object.
      *
      * @param text the string to be parsed
-     * @param pos  A <code>ParsePosition</code> object with index and error
+     * @param pos  A {@code ParsePosition} object with index and error
      *             index information as described above.
-     * @return     the parsed value, or <code>null</code> if the parse fails
-     * @exception  NullPointerException if <code>text</code> or
-     *             <code>pos</code> is null.
+     * @return     the parsed value, or {@code null} if the parse fails
+     * @exception  NullPointerException if {@code text} or
+     *             {@code pos} is null.
      */
     @Override
     public Number parse(String text, ParsePosition pos) {
@@ -2475,7 +2475,7 @@
                     boolean[] stat = new boolean[STATUS_LENGTH];
                     DigitList exponentDigits = new DigitList();
 
-                    if (subparse(text, pos, "", Character.toString(symbols.getMinusSign()), exponentDigits, true, stat) &&
+                    if (subparse(text, pos, "", symbols.getMinusSignText(), exponentDigits, true, stat) &&
                             exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
                         position = pos.index; // Advance past the exponent
                         exponent = (int)exponentDigits.getLong();
@@ -2573,7 +2573,7 @@
     /**
      * Returns the FieldPositions of the fields in the prefix used for
      * positive numbers. This is not used if the user has explicitly set
-     * a positive prefix via <code>setPositivePrefix</code>. This is
+     * a positive prefix via {@code setPositivePrefix}. This is
      * lazily created.
      *
      * @return FieldPositions in positive prefix
@@ -2614,7 +2614,7 @@
     /**
      * Returns the FieldPositions of the fields in the prefix used for
      * negative numbers. This is not used if the user has explicitly set
-     * a negative prefix via <code>setNegativePrefix</code>. This is
+     * a negative prefix via {@code setNegativePrefix}. This is
      * lazily created.
      *
      * @return FieldPositions in positive prefix
@@ -2655,7 +2655,7 @@
     /**
      * Returns the FieldPositions of the fields in the suffix used for
      * positive numbers. This is not used if the user has explicitly set
-     * a positive suffix via <code>setPositiveSuffix</code>. This is
+     * a positive suffix via {@code setPositiveSuffix}. This is
      * lazily created.
      *
      * @return FieldPositions in positive prefix
@@ -2696,7 +2696,7 @@
     /**
      * Returns the FieldPositions of the fields in the suffix used for
      * negative numbers. This is not used if the user has explicitly set
-     * a negative suffix via <code>setNegativeSuffix</code>. This is
+     * a negative suffix via {@code setNegativeSuffix}. This is
      * lazily created.
      *
      * @return FieldPositions in positive prefix
@@ -2811,7 +2811,7 @@
 
     /**
      * Returns whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
-     * method returns <code>BigDecimal</code>. The default value is false.
+     * method returns {@code BigDecimal}. The default value is false.
      *
      * @return {@code true} if the parse method returns BigDecimal;
      *         {@code false} otherwise
@@ -2824,7 +2824,7 @@
 
     /**
      * Sets whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
-     * method returns <code>BigDecimal</code>.
+     * method returns {@code BigDecimal}.
      *
      * @param newValue {@code true} if the parse method returns BigDecimal;
      *                 {@code false} otherwise
@@ -2991,14 +2991,14 @@
                     }
                     continue;
                 case PATTERN_PERCENT:
-                    c = symbols.getPercent();
-                    break;
+                    buffer.append(symbols.getPercentText());
+                    continue;
                 case PATTERN_PER_MILLE:
-                    c = symbols.getPerMill();
-                    break;
+                    buffer.append(symbols.getPerMillText());
+                    continue;
                 case PATTERN_MINUS:
-                    c = symbols.getMinusSign();
-                    break;
+                    buffer.append(symbols.getMinusSignText());
+                    continue;
                 }
             }
             buffer.append(c);
@@ -3027,12 +3027,11 @@
         for (int i=0; i<pattern.length(); ) {
             char c = pattern.charAt(i++);
             if (c == QUOTE) {
-                int field = -1;
                 Format.Field fieldID = null;
+                String string = null;
                 c = pattern.charAt(i++);
                 switch (c) {
                 case CURRENCY_SIGN:
-                    String string;
                     if (i<pattern.length() &&
                         pattern.charAt(i) == CURRENCY_SIGN) {
                         ++i;
@@ -3040,41 +3039,32 @@
                     } else {
                         string = symbols.getCurrencySymbol();
                     }
-                    if (!string.isEmpty()) {
-                        if (positions == null) {
-                            positions = new ArrayList<>(2);
-                        }
-                        FieldPosition fp = new FieldPosition(Field.CURRENCY);
-                        fp.setBeginIndex(stringIndex);
-                        fp.setEndIndex(stringIndex + string.length());
-                        positions.add(fp);
-                        stringIndex += string.length();
-                    }
-                    continue;
+                    fieldID = Field.CURRENCY;
+                    break;
                 case PATTERN_PERCENT:
-                    c = symbols.getPercent();
-                    field = -1;
+                    string = symbols.getPercentText();
                     fieldID = Field.PERCENT;
                     break;
                 case PATTERN_PER_MILLE:
-                    c = symbols.getPerMill();
-                    field = -1;
+                    string = symbols.getPerMillText();
                     fieldID = Field.PERMILLE;
                     break;
                 case PATTERN_MINUS:
-                    c = symbols.getMinusSign();
-                    field = -1;
+                    string = symbols.getMinusSignText();
                     fieldID = Field.SIGN;
                     break;
                 }
-                if (fieldID != null) {
+
+                if (fieldID != null && !string.isEmpty()) {
                     if (positions == null) {
                         positions = new ArrayList<>(2);
                     }
-                    FieldPosition fp = new FieldPosition(fieldID, field);
+                    FieldPosition fp = new FieldPosition(fieldID);
                     fp.setBeginIndex(stringIndex);
-                    fp.setEndIndex(stringIndex + 1);
+                    fp.setEndIndex(stringIndex + string.length());
                     positions.add(fp);
+                    stringIndex += string.length();
+                    continue;
                 }
             }
             stringIndex++;
@@ -3129,14 +3119,14 @@
                 } else if (localized) {
                     switch (c) {
                     case PATTERN_PERCENT:
-                        c = symbols.getPercent();
-                        break;
+                        buffer.append(symbols.getPercentText());
+                        continue;
                     case PATTERN_PER_MILLE:
-                        c = symbols.getPerMill();
-                        break;
+                        buffer.append(symbols.getPerMillText());
+                        continue;
                     case PATTERN_MINUS:
-                        c = symbols.getMinusSign();
-                        break;
+                        buffer.append(symbols.getMinusSignText());
+                        continue;
                     }
                 }
                 buffer.append(c);
@@ -3155,11 +3145,11 @@
             needQuote = affix.indexOf(symbols.getZeroDigit()) >= 0
                 || affix.indexOf(symbols.getGroupingSeparator()) >= 0
                 || affix.indexOf(symbols.getDecimalSeparator()) >= 0
-                || affix.indexOf(symbols.getPercent()) >= 0
-                || affix.indexOf(symbols.getPerMill()) >= 0
+                || affix.indexOf(symbols.getPercentText()) >= 0
+                || affix.indexOf(symbols.getPerMillText()) >= 0
                 || affix.indexOf(symbols.getDigit()) >= 0
                 || affix.indexOf(symbols.getPatternSeparator()) >= 0
-                || affix.indexOf(symbols.getMinusSign()) >= 0
+                || affix.indexOf(symbols.getMinusSignText()) >= 0
                 || affix.indexOf(CURRENCY_SIGN) >= 0;
         } else {
             needQuote = affix.indexOf(PATTERN_ZERO_DIGIT) >= 0
@@ -3235,7 +3225,7 @@
                     if ((negPrefixPattern != null && posPrefixPattern != null &&
                          negPrefixPattern.equals("'-" + posPrefixPattern)) ||
                         (negPrefixPattern == posPrefixPattern && // n == p == null
-                         negativePrefix.equals(symbols.getMinusSign() + positivePrefix)))
+                         negativePrefix.equals(symbols.getMinusSignText() + positivePrefix)))
                         break;
                 }
                 result.append(localized ? symbols.getPatternSeparator() :
@@ -3255,16 +3245,16 @@
      * by this routine, since that is the typical end-user desire;
      * use setMaximumInteger if you want to set a real value.
      * For negative numbers, use a second pattern, separated by a semicolon
-     * <P>Example <code>"#,#00.0#"</code> &rarr; 1,234.56
+     * <P>Example {@code "#,#00.0#"} &rarr; 1,234.56
      * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
      * a maximum of 2 fraction digits.
-     * <p>Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in
+     * <p>Example: {@code "#,#00.0#;(#,#00.0#)"} for negatives in
      * parentheses.
      * <p>In negative patterns, the minimum and maximum counts are ignored;
      * these are presumed to be set in the positive pattern.
      *
      * @param pattern a new pattern
-     * @exception NullPointerException if <code>pattern</code> is null
+     * @exception NullPointerException if {@code pattern} is null
      * @exception IllegalArgumentException if the given pattern is invalid.
      */
     public void applyPattern(String pattern) {
@@ -3282,16 +3272,16 @@
      * by this routine, since that is the typical end-user desire;
      * use setMaximumInteger if you want to set a real value.
      * For negative numbers, use a second pattern, separated by a semicolon
-     * <P>Example <code>"#,#00.0#"</code> &rarr; 1,234.56
+     * <P>Example {@code "#,#00.0#"} &rarr; 1,234.56
      * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
      * a maximum of 2 fraction digits.
-     * <p>Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in
+     * <p>Example: {@code "#,#00.0#;(#,#00.0#)"} for negatives in
      * parentheses.
      * <p>In negative patterns, the minimum and maximum counts are ignored;
      * these are presumed to be set in the positive pattern.
      *
      * @param pattern a new pattern
-     * @exception NullPointerException if <code>pattern</code> is null
+     * @exception NullPointerException if {@code pattern} is null
      * @exception IllegalArgumentException if the given pattern is invalid.
      */
     public void applyLocalizedPattern(String pattern) {
@@ -3309,7 +3299,7 @@
         char perMill           = PATTERN_PER_MILLE;
         char digit             = PATTERN_DIGIT;
         char separator         = PATTERN_SEPARATOR;
-        String exponent          = PATTERN_EXPONENT;
+        String exponent        = PATTERN_EXPONENT;
         char minus             = PATTERN_MINUS;
         if (localized) {
             zeroDigit         = symbols.getZeroDigit();
@@ -3635,8 +3625,8 @@
     /**
      * Sets the maximum number of digits allowed in the integer portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of {@code newValue} and
      * 309 is used. Negative input values are replaced with 0.
      * @see NumberFormat#setMaximumIntegerDigits
      */
@@ -3656,8 +3646,8 @@
     /**
      * Sets the minimum number of digits allowed in the integer portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of {@code newValue} and
      * 309 is used. Negative input values are replaced with 0.
      * @see NumberFormat#setMinimumIntegerDigits
      */
@@ -3677,8 +3667,8 @@
     /**
      * Sets the maximum number of digits allowed in the fraction portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of {@code newValue} and
      * 340 is used. Negative input values are replaced with 0.
      * @see NumberFormat#setMaximumFractionDigits
      */
@@ -3698,8 +3688,8 @@
     /**
      * Sets the minimum number of digits allowed in the fraction portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of {@code newValue} and
      * 340 is used. Negative input values are replaced with 0.
      * @see NumberFormat#setMinimumFractionDigits
      */
@@ -3719,8 +3709,8 @@
     /**
      * Gets the maximum number of digits allowed in the integer portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of the return value and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of the return value and
      * 309 is used.
      * @see #setMaximumIntegerDigits
      */
@@ -3732,8 +3722,8 @@
     /**
      * Gets the minimum number of digits allowed in the integer portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of the return value and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of the return value and
      * 309 is used.
      * @see #setMinimumIntegerDigits
      */
@@ -3745,8 +3735,8 @@
     /**
      * Gets the maximum number of digits allowed in the fraction portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of the return value and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of the return value and
      * 340 is used.
      * @see #setMaximumFractionDigits
      */
@@ -3758,8 +3748,8 @@
     /**
      * Gets the minimum number of digits allowed in the fraction portion of a
      * number.
-     * For formatting numbers other than <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects, the lower of the return value and
+     * For formatting numbers other than {@code BigInteger} and
+     * {@code BigDecimal} objects, the lower of the return value and
      * 340 is used.
      * @see #setMinimumFractionDigits
      */
@@ -3775,7 +3765,7 @@
      * {@link DecimalFormatSymbols#getCurrency DecimalFormatSymbols.getCurrency}
      * on this number format's symbols.
      *
-     * @return the currency used by this decimal format, or <code>null</code>
+     * @return the currency used by this decimal format, or {@code null}
      * @since 1.4
      */
     @Override
@@ -3792,7 +3782,7 @@
      * on this number format's symbols.
      *
      * @param currency the new currency to be used by this decimal format
-     * @exception NullPointerException if <code>currency</code> is null
+     * @exception NullPointerException if {@code currency} is null
      * @since 1.4
      */
     @Override
@@ -3809,7 +3799,7 @@
     /**
      * Gets the {@link java.math.RoundingMode} used in this DecimalFormat.
      *
-     * @return The <code>RoundingMode</code> used for this DecimalFormat.
+     * @return The {@code RoundingMode} used for this DecimalFormat.
      * @see #setRoundingMode(RoundingMode)
      * @since 1.6
      */
@@ -3821,9 +3811,9 @@
     /**
      * Sets the {@link java.math.RoundingMode} used in this DecimalFormat.
      *
-     * @param roundingMode The <code>RoundingMode</code> to be used
+     * @param roundingMode The {@code RoundingMode} to be used
      * @see #getRoundingMode()
-     * @exception NullPointerException if <code>roundingMode</code> is null.
+     * @exception NullPointerException if {@code roundingMode} is null.
      * @since 1.6
      */
     @Override
@@ -3845,38 +3835,38 @@
      * <li>
      * Verify that the superclass's digit count fields correctly reflect
      * the limits imposed on formatting numbers other than
-     * <code>BigInteger</code> and <code>BigDecimal</code> objects. These
+     * {@code BigInteger} and {@code BigDecimal} objects. These
      * limits are stored in the superclass for serialization compatibility
-     * with older versions, while the limits for <code>BigInteger</code> and
-     * <code>BigDecimal</code> objects are kept in this class.
+     * with older versions, while the limits for {@code BigInteger} and
+     * {@code BigDecimal} objects are kept in this class.
      * If, in the superclass, the minimum or maximum integer digit count is
-     * larger than <code>DOUBLE_INTEGER_DIGITS</code> or if the minimum or
+     * larger than {@code DOUBLE_INTEGER_DIGITS} or if the minimum or
      * maximum fraction digit count is larger than
-     * <code>DOUBLE_FRACTION_DIGITS</code>, then the stream data is invalid
-     * and this method throws an <code>InvalidObjectException</code>.
+     * {@code DOUBLE_FRACTION_DIGITS}, then the stream data is invalid
+     * and this method throws an {@code InvalidObjectException}.
      * <li>
-     * If <code>serialVersionOnStream</code> is less than 4, initialize
-     * <code>roundingMode</code> to {@link java.math.RoundingMode#HALF_EVEN
+     * If {@code serialVersionOnStream} is less than 4, initialize
+     * {@code roundingMode} to {@link java.math.RoundingMode#HALF_EVEN
      * RoundingMode.HALF_EVEN}.  This field is new with version 4.
      * <li>
-     * If <code>serialVersionOnStream</code> is less than 3, then call
+     * If {@code serialVersionOnStream} is less than 3, then call
      * the setters for the minimum and maximum integer and fraction digits with
      * the values of the corresponding superclass getters to initialize the
      * fields in this class. The fields in this class are new with version 3.
      * <li>
-     * If <code>serialVersionOnStream</code> is less than 1, indicating that
+     * If {@code serialVersionOnStream} is less than 1, indicating that
      * the stream was written by JDK 1.1, initialize
-     * <code>useExponentialNotation</code>
+     * {@code useExponentialNotation}
      * to false, since it was not present in JDK 1.1.
      * <li>
-     * Set <code>serialVersionOnStream</code> to the maximum allowed value so
+     * Set {@code serialVersionOnStream} to the maximum allowed value so
      * that default serialization will work properly if this object is streamed
      * out again.
      * </ol>
      *
      * <p>Stream versions older than 2 will not have the affix pattern variables
-     * <code>posPrefixPattern</code> etc.  As a result, they will be initialized
-     * to <code>null</code>, which means the affix strings will be taken as
+     * {@code posPrefixPattern} etc.  As a result, they will be initialized
+     * to {@code null}, which means the affix strings will be taken as
      * literal values.  This is exactly what we want, since that corresponds to
      * the pre-version-2 behavior.
      */
@@ -3960,14 +3950,14 @@
 
     /**
      * The prefix pattern for non-negative numbers.  This variable corresponds
-     * to <code>positivePrefix</code>.
+     * to {@code positivePrefix}.
      *
-     * <p>This pattern is expanded by the method <code>expandAffix()</code> to
-     * <code>positivePrefix</code> to update the latter to reflect changes in
-     * <code>symbols</code>.  If this variable is <code>null</code> then
-     * <code>positivePrefix</code> is taken as a literal value that does not
-     * change when <code>symbols</code> changes.  This variable is always
-     * <code>null</code> for <code>DecimalFormat</code> objects older than
+     * <p>This pattern is expanded by the method {@code expandAffix()} to
+     * {@code positivePrefix} to update the latter to reflect changes in
+     * {@code symbols}.  If this variable is {@code null} then
+     * {@code positivePrefix} is taken as a literal value that does not
+     * change when {@code symbols} changes.  This variable is always
+     * {@code null} for {@code DecimalFormat} objects older than
      * stream version 2 restored from stream.
      *
      * @serial
@@ -3977,8 +3967,8 @@
 
     /**
      * The suffix pattern for non-negative numbers.  This variable corresponds
-     * to <code>positiveSuffix</code>.  This variable is analogous to
-     * <code>posPrefixPattern</code>; see that variable for further
+     * to {@code positiveSuffix}.  This variable is analogous to
+     * {@code posPrefixPattern}; see that variable for further
      * documentation.
      *
      * @serial
@@ -3988,8 +3978,8 @@
 
     /**
      * The prefix pattern for negative numbers.  This variable corresponds
-     * to <code>negativePrefix</code>.  This variable is analogous to
-     * <code>posPrefixPattern</code>; see that variable for further
+     * to {@code negativePrefix}.  This variable is analogous to
+     * {@code posPrefixPattern}; see that variable for further
      * documentation.
      *
      * @serial
@@ -3999,8 +3989,8 @@
 
     /**
      * The suffix pattern for negative numbers.  This variable corresponds
-     * to <code>negativeSuffix</code>.  This variable is analogous to
-     * <code>posPrefixPattern</code>; see that variable for further
+     * to {@code negativeSuffix}.  This variable is analogous to
+     * {@code posPrefixPattern}; see that variable for further
      * documentation.
      *
      * @serial
@@ -4019,7 +4009,7 @@
     /**
      * The number of digits between grouping separators in the integer
      * portion of a number.  Must be greater than 0 if
-     * <code>NumberFormat.groupingUsed</code> is true.
+     * {@code NumberFormat.groupingUsed} is true.
      *
      * @serial
      * @see #getGroupingSize
@@ -4053,7 +4043,7 @@
     private transient boolean isCurrencyFormat = false;
 
     /**
-     * The <code>DecimalFormatSymbols</code> object used by this format.
+     * The {@code DecimalFormatSymbols} object used by this format.
      * It contains the symbols used to format numbers, e.g. the grouping separator,
      * decimal separator, and so on.
      *
@@ -4074,28 +4064,28 @@
 
     /**
      * FieldPositions describing the positive prefix String. This is
-     * lazily created. Use <code>getPositivePrefixFieldPositions</code>
+     * lazily created. Use {@code getPositivePrefixFieldPositions}
      * when needed.
      */
     private transient FieldPosition[] positivePrefixFieldPositions;
 
     /**
      * FieldPositions describing the positive suffix String. This is
-     * lazily created. Use <code>getPositiveSuffixFieldPositions</code>
+     * lazily created. Use {@code getPositiveSuffixFieldPositions}
      * when needed.
      */
     private transient FieldPosition[] positiveSuffixFieldPositions;
 
     /**
      * FieldPositions describing the negative prefix String. This is
-     * lazily created. Use <code>getNegativePrefixFieldPositions</code>
+     * lazily created. Use {@code getNegativePrefixFieldPositions}
      * when needed.
      */
     private transient FieldPosition[] negativePrefixFieldPositions;
 
     /**
      * FieldPositions describing the negative suffix String. This is
-     * lazily created. Use <code>getNegativeSuffixFieldPositions</code>
+     * lazily created. Use {@code getNegativeSuffixFieldPositions}
      * when needed.
      */
     private transient FieldPosition[] negativeSuffixFieldPositions;
@@ -4103,7 +4093,7 @@
     /**
      * The minimum number of digits used to display the exponent when a number is
      * formatted in exponential notation.  This field is ignored if
-     * <code>useExponentialNotation</code> is not true.
+     * {@code useExponentialNotation} is not true.
      *
      * @serial
      * @since 1.2
@@ -4112,9 +4102,9 @@
 
     /**
      * The maximum number of digits allowed in the integer portion of a
-     * <code>BigInteger</code> or <code>BigDecimal</code> number.
-     * <code>maximumIntegerDigits</code> must be greater than or equal to
-     * <code>minimumIntegerDigits</code>.
+     * {@code BigInteger} or {@code BigDecimal} number.
+     * {@code maximumIntegerDigits} must be greater than or equal to
+     * {@code minimumIntegerDigits}.
      *
      * @serial
      * @see #getMaximumIntegerDigits
@@ -4124,9 +4114,9 @@
 
     /**
      * The minimum number of digits allowed in the integer portion of a
-     * <code>BigInteger</code> or <code>BigDecimal</code> number.
-     * <code>minimumIntegerDigits</code> must be less than or equal to
-     * <code>maximumIntegerDigits</code>.
+     * {@code BigInteger} or {@code BigDecimal} number.
+     * {@code minimumIntegerDigits} must be less than or equal to
+     * {@code maximumIntegerDigits}.
      *
      * @serial
      * @see #getMinimumIntegerDigits
@@ -4136,9 +4126,9 @@
 
     /**
      * The maximum number of digits allowed in the fractional portion of a
-     * <code>BigInteger</code> or <code>BigDecimal</code> number.
-     * <code>maximumFractionDigits</code> must be greater than or equal to
-     * <code>minimumFractionDigits</code>.
+     * {@code BigInteger} or {@code BigDecimal} number.
+     * {@code maximumFractionDigits} must be greater than or equal to
+     * {@code minimumFractionDigits}.
      *
      * @serial
      * @see #getMaximumFractionDigits
@@ -4148,9 +4138,9 @@
 
     /**
      * The minimum number of digits allowed in the fractional portion of a
-     * <code>BigInteger</code> or <code>BigDecimal</code> number.
-     * <code>minimumFractionDigits</code> must be less than or equal to
-     * <code>maximumFractionDigits</code>.
+     * {@code BigInteger} or {@code BigDecimal} number.
+     * {@code minimumFractionDigits} must be less than or equal to
+     * {@code maximumFractionDigits}.
      *
      * @serial
      * @see #getMinimumFractionDigits
@@ -4247,19 +4237,19 @@
      * <ul>
      * <li><b>0</b> (default): versions before the Java 2 platform v1.2
      * <li><b>1</b>: version for 1.2, which includes the two new fields
-     *      <code>useExponentialNotation</code> and
-     *      <code>minExponentDigits</code>.
+     *      {@code useExponentialNotation} and
+     *      {@code minExponentDigits}.
      * <li><b>2</b>: version for 1.3 and later, which adds four new fields:
-     *      <code>posPrefixPattern</code>, <code>posSuffixPattern</code>,
-     *      <code>negPrefixPattern</code>, and <code>negSuffixPattern</code>.
+     *      {@code posPrefixPattern}, {@code posSuffixPattern},
+     *      {@code negPrefixPattern}, and {@code negSuffixPattern}.
      * <li><b>3</b>: version for 1.5 and later, which adds five new fields:
-     *      <code>maximumIntegerDigits</code>,
-     *      <code>minimumIntegerDigits</code>,
-     *      <code>maximumFractionDigits</code>,
-     *      <code>minimumFractionDigits</code>, and
-     *      <code>parseBigDecimal</code>.
+     *      {@code maximumIntegerDigits},
+     *      {@code minimumIntegerDigits},
+     *      {@code maximumFractionDigits},
+     *      {@code minimumFractionDigits}, and
+     *      {@code parseBigDecimal}.
      * <li><b>4</b>: version for 1.6 and later, which adds one new field:
-     *      <code>roundingMode</code>.
+     *      {@code roundingMode}.
      * </ul>
      * @since 1.2
      * @serial
--- a/src/java.base/share/classes/java/text/DecimalFormatSymbols.java	Fri Mar 22 08:18:26 2019 -0700
+++ b/src/java.base/share/classes/java/text/DecimalFormatSymbols.java	Fri Mar 22 09:31:36 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,12 +38,14 @@
 
 package java.text;
 
+import java.io.InvalidObjectException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.text.spi.DecimalFormatSymbolsProvider;
 import java.util.Currency;
 import java.util.Locale;
+import java.util.Objects;
 import sun.util.locale.provider.CalendarDataUtility;
 import sun.util.locale.provider.LocaleProviderAdapter;
 import sun.util.locale.provider.LocaleServiceProviderPool;
@@ -51,11 +53,11 @@
 
 /**
  * This class represents the set of symbols (such as the decimal separator,
- * the grouping separator, and so on) needed by <code>DecimalFormat</code>
- * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
- * <code>DecimalFormatSymbols</code> from its locale data.  If you need to change any
- * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
- * your <code>DecimalFormat</code> and modify it.
+ * the grouping separator, and so on) needed by {@code DecimalFormat}
+ * to format numbers. {@code DecimalFormat} creates for itself an instance of
+ * {@code DecimalFormatSymbols} from its locale data.  If you need to change any
+ * of these symbols, you can get the {@code DecimalFormatSymbols} object from
+ * your {@code DecimalFormat} and modify it.
  *
  * <p>If the locale contains "rg" (region override)
  * <a href="../util/Locale.html#def_locale_extension">Unicode extension</a>,
@@ -107,7 +109,7 @@
      * instead of the Latin numbering system.
      *
      * @param locale the desired locale
-     * @exception NullPointerException if <code>locale</code> is null
+     * @exception NullPointerException if {@code locale} is null
      */
     public DecimalFormatSymbols( Locale locale ) {
         initialize( locale );
@@ -115,16 +117,16 @@
 
     /**
      * Returns an array of all locales for which the
-     * <code>getInstance</code> methods of this class can return
+     * {@code getInstance} methods of this class can return
      * localized instances.
      * The returned array represents the union of locales supported by the Java
      * runtime and by installed
      * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
-     * implementations.  It must contain at least a <code>Locale</code>
+     * implementations.  It must contain at least a {@code Locale}
      * instance equal to {@link java.util.Locale#US Locale.US}.
      *
      * @return an array of locales for which localized
-     *         <code>DecimalFormatSymbols</code> instances are available.
+     *         {@code DecimalFormatSymbols} instances are available.
      * @since 1.6
      */
     public static Locale[] getAvailableLocales() {
@@ -134,8 +136,8 @@
     }
 
     /**
-     * Gets the <code>DecimalFormatSymbols</code> instance for the default
-     * locale.  This method provides access to <code>DecimalFormatSymbols</code>
+     * Gets the {@code DecimalFormatSymbols} instance for the default
+     * locale.  This method provides access to {@code DecimalFormatSymbols}
      * instances for locales supported by the Java runtime itself as well
      * as for those supported by installed
      * {@link java.text.spi.DecimalFormatSymbolsProvider
@@ -145,7 +147,7 @@
      *     getInstance(Locale.getDefault(Locale.Category.FORMAT))}.
      * @see java.util.Locale#getDefault(java.util.Locale.Category)
      * @see java.util.Locale.Category#FORMAT
-     * @return a <code>DecimalFormatSymbols</code> instance.
+     * @return a {@code DecimalFormatSymbols} instance.
      * @since 1.6
      */
     public static final DecimalFormatSymbols getInstance() {
@@ -153,8 +155,8 @@
     }
 
     /**
-     * Gets the <code>DecimalFormatSymbols</code> instance for the specified
-     * locale.  This method provides access to <code>DecimalFormatSymbols</code>
+     * Gets the {@code DecimalFormatSymbols} instance for the specified
+     * locale.  This method provides access to {@code DecimalFormatSymbols}
      * instances for locales supported by the Java runtime itself as well
      * as for those supported by installed
      * {@link java.text.spi.DecimalFormatSymbolsProvider
@@ -169,8 +171,8 @@
      * instead of the Latin numbering system.
      *
      * @param locale the desired locale.
-     * @return a <code>DecimalFormatSymbols</code> instance.
-     * @exception NullPointerException if <code>locale</code> is null
+     * @return a {@code DecimalFormatSymbols} instance.
+     * @exception NullPointerException if {@code locale} is null
      * @since 1.6
      */
     public static final DecimalFormatSymbols getInstance(Locale locale) {
@@ -255,6 +257,41 @@
      */
     public void setPerMill(char perMill) {
         this.perMill = perMill;
+        this.perMillText = Character.toString(perMill);
+    }
+
+    /**
+     * Gets the string used for per mille sign. Different for Arabic, etc.
+     *
+     * @return the string used for per mille sign
+     * @since 13
+     */
+    String getPerMillText() {
+        return perMillText;
+    }
+
+    /**
+     * Sets the string used for per mille sign. Different for Arabic, etc.
+     *
+     * Setting the {@code perMillText} affects the return value of
+     * {@link #getPerMill()}, in which the first non-format character of
+     * {@code perMillText} is returned.
+     *
+     * @param perMillText the string used for per mille sign
+     * @throws NullPointerException if {@code perMillText} is null
+     * @throws IllegalArgumentException if {@code perMillText} is an empty string
+     * @see #getPerMill()
+     * @see #getPerMillText()
+     * @since 13
+     */
+    void setPerMillText(String perMillText) {
+        Objects.requireNonNull(perMillText);
+        if (perMillText.isEmpty()) {
+            throw new IllegalArgumentException("Empty argument string");
+        }
+
+        this.perMillText = perMillText;
+        this.perMill = findNonFormatChar(perMillText, '\u2030');
     }
 
     /**
@@ -273,6 +310,41 @@
      */
     public void setPercent(char percent) {
         this.percent = percent;
+        this.percentText = Character.toString(percent);
+    }
+
+    /**
+     * Gets the string used for percent sign. Different for Arabic, etc.
+     *
+     * @return the string used for percent sign
+     * @since 13
+     */
+    String getPercentText() {
+        return percentText;
+    }
+
+    /**
+     * Sets the string used for percent sign. Different for Arabic, etc.
+     *
+     * Setting the {@code percentText} affects the return value of
+     * {@link #getPercent()}, in which the first non-format character of
+     * {@code percentText} is returned.
+     *
+     * @param percentText the string used for percent sign
+     * @throws NullPointerException if {@code percentText} is null
+     * @throws IllegalArgumentException if {@code percentText} is an empty string
+     * @see #getPercent()
+     * @see #getPercentText()
+     * @since 13
+     */
+    void setPercentText(String percentText) {
+        Objects.requireNonNull(percentText);
+        if (percentText.isEmpty()) {
+            throw new IllegalArgumentException("Empty argument string");
+        }
+
+        this.percentText = percentText;
+        this.percent = findNonFormatChar(percentText, '%');
     }
 
     /**
@@ -373,6 +445,46 @@
      */
     public void setMinusSign(char minusSign) {
         this.minusSign = minusSign;
+        this.minusSignText = Character.toString(minusSign);
+    }
+
+    /**
+     * Gets the string used to represent minus sign. If no explicit
+     * negative format is specified, one is formed by prefixing
+     * minusSignText to the positive format.
+     *
+     * @return the string representing minus sign
+     * @since 13
+     */
+    String getMinusSignText() {
+        return minusSignText;
+    }
+
+    /**
+     * Sets the string used to represent minus sign. If no explicit
+     * negative format is specified, one is formed by prefixing
+     * minusSignText to the positive format.
+     *
+     * Setting the {@code minusSignText} affects the return value of
+     * {@link #getMinusSign()}, in which the first non-format character of
+     * {@code minusSignText} is returned.
+     *
+     * @param minusSignText the character representing minus sign
+     * @throws NullPointerException if {@code minusSignText} is null
+     * @throws IllegalArgumentException if {@code minusSignText} is an
+     *  empty string
+     * @see #getMinusSign()
+     * @see #getMinusSignText()
+     * @since 13
+     */
+    void setMinusSignText(String minusSignText) {
+        Objects.requireNonNull(minusSignText);
+        if (minusSignText.isEmpty()) {
+            throw new IllegalArgumentException("Empty argument string");
+        }
+
+        this.minusSignText = minusSignText;
+        this.minusSign = findNonFormatChar(minusSignText, '-');
     }
 
     /**
@@ -464,7 +576,7 @@
      * symbol attribute to the currency's ISO 4217 currency code.
      *
      * @param currency the new currency to be used
-     * @exception NullPointerException if <code>currency</code> is null
+     * @exception NullPointerException if {@code currency} is null
      * @since 1.4
      * @see #setCurrencySymbol
      * @see #setInternationalCurrencySymbol
@@ -540,7 +652,7 @@
      * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
      *
      * @param exp the exponent separator string
-     * @exception NullPointerException if <code>exp</code> is null
+     * @exception NullPointerException if {@code exp} is null
      * @see #getExponentSeparator()
      * @since 1.6
      */
@@ -583,9 +695,12 @@
         groupingSeparator == other.groupingSeparator &&
         decimalSeparator == other.decimalSeparator &&
         percent == other.percent &&
+        percentText.equals(other.percentText) &&
         perMill == other.perMill &&
+        perMillText.equals(other.perMillText) &&
         digit == other.digit &&
         minusSign == other.minusSign &&
+        minusSignText.equals(other.minusSignText) &&
         patternSeparator == other.patternSeparator &&
         infinity.equals(other.infinity) &&
         NaN.equals(other.NaN) &&
@@ -631,13 +746,16 @@
         decimalSeparator = numberElements[0].charAt(0);
         groupingSeparator = numberElements[1].charAt(0);
         patternSeparator = numberElements[2].charAt(0);
-        percent = numberElements[3].charAt(0);
+        percentText = numberElements[3];
+        percent = findNonFormatChar(percentText, '%');
         zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
         digit = numberElements[5].charAt(0);
-        minusSign = numberElements[6].charAt(0);
+        minusSignText = numberElements[6];
+        minusSign = findNonFormatChar(minusSignText, '-');
         exponential = numberElements[7].charAt(0);
         exponentialSeparator = numberElements[7]; //string representation new since 1.6
-        perMill = numberElements[8].charAt(0);
+        perMillText = numberElements[8];
+        perMill = findNonFormatChar(perMillText, '\u2030');
         infinity  = numberElements[9];
         NaN = numberElements[10];
 
@@ -652,6 +770,16 @@
     }
 
     /**
+     * Obtains non-format single character from String
+     */
+    private char findNonFormatChar(String src, char defChar) {
+        return (char)src.chars()
+            .filter(c -> Character.getType(c) != Character.FORMAT)
+            .findFirst()
+            .orElse(defChar);
+    }
+
+    /**
      * Lazy initialization for currency related fields
      */
     private void initializeCurrency(Locale locale) {
@@ -704,18 +832,24 @@
     /**
      * Reads the default serializable fields, provides default values for objects
      * in older serial versions, and initializes non-serializable fields.
-     * If <code>serialVersionOnStream</code>
-     * is less than 1, initializes <code>monetarySeparator</code> to be
-     * the same as <code>decimalSeparator</code> and <code>exponential</code>
+     * If {@code serialVersionOnStream}
+     * is less than 1, initializes {@code monetarySeparator} to be
+     * the same as {@code decimalSeparator} and {@code exponential}
      * to be 'E'.
-     * If <code>serialVersionOnStream</code> is less than 2,
-     * initializes <code>locale</code>to the root locale, and initializes
-     * If <code>serialVersionOnStream</code> is less than 3, it initializes
-     * <code>exponentialSeparator</code> using <code>exponential</code>.
-     * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
+     * If {@code serialVersionOnStream} is less than 2,
+     * initializes {@code locale}to the root locale, and initializes
+     * If {@code serialVersionOnStream} is less than 3, it initializes
+     * {@code exponentialSeparator} using {@code exponential}.
+     * If {@code serialVersionOnStream} is less than 4, it initializes
+     * {@code perMillText}, {@code percentText}, and
+     * {@code minusSignText} using {@code perMill}, {@code percent}, and
+     * {@code minusSign} respectively.
+     * Sets {@code serialVersionOnStream} back to the maximum allowed value so that
      * default serialization will work properly if this object is streamed out again.
      * Initializes the currency from the intlCurrencySymbol field.
      *
+     * @throws InvalidObjectException if {@code char} and {@code String}
+     *      representations of either percent, per mille, and/or minus sign disagree.
      * @since  1.1.6
      */
     private void readObject(ObjectInputStream stream)
@@ -735,6 +869,23 @@
             // didn't have exponentialSeparator. Create one using exponential
             exponentialSeparator = Character.toString(exponential);
         }
+        if (serialVersionOnStream < 4) {
+            // didn't have perMillText, percentText, and minusSignText.
+            // Create one using corresponding char variations.
+            perMillText = Character.toString(perMill);
+            percentText = Character.toString(percent);
+            minusSignText = Character.toString(minusSign);
+        } else {
+            // Check whether char and text fields agree
+            if (findNonFormatChar(perMillText, '\uFFFF') != perMill ||
+                findNonFormatChar(percentText, '\uFFFF') != percent ||
+                findNonFormatChar(minusSignText, '\uFFFF') != minusSign) {
+                throw new InvalidObjectException(
+                    "'char' and 'String' representations of either percent, " +
+                    "per mille, and/or minus sign disagree.");
+            }
+        }
+
         serialVersionOnStream = currentSerialVersion;
 
         if (intlCurrencySymbol != null) {
@@ -862,8 +1013,8 @@
      * The string used to separate the mantissa from the exponent.
      * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
      * <p>
-     * If both <code>exponential</code> and <code>exponentialSeparator</code>
-     * exist, this <code>exponentialSeparator</code> has the precedence.
+     * If both {@code exponential} and {@code exponentialSeparator}
+     * exist, this {@code exponentialSeparator} has the precedence.
      *
      * @serial
      * @since 1.6
@@ -878,6 +1029,39 @@
      */
     private Locale locale;
 
+    /**
+     * String representation of per mille sign, which may include
+     * formatting characters, such as BiDi control characters.
+     * The first non-format character of this string is the same as
+     * {@code perMill}.
+     *
+     * @serial
+     * @since 13
+     */
+    private  String perMillText;
+
+    /**
+     * String representation of percent sign, which may include
+     * formatting characters, such as BiDi control characters.
+     * The first non-format character of this string is the same as
+     * {@code percent}.
+     *
+     * @serial
+     * @since 13
+     */
+    private  String percentText;
+
+    /**
+     * String representation of minus sign, which may include
+     * formatting characters, such as BiDi control characters.
+     * The first non-format character of this string is the same as
+     * {@code minusSign}.
+     *
+     * @serial
+     * @since 13
+     */
+    private  String minusSignText;
+
     // currency; only the ISO code is serialized.
     private transient Currency currency;
     private transient volatile boolean currencyInitialized;
@@ -891,23 +1075,28 @@
     //     monetarySeparator and exponential.
     // - 2 for version from J2SE 1.4, which includes locale field.
     // - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
-    private static final int currentSerialVersion = 3;
+    // - 4 for version from Java SE 13, which includes perMillText, percentText,
+    //      and minusSignText field.
+    private static final int currentSerialVersion = 4;
 
     /**
-     * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
+     * Describes the version of {@code DecimalFormatSymbols} present on the stream.
      * Possible values are:
      * <ul>
      * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
      *
      * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
-     *      two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
+     *      two new fields: {@code monetarySeparator} and {@code exponential}.
      * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
-     *      new <code>locale</code> field.
+     *      new {@code locale} field.
      * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
-     *      new <code>exponentialSeparator</code> field.
+     *      new {@code exponentialSeparator} field.
+     * <li><b>4</b>: Versions written by Java SE 13 or later, which include
+     *      new {@code perMillText}, {@code percentText}, and
+     *      {@code minusSignText} field.
      * </ul>
-     * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
-     * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
+     * When streaming out a {@code DecimalFormatSymbols}, the most recent format
+     * (corresponding to the highest allowable {@code serialVersionOnStream})
      * is always written.
      *
      * @serial
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/text/Format/NumberFormat/DFSMinusPerCentMill.java	Fri Mar 22 09:31:36 2019 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8220309
+ * @library /java/text/testlib
+ * @summary Test String representation of MinusSign/Percent/PerMill symbols.
+ *          This test assumes CLDR has numbering systems for "arab" and
+ *          "arabext", and their minus/percent representations include
+ *          BiDi formatting control characters.
+ * @run testng/othervm DFSMinusPerCentMill
+ */
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+import static org.testng.Assert.*;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class DFSMinusPerCentMill {
+    private enum Type {
+        NUMBER, PERCENT, CURRENCY, INTEGER, COMPACT, PERMILL
+    }
+
+    private static final Locale US_ARAB = Locale.forLanguageTag("en-US-u-nu-arab");
+    private static final Locale US_ARABEXT = Locale.forLanguageTag("en-US-u-nu-arabext");
+    private static final double SRC_NUM = -1234.56;
+
+    @DataProvider
+    Object[][] formatData() {
+        return new Object[][] {
+            // Locale, FormatStyle, expected format, expected single char symbol
+            {US_ARAB, Type.NUMBER, "\u061c-\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
+            {US_ARAB, Type.PERCENT, "\u061c-\u0661\u0662\u0663\u066c\u0664\u0665\u0666\u066a\u061c"},
+            {US_ARAB, Type.CURRENCY, "\u061c-$\u0661\u066c\u0662\u0663\u0664\u066b\u0665\u0666"},
+            {US_ARAB, Type.INTEGER, "\u061c-\u0661\u066c\u0662\u0663\u0665"},
+            {US_ARAB, Type.COMPACT, "\u061c-\u0661K"},
+            {US_ARAB, Type.PERMILL, "\u061c-\u0661\u0662\u0663\u0664\u0665\u0666\u0660\u0609"},
+
+            {US_ARABEXT, Type.NUMBER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
+            {US_ARABEXT, Type.PERCENT, "\u200e-\u200e\u06f1\u06f2\u06f3\u066c\u06f4\u06f5\u06f6\u066a"},
+            {US_ARABEXT, Type.CURRENCY, "\u200e-\u200e$\u06f1\u066c\u06f2\u06f3\u06f4\u066b\u06f5\u06f6"},
+            {US_ARABEXT, Type.INTEGER, "\u200e-\u200e\u06f1\u066c\u06f2\u06f3\u06f5"},
+            {US_ARABEXT, Type.COMPACT, "\u200e-\u200e\u06f1K"},
+            {US_ARABEXT, Type.PERMILL, "\u200e-\u200e\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f0\u0609"},
+        };
+    }
+
+    @DataProvider
+    Object[][] charSymbols() {
+        return new Object[][]{
+            // Locale, percent, per mille, minus sign
+            {US_ARAB, '\u066a', '\u0609', '-'},
+            {US_ARABEXT, '\u066a', '\u0609', '-'},
+        };
+    }
+
+    @Test(dataProvider="formatData")
+    public void testFormatData(Locale l, Type style, String expected) {
+        NumberFormat nf = null;
+        switch (style) {
+            case NUMBER:
+                nf = NumberFormat.getNumberInstance(l);
+                break;
+            case PERCENT:
+                nf = NumberFormat.getPercentInstance(l);
+                break;
+            case CURRENCY:
+                nf = NumberFormat.getCurrencyInstance(l);
+                break;
+            case INTEGER:
+                nf = NumberFormat.getIntegerInstance(l);
+                break;
+            case COMPACT:
+                nf = NumberFormat.getCompactNumberInstance(l, NumberFormat.Style.SHORT);
+                break;
+            case PERMILL:
+                nf = new DecimalFormat("#.#\u2030", DecimalFormatSymbols.getInstance(l));
+                break;
+        }
+
+        assertEquals(nf.format(SRC_NUM), expected);
+    }
+
+    @Test(dataProvider="charSymbols")
+    public void testCharSymbols(Locale l, char percent, char permill, char minus) {
+        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
+        assertEquals(dfs.getPercent(), percent);
+        assertEquals(dfs.getPerMill(), permill);
+        assertEquals(dfs.getMinusSign(), minus);
+    }
+
+    @Test
+    public void testSerialization() throws Exception {
+        DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance();
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        new ObjectOutputStream(bos).writeObject(dfs);
+        DecimalFormatSymbols dfsSerialized = (DecimalFormatSymbols)new ObjectInputStream(
+                new ByteArrayInputStream(bos.toByteArray())
+        ).readObject();
+
+        assertEquals(dfs, dfsSerialized);
+
+        // set minus/percent/permille
+        dfs.setMinusSign('a');
+        dfs.setPercent('b');
+        dfs.setPerMill('c');
+        bos = new ByteArrayOutputStream();
+        new ObjectOutputStream(bos).writeObject(dfs);
+        dfsSerialized = (DecimalFormatSymbols)new ObjectInputStream(
+                new ByteArrayInputStream(bos.toByteArray())
+        ).readObject();
+
+        assertEquals(dfs, dfsSerialized);
+    }
+}