1 /*
  2  * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 4838107 8008577
 27  * @summary Confirm that DecimalFormat can format a number with negative exponent number correctly.
 28  * @run junit/othervm -Djava.locale.providers=COMPAT,SPI Bug4838107
 29  */
 30 
 31 import java.math.*;
 32 import java.util.*;
 33 import java.text.*;
 34 
 35 import org.junit.jupiter.api.Test;
 36 
 37 import static org.junit.jupiter.api.Assertions.fail;
 38 
 39 public class Bug4838107 {
 40 
 41     static DecimalFormat df;
 42     static DecimalFormatSymbols dfs;
 43     static boolean err = false;
 44 
 45     static public void main(String[] args) {
 46         Locale defaultLoc = Locale.getDefault();
 47         Locale.setDefault(Locale.US);
 48 
 49         /**
 50          * This bug is about exponential formatting. But I added test cases for:
 51          *   - Double and BigDecimal numbers which don't have exponent parts.
 52          *   - Long and BigInteger numbers which don't support exponential
 53          *     notation.
 54          * because there are few test cases for suffix and prefix.
 55          * And also, I added test cases to guarantee further formatting and
 56          * parsing using the same DecimalFormat instance will not change the
 57          * Number's value anymore.
 58          */
 59 
 60         test_double();
 61         test_long();
 62         test_BigDecimal();
 63         test_BigInteger();
 64 
 65         Locale.setDefault(defaultLoc);
 66 
 67         if (err) {
 68             throw new RuntimeException("Wrong format with DecimalFormat");
 69         }
 70     }
 71 
 72     static void test_double() {
 73         df = new DecimalFormat();
 74         dfs = df.getDecimalFormatSymbols();
 75 
 76         /* Test with default pattern */
 77         test(1234D,    "1,234");
 78         test(0.1234,  "0.123");     // rounded
 79         test(-1234D,   "-1,234");
 80         test(-0.1234, "-0.123");    // rounded
 81 
 82         test(Double.POSITIVE_INFINITY, "\u221e");
 83         test(Double.NEGATIVE_INFINITY, "-\u221e");
 84         test(Double.NaN, "\ufffd"); // without prefix and suffix
 85         test(0.0,  "0");
 86         test(-0.0, "-0");   // with the minus sign
 87 
 88         /* Specify a pattern and the minus sign. */
 89         prepareFormatter("<P>#.###E00<S>", 'm');
 90         test(1234D,    "<P>1.234E03<S>");
 91         test(0.1234,  "<P>1.234Em01<S>");
 92         test(-1234D,   "m<P>1.234E03<S>");
 93         test(-0.1234, "m<P>1.234Em01<S>");
 94 
 95         prepareFormatter("<P>#.###E00<S>;#.###E00", 'm');
 96         test(1234D,    "<P>1.234E03<S>");
 97         test(0.1234,  "<P>1.234Em01<S>");
 98         test(-1234D,   "1.234E03");
 99         test(-0.1234, "1.234Em01");
100 
101         prepareFormatter("#.###E00;<P>#.###E00<S>", 'm');
102         test(1234D,    "1.234E03");
103         test(0.1234,  "1.234Em01");
104         test(-1234D,   "<P>1.234E03<S>");
105         test(-0.1234, "<P>1.234Em01<S>");
106 
107         prepareFormatter("<P>#.###E00<S>;<p>-#.###E00<s>", 'm');
108         test(1234D,    "<P>1.234E03<S>");
109         test(0.1234,  "<P>1.234Em01<S>");
110         test(-1234D,   "<p>m1.234E03<s>");
111         test(-0.1234, "<p>m1.234Em01<s>");
112 
113         test(Double.POSITIVE_INFINITY, "<P>\u221e<S>");
114         test(Double.NEGATIVE_INFINITY, "<p>m\u221e<s>");
115         test(Double.NaN, "\ufffd"); // without prefix and suffix
116         test(0.0,  "<P>0E00<S>");
117         test(-0.0, "<p>m0E00<s>");  // with the minus sign
118     }
119 
120     static void test_BigDecimal() {
121         df = new DecimalFormat();
122         dfs = df.getDecimalFormatSymbols();
123 
124         /* Test with default pattern */
125         test(new BigDecimal("123456789012345678901234567890"),
126              "123,456,789,012,345,678,901,234,567,890");
127         test(new BigDecimal("0.000000000123456789012345678901234567890"),
128              "0");
129         test(new BigDecimal("-123456789012345678901234567890"),
130              "-123,456,789,012,345,678,901,234,567,890");
131         test(new BigDecimal("-0.000000000123456789012345678901234567890"),
132               "-0");
133 
134         test(new BigDecimal("0"), "0");
135         test(new BigDecimal("-0"), "0");
136 
137         /* Specify a pattern and the minus sign. */
138         prepareFormatter("<P>#.####################E00<S>;<p>-#.####################E00<s>", 'm');
139         test(new BigDecimal("123456789012345678901234567890"),
140              "<P>1.23456789012345678901E29<S>");
141         test(new BigDecimal("0.000000000123456789012345678901234567890"),
142              "<P>1.23456789012345678901Em10<S>");
143         test(new BigDecimal("-123456789012345678901234567890"),
144              "<p>m1.23456789012345678901E29<s>");
145         test(new BigDecimal("-0.000000000123456789012345678901234567890"),
146               "<p>m1.23456789012345678901Em10<s>");
147 
148         test(new BigDecimal("0"), "<P>0E00<S>");
149         test(new BigDecimal("-0"), "<P>0E00<S>");
150     }
151 
152     static void test_long() {
153         df = new DecimalFormat();
154         dfs = df.getDecimalFormatSymbols();
155 
156         /* Test with default pattern */
157         test(123456789L,  "123,456,789");
158         test(-123456789L, "-123,456,789");
159 
160         test(0L, "0");
161         test(-0L, "0");
162 
163         /* Specify a pattern and the minus sign. */
164         prepareFormatter("<P>#,###<S>;<p>-#,###<s>", 'm');
165         test(123456789L,  "<P>123,456,789<S>");
166         test(-123456789L, "<p>m123,456,789<s>");
167 
168         test(0L, "<P>0<S>");
169         test(-0L, "<P>0<S>");
170     }
171 
172     static void test_BigInteger() {
173         df = new DecimalFormat();
174         dfs = df.getDecimalFormatSymbols();
175 
176         /* Test with default pattern */
177         test(new BigInteger("123456789012345678901234567890"),
178              "123,456,789,012,345,678,901,234,567,890");
179         test(new BigInteger("-123456789012345678901234567890"),
180              "-123,456,789,012,345,678,901,234,567,890");
181 
182         test(new BigInteger("0"), "0");
183         test(new BigInteger("-0"), "0");
184 
185         /* Specify a pattern and the minus sign. */
186         prepareFormatter("<P>#,###<S>;<p>-#,###<s>", 'm');
187         test(new BigInteger("123456789012345678901234567890"),
188              "<P>123,456,789,012,345,678,901,234,567,890<S>");
189         test(new BigInteger("-123456789012345678901234567890"),
190              "<p>m123,456,789,012,345,678,901,234,567,890<s>");
191 
192         test(new BigInteger("0"), "<P>0<S>");
193         test(new BigInteger("-0"), "<P>0<S>");
194     }
195 
196     static void prepareFormatter(String pattern, char minusSign) {
197         dfs = df.getDecimalFormatSymbols();
198         df.applyPattern(pattern);
199         dfs.setMinusSign(minusSign);
200         df.setDecimalFormatSymbols(dfs);
201     }
202 
203     static void test(Number num, String str) {
204         String formatted = df.format(num);
205         if (!formatted.equals(str)) {
206             err = true;
207             System.err.println("    DecimalFormat format(" +
208                                num.getClass().getName() +
209                                ") error: \n\tnumber: " + num +
210                                "\n\tminus sign: " + dfs.getMinusSign() +
211                                "\n\tgot:        " + formatted +
212                                "\n\texpected:   " + str);
213             return;
214         }
215 
216         if (num instanceof BigDecimal || num instanceof BigInteger) {
217             df.setParseBigDecimal(true);
218         }
219         Number parsed1 = null, parsed2 = null;
220         try {
221             parsed1 = df.parse(formatted);
222             formatted = df.format(parsed1);
223             parsed2 = df.parse(formatted);
224             if (!parsed1.equals(parsed2)) {
225                 err = true;
226                 System.err.println("    DecimalFormat roundtrip parse(" +
227                                    num.getClass().getName() +
228                                    ") error: \n\toriginal number:  " + str +
229                                    "\n\tparsed number:    " + parsed1 +
230                                    "  (" + parsed1.getClass().getName() + ")" +
231                                    "\n\tformatted number: " + formatted +
232                                    "\n\tre-parsed number: " + parsed2 +
233                                    "  (" + parsed2.getClass().getName() + ")" +
234                                    "\n\tminus sign: " + dfs.getMinusSign());
235             }
236         }
237         catch (Exception e) {
238             err = true;
239             System.err.println("    DecimalFormat parse(" +
240                                num.getClass().getName() +
241                                ") threw an Exception:  " + e.getMessage() +
242                                "\n\toriginal number:  " + str +
243                                "\n\tparsed number   : " + parsed1 +
244                                "  (" + parsed1.getClass().getName() + ")" +
245                                "\n\tformatted number: " + formatted +
246                                "\n\tre-parsed number: " + parsed2 +
247                                "  (" + parsed2.getClass().getName() + ")" +
248                                "\n\tminus sign: " + dfs.getMinusSign());
249         }
250     }
251 }