1 /* 2 * Copyright (c) 1997, 2016, 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 4122840 4135202 4408066 4838107 8008577 27 * @summary test NumberFormat 28 * @library /java/text/testlib 29 * @modules java.base/sun.util.resources 30 * jdk.localedata 31 * @compile -XDignore.symbol.file NumberTest.java 32 * @run main/othervm -Djava.locale.providers=COMPAT,SPI NumberTest 33 */ 34 35 import java.util.*; 36 import java.text.*; 37 import sun.util.resources.LocaleData; 38 39 public class NumberTest extends IntlTest 40 { 41 public static void main(String[] args) throws Exception { 42 new NumberTest().run(args); 43 } 44 45 // Test pattern handling 46 public void TestPatterns() 47 { 48 DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US); 49 String pat[] = { "#.#", "#.", ".#", "#" }; 50 String newpat[] = { "#0.#", "#0.", "#.0", "#" }; 51 String num[] = { "0", "0.", ".0", "0" }; 52 for (int i=0; i<pat.length; ++i) 53 { 54 DecimalFormat fmt = new DecimalFormat(pat[i], sym); 55 String newp = fmt.toPattern(); 56 if (!newp.equals(newpat[i])) 57 errln("FAIL: Pattern " + pat[i] + " should transmute to " + newpat[i] + 58 "; " + newp + " seen instead"); 59 60 String s = fmt.format(0); 61 if (!s.equals(num[i])) 62 { 63 errln("FAIL: Pattern " + pat[i] + " should format zero as " + num[i] + 64 "; " + s + " seen instead"); 65 logln("Min integer digits = " + fmt.getMinimumIntegerDigits()); 66 } 67 } 68 } 69 70 // Test exponential pattern 71 public void TestExponential() { 72 DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US); 73 String pat[] = { "0.####E0", "00.000E00", "##0.####E000", "0.###E0;[0.###E0]" }; 74 double val[] = { 0.01234, 123456789, 1.23e300, -3.141592653e-271 }; 75 long lval[] = { 0, -1, 1, 123456789 }; 76 String valFormat[] = { 77 "1.234E-2", "1.2346E8", "1.23E300", "-3.1416E-271", 78 "12.340E-03", "12.346E07", "12.300E299", "-31.416E-272", 79 "12.34E-003", "123.4568E006", "1.23E300", "-314.1593E-273", 80 "1.234E-2", "1.235E8", "1.23E300", "[3.142E-271]" 81 }; 82 String lvalFormat[] = { 83 "0E0", "-1E0", "1E0", "1.2346E8", 84 "00.000E00", "-10.000E-01", "10.000E-01", "12.346E07", 85 "0E000", "-1E000", "1E000", "123.4568E006", 86 "0E0", "[1E0]", "1E0", "1.235E8" 87 }; 88 double valParse[] = { 89 0.01234, 123460000, 1.23E300, -3.1416E-271, 90 0.01234, 123460000, 1.23E300, -3.1416E-271, 91 0.01234, 123456800, 1.23E300, -3.141593E-271, 92 0.01234, 123500000, 1.23E300, -3.142E-271, 93 }; 94 long lvalParse[] = { 95 0, -1, 1, 123460000, 96 0, -1, 1, 123460000, 97 0, -1, 1, 123456800, 98 0, -1, 1, 123500000, 99 }; 100 int ival = 0, ilval = 0; 101 for (int p=0; p<pat.length; ++p) { 102 DecimalFormat fmt = new DecimalFormat(pat[p], sym); 103 logln("Pattern \"" + pat[p] + "\" -toPattern-> \"" + 104 fmt.toPattern() + '"'); 105 106 for (int v=0; v<val.length; ++v) { 107 String s = fmt.format(val[v]); 108 logln(" Format " + val[v] + " -> " + escape(s)); 109 if (!s.equals(valFormat[v+ival])) { 110 errln("FAIL: Expected " + valFormat[v+ival] + 111 ", got " + s + 112 ", pattern=" + fmt.toPattern()); 113 } 114 115 ParsePosition pos = new ParsePosition(0); 116 Number a = fmt.parse(s, pos); 117 if (pos.getIndex() == s.length()) { 118 logln(" Parse -> " + a); 119 if (a.doubleValue() != valParse[v+ival]) { 120 errln("FAIL: Expected " + valParse[v+ival] + 121 ", got " + a.doubleValue() + 122 ", pattern=" + fmt.toPattern()); 123 } 124 } else { 125 errln(" FAIL: Partial parse (" + pos.getIndex() + 126 " chars) -> " + a); 127 } 128 } 129 for (int v=0; v<lval.length; ++v) { 130 String s = fmt.format(lval[v]); 131 logln(" Format " + lval[v] + "L -> " + escape(s)); 132 if (!s.equals(lvalFormat[v+ilval])) { 133 errln("ERROR: Expected " + lvalFormat[v+ilval] + 134 ", got " + s + 135 ", pattern=" + fmt.toPattern()); 136 } 137 138 ParsePosition pos = new ParsePosition(0); 139 Number a = fmt.parse(s, pos); 140 if (pos.getIndex() == s.length()) { 141 logln(" Parse -> " + a); 142 if (a.longValue() != lvalParse[v+ilval]) { 143 errln("FAIL: Expected " + lvalParse[v+ilval] + 144 ", got " + a + 145 ", pattern=" + fmt.toPattern()); 146 } 147 } else { 148 errln(" FAIL: Partial parse (" + pos.getIndex() + 149 " chars) -> " + a); 150 } 151 } 152 ival += val.length; 153 ilval += lval.length; 154 } 155 } 156 157 // Test the handling of quotes 158 public void TestQuotes() 159 { 160 String pat; 161 DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US); 162 DecimalFormat fmt = new DecimalFormat(pat = "a'fo''o'b#", sym); 163 String s = fmt.format(123); 164 logln("Pattern \"" + pat + "\""); 165 logln(" Format 123 -> " + escape(s)); 166 if (!s.equals("afo'ob123")) errln("FAIL: Expected afo'ob123"); 167 168 fmt = new DecimalFormat(pat = "a''b#", sym); 169 s = fmt.format(123); 170 logln("Pattern \"" + pat + "\""); 171 logln(" Format 123 -> " + escape(s)); 172 if (!s.equals("a'b123")) errln("FAIL: Expected a'b123"); 173 } 174 175 // Test the use of the currency sign 176 public void TestCurrencySign() 177 { 178 DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.US); 179 DecimalFormat fmt = new DecimalFormat("\u00A4#,##0.00;-\u00A4#,##0.00", sym); 180 // Can't test this properly until currency API goes public 181 // DecimalFormatSymbols sym = fmt.getDecimalFormatSymbols(); 182 183 String s = fmt.format(1234.56); 184 logln("Pattern \"" + fmt.toPattern() + "\""); 185 logln(" Format " + 1234.56 + " -> " + escape(s)); 186 if (!s.equals("$1,234.56")) errln("FAIL: Expected $1,234.56"); 187 s = fmt.format(-1234.56); 188 logln(" Format " + -1234.56 + " -> " + escape(s)); 189 if (!s.equals("-$1,234.56")) errln("FAIL: Expected -$1,234.56"); 190 191 fmt = new DecimalFormat("\u00A4\u00A4 #,##0.00;\u00A4\u00A4 -#,##0.00", sym); 192 s = fmt.format(1234.56); 193 logln("Pattern \"" + fmt.toPattern() + "\""); 194 logln(" Format " + 1234.56 + " -> " + escape(s)); 195 if (!s.equals("USD 1,234.56")) errln("FAIL: Expected USD 1,234.56"); 196 s = fmt.format(-1234.56); 197 logln(" Format " + -1234.56 + " -> " + escape(s)); 198 if (!s.equals("USD -1,234.56")) errln("FAIL: Expected USD -1,234.56"); 199 } 200 static String escape(String s) 201 { 202 StringBuffer buf = new StringBuffer(); 203 char HEX[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' }; 204 for (int i=0; i<s.length(); ++i) 205 { 206 char c = s.charAt(i); 207 if (c <= (char)0x7F) buf.append(c); 208 else 209 { 210 buf.append("\\U"); 211 buf.append(HEX[(c & 0xF000) >> 12]); 212 buf.append(HEX[(c & 0x0F00) >> 8]); 213 buf.append(HEX[(c & 0x00F0) >> 4]); 214 buf.append(HEX[c & 0x000F]); 215 } 216 } 217 return buf.toString(); 218 } 219 220 // Test simple currency format 221 // Bug 4024941; this code used to throw a NumberFormat exception 222 public void TestCurrency() { 223 NumberFormat currencyFmt = 224 NumberFormat.getCurrencyInstance(Locale.CANADA_FRENCH); 225 String s = currencyFmt.format(1.50); 226 logln("Un pauvre ici a..........." + s); 227 if (!s.equals("1,50 $")) { 228 errln("FAIL: Expected 1,50 $; got " + s + "; "+ dumpFmt(currencyFmt)); 229 } 230 currencyFmt = NumberFormat.getCurrencyInstance(Locale.GERMANY); 231 s = currencyFmt.format(1.50); 232 logln("Un pauvre en Allemagne a.." + s); 233 if (!s.equals("1,50 \u20AC")) { 234 errln("FAIL: Expected 1,50 \u20AC; got " + s + "; " + dumpFmt(currencyFmt)); 235 } 236 currencyFmt = NumberFormat.getCurrencyInstance(Locale.FRANCE); 237 s = currencyFmt.format(1.50); 238 logln("Un pauvre en France a....." + s); 239 if (!s.equals("1,50 \u20AC")) { 240 errln("FAIL: Expected 1,50 \u20AC; got " + s + "; " + dumpFmt(currencyFmt)); 241 } 242 } 243 244 String dumpFmt(NumberFormat numfmt) { 245 DecimalFormat fmt = (DecimalFormat)numfmt; 246 StringBuffer buf = new StringBuffer(); 247 buf.append("pattern \""); 248 buf.append(fmt.toPattern()); 249 buf.append("\", currency \""); 250 buf.append(fmt.getDecimalFormatSymbols().getCurrencySymbol()); 251 buf.append("\""); 252 return buf.toString(); 253 } 254 255 // Test numeric parsing 256 // Bug 4059870 257 public void TestParse() 258 { 259 String arg = "0"; 260 java.text.DecimalFormat format = new java.text.DecimalFormat("00"); 261 try { 262 Number n = format.parse(arg); 263 logln("parse(" + arg + ") = " + n); 264 if (n.doubleValue() != 0.0) errln("FAIL: Expected 0"); 265 } catch (Exception e) { errln("Exception caught: " + e); } 266 } 267 268 // Test rounding 269 public void TestRounding487() { 270 NumberFormat nf = NumberFormat.getInstance(Locale.US); 271 roundingTest(nf, 0.00159999, 4, "0.0016"); 272 roundingTest(nf, 0.00995, 4, "0.01"); 273 roundingTest(nf, 12.7995, 3, "12.8"); 274 roundingTest(nf, 12.4999, 0, "12"); 275 roundingTest(nf, -19.5, 0, "-20"); 276 } 277 278 void roundingTest(NumberFormat nf, double x, int maxFractionDigits, String expected) { 279 nf.setMaximumFractionDigits(maxFractionDigits); 280 String out = nf.format(x); 281 logln("" + x + " formats with " + maxFractionDigits + " fractional digits to " + out); 282 if (!out.equals(expected)) { 283 errln("FAIL: Expected " + expected + ", got " + out); 284 } 285 } 286 287 /** 288 * Bug 4135202 289 * DecimalFormat should recognize not only Latin digits 0-9 (\u0030-\u0039) 290 * but also various other ranges of Unicode digits, such as Arabic 291 * digits \u0660-\u0669 and Devanagari digits \u0966-\u096F, to name 292 * a couple. 293 * @see java.lang.Character#isDigit(char) 294 */ 295 public void TestUnicodeDigits() { 296 char[] zeros = { 297 0x0030, // ISO-LATIN-1 digits ('0' through '9') 298 0x0660, // Arabic-Indic digits 299 0x06F0, // Extended Arabic-Indic digits 300 0x0966, // Devanagari digits 301 0x09E6, // Bengali digits 302 0x0A66, // Gurmukhi digits 303 0x0AE6, // Gujarati digits 304 0x0B66, // Oriya digits 305 0x0BE6, // Tamil digits 306 0x0C66, // Telugu digits 307 0x0CE6, // Kannada digits 308 0x0D66, // Malayalam digits 309 0x0E50, // Thai digits 310 0x0ED0, // Lao digits 311 0x0F20, // Tibetan digits 312 0xFF10, // Fullwidth digits 313 }; 314 NumberFormat format = NumberFormat.getInstance(); 315 for (int i=0; i<zeros.length; ++i) { 316 char zero = zeros[i]; 317 StringBuffer buf = new StringBuffer(); 318 buf.append((char)(zero+3)); 319 buf.append((char)(zero+1)); 320 buf.append((char)(zero+4)); 321 int n = -1; 322 try { 323 n = format.parse(buf.toString()).intValue(); 324 } 325 catch (ParseException e) { n = -2; } 326 if (n != 314) 327 errln("Can't parse Unicode " + Integer.toHexString(zero) + " as digit (" + n + ")"); 328 else 329 logln("Parse digit " + Integer.toHexString(zero) + " ok"); 330 } 331 } 332 333 /** 334 * Bug 4122840 335 * Make sure that the currency symbol is not hard-coded in any locale. 336 */ 337 public void TestCurrencySubstitution() { 338 final String SYM = "<currency>"; 339 final String INTL_SYM = "<intl.currency>"; 340 Locale[] locales = NumberFormat.getAvailableLocales(); 341 for (int i=0; i<locales.length; ++i) { 342 NumberFormat nf = NumberFormat.getCurrencyInstance(locales[i]); 343 if (nf instanceof DecimalFormat) { 344 DecimalFormat df = (DecimalFormat)nf; 345 String genericPos = df.format(1234.5678); 346 String genericNeg = df.format(-1234.5678); 347 DecimalFormatSymbols sym = df.getDecimalFormatSymbols(); 348 sym.setCurrencySymbol(SYM); 349 sym.setInternationalCurrencySymbol(INTL_SYM); 350 // We have to make a new DecimalFormat from scratch in order 351 // to make the new symbols 'take'. This may be a bug or 352 // design flaw in DecimalFormat. 353 String[] patterns = LocaleData.getBundle("sun.text.resources.FormatData", locales[i]) 354 .getStringArray("NumberPatterns"); 355 df = new DecimalFormat(patterns[1 /*CURRENCYSTYLE*/], sym); 356 String customPos = df.format(1234.5678); 357 String customNeg = df.format(-1234.5678); 358 if (genericPos.equals(customPos) || genericNeg.equals(customNeg)) { 359 errln("FAIL: " + locales[i] + 360 " not using currency symbol substitution: " + genericPos); 361 } 362 else { 363 if (customPos.indexOf(SYM) >= 0) { 364 if (customNeg.indexOf(INTL_SYM) >= 0) 365 errln("Fail: Positive and negative patterns use different symbols"); 366 else 367 logln("Ok: " + locales[i] + 368 " uses currency symbol: " + genericPos + 369 ", " + customPos); 370 } 371 else if (customPos.indexOf(INTL_SYM) >= 0) { 372 if (customNeg.indexOf(SYM) >= 0) 373 errln("Fail: Positive and negative patterns use different symbols"); 374 else 375 logln("Ok: " + locales[i] + 376 " uses intl. currency symbol: " + genericPos + 377 ", " + customPos); 378 } 379 else { 380 errln("FAIL: " + locales[i] + 381 " contains no currency symbol (impossible!)"); 382 } 383 } 384 } 385 else logln("Skipping " + locales[i] + "; not a DecimalFormat"); 386 } 387 } 388 389 public void TestIntegerFormat() throws ParseException { 390 NumberFormat format = NumberFormat.getIntegerInstance(Locale.GERMANY); 391 392 float[] formatInput = { 12345.67f, -12345.67f, -0, 0 }; 393 String[] formatExpected = { "12.346", "-12.346", "0", "0" }; 394 395 for (int i = 0; i < formatInput.length; i++) { 396 String result = format.format(formatInput[i]); 397 if (!result.equals(formatExpected[i])) { 398 errln("FAIL: Expected " + formatExpected[i] + ", got " + result); 399 } 400 } 401 402 String[] parseInput = { "0", "-0", "12.345,67", "-12.345,67" }; 403 float[] parseExpected = { 0, 0, 12345, -12345 }; 404 405 for (int i = 0; i < parseInput.length; i++) { 406 float result = ((Number) format.parse(parseInput[i])).floatValue(); 407 if (result != parseExpected[i]) { 408 errln("FAIL: Expected " + parseExpected[i] + ", got " + result); 409 } 410 } 411 } 412 }