8165466: DecimalFormat percentage format can contain unexpected %
Reviewed-by: okutsu, peytoia
--- 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
+ + "]");
+ }
+ }
+
+}
+