8165466: DecimalFormat percentage format can contain unexpected %
authornishjain
Tue, 04 Oct 2016 19:28:09 +0900
changeset 41223 843bc8924490
parent 41222 7215e27667e9
child 41224 aa0e64b5711c
8165466: DecimalFormat percentage format can contain unexpected % Reviewed-by: okutsu, peytoia
jdk/src/java.base/share/classes/java/text/DecimalFormat.java
jdk/test/java/text/Format/DecimalFormat/Bug8165466.java
--- a/jdk/src/java.base/share/classes/java/text/DecimalFormat.java	Tue Oct 04 17:18:46 2016 +0900
+++ b/jdk/src/java.base/share/classes/java/text/DecimalFormat.java	Tue Oct 04 19:28:09 2016 +0900
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, 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
@@ -968,7 +968,7 @@
      *     Decimal  : min = 0. max = 3.
      *
      */
-    private void checkAndSetFastPathStatus() {
+    private boolean checkAndSetFastPathStatus() {
 
         boolean fastPathWasOn = isFastPath;
 
@@ -998,12 +998,27 @@
         } else
             isFastPath = false;
 
+        resetFastPathData(fastPathWasOn);
+        fastPathCheckNeeded = false;
+
+        /*
+         * Returns true after successfully checking the fast path condition and
+         * setting the fast path data. The return value is used by the
+         * fastFormat() method to decide whether to call the resetFastPathData
+         * method to reinitialize fast path data or is it already initialized
+         * in this method.
+         */
+        return true;
+    }
+
+    private void resetFastPathData(boolean fastPathWasOn) {
         // Since some instance properties may have changed while still falling
         // in the fast-path case, we need to reinitialize fastPathData anyway.
         if (isFastPath) {
             // We need to instantiate fastPathData if not already done.
-            if (fastPathData == null)
+            if (fastPathData == null) {
                 fastPathData = new FastPathData();
+            }
 
             // Sets up the locale specific constants used when formatting.
             // '0' is our default representation of zero.
@@ -1011,22 +1026,27 @@
             fastPathData.groupingChar = symbols.getGroupingSeparator();
 
             // Sets up fractional constants related to currency/decimal pattern.
-            fastPathData.fractionalMaxIntBound = (isCurrencyFormat) ? 99 : 999;
-            fastPathData.fractionalScaleFactor = (isCurrencyFormat) ? 100.0d : 1000.0d;
+            fastPathData.fractionalMaxIntBound = (isCurrencyFormat)
+                    ? 99 : 999;
+            fastPathData.fractionalScaleFactor = (isCurrencyFormat)
+                    ? 100.0d : 1000.0d;
 
             // Records the need for adding prefix or suffix
-            fastPathData.positiveAffixesRequired =
-                (positivePrefix.length() != 0) || (positiveSuffix.length() != 0);
-            fastPathData.negativeAffixesRequired =
-                (negativePrefix.length() != 0) || (negativeSuffix.length() != 0);
+            fastPathData.positiveAffixesRequired
+                    = (positivePrefix.length() != 0)
+                        || (positiveSuffix.length() != 0);
+            fastPathData.negativeAffixesRequired
+                    = (negativePrefix.length() != 0)
+                        || (negativeSuffix.length() != 0);
 
             // Creates a cached char container for result, with max possible size.
             int maxNbIntegralDigits = 10;
             int maxNbGroups = 3;
-            int containerSize =
-                Math.max(positivePrefix.length(), negativePrefix.length()) +
-                maxNbIntegralDigits + maxNbGroups + 1 + maximumFractionDigits +
-                Math.max(positiveSuffix.length(), negativeSuffix.length());
+            int containerSize
+                    = Math.max(positivePrefix.length(), negativePrefix.length())
+                    + maxNbIntegralDigits + maxNbGroups + 1
+                    + maximumFractionDigits
+                    + Math.max(positiveSuffix.length(), negativeSuffix.length());
 
             fastPathData.fastPathContainer = new char[containerSize];
 
@@ -1038,17 +1058,18 @@
 
             // Sets up fixed index positions for integral and fractional digits.
             // Sets up decimal point in cached result container.
-            int longestPrefixLength =
-                Math.max(positivePrefix.length(), negativePrefix.length());
-            int decimalPointIndex =
-                maxNbIntegralDigits + maxNbGroups + longestPrefixLength;
-
-            fastPathData.integralLastIndex    = decimalPointIndex - 1;
+            int longestPrefixLength
+                    = Math.max(positivePrefix.length(),
+                            negativePrefix.length());
+            int decimalPointIndex
+                    = maxNbIntegralDigits + maxNbGroups + longestPrefixLength;
+
+            fastPathData.integralLastIndex = decimalPointIndex - 1;
             fastPathData.fractionalFirstIndex = decimalPointIndex + 1;
-            fastPathData.fastPathContainer[decimalPointIndex] =
-                isCurrencyFormat ?
-                symbols.getMonetaryDecimalSeparator() :
-                symbols.getDecimalSeparator();
+            fastPathData.fastPathContainer[decimalPointIndex]
+                    = isCurrencyFormat
+                            ? symbols.getMonetaryDecimalSeparator()
+                            : symbols.getDecimalSeparator();
 
         } else if (fastPathWasOn) {
             // Previous state was fast-path and is no more.
@@ -1059,8 +1080,6 @@
             fastPathData.charsPositivePrefix = null;
             fastPathData.charsNegativePrefix = null;
         }
-
-        fastPathCheckNeeded = false;
     }
 
     /**
@@ -1554,9 +1573,11 @@
      * @return the formatted result for {@code d} as a string.
      */
     String fastFormat(double d) {
+        boolean isDataSet = false;
         // (Re-)Evaluates fast-path status if needed.
-        if (fastPathCheckNeeded)
-            checkAndSetFastPathStatus();
+        if (fastPathCheckNeeded) {
+            isDataSet = checkAndSetFastPathStatus();
+        }
 
         if (!isFastPath )
             // DecimalFormat instance is not in a fast-path state.
@@ -1580,9 +1601,21 @@
         if (d > MAX_INT_AS_DOUBLE)
             // Filters out values that are outside expected fast-path range
             return null;
-        else
+        else {
+            if (!isDataSet) {
+                /*
+                 * If the fast path data is not set through
+                 * checkAndSetFastPathStatus() and fulfil the
+                 * fast path conditions then reset the data
+                 * directly through resetFastPathData()
+                 */
+                resetFastPathData(isFastPath);
+            }
             fastDoubleFormat(d, negative);
 
+        }
+
+
         // Returns a new string from updated fastPathContainer.
         return new String(fastPathData.fastPathContainer,
                           fastPathData.firstUsedIndex,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/text/Format/DecimalFormat/Bug8165466.java	Tue Oct 04 19:28:09 2016 +0900
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2016, 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 8165466
+ * @summary Checks the subsequent function calls of the DecimalFormat.format()
+ *          method in which the minimumFractionDigit is set to 0 and one of
+ *          the format() call include formatting of the number with zero
+ *          fraction value e.g. 0.00, 9.00
+ */
+
+import java.text.DecimalFormat;
+import java.util.Locale;
+
+public class Bug8165466 {
+
+    public static void main(String[] args) {
+        DecimalFormat nf = (DecimalFormat) DecimalFormat
+                .getPercentInstance(Locale.US);
+        nf.setMaximumFractionDigits(3);
+        nf.setMinimumFractionDigits(0);
+        nf.setMultiplier(1);
+
+        double d = 0.005678;
+        String result = nf.format(d);
+        if (!result.equals("0.006%")) {
+            throw new RuntimeException("[Failed while formatting the double"
+                    + " value: " + d + " Expected: 0.006%, Found: " + result
+                    + "]");
+        }
+
+        d = 0.00;
+        result = nf.format(d);
+        if (!result.equals("0%")) {
+            throw new RuntimeException("[Failed while formatting the double"
+                    + " value: " + d + " Expected: 0%, Found: " + result
+                    + "]");
+        }
+
+        d = 0.005678;
+        result = nf.format(d);
+        if (!result.equals("0.006%")) {
+            throw new RuntimeException("[Failed while formatting the double"
+                    + " value: " + d + " Expected: 0.006%, Found: " + result
+                    + "]");
+        }
+
+        //checking with the non zero value
+        d = 0.005678;
+        result = nf.format(d);
+        if (!result.equals("0.006%")) {
+            throw new RuntimeException("[Failed while formatting the double"
+                    + " value: " + d + " Expected: 0.006%, Found: " + result
+                    + "]");
+        }
+
+        d = 9.00;
+        result = nf.format(d);
+        if (!result.equals("9%")) {
+            throw new RuntimeException("[Failed while formatting the double"
+                    + " value: " + d + " Expected: 9%, Found: " + result
+                    + "]");
+        }
+
+        d = 0.005678;
+        result = nf.format(d);
+        if (!result.equals("0.006%")) {
+            throw new RuntimeException("[Failed while formatting the double"
+                    + " value: " + d + " Expected: 0.006%, Found: " + result
+                    + "]");
+        }
+    }
+
+}
+