< prev index next >

src/java.base/share/classes/java/text/DecimalFormatSymbols.java

Print this page
rev 54198 : [mq]: 8220224

@@ -1,7 +1,7 @@
 /*
- * 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
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -36,16 +36,18 @@
  *
  */
 
 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;
 import sun.util.locale.provider.ResourceBundleBasedAdapter;
 

@@ -253,10 +255,45 @@
      *
      * @param perMill the character used for per mille sign
      */
     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');
     }
 
     /**
      * Gets the character used for percent sign. Different for Arabic, etc.
      *

@@ -271,10 +308,45 @@
      *
      * @param percent the character used for percent sign
      */
     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, '%');
     }
 
     /**
      * Gets the character used for a digit in a pattern.
      *

@@ -371,10 +443,50 @@
      *
      * @param minusSign the character representing minus sign
      */
     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, '-');
     }
 
     /**
      * Returns the currency symbol for the currency of these
      * DecimalFormatSymbols in their locale.

@@ -581,13 +693,16 @@
         DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
         return (zeroDigit == other.zeroDigit &&
         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) &&
         getCurrencySymbol().equals(other.getCurrencySymbol()) && // possible currency init occurs here
         intlCurrencySymbol.equals(other.intlCurrencySymbol) &&

@@ -629,17 +744,20 @@
         String[] numberElements = (String[]) data[0];
 
         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];
 
         // maybe filled with previously cached values, or null.
         intlCurrencySymbol = (String) data[1];

@@ -650,10 +768,20 @@
         // If that changes, add a new entry to NumberElements.
         monetarySeparator = decimalSeparator;
     }
 
     /**
+     * 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) {
         if (currencyInitialized) {
             return;

@@ -710,14 +838,20 @@
      * 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>.
+     * If <code>serialVersionOnStream</code> is less than 4, it initializes
+     * <code>perMillText</code>, <code>percentText</code>, and
+     * <code>minusSignText</code> using <code>perMill</code>, <code>percent</code>, and
+     * <code>minusSign</code> respectively.
      * Sets <code>serialVersionOnStream</code> 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</code> and <code>String</code>
+     *      representations of either percent, per mille, and/or minus sign disagree.
      * @since  1.1.6
      */
     private void readObject(ObjectInputStream stream)
             throws IOException, ClassNotFoundException {
         stream.defaultReadObject();

@@ -733,10 +867,27 @@
         }
         if (serialVersionOnStream < 3) {
             // 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) {
             try {
                  currency = Currency.getInstance(intlCurrencySymbol);

@@ -876,10 +1027,43 @@
      * @serial
      * @since 1.4
      */
     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</code>.
+     *
+     * @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</code>.
+     *
+     * @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</code>.
+     *
+     * @serial
+     * @since 13
+     */
+    private  String minusSignText;
+
     // currency; only the ISO code is serialized.
     private transient Currency currency;
     private transient volatile boolean currencyInitialized;
 
     // Proclaim JDK 1.1 FCS compatibility

@@ -889,11 +1073,13 @@
     // - 0 (default) for version up to JDK 1.1.5
     // - 1 for version from JDK 1.1.6, which includes two new fields:
     //     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.
      * Possible values are:
      * <ul>

@@ -903,10 +1089,13 @@
      *      two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
      * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
      *      new <code>locale</code> field.
      * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
      *      new <code>exponentialSeparator</code> field.
+     * <li><b>4</b>: Versions written by Java SE 13 or later, which include
+     *      new <code>perMillText</code>, <code>percentText</code>, and
+     *      <code>minusSignText</code> field.
      * </ul>
      * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
      * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
      * is always written.
      *
< prev index next >