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