1 /*
   2  * Copyright (c) 2008, 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 4823811 8008577
  27  * @summary Confirm that text which includes numbers with a trailing minus sign is parsed correctly.
  28  * @modules jdk.localedata
  29  * @run main/othervm -Duser.timezone=GMT+09:00 -Djava.locale.providers=JRE,SPI Bug4823811
  30  */
  31 
  32 import java.text.*;
  33 import java.util.*;
  34 
  35 public class Bug4823811 {
  36 
  37     private static Locale localeEG = new Locale("ar", "EG");
  38     private static Locale localeUS = Locale.US;
  39 
  40     private static String JuneInArabic = "\u064a\u0648\u0646\u064a\u0648";
  41     private static String JulyInArabic = "\u064a\u0648\u0644\u064a\u0648";
  42     private static String JuneInEnglish = "June";
  43     private static String JulyInEnglish = "July";
  44 
  45     private static String BORDER =
  46         "============================================================";
  47 
  48     /*
  49      * I don't use static import here intentionally so that this test program
  50      * can be run on JDK 1.4.2.
  51      */
  52     private static int ERA = Calendar.ERA;
  53     private static int BC = GregorianCalendar.BC;
  54 //    private static int JAN = Calendar.JANUARY;
  55 //    private static int FEB = Calendar.FEBRUARY;
  56 //    private static int MAR = Calendar.MARCH;
  57     private static int APR = Calendar.APRIL;
  58     private static int MAY = Calendar.MAY;
  59     private static int JUN = Calendar.JUNE;
  60     private static int JUL = Calendar.JULY;
  61 //    private static int AUG = Calendar.AUGUST;
  62 //    private static int SEP = Calendar.SEPTEMBER;
  63 //    private static int OCT = Calendar.OCTOBER;
  64 //    private static int NOV = Calendar.NOVEMBER;
  65 //    private static int DEC = Calendar.DECEMBER;
  66 
  67     private static String[] patterns = {
  68         "yyyy MMMM d H m s",
  69         "yyyy MM dd hh mm ss",
  70 
  71         /*
  72          * Because 1-based HOUR_OF_DAY, 1-based HOUR, MONTH, and YEAR fields
  73          * are parsed using different code from the code for other numeric
  74          * fields, I prepared YEAR-preceding patterns and SECOND-preceding
  75          * patterns.
  76          */
  77         "yyyy M d h m s",
  78         " yyyy M d h m s",
  79         "yyyy M d h m s ",
  80 
  81         "s m h d M yyyy",
  82         " s m h d M yyyy",
  83         "s m h d M yyyy ",
  84     };
  85 
  86     private static char originalMinusSign1 = ':';
  87     private static char originalMinusSign2 = '\uff0d';  // fullwidth minus
  88     private static String[] delimiters = {"-", "/", ":", "/", "\uff0d", "/"};
  89     private static String[][] specialDelimiters = {
  90         // for Arabic formatter and modified English formatter
  91         {"--", "-/", "::", ":/", "\uff0d\uff0d", "\uff0d/"},
  92 
  93         // for English formatter and modified Arabic formatter
  94         {"--", "/-", "::", "/:", "\uff0d\uff0d", "/\uff0d"},
  95     };
  96 
  97     /*
  98      * Format:
  99      *   +-------------------------------------------------------------------+
 100      *   | Input               | Output                                      |
 101      *   +---------------------+---------------------------------------------|
 102      *   | datesEG & datesUS   | formattedDatesEG & formattedDatesUS         |
 103      *   +-------------------------------------------------------------------+
 104      *
 105      * Parse:
 106      *   +-------------------------------------------------------------------+
 107      *   | Input               | Output                                      |
 108      *   |---------------------+---------------------------------------------|
 109      *   | datesToParse        | datesEG & datesUS                           |
 110      *   +-------------------------------------------------------------------+
 111      */
 112     private static String[][] datesToParse = {
 113         // "JUNE" and "JULY" are replaced with a localized month name later.
 114         {"2008 JULY 20 3 12 83",
 115          "2008  JULY 20 3 12 83",
 116          "2008 JULY  20 3 12 83"},
 117 
 118         {"2008 07 20 03 12 83",
 119          "2008  07 20 03 12 83",
 120          "2008 07  20 03 12 83"},
 121 
 122         {"2008 7 20 3 12 83",
 123          "2008  7 20  3 12 83",
 124          "2008 7  20  3 12 83"},
 125 
 126         {" 2008 7 20 3 12 83",
 127          "  2008 7 20 3 12 83",
 128          " 2008  7 20 3 12 83",
 129          "2008 7 20 3 12 83"},
 130 
 131         {"2008 7 20 3 12 83 ",
 132          "2008 7 20 3 12 83  ",
 133          "2008 7 20 3 12 83"},
 134 
 135         {"83 12 3 20 7 2008",
 136          "83  12 3  20 7 2008",
 137          "83 12  3  20 7 2008"},
 138 
 139         {" 83 12 3 20 7 2008",
 140          "  83 12 3 20 7 2008",
 141          " 83  12 3 20 7 2008",
 142          "83 12 3 20 7 2008"},
 143 
 144         {"83 12 3 20 7 2008 ",
 145          "83 12 3 20 7 2008  ",
 146          "83 12 3 20 7 2008"},
 147     };
 148 
 149     // For formatting
 150     private static String[][] formattedDatesEG = {
 151         {"2008 JULY 20 3 13 23",
 152          "2009 JULY 20 3 13 23",
 153          null},
 154 
 155         {"2008 07 20 03 13 23",
 156          "2009 07 20 03 13 23",
 157          "2007 05 20 03 13 23"},
 158 
 159         {"2008 7 20 3 13 23",
 160          "2009 6 10 3 13 23",
 161          "2007 4 10 3 13 23"},
 162 
 163         {" 2008 7 20 3 13 23",
 164          null,
 165          " 2009 7 20 3 13 23",
 166          null},
 167 
 168         {"2008 7 20 3 13 23 ",
 169          "2008 7 20 3 10 37 ",
 170          null},
 171 
 172         {"23 13 3 20 7 2008",
 173          "37 10 9 19 7 2008",
 174          "23 49 8 19 7 2008"},
 175 
 176         {" 23 13 3 20 7 2008",
 177          null,
 178          " 37 10 3 20 7 2008",
 179          null},
 180 
 181         {"23 13 3 20 7 2008 ",
 182          "23 13 3 20 7 2009 ",
 183          null},
 184     };
 185 
 186     private static String[][] formattedDatesUS = {
 187         {"2008 JULY 20 3 13 23",
 188          null,
 189          "2008 JUNE 10 3 13 23"},
 190 
 191         {"2008 07 20 03 13 23",
 192          "2007 05 20 03 13 23",
 193          "2008 06 10 03 13 23"},
 194 
 195         {"2008 7 20 3 13 23",
 196          "2007 5 19 9 13 23",
 197          "2008 6 9 9 13 23"},
 198 
 199         {" 2008 7 20 3 13 23",
 200          " 2009 7 20 3 13 23",
 201          " 2007 5 20 3 13 23",
 202          null},
 203 
 204         {"2008 7 20 3 13 23 ",
 205          "2008 7 20 3 13 23 ",
 206          null},
 207 
 208         {"23 13 3 20 7 2008",
 209          "23 49 2 10 6 2008",
 210          "23 13 9 9 6 2008"},
 211 
 212         {" 23 13 3 20 7 2008",
 213          " 37 10 3 20 7 2008",
 214          " 23 49 2 20 7 2008",
 215          null},
 216 
 217         {"23 13 3 20 7 2008 ",
 218          "23 13 3 20 7 2008 ",
 219          null},
 220     };
 221 
 222     private static GregorianCalendar[][] datesEG = {
 223         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 224          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
 225          null},
 226 
 227         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 228          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
 229          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83)},
 230 
 231         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 232          new GregorianCalendar(-2008, JUL, -20,  3,  12,  83),
 233          new GregorianCalendar( 2007, APR,  10,  3,  12,  83)},
 234 
 235         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 236          null,
 237          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
 238          null},
 239 
 240         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 241          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
 242          null},
 243 
 244         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 245          new GregorianCalendar( 2008, JUL,  20, -3,  12, -83),
 246          new GregorianCalendar( 2008, JUL,  20, -3, -12,  83)},
 247 
 248         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 249          null,
 250          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
 251          null},
 252 
 253         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 254          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
 255          null},
 256     };
 257 
 258     private static GregorianCalendar[][] datesUS = {
 259         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 260          null,
 261          new GregorianCalendar( 2008, JUN,  10,  3,  12,  83)},
 262 
 263         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 264          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83),
 265          new GregorianCalendar( 2008, JUN,  10,  3,  12,  83)},
 266 
 267         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 268          new GregorianCalendar( 2007, MAY,  20, -3,  12,  83),
 269          new GregorianCalendar( 2008, JUL, -20, -3,  12,  83)},
 270 
 271         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 272          new GregorianCalendar(-2008, JUL,  20,  3,  12,  83),
 273          new GregorianCalendar( 2007, MAY,  20,  3,  12,  83),
 274          null},
 275 
 276         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 277          new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 278          null},
 279 
 280         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 281          new GregorianCalendar( 2008, JUL, -20,  3, -12,  83),
 282          new GregorianCalendar( 2008, JUL, -20, -3,  12,  83)},
 283 
 284         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 285          new GregorianCalendar( 2008, JUL,  20,  3,  12, -83),
 286          new GregorianCalendar( 2008, JUL,  20,  3, -12,  83),
 287          null},
 288 
 289         {new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 290          new GregorianCalendar( 2008, JUL,  20,  3,  12,  83),
 291          null},
 292     };
 293 
 294     /* flags */
 295     private static boolean err = false;
 296     private static boolean verbose = false;
 297 
 298 
 299     public static void main(String[] args) {
 300         if (args.length == 1 && args[0].equals("-v")) {
 301             verbose = true;
 302         }
 303 
 304         Locale defaultLocale = Locale.getDefault();
 305         TimeZone defaultTimeZone = TimeZone.getDefault();
 306 
 307         TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
 308 
 309         try {
 310             /*
 311              * Test SimpleDateFormat.parse() and format() for original
 312              * SimpleDateFormat instances
 313              */
 314             testDateFormat1();
 315 
 316             /*
 317              * Test SimpleDateFormat.parse() and format() for modified
 318              * SimpleDateFormat instances using an original minus sign,
 319              * pattern, and diffenrent month names in DecimalFormat
 320              */
 321             testDateFormat2();
 322 
 323             /*
 324              * Test SimpleDateFormat.parse() and format() for modified
 325              * SimpleDateFormat instances using a fullwidth minus sign
 326              */
 327             testDateFormat3();
 328 
 329             /*
 330              * Just to confirm that regressions aren't introduced in
 331              * DecimalFormat. This cannot happen, though. Because I didn't
 332              * change DecimalFormat at all.
 333              */
 334             testNumberFormat();
 335         }
 336         catch (Exception e) {
 337             err = true;
 338             System.err.println("Unexpected exception: " + e);
 339         }
 340         finally {
 341             Locale.setDefault(defaultLocale);
 342             TimeZone.setDefault(defaultTimeZone);
 343 
 344             if (err) {
 345                 System.err.println(BORDER + " Test failed.");
 346                 throw new RuntimeException("Date/Number formatting/parsing error.");
 347             } else {
 348                 System.out.println(BORDER + " Test passed.");
 349             }
 350         }
 351     }
 352 
 353 
 354     //
 355     // DateFormat test
 356     //
 357     private static void testDateFormat1() {
 358         for (int i = 0; i < patterns.length; i++) {
 359             System.out.println(BORDER);
 360             for (int j = 0; j <= 1; j++) {
 361                 // Generate a pattern
 362                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
 363                 System.out.println("Pattern=\"" + pattern + "\"");
 364 
 365                 System.out.println("*** DateFormat.format test in ar_EG");
 366                 testDateFormatFormattingInRTL(pattern, i, j, null, localeEG, false);
 367 
 368                 System.out.println("*** DateFormat.parse test in ar_EG");
 369                 testDateFormatParsingInRTL(pattern, i, j, null, localeEG, false);
 370 
 371                 System.out.println("*** DateFormat.format test in en_US");
 372                 testDateFormatFormattingInLTR(pattern, i, j, null, localeUS, true);
 373 
 374                 System.out.println("*** DateFormat.parse test in en_US");
 375                 testDateFormatParsingInLTR(pattern, i, j, null, localeUS, true);
 376             }
 377         }
 378     }
 379 
 380     private static void testDateFormat2() {
 381         /*
 382          * modified ar_EG Date&Time formatter :
 383          *   minus sign:  ':'
 384          *   pattern:     "#,##0.###"
 385          *   month names: In Arabic
 386          *
 387          * modified en_US Date&Time formatter :
 388          *   minus sign:  ':'
 389          *   pattern:     "#,##0.###;#,##0.###-"
 390          *   month names: In English
 391          */
 392         DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG);
 393         DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS);
 394 
 395         DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols();
 396         DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols();
 397         dfsEG.setMinusSign(originalMinusSign1);
 398         dfsUS.setMinusSign(originalMinusSign1);
 399         dfEG.setDecimalFormatSymbols(dfsUS);
 400         dfUS.setDecimalFormatSymbols(dfsEG);
 401 
 402         String patternEG = dfEG.toPattern();
 403         String patternUS = dfUS.toPattern();
 404 
 405         dfEG.applyPattern(patternUS);
 406         dfUS.applyPattern(patternEG);
 407 
 408         for (int i = 0; i < patterns.length; i++) {
 409             System.out.println(BORDER);
 410             for (int j = 2; j <= 3; j++) {
 411                 // Generate a pattern
 412                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
 413                 System.out.println("Pattern=\"" + pattern + "\"");
 414 
 415                 System.out.println("*** DateFormat.format test in modified en_US");
 416                 testDateFormatFormattingInRTL(pattern, i, j, dfUS, localeUS, true);
 417 
 418                 System.out.println("*** DateFormat.parse test in modified en_US");
 419                 testDateFormatParsingInRTL(pattern, i, j, dfUS, localeUS, true);
 420 
 421                 System.out.println("*** DateFormat.format test in modified ar_EG");
 422                 testDateFormatFormattingInLTR(pattern, i, j, dfEG, localeEG, false);
 423 
 424                 System.out.println("*** DateFormat.parse test in modified ar_EG");
 425                 testDateFormatParsingInLTR(pattern, i, j, dfEG, localeEG, false);
 426             }
 427         }
 428     }
 429 
 430     private static void testDateFormat3() {
 431         /*
 432          * modified ar_EG Date&Time formatter :
 433          *   minus sign:  '\uff0d'  // fullwidth minus
 434          *   pattern:     "#,##0.###;#,##0.###-"
 435          *   month names: In Arabic
 436          *
 437          * modified en_US Date&Time formatter :
 438          *   minus sign:  '\uff0d'  // fullwidth minus
 439          *   pattern:     "#,##0.###"
 440          *   month names: In English
 441          */
 442         DecimalFormat dfEG = (DecimalFormat)NumberFormat.getInstance(localeEG);
 443         DecimalFormat dfUS = (DecimalFormat)NumberFormat.getInstance(localeUS);
 444 
 445         DecimalFormatSymbols dfsEG = dfEG.getDecimalFormatSymbols();
 446         DecimalFormatSymbols dfsUS = dfUS.getDecimalFormatSymbols();
 447         dfsEG.setMinusSign(originalMinusSign2);
 448         dfsUS.setMinusSign(originalMinusSign2);
 449         dfEG.setDecimalFormatSymbols(dfsEG);
 450         dfUS.setDecimalFormatSymbols(dfsUS);
 451 
 452         for (int i = 0; i < patterns.length; i++) {
 453             System.out.println(BORDER);
 454             for (int j = 4; j <= 5; j++) {
 455                 // Generate a pattern
 456                 String pattern = patterns[i].replaceAll(" ", delimiters[j]);
 457                 System.out.println("Pattern=\"" + pattern + "\"");
 458 
 459                 System.out.println("*** DateFormat.format test in modified ar_EG");
 460                 testDateFormatFormattingInRTL(pattern, i, j, dfEG, localeEG, false);
 461 
 462                 System.out.println("*** DateFormat.parse test in modified ar_EG");
 463                 testDateFormatParsingInRTL(pattern, i, j, dfEG, localeEG, false);
 464 
 465                 System.out.println("*** DateFormat.format test in modified en_US");
 466                 testDateFormatFormattingInLTR(pattern, i, j, dfUS, localeUS, true);
 467 
 468                 System.out.println("*** DateFormat.parse test in modified en_US");
 469                 testDateFormatParsingInLTR(pattern, i, j, dfUS, localeUS, true);
 470             }
 471         }
 472     }
 473 
 474     private static void testDateFormatFormattingInRTL(String pattern,
 475                                                       int basePattern,
 476                                                       int delimiter,
 477                                                       NumberFormat nf,
 478                                                       Locale locale,
 479                                                       boolean useEnglishMonthName) {
 480         Locale.setDefault(locale);
 481 
 482         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
 483         if (nf != null) {
 484             sdf.setNumberFormat(nf);
 485         }
 486         for (int i = 0; i < datesToParse[basePattern].length; i++) {
 487             if (datesEG[basePattern][i] == null) {
 488                 continue;
 489             }
 490 
 491             String expected = formattedDatesEG[basePattern][i]
 492                               .replaceAll("JUNE", (useEnglishMonthName ?
 493                                                    JuneInEnglish : JuneInArabic))
 494                               .replaceAll("JULY", (useEnglishMonthName ?
 495                                                    JulyInEnglish : JulyInArabic))
 496                               .replaceAll(" ", delimiters[delimiter]);
 497             testDateFormatFormatting(sdf, pattern, datesEG[basePattern][i],
 498                 expected, locale.toString());
 499         }
 500     }
 501 
 502     private static void testDateFormatFormattingInLTR(String pattern,
 503                                                       int basePattern,
 504                                                       int delimiter,
 505                                                       NumberFormat nf,
 506                                                       Locale locale,
 507                                                       boolean useEnglishMonthName) {
 508         Locale.setDefault(locale);
 509 
 510         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
 511         if (nf != null) {
 512             sdf.setNumberFormat(nf);
 513         }
 514         for (int i = 0; i < datesToParse[basePattern].length; i++) {
 515             if (datesUS[basePattern][i] == null) {
 516                 continue;
 517             }
 518 
 519             String expected = formattedDatesUS[basePattern][i]
 520                               .replaceAll("JUNE", (useEnglishMonthName ?
 521                                                    JuneInEnglish : JuneInArabic))
 522                               .replaceAll("JULY", (useEnglishMonthName ?
 523                                                    JulyInEnglish : JulyInArabic))
 524                               .replaceAll(" ", delimiters[delimiter]);
 525             testDateFormatFormatting(sdf, pattern, datesUS[basePattern][i],
 526                 expected, locale.toString());
 527         }
 528     }
 529 
 530     private static void testDateFormatFormatting(SimpleDateFormat sdf,
 531                                                  String pattern,
 532                                                  GregorianCalendar givenGC,
 533                                                  String expected,
 534                                                  String locale) {
 535         Date given = givenGC.getTime();
 536         String str = sdf.format(given);
 537         if (expected.equals(str)) {
 538             if (verbose) {
 539                 System.out.print("  Passed: SimpleDateFormat(");
 540                 System.out.print(locale + ", \"" + pattern + "\").format(");
 541                 System.out.println(given + ")");
 542 
 543                 System.out.print("      ---> \"" + str + "\" ");
 544                 System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)");
 545             }
 546         } else {
 547             err = true;
 548 
 549             System.err.print("  Failed: Unexpected SimpleDateFormat(");
 550             System.out.print(locale + ", \"" + pattern + "\").format(");
 551             System.out.println(given + ") result.");
 552 
 553             System.out.println("      Expected: \"" + expected + "\"");
 554 
 555             System.out.print("      Got:      \"" + str + "\" ");
 556             System.out.println((givenGC.get(ERA) == BC) ? "(BC)" : "(AD)");
 557         }
 558     }
 559 
 560     private static void testDateFormatParsingInRTL(String pattern,
 561                                                    int basePattern,
 562                                                    int delimiter,
 563                                                    NumberFormat nf,
 564                                                    Locale locale,
 565                                                    boolean useEnglishMonthName) {
 566         Locale.setDefault(locale);
 567 
 568         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
 569         if (nf != null) {
 570             sdf.setNumberFormat(nf);
 571         }
 572         for (int i = 0; i < datesToParse[basePattern].length; i++) {
 573             String given = datesToParse[basePattern][i]
 574                            .replaceAll("  ", specialDelimiters[0][delimiter])
 575                            .replaceAll(" ", delimiters[delimiter]);
 576 
 577             testDateFormatParsing(sdf, pattern,
 578                 given.replaceAll("JULY", (useEnglishMonthName ?
 579                                           JulyInEnglish :  JulyInArabic)),
 580                 datesEG[basePattern][i], locale.toString());
 581         }
 582     }
 583 
 584     private static void testDateFormatParsingInLTR(String pattern,
 585                                                    int basePattern,
 586                                                    int delimiter,
 587                                                    NumberFormat nf,
 588                                                    Locale locale,
 589                                                    boolean useEnglishMonthName) {
 590         Locale.setDefault(locale);
 591 
 592         SimpleDateFormat sdf = new SimpleDateFormat(pattern);
 593         if (nf != null) {
 594             sdf.setNumberFormat(nf);
 595         }
 596         for (int i = 0; i < datesToParse[basePattern].length; i++) {
 597             String given = datesToParse[basePattern][i]
 598                            .replaceAll("  ", specialDelimiters[1][delimiter])
 599                            .replaceAll(" ", delimiters[delimiter]);
 600 
 601             testDateFormatParsing(sdf, pattern,
 602                 given.replaceAll("JULY", (useEnglishMonthName ?
 603                                           JulyInEnglish :  JulyInArabic)),
 604                 datesUS[basePattern][i], locale.toString());
 605         }
 606     }
 607 
 608     private static void testDateFormatParsing(SimpleDateFormat sdf,
 609                                               String pattern,
 610                                               String given,
 611                                               GregorianCalendar expectedGC,
 612                                               String locale) {
 613         try {
 614             Date d = sdf.parse(given);
 615             if (expectedGC == null) {
 616                 err = true;
 617                 System.err.print("  Failed: SimpleDateFormat(" + locale);
 618                 System.err.print(", \"" + pattern + "\").parse(\"" + given);
 619                 System.err.println("\") should have thrown ParseException");
 620             } else if (expectedGC.getTime().equals(d)) {
 621                 if (verbose) {
 622                     System.out.print("  Passed: SimpleDateFormat(" + locale);
 623                     System.out.print(", \"" + pattern + "\").parse(\"" + given);
 624                     System.out.println("\")");
 625 
 626                     System.out.print("      ---> " + d + " (" + d.getTime());
 627                     System.out.println(")");
 628                 }
 629             } else {
 630                 err = true;
 631                 System.err.print("  Failed: SimpleDateFormat(" + locale);
 632                 System.err.print(", \"" + pattern + "\").parse(\"" + given);
 633                 System.err.println("\")");
 634 
 635                 System.err.print("      Expected: " + expectedGC.getTime());
 636                 System.err.println(" (" + d.getTime() + ")");
 637 
 638                 System.err.print("      Got:      " + d + " (" + d.getTime());
 639                 System.err.println(")");
 640 
 641                 System.err.print("      Pattern:  \"");
 642                 System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern());
 643                 System.err.println("\"");
 644             }
 645         }
 646         catch (ParseException pe) {
 647             if (expectedGC == null) {
 648                 if (verbose) {
 649                     System.out.print("  Passed: SimpleDateFormat(" + locale);
 650                     System.out.print(", \"" + pattern + "\").parse(\"" + given);
 651                     System.out.println("\")");
 652 
 653                     System.out.println("      threw ParseException as expected");
 654                 }
 655             } else {
 656                 err = true;
 657                 System.err.println("  Failed: Unexpected exception with");
 658 
 659                 System.err.print("    SimpleDateFormat(" + locale);
 660                 System.err.print(", \"" + pattern + "\").parse(\"");
 661                 System.err.println(given + "\"):");
 662 
 663                 System.err.println("      " + pe);
 664 
 665                 System.err.print("      Pattern: \"");
 666                 System.err.print(((DecimalFormat)sdf.getNumberFormat()).toPattern());
 667                 System.err.println("\"");
 668 
 669                 System.err.print("      Month 0: ");
 670                 System.err.println(sdf.getDateFormatSymbols().getMonths()[0]);
 671             }
 672         }
 673     }
 674 
 675 
 676     //
 677     // NumberFormat test
 678     //
 679     private static void testNumberFormat() {
 680         NumberFormat nfEG = NumberFormat.getInstance(localeEG);
 681         NumberFormat nfUS = NumberFormat.getInstance(localeUS);
 682 
 683         System.out.println("*** DecimalFormat.format test in ar_EG");
 684         testNumberFormatFormatting(nfEG, -123456789, "123,456,789-", "ar_EG");
 685         testNumberFormatFormatting(nfEG, -456, "456-", "ar_EG");
 686 
 687         System.out.println("*** DecimalFormat.parse test in ar_EG");
 688         testNumberFormatParsing(nfEG, "123-", new Long(-123), "ar_EG");
 689         testNumberFormatParsing(nfEG, "123--", new Long(-123), "ar_EG");
 690         testNumberFormatParsingCheckException(nfEG, "-123", 0, "ar_EG");
 691 
 692         System.out.println("*** DecimalFormat.format test in en_US");
 693         testNumberFormatFormatting(nfUS, -123456789, "-123,456,789", "en_US");
 694         testNumberFormatFormatting(nfUS, -456, "-456", "en_US");
 695 
 696         System.out.println("*** DecimalFormat.parse test in en_US");
 697         testNumberFormatParsing(nfUS, "123-", new Long(123), "en_US");
 698         testNumberFormatParsing(nfUS, "-123", new Long(-123), "en_US");
 699         testNumberFormatParsingCheckException(nfUS, "--123", 0, "en_US");
 700     }
 701 
 702     private static void testNumberFormatFormatting(NumberFormat nf,
 703                                                    int given,
 704                                                    String expected,
 705                                                    String locale) {
 706         String str = nf.format(given);
 707         if (expected.equals(str)) {
 708             if (verbose) {
 709                 System.out.print("  Passed: NumberFormat(" + locale);
 710                 System.out.println(").format(" + given + ")");
 711 
 712                 System.out.println("      ---> \"" + str + "\"");
 713             }
 714         } else {
 715             err = true;
 716             System.err.print("  Failed: Unexpected NumberFormat(" + locale);
 717             System.err.println(").format(" + given + ") result.");
 718 
 719             System.err.println("      Expected: \"" + expected + "\"");
 720 
 721             System.err.println("      Got:      \"" + str + "\"");
 722         }
 723     }
 724 
 725     private static void testNumberFormatParsing(NumberFormat nf,
 726                                                 String given,
 727                                                 Number expected,
 728                                                 String locale) {
 729         try {
 730             Number n = nf.parse(given);
 731             if (n.equals(expected)) {
 732                 if (verbose) {
 733                     System.out.print("  Passed: NumberFormat(" + locale);
 734                     System.out.println(").parse(\"" + given + "\")");
 735 
 736                     System.out.println("      ---> " + n);
 737                 }
 738             } else {
 739                 err = true;
 740                 System.err.print("  Failed: Unexpected NumberFormat(" + locale);
 741                 System.err.println(").parse(\"" + given + "\") result.");
 742 
 743                 System.err.println("      Expected: " + expected);
 744 
 745                 System.err.println("      Got:      " + n);
 746             }
 747         }
 748         catch (ParseException pe) {
 749             err = true;
 750             System.err.print("  Failed: Unexpected exception with NumberFormat(");
 751             System.err.println(locale + ").parse(\"" + given + "\") :");
 752 
 753             System.err.println("    " + pe);
 754         }
 755     }
 756 
 757     private static void testNumberFormatParsingCheckException(NumberFormat nf,
 758                                                               String given,
 759                                                               int expected,
 760                                                               String locale) {
 761         try {
 762             Number n = nf.parse(given);
 763             err = true;
 764 
 765             System.err.print("  Failed: NumberFormat(" + locale);
 766             System.err.println(").parse(\"" + given + "\")");
 767 
 768             System.err.println("      should have thrown ParseException");
 769         }
 770         catch (ParseException pe) {
 771             int errorOffset = pe.getErrorOffset();
 772             if (errorOffset == expected) {
 773                 if (verbose) {
 774                     System.out.print("  Passed: NumberFormat(" + locale);
 775                     System.out.println(").parse(\"" + given + "\")");
 776 
 777                     System.out.print("      threw ParseException as expected, and its errorOffset was correct: ");
 778                     System.out.println(errorOffset);
 779                 }
 780             } else {
 781                 err = true;
 782                 System.err.print("  Failed: NumberFormat(" + locale);
 783                 System.err.println(").parse(\"" + given + "\")");
 784 
 785                 System.err.print("      threw ParseException as expected, but its errorOffset was incorrect: ");
 786                 System.err.println(errorOffset);
 787             }
 788         }
 789     }
 790 
 791 }