Home | History | Annotate | Download | only in format
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /*
      4  *******************************************************************************
      5  * Copyright (C) 2006-2016, Google, International Business Machines Corporation
      6  * and others. All Rights Reserved.
      7  *******************************************************************************
      8  */
      9 
     10 package com.ibm.icu.dev.test.format;
     11 
     12 import java.text.ParsePosition;
     13 import java.util.Collection;
     14 import java.util.Date;
     15 import java.util.HashSet;
     16 import java.util.Iterator;
     17 import java.util.LinkedHashMap;
     18 import java.util.LinkedHashSet;
     19 import java.util.List;
     20 import java.util.Locale;
     21 import java.util.Map;
     22 import java.util.Random;
     23 import java.util.Set;
     24 
     25 import org.junit.Test;
     26 import org.junit.runner.RunWith;
     27 import org.junit.runners.JUnit4;
     28 
     29 import com.ibm.icu.dev.test.TestFmwk;
     30 import com.ibm.icu.impl.PatternTokenizer;
     31 import com.ibm.icu.impl.Utility;
     32 import com.ibm.icu.text.DateFormat;
     33 import com.ibm.icu.text.DateTimePatternGenerator;
     34 import com.ibm.icu.text.DateTimePatternGenerator.FormatParser;
     35 import com.ibm.icu.text.DateTimePatternGenerator.VariableField;
     36 import com.ibm.icu.text.SimpleDateFormat;
     37 import com.ibm.icu.text.UTF16;
     38 import com.ibm.icu.text.UnicodeSet;
     39 import com.ibm.icu.util.Calendar;
     40 import com.ibm.icu.util.GregorianCalendar;
     41 import com.ibm.icu.util.SimpleTimeZone;
     42 import com.ibm.icu.util.TimeZone;
     43 import com.ibm.icu.util.ULocale;
     44 
     45 @RunWith(JUnit4.class)
     46 public class DateTimeGeneratorTest extends TestFmwk {
     47     public static boolean GENERATE_TEST_DATA;
     48     static {
     49         try {
     50             GENERATE_TEST_DATA = System.getProperty("GENERATE_TEST_DATA") != null;
     51         } catch (SecurityException e) {
     52             GENERATE_TEST_DATA = false;
     53         }
     54     };
     55     public static int RANDOM_COUNT = 1000;
     56     public static boolean DEBUG = false;
     57 
     58     @Test
     59     public void TestC() {
     60         String[][] tests = {
     61                 // These may change with actual data for Bhmm/bhmm skeletons
     62                 {"zh",     "Cm",      "Bh:mm"},
     63                 {"zh",     "CCm",     "Bhh:mm"},
     64                 {"zh",     "CCCm",    "BBBBh:mm"},
     65                 {"zh",     "CCCCm",   "BBBBhh:mm"},
     66                 {"zh",     "CCCCCm",  "BBBBBh:mm"},
     67                 {"zh",     "CCCCCCm", "BBBBBhh:mm"},
     68                 {"de",     "Cm",      "HH:mm"},
     69                 {"de",     "CCm",     "HH:mm"},
     70                 {"de",     "CCCm",    "HH:mm"},
     71                 {"de",     "CCCCm",   "HH:mm"},
     72                 {"en",     "Cm",      "h:mm a"},
     73                 {"en",     "CCm",     "hh:mm a"},
     74                 {"en",     "CCCm",    "h:mm aaaa"},
     75                 {"en",     "CCCCm",   "hh:mm aaaa"},
     76                 {"en",     "CCCCCm",  "h:mm aaaaa"},
     77                 {"en",     "CCCCCCm", "hh:mm aaaaa"},
     78                 {"en-BN",  "Cm",      "h:mm b"},
     79                 {"gu-IN",  "Cm",      "h:mm B"},
     80                 {"und-IN", "Cm",      "h:mm a"},
     81         };
     82         for (String[] test : tests) {
     83             DateTimePatternGenerator gen = DateTimePatternGenerator.getInstance(ULocale.forLanguageTag(test[0]));
     84             String skeleton = test[1];
     85             int options = DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH;
     86             String pattern = gen.getBestPattern(skeleton, options);
     87             assertEquals(test[0] + "/" + skeleton, test[2], pattern);
     88         }
     89     }
     90 
     91     @Test
     92     public void TestSkeletonsWithDayPeriods() {
     93         String[][] dataItems = {
     94                 // sample data in a locale (base is not in locale, just here for test)
     95                 // skel (base) pattern
     96                 { "aH", "H",  "H"  }, // should ignore a
     97                 { "h",  "h",  "h a"},
     98                 { "Bh", "Bh", "B h"},
     99         };
    100         String[][] testItems = {
    101                 // sample requested skeletons and results
    102                 // skel     pattern
    103                 { "H",      "H"},
    104                 { "HH",     "HH"},
    105                 { "aH",     "H"},
    106                 { "aHH",    "HH"},
    107                 { "BH",     "H"},
    108                 { "BHH",    "HH"},
    109                 { "BBBBH",  "H"},
    110                 { "h",      "h a"},
    111                 { "hh",     "hh a"},
    112                 { "ah",     "h a"},
    113                 { "ahh",    "hh a"},
    114                 { "aaaah",  "h aaaa"},
    115                 { "aaaahh", "hh aaaa"},
    116                 { "bh",     "h b"},
    117                 { "bhh",    "hh b"},
    118                 { "bbbbh",  "h bbbb"},
    119                 { "Bh",     "B h"},
    120                 { "Bhh",    "B hh"},
    121                 { "BBBBh",  "BBBB h"},
    122                 { "BBBBhh", "BBBB hh"},
    123                 { "a",      "a"},
    124                 { "aaaaa",  "aaaaa"},
    125                 { "b",      "b"},
    126                 { "bbbb",   "bbbb"},
    127                 { "B",      "B"},
    128                 { "BBBB",  "BBBB"},
    129         };
    130         DateTimePatternGenerator gen = DateTimePatternGenerator.getEmptyInstance();
    131         DateTimePatternGenerator.PatternInfo returnInfo = new DateTimePatternGenerator.PatternInfo();
    132         for (String[] dataItem : dataItems) {
    133             gen.addPatternWithSkeleton(dataItem[2], dataItem[0], true, returnInfo);
    134             String base = gen.getBaseSkeleton(dataItem[0]);
    135             if (!base.equals(dataItem[1])) {
    136                  errln("getBaseSkeleton for skeleton " + dataItem[0] + ", expected " + dataItem[1] +  ", got " + base);
    137             }
    138         }
    139         for (String[] testItem : testItems) {
    140             int options = DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH;
    141             String pattern = gen.getBestPattern(testItem[0], options);
    142             if (!pattern.equals(testItem[1])) {
    143                  errln("getBestPattern  for skeleton " + testItem[0] + ", expected " + testItem[1] +  ", got " + pattern);
    144             }
    145         }
    146     }
    147 
    148     @Test
    149     public void TestSimple() {
    150         // some simple use cases
    151         ULocale locale = ULocale.GERMANY;
    152         TimeZone zone = TimeZone.getTimeZone("Europe/Paris");
    153 
    154         // make from locale
    155         DateTimePatternGenerator gen = DateTimePatternGenerator.getInstance(locale);
    156         SimpleDateFormat format = new SimpleDateFormat(gen.getBestPattern("MMMddHmm"), locale);
    157         format.setTimeZone(zone);
    158         assertEquals("simple format: MMMddHmm", "14. Okt., 08:58", format.format(sampleDate));
    159         // (a generator can be built from scratch, but that is not a typical use case)
    160 
    161         // modify the generator by adding patterns
    162         DateTimePatternGenerator.PatternInfo returnInfo = new DateTimePatternGenerator.PatternInfo();
    163         gen.addPattern("d'. von' MMMM", true, returnInfo);
    164         // the returnInfo is mostly useful for debugging problem cases
    165         format.applyPattern(gen.getBestPattern("MMMMdHmm"));
    166         assertEquals("modified format: MMMdHmm", "14. von Oktober, 08:58", format.format(sampleDate));
    167 
    168         // get a pattern and modify it
    169         format = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, locale);
    170         format.setTimeZone(zone);
    171         String pattern = format.toPattern();
    172         assertEquals("full-date", "Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\u00E4ische Sommerzeit", format.format(sampleDate));
    173 
    174         // modify it to change the zone.
    175         String newPattern = gen.replaceFieldTypes(pattern, "vvvv");
    176         format.applyPattern(newPattern);
    177         assertEquals("full-date: modified zone", "Donnerstag, 14. Oktober 1999 um 08:58:59 Mitteleurop\u00E4ische Zeit", format.format(sampleDate));
    178 
    179         // add test of basic cases
    180 
    181         //lang  YYYYMMM MMMd    MMMdhmm hmm hhmm    Full Date-Time
    182         // en  Mar 2007    Mar 4   6:05 PM Mar 4   6:05 PM 06:05 PM    Sunday, March 4, 2007 6:05:05 PM PT
    183         DateTimePatternGenerator enGen = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
    184         TimeZone enZone = TimeZone.getTimeZone("Etc/GMT");
    185         SimpleDateFormat enFormat = (SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, ULocale.ENGLISH);
    186         enFormat.setTimeZone(enZone);
    187         String[][] tests = {
    188                 {"yyyyMMMdd", "Oct 14, 1999"},
    189                 {"yyyyqqqq", "4th quarter 1999"},
    190                 {"yMMMdd", "Oct 14, 1999"},
    191                 {"EyyyyMMMdd", "Thu, Oct 14, 1999"},
    192                 {"yyyyMMdd", "10/14/1999"},
    193                 {"yyyyMMM", "Oct 1999"},
    194                 {"yyyyMM", "10/1999"},
    195                 {"yyMM", "10/99"},
    196                 {"yMMMMMd", "O 14, 1999"},  // narrow format
    197                 {"EEEEEMMMMMd", "T, O 14"},  // narrow format
    198                 {"MMMd", "Oct 14"},
    199                 {"MMMdhmm", "Oct 14, 6:58 AM"},
    200                 {"EMMMdhmms", "Thu, Oct 14, 6:58:59 AM"},
    201                 {"MMdhmm", "10/14, 6:58 AM"},
    202                 {"EEEEMMMdhmms", "Thursday, Oct 14, 6:58:59 AM"},
    203                 {"yyyyMMMddhhmmss", "Oct 14, 1999, 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
    204                 {"EyyyyMMMddhhmmss", "Thu, Oct 14, 1999, 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
    205                 {"hmm", "6:58 AM"},
    206                 {"hhmm", "6:58 AM"}, // (fixed expected result per ticket 6872<-7180)
    207                 {"hhmmVVVV", "6:58 AM GMT"}, // (fixed expected result per ticket 6872<-7180)
    208         };
    209         for (int i = 0; i < tests.length; ++i) {
    210             final String testSkeleton = tests[i][0];
    211             String pat = enGen.getBestPattern(testSkeleton);
    212             enFormat.applyPattern(pat);
    213             String formattedDate = enFormat.format(sampleDate);
    214             assertEquals("Testing skeleton '" + testSkeleton + "' with  " + sampleDate, tests[i][1], formattedDate);
    215         }
    216     }
    217 
    218     @Test
    219     public void TestRoot() {
    220         DateTimePatternGenerator rootGen = DateTimePatternGenerator.getInstance(ULocale.ROOT);
    221         SimpleDateFormat rootFormat = new SimpleDateFormat(rootGen.getBestPattern("yMdHms"), ULocale.ROOT);
    222         rootFormat.setTimeZone(gmt);
    223         // *** expected result should be "1999-10-14 6:58:59" with current data, changed test temporarily to match current result, needs investigation
    224         assertEquals("root format: yMdHms", "1999-10-14 06:58:59", rootFormat.format(sampleDate));
    225     }
    226 
    227     @Test
    228     public void TestEmpty() {
    229         // now nothing
    230         DateTimePatternGenerator nullGen = DateTimePatternGenerator.getEmptyInstance();
    231         SimpleDateFormat format = new SimpleDateFormat(nullGen.getBestPattern("yMdHms"), ULocale.ROOT);
    232         TimeZone rootZone = TimeZone.getTimeZone("Etc/GMT");
    233         format.setTimeZone(rootZone);
    234     }
    235 
    236     @Test
    237     public void TestPatternParser() {
    238         StringBuffer buffer = new StringBuffer();
    239         PatternTokenizer pp = new PatternTokenizer()
    240         .setIgnorableCharacters(new UnicodeSet("[-]"))
    241         .setSyntaxCharacters(new UnicodeSet("[a-zA-Z]"))
    242         .setEscapeCharacters(new UnicodeSet("[b#]"))
    243         .setUsingQuote(true);
    244         logln("Using Quote");
    245         for (int i = 0; i < patternTestData.length; ++i) {
    246             String patternTest = (String) patternTestData[i];
    247             CheckPattern(buffer, pp, patternTest);
    248         }
    249         String[] randomSet = {"abcdef", "$12!@#-", "'\\"};
    250         for (int i = 0; i < RANDOM_COUNT; ++i) {
    251             String patternTest = getRandomString(randomSet, 0, 10);
    252             CheckPattern(buffer, pp, patternTest);
    253         }
    254         logln("Using Backslash");
    255         pp.setUsingQuote(false).setUsingSlash(true);
    256         for (int i = 0; i < patternTestData.length; ++i) {
    257             String patternTest = (String) patternTestData[i];
    258             CheckPattern(buffer, pp, patternTest);
    259         }
    260         for (int i = 0; i < RANDOM_COUNT; ++i) {
    261             String patternTest = getRandomString(randomSet, 0, 10);
    262             CheckPattern(buffer, pp, patternTest);
    263         }
    264     }
    265 
    266     Random random = new java.util.Random(-1);
    267 
    268     private String getRandomString(String[] randomList, int minLen, int maxLen) {
    269         StringBuffer result = new StringBuffer();
    270         int len = random.nextInt(maxLen + 1 - minLen) + minLen;
    271         for (int i = minLen; i < len; ++ i) {
    272             String source = randomList[random.nextInt(randomList.length)]; // don't bother with surrogates
    273             char ch = source.charAt(random.nextInt(source.length()));
    274             UTF16.append(result, ch);
    275         }
    276         return result.toString();
    277     }
    278 
    279     private void CheckPattern(StringBuffer buffer, PatternTokenizer pp, String patternTest) {
    280         pp.setPattern(patternTest);
    281         if (DEBUG && isVerbose()) {
    282             showItems(buffer, pp, patternTest);
    283         }
    284         String normalized = pp.setStart(0).normalize();
    285         logln("input:\t<" + patternTest + ">" + "\tnormalized:\t<" + normalized + ">");
    286         String doubleNormalized = pp.setPattern(normalized).normalize();
    287         if (!normalized.equals(doubleNormalized)) {
    288             errln("Normalization not idempotent:\t" + patternTest + "\tnormalized: " + normalized +  "\tnormalized2: " + doubleNormalized);
    289             // allow for debugging at the point of failure
    290             if (DEBUG) {
    291                 pp.setPattern(patternTest);
    292                 normalized = pp.setStart(0).normalize();
    293                 pp.setPattern(normalized);
    294                 showItems(buffer, pp, normalized);
    295                 doubleNormalized = pp.normalize();
    296             }
    297         }
    298     }
    299 
    300     private void showItems(StringBuffer buffer, PatternTokenizer pp, String patternTest) {
    301         logln("input:\t<" + patternTest + ">");
    302         while (true) {
    303             buffer.setLength(0);
    304             int status = pp.next(buffer);
    305             if (status == PatternTokenizer.DONE) break;
    306             String lit = "";
    307             if (status != PatternTokenizer.SYNTAX ) {
    308                 lit = "\t<" + pp.quoteLiteral(buffer) + ">";
    309             }
    310             logln("\t" + statusName[status] + "\t<" + buffer + ">" + lit);
    311         }
    312     }
    313 
    314     static final String[] statusName = {"DONE", "SYNTAX", "LITERAL", "BROKEN_QUOTE", "BROKEN_ESCAPE", "UNKNOWN"};
    315 
    316     @Test
    317     public void TestBasic() {
    318         ULocale uLocale = null;
    319         DateTimePatternGenerator dtfg = null;
    320         Date date = null;
    321         for (int i = 0; i < dateTestData.length; ++i) {
    322             if (dateTestData[i] instanceof ULocale) {
    323                 uLocale = (ULocale) dateTestData[i];
    324                 dtfg = DateTimePatternGenerator.getInstance(uLocale);
    325                 if (GENERATE_TEST_DATA) logln("new ULocale(\"" + uLocale.toString() + "\"),");
    326             } else if (dateTestData[i] instanceof Date) {
    327                 date = (Date) dateTestData[i];
    328                 if (GENERATE_TEST_DATA) logln("new Date(" + date.getTime()+ "L),");
    329             } else if (dateTestData[i] instanceof String) {
    330                 String testSkeleton = (String) dateTestData[i];
    331                 String pattern = dtfg.getBestPattern(testSkeleton);
    332                 SimpleDateFormat sdf = new SimpleDateFormat(pattern, uLocale);
    333                 String formatted = sdf.format(date);
    334                 if (GENERATE_TEST_DATA) logln("new String[] {\"" + testSkeleton + "\", \"" + Utility.escape(formatted) + "\"},");
    335                 //logln(uLocale + "\t" + testSkeleton + "\t" + pattern + "\t" + sdf.format(date));
    336             } else {
    337                 String[] testPair = (String[]) dateTestData[i];
    338                 String testSkeleton = testPair[0];
    339                 String testFormatted = testPair[1];
    340                 String pattern = dtfg.getBestPattern(testSkeleton);
    341                 SimpleDateFormat sdf = new SimpleDateFormat(pattern, uLocale);
    342                 String formatted = sdf.format(date);
    343                 if (GENERATE_TEST_DATA) {
    344                     logln("new String[] {\"" + testSkeleton + "\", \"" + Utility.escape(formatted) + "\"},");
    345                 } else if (!formatted.equals(testFormatted)) {
    346                     errln(uLocale + "\tformatted string doesn't match test case: " + testSkeleton + "\t generated: " +  pattern + "\t expected: " + testFormatted + "\t got: " + formatted);
    347                     if (true) { // debug
    348                         pattern = dtfg.getBestPattern(testSkeleton);
    349                         sdf = new SimpleDateFormat(pattern, uLocale);
    350                         formatted = sdf.format(date);
    351                     }
    352                 }
    353                 //logln(uLocale + "\t" + testSkeleton + "\t" + pattern + "\t" + sdf.format(date));
    354             }
    355         }
    356     }
    357 
    358     static final Object[] patternTestData = {
    359         "'$f''#c",
    360         "'' 'a",
    361         "'.''.'",
    362         "\\u0061\\\\",
    363         "mm.dd 'dd ' x",
    364         "'' ''",
    365     };
    366 
    367     // can be generated by using GENERATE_TEST_DATA. Must be reviewed before adding
    368     static final Object[] dateTestData = {
    369         new Date(916300739123L), // 1999-01-13T23:58:59.123,0-0800
    370 
    371         new ULocale("en_US"),
    372         new String[] {"yM", "1/1999"},
    373         new String[] {"yMMM", "Jan 1999"},
    374         new String[] {"yMd", "1/13/1999"},
    375         new String[] {"yMMMd", "Jan 13, 1999"},
    376         new String[] {"Md", "1/13"},
    377         new String[] {"MMMd", "Jan 13"},
    378         new String[] {"MMMMd", "January 13"},
    379         new String[] {"yQQQ", "Q1 1999"},
    380         new String[] {"hhmm", "11:58 PM"},
    381         new String[] {"HHmm", "23:58"},
    382         new String[] {"jjmm", "11:58 PM"},
    383         new String[] {"mmss", "58:59"},
    384         new String[] {"yyyyMMMM", "January 1999"}, // (new item for testing 6872<-5702)
    385         new String[] {"MMMEd", "Wed, Jan 13"},
    386         new String[] {"Ed", "13 Wed"},
    387         new String[] {"jmmssSSS", "11:58:59.123 PM"},
    388         new String[] {"JJmm", "11:58"},
    389 
    390         new ULocale("en_US@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
    391         new String[] {"yM", "1/11 H"},
    392         new String[] {"yMMM", "Jan 11 Heisei"},
    393         new String[] {"yMd", "1/13/11 H"},
    394         new String[] {"yMMMd", "Jan 13, 11 Heisei"},
    395         new String[] {"Md", "1/13"},
    396         new String[] {"MMMd", "Jan 13"},
    397         new String[] {"MMMMd", "January 13"},
    398         new String[] {"yQQQ", "Q1 11 Heisei"},
    399         new String[] {"hhmm", "11:58 PM"},
    400         new String[] {"HHmm", "23:58"},
    401         new String[] {"jjmm", "11:58 PM"},
    402         new String[] {"mmss", "58:59"},
    403         new String[] {"yyyyMMMM", "January 11 Heisei"},
    404         new String[] {"MMMEd", "Wed, Jan 13"},
    405         new String[] {"Ed", "13 Wed"},
    406         new String[] {"jmmssSSS", "11:58:59.123 PM"},
    407         new String[] {"JJmm", "11:58"},
    408 
    409         new ULocale("de_DE"),
    410         new String[] {"yM", "1.1999"},
    411         new String[] {"yMMM", "Jan. 1999"},
    412         new String[] {"yMd", "13.1.1999"},
    413         new String[] {"yMMMd", "13. Jan. 1999"},
    414         new String[] {"Md", "13.1."},   // 13.1
    415         new String[] {"MMMd", "13. Jan."},
    416         new String[] {"MMMMd", "13. Januar"},
    417         new String[] {"yQQQ", "Q1 1999"},
    418         new String[] {"hhmm", "11:58 nachm."},
    419         new String[] {"HHmm", "23:58"},
    420         new String[] {"jjmm", "23:58"},
    421         new String[] {"mmss", "58:59"},
    422         new String[] {"yyyyMMMM", "Januar 1999"}, // (new item for testing 6872<-5702)
    423         new String[] {"MMMEd", "Mi., 13. Jan."},
    424         new String[] {"Ed", "Mi., 13."},
    425         new String[] {"jmmssSSS", "23:58:59,123"},
    426         new String[] {"JJmm", "23:58"},
    427 
    428         new ULocale("fi"),
    429         new String[] {"yM", "1.1999"}, // (fixed expected result per ticket 6872<-6626)
    430         new String[] {"yMMM", "tammi 1999"}, // (fixed expected result per ticket 6872<-7007)
    431         new String[] {"yMd", "13.1.1999"},
    432         new String[] {"yMMMd", "13. tammik. 1999"},
    433         new String[] {"Md", "13.1."},
    434         new String[] {"MMMd", "13. tammik."},
    435         new String[] {"MMMMd", "13. tammikuuta"},
    436         new String[] {"yQQQ", "1. nelj. 1999"},
    437         new String[] {"hhmm", "11.58 ip."},
    438         new String[] {"HHmm", "23.58"},
    439         new String[] {"jjmm", "23.58"},
    440         new String[] {"mmss", "58.59"},
    441         new String[] {"yyyyMMMM", "tammikuu 1999"}, // (new item for testing 6872<-5702,7007)
    442         new String[] {"MMMEd", "ke 13. tammik."},
    443         new String[] {"Ed", "ke 13."},
    444         new String[] {"jmmssSSS", "23.58.59,123"},
    445         new String[] {"JJmm", "23.58"},
    446 
    447         new ULocale("es"),
    448         new String[] {"yM", "1/1999"},
    449         new String[] {"yMMM", "ene. 1999"},
    450         new String[] {"yMd", "13/1/1999"},
    451         new String[] {"yMMMd", "13 ene. 1999"},
    452         new String[] {"Md", "13/1"},
    453         new String[] {"MMMd", "13 ene."},
    454         new String[] {"MMMMd", "13 de enero"},
    455         new String[] {"yQQQ", "T1 1999"},
    456         new String[] {"hhmm", "11:58 p. m."},
    457         new String[] {"HHmm", "23:58"},
    458         new String[] {"jjmm", "23:58"},
    459         new String[] {"mmss", "58:59"},
    460         new String[] {"yyyyMMMM", "enero de 1999"},
    461         new String[] {"MMMEd", "mi\u00E9., 13 ene."},
    462         new String[] {"Ed", "mi\u00E9. 13"},
    463         new String[] {"jmmssSSS", "23:58:59,123"},
    464         new String[] {"JJmm", "23:58"},
    465 
    466         new ULocale("ja"), // (new locale for testing ticket 6872<-6626)
    467         new String[] {"yM", "1999/1"},
    468         new String[] {"yMMM", "1999\u5E741\u6708"},
    469         new String[] {"yMd", "1999/1/13"},
    470         new String[] {"yMMMd", "1999\u5E741\u670813\u65E5"},
    471         new String[] {"Md", "1/13"},
    472         new String[] {"MMMd", "1\u670813\u65E5"},
    473         new String[] {"MMMMd", "1\u670813\u65E5"},
    474         new String[] {"yQQQ", "1999/Q1"},
    475         new String[] {"hhmm", "\u5348\u5F8C11:58"},
    476         new String[] {"HHmm", "23:58"},
    477         new String[] {"jjmm", "23:58"},
    478         new String[] {"mmss", "58:59"},
    479         new String[] {"yyyyMMMM", "1999\u5E741\u6708"}, // (new item for testing 6872<-5702)
    480         new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
    481         new String[] {"Ed", "13\u65E5(\u6C34)"},
    482         new String[] {"jmmssSSS", "23:58:59.123"},
    483         new String[] {"JJmm", "23:58"},
    484 
    485         new ULocale("ja@calendar=japanese"), // (new locale for testing ticket 6872<-5702)
    486         new String[] {"yM", "\u5E73\u621011/1"},
    487         new String[] {"yMMM", "\u5E73\u621011\u5E741\u6708"},
    488         new String[] {"yMd", "\u5E73\u621011/1/13"},
    489         new String[] {"yMMMd", "\u5E73\u621011\u5E741\u670813\u65E5"},
    490         new String[] {"Md", "1/13"},
    491         new String[] {"MMMd", "1\u670813\u65E5"},
    492         new String[] {"MMMMd", "1\u670813\u65E5"},
    493         new String[] {"yQQQ", "\u5E73\u621011/Q1"},
    494         new String[] {"hhmm", "\u5348\u5F8C11:58"},
    495         new String[] {"HHmm", "23:58"},
    496         new String[] {"jjmm", "23:58"},
    497         new String[] {"mmss", "58:59"},
    498         new String[] {"yyyyMMMM", "\u5E73\u621011\u5E741\u6708"},
    499         new String[] {"MMMEd", "1\u670813\u65E5(\u6C34)"},
    500         new String[] {"Ed", "13\u65E5(\u6C34)"},
    501         new String[] {"jmmssSSS", "23:58:59.123"},
    502         new String[] {"JJmm", "23:58"},
    503 
    504         new ULocale("zh_Hans_CN"),
    505         new String[] {"yM", "1999\u5E741\u6708"},
    506         new String[] {"yMMM", "1999\u5E741\u6708"}, // (fixed expected result per ticket 6872<-6626)
    507         new String[] {"yMd", "1999/1/13"},
    508         new String[] {"yMMMd", "1999\u5E741\u670813\u65E5"}, // (fixed expected result per ticket 6872<-6626)
    509         new String[] {"Md", "1/13"},
    510         new String[] {"MMMd", "1\u670813\u65E5"}, // (fixed expected result per ticket 6872<-6626)
    511         new String[] {"MMMMd", "1\u670813\u65E5"},
    512         new String[] {"yQQQ", "1999\u5E74\u7B2C1\u5B63\u5EA6"},
    513         new String[] {"hhmm", "\u4E0B\u534811:58"},
    514         new String[] {"HHmm", "23:58"},
    515         new String[] {"jjmm", "\u4E0B\u534811:58"},
    516         new String[] {"mmss", "58:59"},
    517         new String[] {"yyyyMMMM", "1999\u5E741\u6708"}, // (new item for testing 6872<-5702)
    518         new String[] {"MMMEd", "1\u670813\u65E5\u5468\u4E09"},
    519         new String[] {"Ed", "13\u65E5\u5468\u4E09"},
    520         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
    521         new String[] {"JJmm", "11:58"},
    522 
    523         new ULocale("zh_TW@calendar=roc"), // (new locale for testing ticket 6872<-5702)
    524         new String[] {"yM", "\u6C11\u570B88/1"},
    525         new String[] {"yMMM", "\u6C11\u570B88\u5E741\u6708"},
    526         new String[] {"yMd", "\u6C11\u570B88/1/13"},
    527         new String[] {"yMMMd", "\u6C11\u570B88\u5E741\u670813\u65E5"},
    528         new String[] {"Md", "1/13"},
    529         new String[] {"MMMd", "1\u670813\u65E5"},
    530         new String[] {"MMMMd", "1\u670813\u65E5"},
    531         new String[] {"yQQQ", "\u6C11\u570B88\u5E74\u7B2C1\u5B63"},
    532         new String[] {"hhmm", "\u4E0B\u534811:58"},
    533         new String[] {"HHmm", "23:58"},
    534         new String[] {"jjmm", "\u4E0B\u534811:58"},
    535         new String[] {"mmss", "58:59"},
    536         new String[] {"yyyyMMMM", "\u6C11\u570B88\u5E741\u6708"},
    537         new String[] {"MMMEd", "1\u670813\u65E5\u9031\u4E09"},
    538         new String[] {"Ed", "13 \u9031\u4E09"},
    539         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
    540         new String[] {"JJmm", "11:58"},
    541 
    542         new ULocale("ru"),
    543         new String[] {"yM", "01.1999"},
    544         new String[] {"yMMM", "\u044F\u043D\u0432. 1999 \u0433."},
    545         new String[] {"yMd", "13.01.1999"},
    546         new String[] {"yMMMd", "13 \u044F\u043D\u0432. 1999 \u0433."},
    547         new String[] {"Md", "13.01"},
    548         new String[] {"MMMd", "13 \u044F\u043D\u0432."},
    549         new String[] {"MMMMd", "13 \u044F\u043D\u0432\u0430\u0440\u044F"},
    550         new String[] {"yQQQ", "1-\u0439 \u043A\u0432. 1999 \u0433."},
    551         new String[] {"hhmm", "11:58 PM"},
    552         new String[] {"HHmm", "23:58"},
    553         new String[] {"jjmm", "23:58"},
    554         new String[] {"mmss", "58:59"},
    555         new String[] {"yyyyMMMM", "\u044F\u043D\u0432\u0430\u0440\u044C 1999 \u0433."},
    556         new String[] {"MMMEd", "\u0441\u0440, 13 \u044F\u043D\u0432."},
    557         new String[] {"Ed", "\u0441\u0440, 13"},
    558         new String[] {"jmmssSSS", "23:58:59,123"},
    559         new String[] {"JJmm", "23:58"},
    560 
    561         new ULocale("zh@calendar=chinese"),
    562         new String[] {"yM", "1998\u620A\u5BC5\u5E74\u5341\u4E00\u6708"},
    563         new String[] {"yMMM", "1998\u620A\u5BC5\u5E74\u5341\u4E00\u6708"},
    564         new String[] {"yMd", "1998\u5E74\u5341\u4E00\u670826"},
    565         new String[] {"yMMMd", "1998\u5E74\u5341\u4E00\u670826"},
    566         new String[] {"Md", "11-26"},
    567         new String[] {"MMMd", "\u5341\u4E00\u670826\u65E5"},
    568         new String[] {"MMMMd", "\u5341\u4E00\u670826\u65E5"},
    569         new String[] {"yQQQ", "1998\u620A\u5BC5\u5E74\u7B2C\u56DB\u5B63\u5EA6"},
    570         new String[] {"hhmm", "\u4E0B\u534811:58"},
    571         new String[] {"HHmm", "23:58"},
    572         new String[] {"jjmm", "\u4E0B\u534811:58"},
    573         new String[] {"mmss", "58:59"},
    574         new String[] {"yyyyMMMM", "1998\u620A\u5BC5\u5E74\u5341\u4E00\u6708"},
    575         new String[] {"MMMEd", "\u5341\u4E00\u670826\u65E5\u5468\u4E09"},
    576         new String[] {"Ed", "26\u65E5\u5468\u4E09"},
    577         new String[] {"jmmssSSS", "\u4E0B\u534811:58:59.123"},
    578         new String[] {"JJmm", "11:58"},
    579     };
    580 
    581     @Test
    582     public void DayMonthTest() {
    583         final ULocale locale = ULocale.FRANCE;
    584 
    585         // set up the generator
    586         DateTimePatternGenerator dtpgen
    587         = DateTimePatternGenerator.getInstance(locale);
    588 
    589         // get a pattern for an abbreviated month and day
    590         final String pattern = dtpgen.getBestPattern("MMMd");
    591         SimpleDateFormat formatter = new SimpleDateFormat(pattern, locale);
    592 
    593         // use it to format (or parse)
    594         String formatted = formatter.format(new Date());
    595         logln("formatted=" + formatted);
    596         // for French, the result is "13 sept."
    597     }
    598 
    599     @Test
    600     public void TestOrdering() {
    601         ULocale[] locales = ULocale.getAvailableLocales();
    602         for (int i = 0; i < locales.length; ++i) {
    603             for (int style1 = DateFormat.FULL; style1 <= DateFormat.SHORT; ++style1) {
    604                 for (int style2 = DateFormat.FULL; style2 < style1; ++style2) {
    605                     checkCompatible(style1, style2, locales[i]);
    606                 }
    607             }
    608         }
    609     }
    610 
    611     @Test
    612     public void TestReplacingZoneString() {
    613         Date testDate = new Date();
    614         TimeZone testTimeZone = TimeZone.getTimeZone("America/New_York");
    615         TimeZone bogusTimeZone = new SimpleTimeZone(1234, "Etc/Unknown");
    616         Calendar calendar = Calendar.getInstance();
    617         ParsePosition parsePosition = new ParsePosition(0);
    618 
    619         ULocale[] locales = ULocale.getAvailableLocales();
    620         int count = 0;
    621         for (int i = 0; i < locales.length; ++i) {
    622             // skip the country locales unless we are doing exhaustive tests
    623             if (getExhaustiveness() < 6) {
    624                 if (locales[i].getCountry().length() > 0) {
    625                     continue;
    626                 }
    627             }
    628             count++;
    629             // Skipping some test case in the non-exhaustive mode to reduce the test time
    630             //ticket#6503
    631             if(getExhaustiveness()<=5 && count%3!=0){
    632                 continue;
    633             }
    634             logln(locales[i].toString());
    635             DateTimePatternGenerator dtpgen
    636             = DateTimePatternGenerator.getInstance(locales[i]);
    637 
    638             for (int style1 = DateFormat.FULL; style1 <= DateFormat.SHORT; ++style1) {
    639                 final SimpleDateFormat oldFormat = (SimpleDateFormat) DateFormat.getTimeInstance(style1, locales[i]);
    640                 String pattern = oldFormat.toPattern();
    641                 String newPattern = dtpgen.replaceFieldTypes(pattern, "VVVV"); // replaceZoneString(pattern, "VVVV");
    642                 if (newPattern.equals(pattern)) {
    643                     continue;
    644                 }
    645                 // verify that it roundtrips parsing
    646                 SimpleDateFormat newFormat = new SimpleDateFormat(newPattern, locales[i]);
    647                 newFormat.setTimeZone(testTimeZone);
    648                 String formatted = newFormat.format(testDate);
    649                 calendar.setTimeZone(bogusTimeZone);
    650                 parsePosition.setIndex(0);
    651                 newFormat.parse(formatted, calendar, parsePosition);
    652                 if (parsePosition.getErrorIndex() >= 0) {
    653                     errln("Failed parse with VVVV:\t" + locales[i] + ",\t\"" + pattern + "\",\t\"" + newPattern + "\",\t\"" + formatted.substring(0,parsePosition.getErrorIndex()) + "{}" + formatted.substring(parsePosition.getErrorIndex()) + "\"");
    654                 } else if (!calendar.getTimeZone().getID().equals(testTimeZone.getID())) {
    655                     errln("Failed timezone roundtrip with VVVV:\t" + locales[i] + ",\t\"" + pattern + "\",\t\"" + newPattern + "\",\t\"" + formatted + "\",\t" + calendar.getTimeZone().getID() + " != " + testTimeZone.getID());
    656                 } else {
    657                     logln(locales[i] + ":\t\"" + pattern + "\" => \t\"" + newPattern + "\"\t" + formatted);
    658                 }
    659             }
    660         }
    661     }
    662 
    663     @Test
    664     public void TestVariableCharacters() {
    665         UnicodeSet valid = new UnicodeSet("[G y Y u U r Q q M L l w W d D F g E e c a b B h H K k m s S A z Z O v V X x]");
    666         for (char c = 0; c < 0xFF; ++c) {
    667             boolean works = false;
    668             try {
    669                 VariableField vf = new VariableField(String.valueOf(c), true);
    670                 logln("VariableField " + vf.toString());
    671                 works = true;
    672             } catch (Exception e) {}
    673             if (works != valid.contains(c)) {
    674                 if (works) {
    675                     errln("VariableField can be created with illegal character: " + c);
    676                 } else {
    677                     errln("VariableField can't be created with legal character: " + c);
    678                 }
    679             }
    680         }
    681     }
    682 
    683     static String[] DATE_STYLE_NAMES = {
    684         "FULL", "LONG", "MEDIUM", "SHORT"
    685     };
    686 
    687     /**
    688      * @param fullOrder
    689      * @param longOrder
    690      */
    691     private void checkCompatible(int style1, int style2, ULocale uLocale) {
    692         DateOrder order1 = getOrdering(style1, uLocale);
    693         DateOrder order2 = getOrdering(style2, uLocale);
    694         if (!order1.hasSameOrderAs(order2)) {
    695             // Note: This test case was updated by #6806 and no longer reports
    696             // ordering difference as an error case.
    697             logln(showOrderComparison(uLocale, style1, style2, order1, order2));
    698         }
    699     }
    700 
    701     private String showOrderComparison(ULocale uLocale, int style1, int style2, DateOrder order1, DateOrder order2) {
    702         String pattern1 = ((SimpleDateFormat) DateFormat.getDateInstance(style1, uLocale)).toPattern();
    703         String pattern2 = ((SimpleDateFormat) DateFormat.getDateInstance(style2, uLocale)).toPattern();
    704         return "Mismatch in in ordering for " + uLocale + ": " + DATE_STYLE_NAMES[style1] + ": " + order1 + ", <" + pattern1
    705                 + ">; "
    706                 + DATE_STYLE_NAMES[style2] + ": " + order2 + ", <" + pattern2 + ">; " ;
    707     }
    708 
    709     /**
    710      * Main date fields -- Poor-man's enum -- change to real enum when we get JDK 1.5
    711      */
    712     public static class DateFieldType {
    713         private String name;
    714         private DateFieldType(String string) {
    715             name = string;
    716         }
    717 
    718         public static DateFieldType
    719         YEAR = new DateFieldType("YEAR"),
    720         MONTH = new DateFieldType("MONTH"),
    721         DAY = new DateFieldType("DAY");
    722 
    723         @Override
    724         public String toString() {
    725             return name;
    726         }
    727     }
    728 
    729     /**
    730      * Simple struct for output from getOrdering
    731      */
    732     static class DateOrder {
    733         int monthLength;
    734         DateFieldType[] fields = new DateFieldType[3];
    735 
    736         public boolean isCompatible(DateOrder other) {
    737             return monthLength == other.monthLength;
    738         }
    739         /**
    740          * @param order2
    741          * @return
    742          */
    743         public boolean hasSameOrderAs(DateOrder other) {
    744             // TODO Auto-generated method stub
    745             return fields[0] == other.fields[0] && fields[1] == other.fields[1] && fields[2] == other.fields[2];
    746         }
    747         @Override
    748         public String toString() {
    749             return "{" + monthLength + ", " + fields[0]  + ", " + fields[1]  + ", " + fields[2] + "}";
    750         }
    751         @Override
    752         public boolean equals(Object that) {
    753             DateOrder other = (DateOrder) that;
    754             return monthLength == other.monthLength && fields[0] == other.fields[0] && fields[1] == other.fields[1] && fields[2] == other.fields[2];
    755         }
    756     }
    757 
    758     DateTimePatternGenerator.FormatParser formatParser = new DateTimePatternGenerator.FormatParser ();
    759     DateTimePatternGenerator generator = DateTimePatternGenerator.getEmptyInstance();
    760 
    761     private Calendar sampleCalendar;
    762     {
    763         sampleCalendar = new GregorianCalendar(TimeZone.getTimeZone("America/Los_Angeles"));
    764         sampleCalendar.set(1999, Calendar.OCTOBER, 13, 23, 58, 59);
    765     }
    766 
    767     private Date sampleDate = sampleCalendar.getTime();
    768     private TimeZone gmt = TimeZone.getTimeZone("Etc/GMT");
    769 
    770     /**
    771      * Replace the zone string with a different type, eg v's for z's, etc. <p>Called with a pattern, such as one gotten from
    772      * <pre>
    773      * String pattern = ((SimpleDateFormat) DateFormat.getTimeInstance(style, locale)).toPattern();
    774      * </pre>
    775      * @param pattern original pattern to change, such as "HH:mm zzzz"
    776      * @param newZone Must be: z, zzzz, Z, ZZZZ, v, vvvv, V, or VVVV
    777      * @return
    778      */
    779     public String replaceZoneString(String pattern, String newZone) {
    780         final List itemList = formatParser.set(pattern).getItems();
    781         boolean changed = false;
    782         for (int i = 0; i < itemList.size(); ++i) {
    783             Object item = itemList.get(i);
    784             if (item instanceof VariableField) {
    785                 VariableField variableField = (VariableField) item;
    786                 if (variableField.getType() == DateTimePatternGenerator.ZONE) {
    787                     if (!variableField.toString().equals(newZone)) {
    788                         changed = true;
    789                         itemList.set(i, new VariableField(newZone, true));
    790                     }
    791                 }
    792             }
    793         }
    794         return changed ? formatParser.toString() : pattern;
    795     }
    796 
    797     public boolean containsZone(String pattern) {
    798         for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
    799             Object item = it.next();
    800             if (item instanceof VariableField) {
    801                 VariableField variableField = (VariableField) item;
    802                 if (variableField.getType() == DateTimePatternGenerator.ZONE) {
    803                     return true;
    804                 }
    805             }
    806         }
    807         return false;
    808     }
    809 
    810     /**
    811      * Get the ordering from a particular date format. Best is to use
    812      * DateFormat.FULL to get the format with String form month (like "January")
    813      * and DateFormat.SHORT for the numeric format order. They may be different.
    814      * (Theoretically all 4 formats could be different but that never happens in
    815      * practice.)
    816      *
    817      * @param style
    818      *          DateFormat.FULL..DateFormat.SHORT
    819      * @param locale
    820      *          desired locale.
    821      * @return
    822      * @return list of ordered items DateFieldType (I
    823      *         didn't know what form you really wanted so this is just a
    824      *         stand-in.)
    825      */
    826     private DateOrder getOrdering(int style, ULocale locale) {
    827         // and the date pattern
    828         String pattern = ((SimpleDateFormat) DateFormat.getDateInstance(style, locale)).toPattern();
    829         int count = 0;
    830         DateOrder result = new DateOrder();
    831 
    832         for (Iterator it = formatParser.set(pattern).getItems().iterator(); it.hasNext();) {
    833             Object item = it.next();
    834             if (!(item instanceof String)) {
    835                 // the first character of the variable field determines the type,
    836                 // according to CLDR.
    837                 String variableField = item.toString();
    838                 switch (variableField.charAt(0)) {
    839                 case 'y': case 'Y': case 'u':
    840                     result.fields[count++] = DateFieldType.YEAR;
    841                     break;
    842                 case 'M': case 'L':
    843                     result.monthLength = variableField.length();
    844                     if (result.monthLength < 2) {
    845                         result.monthLength = 2;
    846                     }
    847                     result.fields[count++] = DateFieldType.MONTH;
    848                     break;
    849                 case 'd': case 'D': case 'F': case 'g':
    850                     result.fields[count++] = DateFieldType.DAY;
    851                     break;
    852                 }
    853             }
    854         }
    855         return result;
    856     }
    857 
    858     /* Tests the method
    859      *        public static DateTimePatternGenerator getInstance()
    860      */
    861     @Test
    862     public void TestGetInstance(){
    863         try{
    864             DateTimePatternGenerator.getInstance();
    865         } catch(Exception e){
    866             errln("DateTimePatternGenerator.getInstance() was not suppose to " +
    867                     "return an exception.");
    868         }
    869     }
    870 
    871     /* Tests the method
    872      *        public String getSkeleton(String pattern)
    873      */
    874     @Test
    875     public void TestGetSkeleton(){
    876         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
    877         String[] cases = {"MMDD","MMMDD","MMM-DD","DD/MMM","ddM","MMMMd","h","ah","aaaah","Bh"};
    878         String[] results = {"MMDD","MMMDD","MMMDD","MMMDD","Mdd","MMMMd","h","ah","aaaah","Bh"};
    879         for(int i=0; i<cases.length; i++){
    880             if(!dtpg.getSkeleton(cases[i]).equals(results[i])){
    881                 errln("DateTimePatternGenerator.getSkeleton(String) did " +
    882                         "return the expected result when passing " + cases[i] +
    883                         " and expected " + results[i] + " but got " +
    884                         dtpg.getSkeleton(cases[i]));
    885             }
    886         }
    887     }
    888 
    889     /* Tests the method
    890      *        public String getCanonicalSkeletonAllowingDuplicates(String pattern)
    891      */
    892     @Test
    893     public void TestGetCanonicalSkeletonAllowingDuplicates(){
    894         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
    895         String[] cases = {"GyQMwEdaHmsSv","LegH","Legh"};
    896         String[] results = {"GyQMwEdHmsSv","MEdH","MEdh"};
    897         for(int i=0; i<cases.length; i++){
    898             if(!dtpg.getCanonicalSkeletonAllowingDuplicates(cases[i]).equals(results[i])){
    899                 errln("DateTimePatternGenerator.getCanonicalSkeletonAllowingDuplicates(String) did " +
    900                         "return the expected result when passing " + cases[i] +
    901                         " and expected " + results[i] + " but got " +
    902                         dtpg.getCanonicalSkeletonAllowingDuplicates(cases[i]));
    903             }
    904         }
    905     }
    906 
    907     /* Tests the method
    908      *        public String getBaseSkeleton(String pattern)
    909      */
    910     @Test
    911     public void TestGetBaseSkeleton(){
    912         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
    913         String[] cases = {"MMDD","MMMDD","MMM-DD","DD/MMM","ddM","MMMMd"};
    914         String[] results = {"MD","MMMD","MMMD","MMMD","Md","MMMMd"};
    915         for(int i=0; i<cases.length; i++){
    916             if(!dtpg.getBaseSkeleton(cases[i]).equals(results[i])){
    917                 errln("DateTimePatternGenerator.getSkeleton(String) did " +
    918                         "return the expected result when passing " + cases[i] +
    919                         " and expected " + results[i] + " but got " +
    920                         dtpg.getBaseSkeleton(cases[i]));
    921             }
    922         }
    923     }
    924 
    925     /* Tests the method
    926      *        public Map<String, String> getSkeletons(Map<String, String> result)
    927      */
    928     @Test
    929     public void TestGetSkeletons(){
    930         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
    931         // Tests when "if (result == null)" is true
    932         try{
    933             dtpg.getSkeletons(null);
    934         } catch(Exception e){
    935             errln("DateTimePatternGenerator.getSkeletons(Map) was suppose to " +
    936                     "return a new LinkedHashMap for a null parameter.");
    937         }
    938 
    939         // Tests when "if (result == null)" is false
    940         Map<String,String> mm = new LinkedHashMap<String, String>();
    941         try{
    942             dtpg.getSkeletons(mm);
    943         } catch(Exception e){
    944             errln("DateTimePatternGenerator.getSkeletons(Map) was suppose to " +
    945                     "return a new LinkedHashMap for a LinkedHashMap parameter.");
    946         }
    947     }
    948 
    949     /* Tests the method
    950      *        public Set<String> getBaseSkeletons(Set<String> result)
    951      */
    952     @Test
    953     public void TestGetBaseSkeletons(){
    954         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
    955         // Tests when "if (result == null)" is true
    956         try{
    957             dtpg.getBaseSkeletons(null);
    958         } catch(Exception e){
    959             errln("DateTimePatternGenerator.getBaseSkeletons(Map) was suppose to " +
    960                     "return a new LinkedHashMap for a null parameter.");
    961         }
    962 
    963         // Tests when "if (result == null)" is false
    964         Set<String> mm = new HashSet<String>();
    965         try{
    966             dtpg.getBaseSkeletons(mm);
    967         } catch(Exception e){
    968             errln("DateTimePatternGenerator.getBaseSkeletons(Map) was suppose to " +
    969                     "return a new LinkedHashMap for a HashSet parameter.");
    970         }
    971     }
    972 
    973     /* Tests the method
    974      *        public String getDecimal()
    975      */
    976     @Test
    977     public void TestGetDecimal(){
    978         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
    979         if(!dtpg.getDecimal().equals(".")){
    980             errln("DateTimePatternGenerator.getDecimal() was to return '.' " +
    981                     "when the object gets a new instance.");
    982         }
    983 
    984         String[] cases = {",","-","","*","&","a","0"};
    985         for(int i=0; i<cases.length; i++){
    986             dtpg.setDecimal(cases[i]);
    987             if(!dtpg.getDecimal().equals(cases[i])){
    988                 errln("DateTimePatternGenerator.getDecimal() was to return " + cases[i] +
    989                         "when setting decimal with " + cases[i]);
    990             }
    991         }
    992     }
    993 
    994     /* Tests the method
    995      *        public Collection<String> getRedundants(Collection<String> output)
    996      */
    997     @Test
    998     public void TestGetRedundants(){
    999         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
   1000 
   1001         // Tests when "if (output == null)" is true
   1002         try{
   1003             dtpg.getRedundants(null);
   1004         } catch(Exception e){
   1005             errln("DateTimeGenerator.getRedundants was not supposed to return " +
   1006                     "an exception when passing a null parameter: " + e);
   1007         }
   1008 
   1009         // Tests when "if (output == null)" is false
   1010         try{
   1011             Collection<String> out = new LinkedHashSet<String>();
   1012             dtpg.getRedundants(out);
   1013         } catch(Exception e){
   1014             errln("DateTimeGenerator.getRedundants was not supposed to return " +
   1015                     "an exception when passing a new LinkedHashSet<String>() parameter: " + e);
   1016         }
   1017     }
   1018 
   1019     /* Tests the method
   1020      *        public String setAppendItemFormat(int field)
   1021      */
   1022     @Test
   1023     public void TestSetAppendItemFormat(){
   1024         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
   1025         String[] cases = {"d","u","m","m","y"};
   1026         for(int i=0; i<cases.length; i++){
   1027             dtpg.setAppendItemFormat(i, cases[i]);
   1028             if(!dtpg.getAppendItemFormat(i).equals(cases[i])){
   1029                 errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
   1030                         "did not return as expected. Value set at " + i + " was " +
   1031                         cases[i] + " but got back " + dtpg.getAppendItemFormat(i));
   1032             }
   1033         }
   1034     }
   1035 
   1036     /* Tests the method
   1037      *        public String getAppendItemFormat(int field)
   1038      */
   1039     @Test
   1040     public void TestGetAppendItemFormat(){
   1041         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
   1042         int[] fields = {DateTimePatternGenerator.ERA,DateTimePatternGenerator.DAY,DateTimePatternGenerator.SECOND};
   1043         String[] results = {"{0} {1}","{0} ({2}: {1})","{0} ({2}: {1})"};
   1044         for(int i=0; i<fields.length; i++){
   1045             if(!dtpg.getAppendItemFormat(fields[i]).equals(results[i])){
   1046                 errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
   1047                         "did not return as expected. For field " + fields[i] + ", was expecting " +
   1048                         results[i] + " but got back " + dtpg.getAppendItemFormat(fields[i]));
   1049             }
   1050         }
   1051     }
   1052 
   1053     /* Tests the method
   1054      *    public String getAppendItemName(int field)
   1055      */
   1056     private final class AppendItemName {
   1057         public int field;
   1058         public String name;
   1059         public AppendItemName(int f, String n) {
   1060             field = f;
   1061             name = n;
   1062         }
   1063     }
   1064 
   1065     @Test
   1066     public void TestGetAppendItemName(){
   1067         final AppendItemName[] appendItemNames = {
   1068                 new AppendItemName( DateTimePatternGenerator.YEAR,    "vuosi" ),
   1069                 new AppendItemName( DateTimePatternGenerator.MONTH,   "kuukausi" ),
   1070                 new AppendItemName( DateTimePatternGenerator.WEEKDAY, "viikonp\u00E4iv\u00E4" ),
   1071                 new AppendItemName( DateTimePatternGenerator.DAY,     "p\u00E4iv\u00E4" ),
   1072                 new AppendItemName( DateTimePatternGenerator.HOUR,    "tunti" ),
   1073         };
   1074 
   1075         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
   1076         String[] cases = {"d","u","m","m","y"};
   1077         for(int i=0; i<cases.length; i++){
   1078             dtpg.setAppendItemName(i, cases[i]);
   1079             if(!dtpg.getAppendItemName(i).equals(cases[i])){
   1080                 errln("DateTimePatternGenerator.getAppendItemFormat(int field) " +
   1081                         "did not return as expected. Value set at " + i + " was " +
   1082                         cases[i] + " but got back " + dtpg.getAppendItemName(i));
   1083             }
   1084         }
   1085 
   1086         DateTimePatternGenerator dtpgfi = DateTimePatternGenerator.getInstance(ULocale.forLanguageTag("fi"));
   1087         for (AppendItemName appendItemName: appendItemNames) {
   1088             String name = dtpgfi.getAppendItemName(appendItemName.field);
   1089             if (!name.equals(appendItemName.name)) {
   1090                 errln("DateTimePatternGenerator.getAppendItemName returns invalid name for field " + appendItemName.field
   1091                         + ": got " + name + " but expected " + appendItemName.name);
   1092             }
   1093         }
   1094     }
   1095 
   1096     /* Tests the method
   1097      *    public static boolean isSingleField(String skeleton)
   1098      */
   1099     @SuppressWarnings("static-access")
   1100     @Test
   1101     public void TestIsSingleField(){
   1102         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
   1103         String[] cases = {" ", "m","mm","md","mmd","mmdd"};
   1104         boolean[] results = {true,true,true,false,false,false};
   1105         for(int i=0; i<cases.length; i++){
   1106             if(dtpg.isSingleField(cases[i]) != results[i]){
   1107                 errln("DateTimePatternGenerator.isSingleField(String skeleton) " +
   1108                         "did not return as expected. Value passed was " + cases[i] +
   1109                         " but got back " + dtpg.isSingleField(cases[i]));
   1110             }
   1111         }
   1112     }
   1113 
   1114     /* Tests the method
   1115      *    public Object freeze()
   1116      *    public Object cloneAsThawed()
   1117      */
   1118     @Test
   1119     public void TestFreezeAndCloneAsThawed(){
   1120         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
   1121 
   1122         if(dtpg.isFrozen() != false){
   1123             errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
   1124                     "for a DateTimePatternGenerator object that was just " +
   1125                     "created.");
   1126         }
   1127 
   1128         dtpg.freeze();
   1129         if(dtpg.isFrozen() != true){
   1130             errln("DateTimePatternGenerator.isFrozen() is suppose to return true " +
   1131                     "for a DateTimePatternGenerator object that was just " +
   1132                     "created and freeze.");
   1133         }
   1134 
   1135         DateTimePatternGenerator dtpg2 = dtpg.cloneAsThawed();
   1136         if(dtpg.isFrozen() != false){
   1137             errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
   1138                     "for a DateTimePatternGenerator object that was just " +
   1139                     "clone as thawed.");
   1140         }
   1141         if(dtpg2.isFrozen() != false){
   1142             errln("DateTimePatternGenerator.isFrozen() is suppose to return false " +
   1143                     "for a second DateTimePatternGenerator object that was just " +
   1144                     "clone as thawed.");
   1145         }
   1146     }
   1147 
   1148     /* Tests the method
   1149      *    public Object clone()
   1150      */
   1151     @Test
   1152     public void TestClone(){
   1153         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance();
   1154         DateTimePatternGenerator dtpg2 = (DateTimePatternGenerator) dtpg.clone();
   1155         dtpg = (DateTimePatternGenerator) dtpg2.clone();
   1156     }
   1157 
   1158     /* Tests the constructor
   1159      *    public VariableField(String string)
   1160      */
   1161     @Test
   1162     public void TestVariableField_String(){
   1163         String[] cases = {"d","mm","aa"};
   1164         String[] invalid = {null,"","dummy"};
   1165         for(int i=0; i<cases.length; i++){
   1166             try{
   1167                 new VariableField(cases[i]);
   1168             } catch(Exception e){
   1169                 errln("VariableField constructor was not suppose to return " +
   1170                         "an exception when created when passing " + cases[i]);
   1171             }
   1172         }
   1173         for(int i=0; i<invalid.length; i++){
   1174             try{
   1175                 new VariableField(invalid[i]);
   1176                 errln("VariableField constructor was suppose to return " +
   1177                         "an exception when created when passing " + invalid[i]);
   1178             } catch(Exception e){}
   1179         }
   1180     }
   1181 
   1182     /* Tests the method
   1183      *    public FormatParser set(String string, boolean strict)
   1184      */
   1185     @Test
   1186     public void TestSet(){
   1187         FormatParser fp = new FormatParser();
   1188         //Tests when "if (string.length() == 0)" is true
   1189         try{
   1190             fp.set("",true);
   1191         }catch(Exception e){
   1192             errln("FormatParser.set(String,boolean) was not suppose to " +
   1193                     "return an exception.");
   1194         }
   1195     }
   1196 
   1197     /* Tests the method
   1198      *    public String toString()
   1199      */
   1200     @Test
   1201     public void TestToString(){
   1202         FormatParser fp = new FormatParser();
   1203         if(!fp.toString().equals("")){
   1204             errln("FormatParser.toString() was suppose to return an " +
   1205                     "empty string for a new FormatParser object.");
   1206         }
   1207 
   1208         String[] cases = {"m","d","y","mm","mmm","mm dd","mm':'dd","mm-dd-yyyy"};
   1209         String[] results = {"m","d","y","mm","mmm","mm dd","mm:dd","mm-dd-yyyy"};
   1210         for(int i=0; i<cases.length; i++){
   1211             fp.set(cases[i]);
   1212             if(!fp.toString().equals(results[i])){
   1213                 errln("FormatParser.toString() was suppose to return " + results[i] +
   1214                         " after setting the object. Got: " + fp.toString());
   1215             }
   1216         }
   1217     }
   1218 
   1219     /* Tests the method
   1220      *    public boolean hasDateAndTimeFields()
   1221      */
   1222     @Test
   1223     public void TestHasDateAndTimeFields(){
   1224         FormatParser fp = new FormatParser();
   1225         if(fp.hasDateAndTimeFields() != false){
   1226             errln("FormatParser.hasDateAndTimeFields() was suppose to return " +
   1227                     "false when a new object is created.");
   1228         }
   1229 
   1230         String[] cases = {"MMDDYY", "HHMMSS", "", "MM/DD/YYYY HH:MM:SS",
   1231                 "MMDDYY HHMMSS", "HHMMSS MMDDYYYY", "HMS MDY"};
   1232         boolean[] results = {false,true,false,true,true,true,true};
   1233         for(int i=0; i<cases.length; i++){
   1234             fp.set(cases[i]);
   1235             if(fp.hasDateAndTimeFields() != results[i]){
   1236                 errln("FormatParser.hasDateAndTimeFields() was suppose to " +
   1237                         "return " + results[i] + " but returned " +
   1238                         fp.hasDateAndTimeFields() + " for parameter " +
   1239                         cases[i] + " that is set to FormatParser.");
   1240             }
   1241         }
   1242     }
   1243 
   1244     /* Tests the method
   1245      *    private void checkFrozen()
   1246      * from public void setDateTimeFormat(String dateTimeFormat)
   1247      */
   1248     @Test
   1249     public void TestCheckFrozen(){
   1250         // Tests when "if (isFrozen())" is true
   1251         DateTimePatternGenerator dt = DateTimePatternGenerator.getInstance();
   1252         try{
   1253             dt.freeze();
   1254             dt.setDateTimeFormat("MMDDYYYY");
   1255             errln("DateTimePatternGenerator.checkFrozen() was suppose to " +
   1256                     "return an exception when trying to setDateTimeFormat " +
   1257                     "for a frozen object.");
   1258         } catch(Exception e){}
   1259         dt = dt.cloneAsThawed();
   1260     }
   1261 
   1262     /* Tests the method
   1263      *    public String getFields(String pattern)
   1264      */
   1265     @Test
   1266     public void TestGetFields(){
   1267         DateTimePatternGenerator dt = DateTimePatternGenerator.getInstance();
   1268         String[] cases = {"MMDDYY", "HHMMSS", "", "MM/DD/YYYY HH:MM:SS",
   1269                 "MMDDYY HHMMSS", "HHMMSS MMDDYYYY", "HMS MDY"};
   1270         String[] results = {"{Month:N}{Day_Of_Year:N}{Year:N}",
   1271                 "{Hour:N}{Month:N}{Fractional_Second:N}","",
   1272                 "{Month:N}/{Day_Of_Year:N}/{Year:N} {Hour:N}:{Month:N}:{Fractional_Second:N}",
   1273                 "{Month:N}{Day_Of_Year:N}{Year:N} {Hour:N}{Month:N}{Fractional_Second:N}",
   1274                 "{Hour:N}{Month:N}{Fractional_Second:N} {Month:N}{Day_Of_Year:N}{Year:N}",
   1275         "{Hour:N}{Month:N}{Fractional_Second:N} {Month:N}{Day_Of_Year:N}{Year:N}"};
   1276         for(int i=0; i<cases.length; i++){
   1277             if(!dt.getFields(cases[i]).equals(results[i])) {
   1278                 errln("DateTimePatternGenerator.getFields(String) did not " +
   1279                         "not return an expected result when passing " + cases[i] +
   1280                         ". Got " + dt.getFields(cases[i]) + " but expected " +
   1281                         results[i]);
   1282             }
   1283         }
   1284     }
   1285 
   1286     /*
   1287      * Test case for DateFormatPatternGenerator threading problem #7169
   1288      */
   1289     @Test
   1290     public void TestT7169() {
   1291         Thread[] workers = new Thread[10];
   1292         for (int i = 0 ; i < workers.length; i++) {
   1293             workers[i] = new Thread(new Runnable() {
   1294                 @Override
   1295                 public void run() {
   1296                     try {
   1297                         for (int i = 0; i < 50; i++) {
   1298                             DateTimePatternGenerator patternGenerator =
   1299                                     DateTimePatternGenerator.getFrozenInstance(ULocale.US);
   1300                             patternGenerator.getBestPattern("MMMMd");
   1301                         }
   1302                     } catch (Exception e) {
   1303                         errln("FAIL: Caught an exception (frozen)" + e);
   1304                     }
   1305                     try {
   1306                         for (int i = 0; i < 50; i++) {
   1307                             DateTimePatternGenerator patternGenerator =
   1308                                     DateTimePatternGenerator.getInstance(ULocale.US);
   1309                             patternGenerator.getBestPattern("MMMMd");
   1310                         }
   1311                     } catch (Exception e) {
   1312                         errln("FAIL: Caught an exception " + e);
   1313                     }
   1314                 }
   1315             });
   1316         }
   1317         for (Thread wk : workers) {
   1318             wk.start();
   1319         }
   1320         for (Thread wk : workers) {
   1321             try {
   1322                 wk.join();
   1323             } catch (InterruptedException ie) {
   1324 
   1325             }
   1326         }
   1327     }
   1328 
   1329     /**
   1330      * Test handling of options
   1331      *
   1332      * For reference, as of ICU 4.3.3,
   1333      *  root/gregorian has
   1334      *      Hm{"H:mm"}
   1335      *      Hms{"H:mm:ss"}
   1336      *      hm{"h:mm a"}
   1337      *      hms{"h:mm:ss a"}
   1338      *  en/gregorian has
   1339      *      Hm{"H:mm"}
   1340      *      Hms{"H:mm:ss"}
   1341      *      hm{"h:mm a"}
   1342      *  be/gregorian has
   1343      *      HHmmss{"HH.mm.ss"}
   1344      *      Hm{"HH.mm"}
   1345      *      hm{"h.mm a"}
   1346      *      hms{"h.mm.ss a"}
   1347      */
   1348     private final class TestOptionsItem {
   1349         public String locale;
   1350         public String skeleton;
   1351         public String expectedPattern;
   1352         public int options;
   1353         // Simple constructor
   1354         public TestOptionsItem(String loc, String skel, String expectedPat, int opts) {
   1355             locale = loc;
   1356             skeleton = skel;
   1357             expectedPattern = expectedPat;
   1358             options = opts;
   1359         }
   1360     }
   1361     @Test
   1362     public void TestOptions() {
   1363         final TestOptionsItem[] testOptionsData = {
   1364                 new TestOptionsItem( "en", "Hmm",  "HH:mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
   1365                 new TestOptionsItem( "en", "HHmm", "HH:mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
   1366                 new TestOptionsItem( "en", "hhmm", "h:mm a",  DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
   1367                 new TestOptionsItem( "en", "Hmm",  "HH:mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
   1368                 new TestOptionsItem( "en", "HHmm", "HH:mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
   1369                 new TestOptionsItem( "en", "hhmm", "hh:mm a", DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
   1370                 new TestOptionsItem( "da", "Hmm",  "HH.mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
   1371                 new TestOptionsItem( "da", "HHmm", "HH.mm",   DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
   1372                 new TestOptionsItem( "da", "hhmm", "h.mm a",  DateTimePatternGenerator.MATCH_NO_OPTIONS        ),
   1373                 new TestOptionsItem( "da", "Hmm",  "H.mm",    DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
   1374                 new TestOptionsItem( "da", "HHmm", "HH.mm",   DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
   1375                 new TestOptionsItem( "da", "hhmm", "hh.mm a", DateTimePatternGenerator.MATCH_HOUR_FIELD_LENGTH ),
   1376                 //
   1377                 new TestOptionsItem( "en",                   "yyyy",  "yyyy",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1378                 new TestOptionsItem( "en",                   "YYYY",  "YYYY",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1379                 new TestOptionsItem( "en",                   "U",     "y",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1380                 new TestOptionsItem( "en@calendar=japanese", "yyyy",  "y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1381                 new TestOptionsItem( "en@calendar=japanese", "YYYY",  "Y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1382                 new TestOptionsItem( "en@calendar=japanese", "U",     "y G",   DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1383                 new TestOptionsItem( "en@calendar=chinese",  "yyyy",  "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1384                 new TestOptionsItem( "en@calendar=chinese",  "YYYY",  "Y(Y)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ), // not a good result, want r(Y) or r(U)
   1385                 new TestOptionsItem( "en@calendar=chinese",  "U",     "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1386                 new TestOptionsItem( "en@calendar=chinese",  "Gy",    "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1387                 new TestOptionsItem( "en@calendar=chinese",  "GU",    "r(U)",     DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1388                 new TestOptionsItem( "en@calendar=chinese",  "ULLL",  "MMM U",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1389                 new TestOptionsItem( "en@calendar=chinese",  "yMMM",  "MMM r(U)", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1390                 new TestOptionsItem( "en@calendar=chinese",  "GUMMM", "MMM r(U)", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1391                 new TestOptionsItem( "zh@calendar=chinese",  "yyyy",  "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1392                 new TestOptionsItem( "zh@calendar=chinese",  "YYYY",  "YY\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ), // not a good result, want r(Y) or r(U)
   1393                 new TestOptionsItem( "zh@calendar=chinese",  "U",     "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1394                 new TestOptionsItem( "zh@calendar=chinese",  "Gy",    "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1395                 new TestOptionsItem( "zh@calendar=chinese",  "GU",    "rU\u5E74",    DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1396                 new TestOptionsItem( "zh@calendar=chinese",  "ULLL",  "U\u5E74MMM",  DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1397                 new TestOptionsItem( "zh@calendar=chinese",  "yMMM",  "rU\u5E74MMM", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1398                 new TestOptionsItem( "zh@calendar=chinese",  "GUMMM", "rU\u5E74MMM", DateTimePatternGenerator.MATCH_NO_OPTIONS ),
   1399         };
   1400 
   1401         for (int i = 0; i < testOptionsData.length; ++i) {
   1402             ULocale uloc = new ULocale(testOptionsData[i].locale);
   1403             DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(uloc);
   1404             String pattern = dtpgen.getBestPattern(testOptionsData[i].skeleton, testOptionsData[i].options);
   1405             if (pattern.compareTo(testOptionsData[i].expectedPattern) != 0) {
   1406                 errln("Locale " + testOptionsData[i].locale + ", skeleton " + testOptionsData[i].skeleton +
   1407                         ", options " + ((testOptionsData[i].options != 0)? "!=0": "==0") +
   1408                         ", expected pattern " + testOptionsData[i].expectedPattern + ", got " + pattern);
   1409             }
   1410         }
   1411     }
   1412 
   1413     /**
   1414      * Test that DTPG can handle all valid pattern character / length combinations
   1415      */
   1416     private final class AllFieldsTestItem {
   1417         public char patternChar;
   1418         public int[] fieldLengths;
   1419         public String mustIncludeOneOf;
   1420         // Simple constructor
   1421         public AllFieldsTestItem(char pC, int[] fL, String mI) {
   1422             patternChar = pC;
   1423             fieldLengths = fL;
   1424             mustIncludeOneOf = mI;
   1425         }
   1426     }
   1427 
   1428     @Test
   1429     public void TestAllFieldPatterns() {
   1430         String[] localeNames = {
   1431                 "root",
   1432                 "root@calendar=japanese",
   1433                 "root@calendar=chinese",
   1434                 "en",
   1435                 "en@calendar=japanese",
   1436                 "en@calendar=chinese",
   1437         };
   1438         final AllFieldsTestItem[] testItems = {
   1439                 //                     pat   fieldLengths             generated pattern must
   1440                 //                     chr   to test                  include one of these
   1441                 new AllFieldsTestItem( 'G',  new int[]{1,2,3,4,5},    "G"    ), // era
   1442                 // year
   1443                 new AllFieldsTestItem( 'y',  new int[]{1,2,3,4},      "yU"   ), // year
   1444                 new AllFieldsTestItem( 'Y',  new int[]{1,2,3,4},      "Y"    ), // year for week of year
   1445                 new AllFieldsTestItem( 'u',  new int[]{1,2,3,4,5},    "yuU"  ), // extended year
   1446                 new AllFieldsTestItem( 'U',  new int[]{1,2,3,4,5},    "yU"   ), // cyclic year name
   1447                 // quarter
   1448                 new AllFieldsTestItem( 'Q',  new int[]{1,2,3,4},      "Qq"   ), // x
   1449                 new AllFieldsTestItem( 'q',  new int[]{1,2,3,4},      "Qq"   ), // standalone
   1450                 // month
   1451                 new AllFieldsTestItem( 'M',  new int[]{1,2,3,4,5},    "ML"   ), // x
   1452                 new AllFieldsTestItem( 'L',  new int[]{1,2,3,4,5},    "ML"   ), // standalone
   1453                 // week
   1454                 new AllFieldsTestItem( 'w',  new int[]{1,2},          "w"    ), // week of year
   1455                 new AllFieldsTestItem( 'W',  new int[]{1},            "W"    ), // week of month
   1456                 // day
   1457                 new AllFieldsTestItem( 'd',  new int[]{1,2},          "d"    ), // day of month
   1458                 new AllFieldsTestItem( 'D',  new int[]{1,2,3},        "D"    ), // day of year
   1459                 new AllFieldsTestItem( 'F',  new int[]{1},            "F"    ), // day of week in month
   1460                 new AllFieldsTestItem( 'g',  new int[]{7},            "g"    ), // modified julian day
   1461                 // weekday
   1462                 new AllFieldsTestItem( 'E',  new int[]{1,2,3,4,5,6},  "Eec"  ), // day of week
   1463                 new AllFieldsTestItem( 'e',  new int[]{1,2,3,4,5,6},  "Eec"  ), // local day of week
   1464                 new AllFieldsTestItem( 'c',  new int[]{1,2,3,4,5,6},  "Eec"  ), // standalone local day of week
   1465                 // day period
   1466                 //  new AllFieldsTestItem( 'a',  new int[]{1},            "a"    ), // am or pm   // not clear this one is supposed to work (it doesn't)
   1467                 // hour
   1468                 new AllFieldsTestItem( 'h',  new int[]{1,2},          "hK"   ), // 12 (1-12)
   1469                 new AllFieldsTestItem( 'H',  new int[]{1,2},          "Hk"   ), // 24 (0-23)
   1470                 new AllFieldsTestItem( 'K',  new int[]{1,2},          "hK"   ), // 12 (0-11)
   1471                 new AllFieldsTestItem( 'k',  new int[]{1,2},          "Hk"   ), // 24 (1-24)
   1472                 new AllFieldsTestItem( 'j',  new int[]{1,2},          "hHKk" ), // locale default
   1473                 // minute
   1474                 new AllFieldsTestItem( 'm',  new int[]{1,2},          "m"    ), // x
   1475                 // second & fractions
   1476                 new AllFieldsTestItem( 's',  new int[]{1,2},          "s"    ), // x
   1477                 new AllFieldsTestItem( 'S',  new int[]{1,2,3,4},      "S"    ), // fractional second
   1478                 new AllFieldsTestItem( 'A',  new int[]{8},            "A"    ), // milliseconds in day
   1479                 // zone
   1480                 new AllFieldsTestItem( 'z',  new int[]{1,2,3,4},      "z"    ), // x
   1481                 new AllFieldsTestItem( 'Z',  new int[]{1,2,3,4,5},    "Z"    ), // x
   1482                 new AllFieldsTestItem( 'O',  new int[]{1,4},          "O"    ), // x
   1483                 new AllFieldsTestItem( 'v',  new int[]{1,4},          "v"    ), // x
   1484                 new AllFieldsTestItem( 'V',  new int[]{1,2,3,4},      "V"    ), // x
   1485                 new AllFieldsTestItem( 'X',  new int[]{1,2,3,4,5},    "X"    ), // x
   1486                 new AllFieldsTestItem( 'x',  new int[]{1,2,3,4,5},    "x"    ), // x
   1487         };
   1488         final int FIELD_LENGTH_MAX = 8;
   1489 
   1490         for (String localeName: localeNames) {
   1491             ULocale uloc = new ULocale(localeName);
   1492             DateTimePatternGenerator dtpgen = DateTimePatternGenerator.getInstance(uloc);
   1493             for (AllFieldsTestItem testItem: testItems) {
   1494                 char[] skelBuf = new char[FIELD_LENGTH_MAX];
   1495                 for (int chrIndx = 0; chrIndx < FIELD_LENGTH_MAX; chrIndx++) {
   1496                     skelBuf[chrIndx] = testItem.patternChar;
   1497                 }
   1498                 for (int lenIndx = 0; lenIndx < testItem.fieldLengths.length; lenIndx++) {
   1499                     int skelLen = testItem.fieldLengths[lenIndx];
   1500                     if (skelLen > FIELD_LENGTH_MAX) {
   1501                         continue;
   1502                     };
   1503                     String skeleton = new String(skelBuf, 0, skelLen);
   1504                     String pattern = dtpgen.getBestPattern(skeleton);
   1505                     if (pattern.length() <= 0) {
   1506                         errln("DateTimePatternGenerator getBestPattern for locale " + localeName +
   1507                                 ", skeleton " + skeleton + ", produces 0-length pattern");
   1508                     } else {
   1509                         // test that resulting pattern has at least one char in mustIncludeOneOf
   1510                         boolean inQuoted = false;
   1511                         int patIndx, patLen = pattern.length();
   1512                         for (patIndx = 0; patIndx < patLen; patIndx++) {
   1513                             char c = pattern.charAt(patIndx);
   1514                             if (c == '\'') {
   1515                                 inQuoted = !inQuoted;
   1516                             } else if (!inQuoted && c <= 'z' && c >= 'A') {
   1517                                 if (testItem.mustIncludeOneOf.indexOf(c) >= 0) {
   1518                                     break;
   1519                                 }
   1520                             }
   1521                         }
   1522                         if (patIndx >= patLen) {
   1523                             errln("DateTimePatternGenerator getBestPattern for locale " + localeName +
   1524                                     ", skeleton " + skeleton +
   1525                                     ", produces pattern without required chars: " + pattern);
   1526                         }
   1527                     }
   1528                 }
   1529             }
   1530         }
   1531     }
   1532 
   1533     @Test
   1534     public void TestJavaLocale() {
   1535         DateTimePatternGenerator genUloc = DateTimePatternGenerator.getInstance(ULocale.GERMANY);
   1536         DateTimePatternGenerator genLoc = DateTimePatternGenerator.getInstance(Locale.GERMANY);
   1537 
   1538         final String pat = "yMdHms";
   1539         String patUloc = genUloc.getBestPattern(pat);
   1540         String patLoc = genLoc.getBestPattern(pat);
   1541 
   1542         assertEquals("German pattern 'yMdHms' - getInstance with Java Locale", patUloc, patLoc);
   1543     }
   1544 
   1545     /* Tests the method
   1546      *    public static int getAppendFormatNumber(String string)
   1547      */
   1548     @Test
   1549     public void TestGetAppendFormatNumber(){
   1550         int fieldNum;
   1551         fieldNum = DateTimePatternGenerator.getAppendFormatNumber("Era");
   1552         assertEquals("DateTimePatternGenerator.getAppendFormatNumber for Era", 0, fieldNum);
   1553         fieldNum = DateTimePatternGenerator.getAppendFormatNumber("Timezone");
   1554         assertEquals("DateTimePatternGenerator.getAppendFormatNumber for Timezone", 15, fieldNum);
   1555     }
   1556 
   1557     /*
   1558      * Coverage for methods otherwise not covered by other tests.
   1559      */
   1560     @Test
   1561     public void TestCoverage() {
   1562         DateTimePatternGenerator dtpg;
   1563 
   1564         // DateTimePatternGenerator#getDefaultHourFormatChar
   1565         // DateTimePatternGenerator#setDefaultHourFormatChar
   1566         {
   1567             dtpg = DateTimePatternGenerator.getEmptyInstance();
   1568             assertEquals("Default hour char on empty instance", 'H', dtpg.getDefaultHourFormatChar());
   1569             dtpg.setDefaultHourFormatChar('e');
   1570             assertEquals("Default hour char after explicit set", 'e', dtpg.getDefaultHourFormatChar());
   1571             dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
   1572             assertEquals("Default hour char on populated English instance", 'h', dtpg.getDefaultHourFormatChar());
   1573         }
   1574 
   1575         // DateTimePatternGenerator#getSkeletonAllowingDuplicates
   1576         // DateTimePatternGenerator#getCanonicalSkeletonAllowingDuplicates
   1577         // DateTimePatternGenerator#getCanonicalChar
   1578         {
   1579             dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
   1580             assertEquals("Example skeleton with no duplicate fields", "MMMdd", dtpg.getSkeleton("dd/MMM"));
   1581             assertEquals("Should return same result as getSkeleton with no duplicate fields",
   1582                     dtpg.getSkeleton("dd/MMM"), dtpg.getSkeletonAllowingDuplicates("dd/MMM"));
   1583 
   1584             try {
   1585                 dtpg.getSkeleton("dd/MMM Zz");
   1586                 fail("getSkeleton should throw upon duplicate fields");
   1587             } catch(IllegalArgumentException e) {
   1588                 assertEquals("getSkeleton should throw upon duplicate fields",
   1589                         "Conflicting fields:\tZ, z\t in dd/MMM Zz", e.getMessage());
   1590             }
   1591 
   1592             assertEquals("Should not throw upon duplicate fields",
   1593                     "MMMddZ", dtpg.getSkeletonAllowingDuplicates("dd/MMM Zz"));
   1594             assertEquals("Should not throw upon duplicate fields and should return Canonical fields",
   1595                     "MMMddv", dtpg.getCanonicalSkeletonAllowingDuplicates("dd/MMM Zz"));
   1596         }
   1597 
   1598         // DistanceInfo#toString
   1599         // DateTimePatternGenerator#showMask
   1600         try {
   1601             String actual = invokeToString("com.ibm.icu.text.DateTimePatternGenerator$DistanceInfo");
   1602             assertEquals("DistanceInfo toString", "missingFieldMask: , extraFieldMask: ", actual);
   1603         } catch(Exception e) {
   1604             errln("Couldn't call DistanceInfo.toString(): " + e.toString());
   1605         }
   1606 
   1607         // DateTimePatternGenerator#skeletonsAreSimilar
   1608         // DateTimePatternGenerator#getSet
   1609         {
   1610             dtpg = DateTimePatternGenerator.getInstance(ULocale.ENGLISH);
   1611             assertTrue("Trivial skeletonsAreSimilar", dtpg.skeletonsAreSimilar("MMMdd", "MMMdd"));
   1612             assertTrue("Different number of chars in skeletonsAreSimilar", dtpg.skeletonsAreSimilar("Mddd", "MMMdd"));
   1613             assertFalse("Failure case for skeletonsAreSimilar", dtpg.skeletonsAreSimilar("mmDD", "MMMdd"));
   1614         }
   1615     }
   1616 
   1617     @Test
   1618     public void TestEmptyInstance() {
   1619         DateTimePatternGenerator dtpg = DateTimePatternGenerator.getEmptyInstance();
   1620         String skeleton = "GrMMd";
   1621         String message = "DTPG getEmptyInstance should not throw exceptions on basic operations and should conform to "
   1622                 + "the example in setAppendItemFormat";
   1623         assertEquals(message, "G 'F7': d 'F3': MM 'F1': y", dtpg.getBestPattern(skeleton));
   1624         dtpg.addPattern("d-MM-yyyy", false, new DateTimePatternGenerator.PatternInfo());
   1625         assertEquals(message, "d-MM-y 'F0': G", dtpg.getBestPattern(skeleton));
   1626         dtpg.setAppendItemFormat(DateTimePatternGenerator.ERA, "{0}, {1}");
   1627         assertEquals(message, "d-MM-y, G", dtpg.getBestPattern(skeleton));
   1628     }
   1629 }
   1630