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