6609740: [Fmt-De] format error in DecimalFormat
authornishjain
Fri, 19 May 2017 11:06:57 +0530
changeset 45182 83280973e0f7
parent 45181 a84c732c62c3
child 45185 da53250edcf0
6609740: [Fmt-De] format error in DecimalFormat Reviewed-by: naoto Contributed-by: nishit.jain@oracle.com
jdk/src/java.base/share/classes/java/text/DecimalFormat.java
jdk/test/java/text/Format/DecimalFormat/Bug6609740.java
--- a/jdk/src/java.base/share/classes/java/text/DecimalFormat.java	Thu May 18 17:07:36 2017 -0700
+++ b/jdk/src/java.base/share/classes/java/text/DecimalFormat.java	Fri May 19 11:06:57 2017 +0530
@@ -3197,13 +3197,6 @@
         isCurrencyFormat = false;
         useExponentialNotation = false;
 
-        // Two variables are used to record the subrange of the pattern
-        // occupied by phase 1.  This is used during the processing of the
-        // second pattern (the one representing negative numbers) to ensure
-        // that no deviation exists in phase 1 between the two patterns.
-        int phaseOneStart = 0;
-        int phaseOneLength = 0;
-
         int start = 0;
         for (int j = 1; j >= 0 && start < pattern.length(); --j) {
             boolean inQuote = false;
@@ -3254,9 +3247,6 @@
                             ch == groupingSeparator ||
                             ch == decimalSeparator) {
                             phase = 1;
-                            if (j == 1) {
-                                phaseOneStart = pos;
-                            }
                             --pos; // Reprocess this character
                             continue;
                         } else if (ch == CURRENCY_SIGN) {
@@ -3327,17 +3317,29 @@
                     break;
 
                 case 1:
-                    // Phase one must be identical in the two sub-patterns. We
-                    // enforce this by doing a direct comparison. While
-                    // processing the first sub-pattern, we just record its
-                    // length. While processing the second, we compare
-                    // characters.
-                    if (j == 1) {
-                        ++phaseOneLength;
-                    } else {
-                        if (--phaseOneLength == 0) {
-                            phase = 2;
-                            affix = suffix;
+                    // The negative subpattern (j = 0) serves only to specify the
+                    // negative prefix and suffix, so all the phase 1 characters
+                    // e.g. digits, zeroDigit, groupingSeparator,
+                    // decimalSeparator, exponent are ignored
+                    if (j == 0) {
+                        while (pos < pattern.length()) {
+                            char negPatternChar = pattern.charAt(pos);
+                            if (negPatternChar == digit
+                                    || negPatternChar == zeroDigit
+                                    || negPatternChar == groupingSeparator
+                                    || negPatternChar == decimalSeparator) {
+                                ++pos;
+                            } else if (pattern.regionMatches(pos, exponent,
+                                    0, exponent.length())) {
+                                pos = pos + exponent.length();
+                            } else {
+                                // Not a phase 1 character, consider it as
+                                // suffix and parse it in phase 2
+                                --pos; //process it again in outer loop
+                                phase = 2;
+                                affix = suffix;
+                                break;
+                            }
                         }
                         continue;
                     }
@@ -3391,7 +3393,6 @@
                          while (pos < pattern.length() &&
                                pattern.charAt(pos) == zeroDigit) {
                             ++minExponentDigits;
-                            ++phaseOneLength;
                             ++pos;
                         }
 
@@ -3410,7 +3411,6 @@
                         phase = 2;
                         affix = suffix;
                         --pos;
-                        --phaseOneLength;
                         continue;
                     }
                     break;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/text/Format/DecimalFormat/Bug6609740.java	Fri May 19 11:06:57 2017 +0530
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2017, 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 6609740
+ * @summary Checks the formatting and parsing of a number based
+ *          on the positive and negative sub-patterns, also
+ *          checks few invalid number patterns
+ */
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+public class Bug6609740 {
+
+    public static void main(String[] args) {
+
+        double dNumber = -3456.349347;
+        String fOutput = "(3,456.35)";
+        String[] validCases = {"#,##0.0#;(#,##0.0#)", "#,##0.0#;(#)",
+            "#,##0.0#;(#,##0)"};
+
+        // formatting with the valid cases
+        NumberFormat nf = NumberFormat.getInstance(Locale.US);
+        for (String pattern : validCases) {
+            formatOnPattern(nf, pattern, dNumber, fOutput);
+        }
+
+        // parsing with the valid cases
+        String parseString = "(3,456.35)";
+        Number pOutput = -3456.35;
+        for (String pattern : validCases) {
+            parseOnPattern(nf, pattern, parseString, pOutput);
+        }
+
+        // should throw parse exception
+        String[] invalidParseCases = {"#,##0.0#;0", "#,##0.0#;()"};
+        for (String pattern : invalidParseCases) {
+            if (nf instanceof DecimalFormat) {
+                ((DecimalFormat) nf).applyPattern(pattern);
+            }
+
+            try {
+                nf.parse(parseString);
+            } catch (ParseException ex) {
+                continue;
+            }
+            throw new RuntimeException("[FAILED: Should throw"
+                    + " ParseException for pattern: "
+                    + pattern + " and input: " + parseString + "]");
+        }
+
+        // should throw exception on invalid patterns
+        // invalid patterns: no positive subpattern, zero after non-zero in
+        // the decimal part i.e. 0#0, multiple decimal separators,
+        // multiple percent, malformed pattern
+        String[] invalidPatterns = {";(#,##0.0#)", "#,##0.0#0;(#)",
+            "#,##0.0.#", "#,##0%%", ".#,##0"};
+        for (String pattern : invalidPatterns) {
+            if (nf instanceof DecimalFormat) {
+                try {
+                    ((DecimalFormat) nf).applyPattern(pattern);
+                } catch (IllegalArgumentException ex) {
+                    continue;
+                }
+                throw new RuntimeException("[FAILED: Should throw"
+                        + " IllegalArgumentException for invalid pattern: "
+                        + pattern + "]");
+            }
+        }
+    }
+
+    private static void formatOnPattern(NumberFormat nf, String pattern,
+            double number, String expected) {
+
+        if (nf instanceof DecimalFormat) {
+            ((DecimalFormat) nf).applyPattern(pattern);
+        }
+
+        String formatted = nf.format(number);
+        if (!formatted.equals(expected)) {
+            throw new RuntimeException("[FAILED: Unable to format the number"
+                    + " based on the pattern: '" + pattern + "', Expected : '"
+                    + expected + "', Found: '" + formatted + "']");
+        }
+    }
+
+    private static void parseOnPattern(NumberFormat nf, String pattern,
+            String parseString, Number expected) {
+
+        if (nf instanceof DecimalFormat) {
+            ((DecimalFormat) nf).applyPattern(pattern);
+        }
+
+        try {
+            Number output = nf.parse(parseString);
+            if (expected.doubleValue() != output.doubleValue()) {
+                throw new RuntimeException("[FAILED: Unable to parse the number"
+                        + " based on the pattern: '" + pattern + "', Expected : '"
+                        + expected + "', Found: '" + output + "']");
+            }
+        } catch (ParseException ex) {
+            throw new RuntimeException("[FAILED: Unable to parse the pattern:"
+                    + " '" + pattern + "']", ex);
+        }
+    }
+
+}