Home | History | Annotate | Download | only in text
      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) 1996-2016, International Business Machines Corporation and
      6  * others. All Rights Reserved.
      7  *******************************************************************************
      8  */
      9 
     10 package com.ibm.icu.text;
     11 
     12 import java.io.IOException;
     13 import java.io.ObjectInputStream;
     14 import java.io.Serializable;
     15 import java.util.ArrayList;
     16 import java.util.HashMap;
     17 import java.util.HashSet;
     18 import java.util.List;
     19 import java.util.Locale;
     20 import java.util.Map;
     21 import java.util.MissingResourceException;
     22 import java.util.ResourceBundle;
     23 import java.util.Set;
     24 import java.util.TreeMap;
     25 
     26 import com.ibm.icu.impl.CacheBase;
     27 import com.ibm.icu.impl.CalendarUtil;
     28 import com.ibm.icu.impl.ICUData;
     29 import com.ibm.icu.impl.ICUResourceBundle;
     30 import com.ibm.icu.impl.SoftCache;
     31 import com.ibm.icu.impl.UResource;
     32 import com.ibm.icu.impl.Utility;
     33 import com.ibm.icu.text.TimeZoneNames.NameType;
     34 import com.ibm.icu.util.Calendar;
     35 import com.ibm.icu.util.ICUCloneNotSupportedException;
     36 import com.ibm.icu.util.ICUException;
     37 import com.ibm.icu.util.TimeZone;
     38 import com.ibm.icu.util.ULocale;
     39 import com.ibm.icu.util.ULocale.Category;
     40 import com.ibm.icu.util.UResourceBundle;
     41 import com.ibm.icu.util.UResourceBundleIterator;
     42 
     43 /**
     44  * {@icuenhanced java.text.DateFormatSymbols}.{@icu _usage_}
     45  *
     46  * <p><code>DateFormatSymbols</code> is a public class for encapsulating
     47  * localizable date-time formatting data, such as the names of the
     48  * months, the names of the days of the week, and the time zone data.
     49  * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
     50  * <code>DateFormatSymbols</code> to encapsulate this information.
     51  *
     52  * <p>Typically you shouldn't use <code>DateFormatSymbols</code> directly.
     53  * Rather, you are encouraged to create a date-time formatter with the
     54  * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
     55  * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
     56  * These methods automatically create a <code>DateFormatSymbols</code> for
     57  * the formatter so that you don't have to. After the
     58  * formatter is created, you may modify its format pattern using the
     59  * <code>setPattern</code> method. For more information about
     60  * creating formatters using <code>DateFormat</code>'s factory methods,
     61  * see {@link DateFormat}.
     62  *
     63  * <p>If you decide to create a date-time formatter with a specific
     64  * format pattern for a specific locale, you can do so with:
     65  * <blockquote>
     66  * <pre>
     67  * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)).
     68  * </pre>
     69  * </blockquote>
     70  *
     71  * <p><code>DateFormatSymbols</code> objects are clonable. When you obtain
     72  * a <code>DateFormatSymbols</code> object, feel free to modify the
     73  * date-time formatting data. For instance, you can replace the localized
     74  * date-time format pattern characters with the ones that you feel easy
     75  * to remember. Or you can change the representative cities
     76  * to your favorite ones.
     77  *
     78  * <p>New <code>DateFormatSymbols</code> subclasses may be added to support
     79  * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
     80  *
     81  * @see          DateFormat
     82  * @see          SimpleDateFormat
     83  * @see          com.ibm.icu.util.SimpleTimeZone
     84  * @author       Chen-Lieh Huang
     85  * @stable ICU 2.0
     86  */
     87 public class DateFormatSymbols implements Serializable, Cloneable {
     88 
     89     // TODO make sure local pattern char string is 18 characters long,
     90     // that is, that it encompasses the new 'u' char for
     91     // EXTENDED_YEAR.  Two options: 1. Make sure resource data is
     92     // correct; 2. Make code add in 'u' at end if len == 17.
     93 
     94     // Constants for context
     95     /**
     96      * {@icu} Constant for context.
     97      * @stable ICU 3.6
     98      */
     99     public static final int FORMAT = 0;
    100 
    101     /**
    102      * {@icu} Constant for context.
    103      * @stable ICU 3.6
    104      */
    105     public static final int STANDALONE = 1;
    106 
    107     /**
    108      * {@icu} Constant for context. NUMERIC context
    109      * is only supported for leapMonthPatterns.
    110      * @internal
    111      * @deprecated This API is ICU internal only.
    112      */
    113     @Deprecated
    114     public static final int NUMERIC = 2;
    115 
    116     /**
    117      * {@icu} Constant for context.
    118      * @internal
    119      * @deprecated This API is ICU internal only.
    120      */
    121     @Deprecated
    122     public static final int DT_CONTEXT_COUNT = 3;
    123 
    124     // Constants for width
    125 
    126     /**
    127      * {@icu} Constant for width.
    128      * @stable ICU 3.6
    129      */
    130     public static final int ABBREVIATED = 0;
    131 
    132     /**
    133      * {@icu} Constant for width.
    134      * @stable ICU 3.6
    135      */
    136     public static final int WIDE = 1;
    137 
    138     /**
    139      * {@icu} Constant for width.
    140      * @stable ICU 3.6
    141      */
    142     public static final int NARROW = 2;
    143 
    144     /**
    145      * {@icu} Constant for width; only supported for weekdays.
    146      * @stable ICU 51
    147      */
    148     public static final int SHORT = 3;
    149 
    150     /**
    151      * {@icu} Constant for width.
    152      * @internal
    153      * @deprecated This API is ICU internal only.
    154      */
    155     @Deprecated
    156     public static final int DT_WIDTH_COUNT = 4;
    157 
    158     /**
    159      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    160      * @internal
    161      */
    162     static final int DT_LEAP_MONTH_PATTERN_FORMAT_WIDE = 0;
    163 
    164     /**
    165      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    166      * @internal
    167      */
    168     static final int DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV = 1;
    169 
    170     /**
    171      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    172      * @internal
    173      */
    174     static final int DT_LEAP_MONTH_PATTERN_FORMAT_NARROW = 2;
    175 
    176     /**
    177      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    178      * @internal
    179      */
    180     static final int DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE = 3;
    181 
    182     /**
    183      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    184      * @internal
    185      */
    186     static final int DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV = 4;
    187 
    188     /**
    189      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    190      * @internal
    191      */
    192     static final int DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW = 5;
    193 
    194     /**
    195      * {@icu} Somewhat temporary constant for leap month pattern type, adequate for Chinese calendar.
    196      * @internal
    197      */
    198     static final int DT_LEAP_MONTH_PATTERN_NUMERIC = 6;
    199 
    200     /**
    201      * {@icu} Somewhat temporary constant for month pattern count, adequate for Chinese calendar.
    202      * @internal
    203      */
    204     static final int DT_MONTH_PATTERN_COUNT = 7;
    205 
    206     /**
    207      * {@icu} This default time separator is used for formatting when the locale
    208      * doesn't specify any time separator, and always recognized when parsing.
    209      * @internal
    210      */
    211     static final String DEFAULT_TIME_SEPARATOR = ":";
    212 
    213     /**
    214      * {@icu} This alternate time separator is always recognized when parsing.
    215      * @internal
    216      */
    217     static final String ALTERNATE_TIME_SEPARATOR = ".";
    218 
    219    /**
    220      * Constructs a DateFormatSymbols object by loading format data from
    221      * resources for the default <code>FORMAT</code> locale.
    222      *
    223      * @throws java.util.MissingResourceException if the resources for the default locale
    224      *          cannot be found or cannot be loaded.
    225      * @see Category#FORMAT
    226      * @stable ICU 2.0
    227      */
    228     public DateFormatSymbols()
    229     {
    230         this(ULocale.getDefault(Category.FORMAT));
    231     }
    232 
    233     /**
    234      * Constructs a DateFormatSymbols object by loading format data from
    235      * resources for the given locale.
    236      *
    237      * @throws java.util.MissingResourceException if the resources for the specified
    238      *          locale cannot be found or cannot be loaded.
    239      * @stable ICU 2.0
    240      */
    241     public DateFormatSymbols(Locale locale)
    242     {
    243         this(ULocale.forLocale(locale));
    244     }
    245 
    246     /**
    247      * {@icu} Constructs a DateFormatSymbols object by loading format data from
    248      * resources for the given ulocale.
    249      *
    250      * @throws java.util.MissingResourceException if the resources for the specified
    251      *          locale cannot be found or cannot be loaded.
    252      * @stable ICU 3.2
    253      */
    254     public DateFormatSymbols(ULocale locale)
    255     {
    256         initializeData(locale, CalendarUtil.getCalendarType(locale));
    257     }
    258 
    259     /**
    260      * Returns a DateFormatSymbols instance for the default locale.
    261      *
    262      * {@icunote} Unlike <code>java.text.DateFormatSymbols#getInstance</code>,
    263      * this method simply returns <code>new com.ibm.icu.text.DateFormatSymbols()</code>.
    264      * ICU does not support <code>DateFormatSymbolsProvider</code> introduced in Java 6
    265      * or its equivalent implementation for now.
    266      *
    267      * @return A DateFormatSymbols instance.
    268      * @stable ICU 3.8
    269      */
    270     public static DateFormatSymbols getInstance() {
    271         return new DateFormatSymbols();
    272     }
    273 
    274     /**
    275      * Returns a DateFormatSymbols instance for the given locale.
    276      *
    277      * {@icunote} Unlike <code>java.text.DateFormatSymbols#getInstance</code>,
    278      * this method simply returns <code>new com.ibm.icu.text.DateFormatSymbols(locale)</code>.
    279      * ICU does not support <code>DateFormatSymbolsProvider</code> introduced in Java 6
    280      * or its equivalent implementation for now.
    281      *
    282      * @param locale the locale.
    283      * @return A DateFormatSymbols instance.
    284      * @stable ICU 3.8
    285      */
    286     public static DateFormatSymbols getInstance(Locale locale) {
    287         return new DateFormatSymbols(locale);
    288     }
    289 
    290     /**
    291      * {@icu} Returns a DateFormatSymbols instance for the given locale.
    292      *
    293      * {@icunote} Unlike <code>java.text.DateFormatSymbols#getInstance</code>,
    294      * this method simply returns <code>new com.ibm.icu.text.DateFormatSymbols(locale)</code>.
    295      * ICU does not support <code>DateFormatSymbolsProvider</code> introduced in Java 6
    296      * or its equivalent implementation for now.
    297      *
    298      * @param locale the locale.
    299      * @return A DateFormatSymbols instance.
    300      * @stable ICU 3.8
    301      */
    302     public static DateFormatSymbols getInstance(ULocale locale) {
    303         return new DateFormatSymbols(locale);
    304     }
    305 
    306     /**
    307      * Returns an array of all locales for which the <code>getInstance</code> methods of
    308      * this class can return localized instances.
    309      *
    310      * {@icunote} Unlike <code>java.text.DateFormatSymbols#getAvailableLocales</code>,
    311      * this method simply returns the array of <code>Locale</code>s available in this
    312      * class.  ICU does not support <code>DateFormatSymbolsProvider</code> introduced in
    313      * Java 6 or its equivalent implementation for now.
    314      *
    315      * @return An array of <code>Locale</code>s for which localized
    316      * <code>DateFormatSymbols</code> instances are available.
    317      * @stable ICU 3.8
    318      */
    319     public static Locale[] getAvailableLocales() {
    320         return ICUResourceBundle.getAvailableLocales();
    321     }
    322 
    323     /**
    324      * {@icu} Returns an array of all locales for which the <code>getInstance</code>
    325      * methods of this class can return localized instances.
    326      *
    327      * {@icunote} Unlike <code>java.text.DateFormatSymbols#getAvailableLocales</code>,
    328      * this method simply returns the array of <code>ULocale</code>s available in this
    329      * class.  ICU does not support <code>DateFormatSymbolsProvider</code> introduced in
    330      * Java 6 or its equivalent implementation for now.
    331      *
    332      * @return An array of <code>ULocale</code>s for which localized
    333      * <code>DateFormatSymbols</code> instances are available.
    334      * @draft ICU 3.8 (retain)
    335      * @provisional This API might change or be removed in a future release.
    336      */
    337     public static ULocale[] getAvailableULocales() {
    338         return ICUResourceBundle.getAvailableULocales();
    339     }
    340 
    341     /**
    342      * Era strings. For example: "AD" and "BC".  An array of 2 strings,
    343      * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
    344      * @serial
    345      */
    346     String eras[] = null;
    347 
    348     /**
    349      * Era name strings. For example: "Anno Domini" and "Before Christ".  An array of 2 strings,
    350      * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
    351      * @serial
    352      */
    353     String eraNames[] = null;
    354 
    355     /**
    356      * Narrow era names. For example: "A" and "B". An array of 2 strings,
    357      * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
    358      * @serial
    359      */
    360     String narrowEras[] = null;
    361 
    362     /**
    363      * Month strings. For example: "January", "February", etc.  An array
    364      * of 13 strings (some calendars have 13 months), indexed by
    365      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
    366      * @serial
    367      */
    368     String months[] = null;
    369 
    370     /**
    371      * Short month strings. For example: "Jan", "Feb", etc.  An array of
    372      * 13 strings (some calendars have 13 months), indexed by
    373      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
    374 
    375      * @serial
    376      */
    377     String shortMonths[] = null;
    378 
    379     /**
    380      * Narrow month strings. For example: "J", "F", etc.  An array of
    381      * 13 strings (some calendars have 13 months), indexed by
    382      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
    383 
    384      * @serial
    385      */
    386     String narrowMonths[] = null;
    387 
    388     /**
    389      * Standalone month strings. For example: "January", "February", etc.  An array
    390      * of 13 strings (some calendars have 13 months), indexed by
    391      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
    392      * @serial
    393      */
    394     String standaloneMonths[] = null;
    395 
    396     /**
    397      * Standalone short month strings. For example: "Jan", "Feb", etc.  An array of
    398      * 13 strings (some calendars have 13 months), indexed by
    399      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
    400 
    401      * @serial
    402      */
    403     String standaloneShortMonths[] = null;
    404 
    405     /**
    406      * Standalone narrow month strings. For example: "J", "F", etc.  An array of
    407      * 13 strings (some calendars have 13 months), indexed by
    408      * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
    409 
    410      * @serial
    411      */
    412     String standaloneNarrowMonths[] = null;
    413 
    414     /**
    415      * Format wide weekday strings, for example: "Sunday", "Monday", etc.
    416      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    417      * <code>Calendar.MONDAY</code>, etc.
    418      * The element <code>weekdays[0]</code> is ignored.
    419      * @serial
    420      */
    421     String weekdays[] = null;
    422 
    423     /**
    424      * CLDR-style format abbreviated (not short) weekday strings,
    425      * for example: "Sun", "Mon", etc.
    426      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    427      * <code>Calendar.MONDAY</code>, etc.
    428      * The element <code>shortWeekdays[0]</code> is ignored.
    429      * @serial
    430      */
    431     String shortWeekdays[] = null;
    432 
    433     /**
    434      * CLDR-style format short weekday strings, for example: "Su", "Mo", etc.
    435      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    436      * <code>Calendar.MONDAY</code>, etc.
    437      * The element <code>shorterWeekdays[0]</code> is ignored.
    438      * @serial
    439      */
    440    // Note, serialization restore from pre-ICU-51 will leave this null.
    441     String shorterWeekdays[] = null;
    442 
    443     /**
    444      * CLDR-style format narrow weekday strings, for example: "S", "M", etc.
    445      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    446      * <code>Calendar.MONDAY</code>, etc.
    447      * The element <code>narrowWeekdays[0]</code> is ignored.
    448      * @serial
    449      */
    450     String narrowWeekdays[] = null;
    451 
    452     /**
    453      * Standalone wide weekday strings. For example: "Sunday", "Monday", etc.
    454      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    455      * <code>Calendar.MONDAY</code>, etc.
    456      * The element <code>standaloneWeekdays[0]</code> is ignored.
    457      * @serial
    458      */
    459     String standaloneWeekdays[] = null;
    460 
    461     /**
    462      * CLDR-style standalone abbreviated (not short) weekday strings,
    463      * for example: "Sun", "Mon", etc.
    464      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    465      * <code>Calendar.MONDAY</code>, etc.
    466      * The element <code>standaloneShortWeekdays[0]</code> is ignored.
    467      * @serial
    468      */
    469     String standaloneShortWeekdays[] = null;
    470 
    471     /**
    472      * CLDR-style standalone short weekday strings, for example: "Sun", "Mon", etc.
    473      * An array of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    474      * <code>Calendar.MONDAY</code>, etc.
    475      * The element <code>standaloneShorterWeekdays[0]</code> is ignored.
    476      * @serial
    477      */
    478     // Note, serialization restore from pre-ICU-51 will leave this null.
    479     String standaloneShorterWeekdays[] = null;
    480 
    481     /**
    482      * Standalone narrow weekday strings. For example: "S", "M", etc.  An array
    483      * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
    484      * <code>Calendar.MONDAY</code>, etc.
    485      * The element <code>standaloneNarrowWeekdays[0]</code> is ignored.
    486      * @serial
    487      */
    488     String standaloneNarrowWeekdays[] = null;
    489 
    490     /**
    491      * AM and PM strings. For example: "AM" and "PM".  An array of
    492      * 2 strings, indexed by <code>Calendar.AM</code> and
    493      * <code>Calendar.PM</code>.
    494      * @serial
    495      */
    496     String ampms[] = null;
    497 
    498     /**
    499      * narrow AM and PM strings. For example: "a" and "p".  An array of
    500      * 2 strings, indexed by <code>Calendar.AM</code> and
    501      * <code>Calendar.PM</code>.
    502      * @serial
    503      */
    504     String ampmsNarrow[] = null;
    505 
    506     /**
    507      * Time separator string. For example: ":".
    508      * @serial
    509      */
    510     private String timeSeparator = null;
    511 
    512     /**
    513      * Abbreviated quarter names. For example: "Q1", "Q2", "Q3", "Q4". An array
    514      * of 4 strings indexed by the month divided by 3.
    515      * @serial
    516      */
    517     String shortQuarters[] = null;
    518 
    519     /**
    520      * Full quarter names. For example: "1st Quarter", "2nd Quarter", "3rd Quarter",
    521      * "4th Quarter". An array of 4 strings, indexed by the month divided by 3.
    522      * @serial
    523      */
    524     String quarters[] = null;
    525 
    526     /**
    527      * Standalone abbreviated quarter names. For example: "Q1", "Q2", "Q3", "Q4". An array
    528      * of 4 strings indexed by the month divided by 3.
    529      * @serial
    530      */
    531     String standaloneShortQuarters[] = null;
    532 
    533     /**
    534      * Standalone full quarter names. For example: "1st Quarter", "2nd Quarter", "3rd Quarter",
    535      * "4th Quarter". An array of 4 strings, indexed by the month divided by 3.
    536      * @serial
    537      */
    538     String standaloneQuarters[] = null;
    539 
    540     /**
    541      * All leap month patterns, for example "{0}bis".
    542      * An array of DT_MONTH_PATTERN_COUNT strings, indexed by the DT_LEAP_MONTH_PATTERN_XXX value.
    543      * @serial
    544      */
    545     String leapMonthPatterns[] = null;
    546 
    547      /**
    548      * Cyclic year names, for example: "jia-zi", "yi-chou", ... "gui-hai".
    549      * An array of (normally) 60 strings, corresponding to cyclic years 1-60 (in Calendar YEAR field).
    550      * Currently we only have data for format/abbreviated.
    551      * For the others, just get from format/abbreviated, ignore set.
    552      * @serial
    553      */
    554     String shortYearNames[] = null;
    555 
    556      /**
    557      * Cyclic zodiac names, for example: "Rat", "Ox", "Tiger", etc.
    558      * An array of (normally) 12 strings.
    559      * Currently we only have data for format/abbreviated.
    560      * For the others, just get from format/abbreviated, ignore set.
    561      * @serial
    562      */
    563     String shortZodiacNames[] = null;
    564 
    565    /**
    566      * Localized names of time zones in this locale.  This is a
    567      * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
    568      * where <em>m</em> is at least 5 and up to 7.  Each of the <em>n</em> rows is an
    569      * entry containing the localized names for a single <code>TimeZone</code>.
    570      * Each such row contains (with <code>i</code> ranging from
    571      * 0..<em>n</em>-1):
    572      * <ul>
    573      * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
    574      * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
    575      * time</li>
    576      * <li><code>zoneStrings[i][2]</code> - short name of zone in
    577      * standard time</li>
    578      * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
    579      * savings time</li>
    580      * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
    581      * savings time</li>
    582      * <li><code>zoneStrings[i][5]</code> - location name of zone</li>
    583      * <li><code>zoneStrings[i][6]</code> - long generic name of zone</li>
    584      * <li><code>zoneStrings[i][7]</code> - short generic of zone</li>
    585     *  </ul>
    586      * The zone ID is <em>not</em> localized; it corresponds to the ID
    587      * value associated with a system time zone object.  All other entries
    588      * are localized names.  If a zone does not implement daylight savings
    589      * time, the daylight savings time names are ignored.
    590      * <em>Note:</em>CLDR 1.5 introduced metazone and its historical mappings.
    591      * This simple two-dimensional array is no longer sufficient to represent
    592      * localized names and its historic changes.  Since ICU 3.8.1, localized
    593      * zone names extracted from ICU locale data is stored in a ZoneStringFormat
    594      * instance.  But we still need to support the old way of customizing
    595      * localized zone names, so we keep this field for the purpose.
    596      * @see com.ibm.icu.util.TimeZone
    597      * @serial
    598      */
    599     private String zoneStrings[][] = null;
    600 
    601      /**
    602      * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
    603      * All locales use the same unlocalized pattern characters.
    604      */
    605     static final String patternChars = "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxrbB";
    606 
    607     /**
    608      * Localized date-time pattern characters. For example, a locale may
    609      * wish to use 'u' rather than 'y' to represent years in its date format
    610      * pattern strings.
    611      * This string must be exactly 18 characters long, with the index of
    612      * the characters described by <code>DateFormat.ERA_FIELD</code>,
    613      * <code>DateFormat.YEAR_FIELD</code>, etc.  Thus, if the string were
    614      * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
    615      * @serial
    616      */
    617     String localPatternChars = null;
    618 
    619     /**
    620      * Localized names for abbreviated (== short) day periods.
    621      * An array of strings, in the order of DayPeriod constants.
    622      */
    623     String abbreviatedDayPeriods[] = null;
    624 
    625     /**
    626      * Localized names for wide day periods.
    627      * An array of strings, in the order of DayPeriod constants.
    628      */
    629     String wideDayPeriods[] = null;
    630 
    631     /**
    632      * Localized names for narrow day periods.
    633      * An array of strings, in the order of DayPeriod constants.
    634      */
    635     String narrowDayPeriods[] = null;
    636 
    637     /**
    638      * Localized names for standalone abbreviated (== short) day periods.
    639      * An array of strings, in the order of DayPeriod constants.
    640      */
    641     String standaloneAbbreviatedDayPeriods[] = null;
    642 
    643     /**
    644      * Localized names for standalone wide day periods.
    645      * An array of strings, in the order of DayPeriod constants.
    646      */
    647     String standaloneWideDayPeriods[] = null;
    648 
    649     /**
    650      * Localized names for standalone narrow day periods.
    651      * An array of strings, in the order of DayPeriod constants.
    652      */
    653     String standaloneNarrowDayPeriods[] = null;
    654 
    655     /* use serialVersionUID from JDK 1.1.4 for interoperability */
    656     private static final long serialVersionUID = -5987973545549424702L;
    657 
    658     private static final String[][] CALENDAR_CLASSES = {
    659         {"GregorianCalendar", "gregorian"},
    660         {"JapaneseCalendar", "japanese"},
    661         {"BuddhistCalendar", "buddhist"},
    662         {"TaiwanCalendar", "roc"},
    663         {"PersianCalendar", "persian"},
    664         {"IslamicCalendar", "islamic"},
    665         {"HebrewCalendar", "hebrew"},
    666         {"ChineseCalendar", "chinese"},
    667         {"IndianCalendar", "indian"},
    668         {"CopticCalendar", "coptic"},
    669         {"EthiopicCalendar", "ethiopic"},
    670     };
    671 
    672     /**
    673      * {@icu} Constants for capitalization context usage types
    674      * related to date formatting.
    675      * @internal
    676      */
    677     enum CapitalizationContextUsage {
    678         OTHER,
    679         MONTH_FORMAT,     /* except narrow */
    680         MONTH_STANDALONE, /* except narrow */
    681         MONTH_NARROW,
    682         DAY_FORMAT,     /* except narrow */
    683         DAY_STANDALONE, /* except narrow */
    684         DAY_NARROW,
    685         ERA_WIDE,
    686         ERA_ABBREV,
    687         ERA_NARROW,
    688         ZONE_LONG,
    689         ZONE_SHORT,
    690         METAZONE_LONG,
    691         METAZONE_SHORT
    692     }
    693 
    694     /** Map from resource key to CapitalizationContextUsage value
    695      */
    696     private static final Map<String, CapitalizationContextUsage> contextUsageTypeMap;
    697     static {
    698         contextUsageTypeMap=new HashMap<String, CapitalizationContextUsage>();
    699         contextUsageTypeMap.put("month-format-except-narrow", CapitalizationContextUsage.MONTH_FORMAT);
    700         contextUsageTypeMap.put("month-standalone-except-narrow", CapitalizationContextUsage.MONTH_STANDALONE);
    701         contextUsageTypeMap.put("month-narrow",   CapitalizationContextUsage.MONTH_NARROW);
    702         contextUsageTypeMap.put("day-format-except-narrow", CapitalizationContextUsage.DAY_FORMAT);
    703         contextUsageTypeMap.put("day-standalone-except-narrow", CapitalizationContextUsage.DAY_STANDALONE);
    704         contextUsageTypeMap.put("day-narrow",     CapitalizationContextUsage.DAY_NARROW);
    705         contextUsageTypeMap.put("era-name",       CapitalizationContextUsage.ERA_WIDE);
    706         contextUsageTypeMap.put("era-abbr",       CapitalizationContextUsage.ERA_ABBREV);
    707         contextUsageTypeMap.put("era-narrow",     CapitalizationContextUsage.ERA_NARROW);
    708         contextUsageTypeMap.put("zone-long",      CapitalizationContextUsage.ZONE_LONG);
    709         contextUsageTypeMap.put("zone-short",     CapitalizationContextUsage.ZONE_SHORT);
    710         contextUsageTypeMap.put("metazone-long",  CapitalizationContextUsage.METAZONE_LONG);
    711         contextUsageTypeMap.put("metazone-short", CapitalizationContextUsage.METAZONE_SHORT);
    712     }
    713 
    714      /**
    715      * Capitalization transforms. For each usage type, the first array element indicates
    716      * whether to titlecase for uiListOrMenu context, the second indicates whether to
    717      * titlecase for stand-alone context.
    718      * @serial
    719      */
    720     Map<CapitalizationContextUsage,boolean[]> capitalization = null;
    721 
    722     /**
    723      * Returns era strings. For example: "AD" and "BC".
    724      * @return the era strings.
    725      * @stable ICU 2.0
    726      */
    727     public String[] getEras() {
    728         return duplicate(eras);
    729     }
    730 
    731     /**
    732      * Sets era strings. For example: "AD" and "BC".
    733      * @param newEras the new era strings.
    734      * @stable ICU 2.0
    735      */
    736     public void setEras(String[] newEras) {
    737         eras = duplicate(newEras);
    738     }
    739 
    740     /**
    741      * {@icu} Returns era name strings. For example: "Anno Domini" and "Before Christ".
    742      * @return the era strings.
    743      * @stable ICU 3.4
    744      */
    745     public String[] getEraNames() {
    746         return duplicate(eraNames);
    747     }
    748 
    749     /**
    750      * {@icu} Sets era name strings. For example: "Anno Domini" and "Before Christ".
    751      * @param newEraNames the new era strings.
    752      * @stable ICU 3.8
    753      */
    754     public void setEraNames(String[] newEraNames) {
    755         eraNames = duplicate(newEraNames);
    756     }
    757 
    758     // Android patch (http://b/30464240) start: Add getter for narrow eras.
    759     /**
    760      * {@icu} Returns narrow era name strings. For example: "A" and "B".
    761      * @return the era strings.
    762      * @internal
    763      * @deprecated This API is ICU internal only.
    764      */
    765     @Deprecated
    766     public String[] getNarrowEras() {
    767         return duplicate(narrowEras);
    768     }
    769     // Android patch end.
    770 
    771     /**
    772      * Returns month strings. For example: "January", "February", etc.
    773      * @return the month strings.
    774      * @stable ICU 2.0
    775      */
    776     public String[] getMonths() {
    777         return duplicate(months);
    778     }
    779 
    780     /**
    781      * Returns month strings. For example: "January", "February", etc.
    782      * @param context    The month context, FORMAT or STANDALONE.
    783      * @param width      The width or the returned month string,
    784      *                   either WIDE, ABBREVIATED, or NARROW.
    785      * @return the month strings.
    786      * @stable ICU 3.4
    787      */
    788     public String[] getMonths(int context, int width) {
    789         String [] returnValue = null;
    790         switch (context) {
    791            case FORMAT :
    792               switch(width) {
    793                  case WIDE :
    794                     returnValue = months;
    795                     break;
    796                  case ABBREVIATED :
    797                  case SHORT : // no month data for this, defaults to ABBREVIATED
    798                     returnValue = shortMonths;
    799                     break;
    800                  case NARROW :
    801                     returnValue = narrowMonths;
    802                     break;
    803               }
    804               break;
    805            case STANDALONE :
    806               switch(width) {
    807                  case WIDE :
    808                     returnValue = standaloneMonths;
    809                     break;
    810                  case ABBREVIATED :
    811                  case SHORT : // no month data for this, defaults to ABBREVIATED
    812                     returnValue = standaloneShortMonths;
    813                     break;
    814                  case NARROW :
    815                     returnValue = standaloneNarrowMonths;
    816                     break;
    817               }
    818               break;
    819         }
    820         if (returnValue == null) {
    821             throw new IllegalArgumentException("Bad context or width argument");
    822         }
    823         return duplicate(returnValue);
    824     }
    825 
    826     /**
    827      * Sets month strings. For example: "January", "February", etc.
    828      * @param newMonths the new month strings.
    829      * @stable ICU 2.0
    830      */
    831     public void setMonths(String[] newMonths) {
    832         months = duplicate(newMonths);
    833     }
    834 
    835     /**
    836      * Sets month strings. For example: "January", "February", etc.
    837      * @param newMonths the new month strings.
    838      * @param context    The formatting context, FORMAT or STANDALONE.
    839      * @param width      The width of the month string,
    840      *                   either WIDE, ABBREVIATED, or NARROW.
    841      * @stable ICU 3.8
    842      */
    843     public void setMonths(String[] newMonths, int context, int width) {
    844         switch (context) {
    845            case FORMAT :
    846               switch(width) {
    847                  case WIDE :
    848                     months = duplicate(newMonths);
    849                     break;
    850                  case ABBREVIATED :
    851                     shortMonths = duplicate(newMonths);
    852                     break;
    853                  case NARROW :
    854                     narrowMonths = duplicate(newMonths);
    855                     break;
    856                  default : // HANDLE SHORT, etc.
    857                     break;
    858               }
    859               break;
    860            case STANDALONE :
    861               switch(width) {
    862                  case WIDE :
    863                     standaloneMonths = duplicate(newMonths);
    864                     break;
    865                  case ABBREVIATED :
    866                     standaloneShortMonths = duplicate(newMonths);
    867                     break;
    868                  case NARROW :
    869                     standaloneNarrowMonths = duplicate(newMonths);
    870                     break;
    871                  default : // HANDLE SHORT, etc.
    872                     break;
    873               }
    874               break;
    875         }
    876     }
    877 
    878     /**
    879      * Returns short month strings. For example: "Jan", "Feb", etc.
    880      * @return the short month strings.
    881      * @stable ICU 2.0
    882      */
    883     public String[] getShortMonths() {
    884         return duplicate(shortMonths);
    885     }
    886 
    887     /**
    888      * Sets short month strings. For example: "Jan", "Feb", etc.
    889      * @param newShortMonths the new short month strings.
    890      * @stable ICU 2.0
    891      */
    892     public void setShortMonths(String[] newShortMonths) {
    893         shortMonths = duplicate(newShortMonths);
    894     }
    895 
    896     /**
    897      * Returns wide weekday strings. For example: "Sunday", "Monday", etc.
    898      * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
    899      * <code>Calendar.MONDAY</code>, etc. to index the result array.
    900      * @stable ICU 2.0
    901      */
    902     public String[] getWeekdays() {
    903         return duplicate(weekdays);
    904     }
    905 
    906     /**
    907      * Returns weekday strings. For example: "Sunday", "Monday", etc.
    908      * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
    909      * <code>Calendar.MONDAY</code>, etc. to index the result array.
    910      * @param context    Formatting context, either FORMAT or STANDALONE.
    911      * @param width      Width of strings to be returned, either
    912      *                   WIDE, ABBREVIATED, SHORT, or NARROW
    913      * @stable ICU 3.4
    914      */
    915     public String[] getWeekdays(int context, int width) {
    916         String [] returnValue = null;
    917         switch (context) {
    918            case FORMAT :
    919               switch(width) {
    920                  case WIDE :
    921                     returnValue = weekdays;
    922                     break;
    923                  case ABBREVIATED :
    924                     returnValue = shortWeekdays;
    925                     break;
    926                  case SHORT :
    927                     returnValue = (shorterWeekdays != null)? shorterWeekdays: shortWeekdays;
    928                     break;
    929                  case NARROW :
    930                     returnValue = narrowWeekdays;
    931                     break;
    932               }
    933               break;
    934            case STANDALONE :
    935               switch(width) {
    936                  case WIDE :
    937                     returnValue = standaloneWeekdays;
    938                     break;
    939                  case ABBREVIATED :
    940                     returnValue = standaloneShortWeekdays;
    941                     break;
    942                  case SHORT :
    943                     returnValue = (standaloneShorterWeekdays != null)? standaloneShorterWeekdays: standaloneShortWeekdays;
    944                     break;
    945                  case NARROW :
    946                     returnValue = standaloneNarrowWeekdays;
    947                     break;
    948               }
    949               break;
    950         }
    951         if (returnValue == null) {
    952             throw new IllegalArgumentException("Bad context or width argument");
    953         }
    954         return duplicate(returnValue);
    955     }
    956 
    957     /**
    958      * Sets weekday strings. For example: "Sunday", "Monday", etc.
    959      * @param newWeekdays The new weekday strings.
    960      * @param context     The formatting context, FORMAT or STANDALONE.
    961      * @param width       The width of the strings,
    962      *                    either WIDE, ABBREVIATED, SHORT, or NARROW.
    963      * @stable ICU 3.8
    964      */
    965     public void setWeekdays(String[] newWeekdays, int context, int width) {
    966         switch (context) {
    967            case FORMAT :
    968               switch(width) {
    969                  case WIDE :
    970                     weekdays = duplicate(newWeekdays);
    971                     break;
    972                  case ABBREVIATED :
    973                     shortWeekdays = duplicate(newWeekdays);
    974                     break;
    975                  case SHORT :
    976                     shorterWeekdays = duplicate(newWeekdays);
    977                     break;
    978                  case NARROW :
    979                     narrowWeekdays = duplicate(newWeekdays);
    980                     break;
    981               }
    982               break;
    983            case STANDALONE :
    984               switch(width) {
    985                  case WIDE :
    986                     standaloneWeekdays = duplicate(newWeekdays);
    987                     break;
    988                  case ABBREVIATED :
    989                     standaloneShortWeekdays = duplicate(newWeekdays);
    990                     break;
    991                  case SHORT :
    992                     standaloneShorterWeekdays = duplicate(newWeekdays);
    993                     break;
    994                  case NARROW :
    995                     standaloneNarrowWeekdays = duplicate(newWeekdays);
    996                     break;
    997               }
    998               break;
    999         }
   1000     }
   1001 
   1002     /**
   1003      * Sets wide weekday strings. For example: "Sunday", "Monday", etc.
   1004      * @param newWeekdays the new weekday strings. The array should
   1005      * be indexed by <code>Calendar.SUNDAY</code>,
   1006      * <code>Calendar.MONDAY</code>, etc.
   1007      * @stable ICU 2.0
   1008      */
   1009     public void setWeekdays(String[] newWeekdays) {
   1010         weekdays = duplicate(newWeekdays);
   1011     }
   1012 
   1013     /**
   1014      * Returns abbreviated weekday strings; for example: "Sun", "Mon", etc.
   1015      * (Note: the method name is misleading; it does not get the CLDR-style
   1016      * "short" weekday strings, e.g. "Su", "Mo", etc.)
   1017      * @return the abbreviated weekday strings. Use <code>Calendar.SUNDAY</code>,
   1018      * <code>Calendar.MONDAY</code>, etc. to index the result array.
   1019      * @stable ICU 2.0
   1020      */
   1021     public String[] getShortWeekdays() {
   1022         return duplicate(shortWeekdays);
   1023     }
   1024 
   1025     /**
   1026      * Sets abbreviated weekday strings; for example: "Sun", "Mon", etc.
   1027      * (Note: the method name is misleading; it does not set the CLDR-style
   1028      * "short" weekday strings, e.g. "Su", "Mo", etc.)
   1029      * @param newAbbrevWeekdays the new abbreviated weekday strings. The array should
   1030      * be indexed by <code>Calendar.SUNDAY</code>,
   1031      * <code>Calendar.MONDAY</code>, etc.
   1032      * @stable ICU 2.0
   1033      */
   1034     public void setShortWeekdays(String[] newAbbrevWeekdays) {
   1035         shortWeekdays = duplicate(newAbbrevWeekdays);
   1036     }
   1037     /**
   1038      * {@icu} Returns quarter strings. For example: "1st Quarter", "2nd Quarter", etc.
   1039      * @param context    The quarter context, FORMAT or STANDALONE.
   1040      * @param width      The width or the returned quarter string,
   1041      *                   either WIDE or ABBREVIATED. There are no NARROW quarters.
   1042      * @return the quarter strings.
   1043      * @stable ICU 3.6
   1044      */
   1045     public String[] getQuarters(int context, int width) {
   1046         String [] returnValue = null;
   1047         switch (context) {
   1048            case FORMAT :
   1049               switch(width) {
   1050                  case WIDE :
   1051                     returnValue = quarters;
   1052                     break;
   1053                  case ABBREVIATED :
   1054                  case SHORT : // no quarter data for this, defaults to ABBREVIATED
   1055                     returnValue = shortQuarters;
   1056                     break;
   1057                  case NARROW :
   1058                      returnValue = null;
   1059                      break;
   1060               }
   1061               break;
   1062 
   1063            case STANDALONE :
   1064               switch(width) {
   1065                  case WIDE :
   1066                     returnValue = standaloneQuarters;
   1067                     break;
   1068                  case ABBREVIATED :
   1069                  case SHORT : // no quarter data for this, defaults to ABBREVIATED
   1070                     returnValue = standaloneShortQuarters;
   1071                     break;
   1072                  case NARROW:
   1073                      returnValue = null;
   1074                      break;
   1075               }
   1076               break;
   1077         }
   1078         if (returnValue == null) {
   1079             throw new IllegalArgumentException("Bad context or width argument");
   1080         }
   1081         return duplicate(returnValue);
   1082     }
   1083 
   1084     /**
   1085      * {@icu} Sets quarter strings. For example: "1st Quarter", "2nd Quarter", etc.
   1086      * @param newQuarters the new quarter strings.
   1087      * @param context    The formatting context, FORMAT or STANDALONE.
   1088      * @param width      The width of the quarter string,
   1089      *                   either WIDE or ABBREVIATED. There are no NARROW quarters.
   1090      * @stable ICU 3.8
   1091      */
   1092     public void setQuarters(String[] newQuarters, int context, int width) {
   1093         switch (context) {
   1094            case FORMAT :
   1095               switch(width) {
   1096                  case WIDE :
   1097                     quarters = duplicate(newQuarters);
   1098                     break;
   1099                  case ABBREVIATED :
   1100                     shortQuarters = duplicate(newQuarters);
   1101                     break;
   1102                  case NARROW :
   1103                     //narrowQuarters = duplicate(newQuarters);
   1104                     break;
   1105                  default : // HANDLE SHORT, etc.
   1106                     break;
   1107               }
   1108               break;
   1109            case STANDALONE :
   1110               switch(width) {
   1111                  case WIDE :
   1112                     standaloneQuarters = duplicate(newQuarters);
   1113                     break;
   1114                  case ABBREVIATED :
   1115                     standaloneShortQuarters = duplicate(newQuarters);
   1116                     break;
   1117                  case NARROW :
   1118                     //standaloneNarrowQuarters = duplicate(newQuarters);
   1119                     break;
   1120                  default : // HANDLE SHORT, etc.
   1121                     break;
   1122               }
   1123               break;
   1124         }
   1125     }
   1126 
   1127     /**
   1128      * Returns cyclic year name strings if the calendar has them,
   1129      * for example: "jia-zi", "yi-chou", etc.
   1130      * @param context   The usage context: FORMAT, STANDALONE.
   1131      * @param width     The requested name width: WIDE, ABBREVIATED, SHORT, NARROW.
   1132      * @return          The year name strings, or null if they are not
   1133      *                  available for this calendar.
   1134      * @stable ICU 54
   1135      */
   1136     public String[] getYearNames(int context, int width) {
   1137         // context & width ignored for now, one set of names for all uses
   1138         if (shortYearNames != null) {
   1139             return duplicate(shortYearNames);
   1140         }
   1141         return null;
   1142     }
   1143 
   1144     /**
   1145      * Sets cyclic year name strings, for example: "jia-zi", "yi-chou", etc.
   1146      * @param yearNames The new cyclic year name strings.
   1147      * @param context   The usage context: FORMAT, STANDALONE (currently only FORMAT is supported).
   1148      * @param width     The name width: WIDE, ABBREVIATED, NARROW (currently only ABBREVIATED is supported).
   1149      * @stable ICU 54
   1150      */
   1151     public void setYearNames(String[] yearNames, int context, int width) {
   1152         if (context == FORMAT && width == ABBREVIATED) {
   1153             shortYearNames = duplicate(yearNames);
   1154         }
   1155     }
   1156 
   1157     /**
   1158      * Returns calendar zodiac name strings if the calendar has them,
   1159      * for example: "Rat", "Ox", "Tiger", etc.
   1160      * @param context   The usage context: FORMAT, STANDALONE.
   1161      * @param width     The requested name width: WIDE, ABBREVIATED, SHORT, NARROW.
   1162      * @return          The zodiac name strings, or null if they are not
   1163      *                  available for this calendar.
   1164      * @stable ICU 54
   1165      */
   1166     public String[] getZodiacNames(int context, int width) {
   1167         // context & width ignored for now, one set of names for all uses
   1168         if (shortZodiacNames != null) {
   1169             return duplicate(shortZodiacNames);
   1170         }
   1171         return null;
   1172     }
   1173 
   1174     /**
   1175      * Sets calendar zodiac name strings, for example: "Rat", "Ox", "Tiger", etc.
   1176      * @param zodiacNames   The new zodiac name strings.
   1177      * @param context   The usage context: FORMAT, STANDALONE (currently only FORMAT is supported).
   1178      * @param width     The name width: WIDE, ABBREVIATED, NARROW (currently only ABBREVIATED is supported).
   1179      * @stable ICU 54
   1180      */
   1181     public void setZodiacNames(String[] zodiacNames, int context, int width) {
   1182         if (context == FORMAT && width == ABBREVIATED) {
   1183             shortZodiacNames = duplicate(zodiacNames);
   1184         }
   1185     }
   1186 
   1187     /**
   1188      * Returns the appropriate leapMonthPattern if the calendar has them,
   1189      * for example: "{0}bis"
   1190      * @param context   The usage context: FORMAT, STANDALONE, NUMERIC.
   1191      * @param width     The requested pattern width: WIDE, ABBREVIATED, SHORT, NARROW.
   1192      * @return          The leapMonthPattern, or null if not available for
   1193      *                  this calendar.
   1194      * @internal
   1195      * @deprecated This API is ICU internal only.
   1196      */
   1197     @Deprecated
   1198     public String getLeapMonthPattern(int context, int width) {
   1199         if (leapMonthPatterns != null) {
   1200             int leapMonthPatternIndex = -1;
   1201             switch (context) {
   1202                case FORMAT :
   1203                   switch(width) {
   1204                      case WIDE :
   1205                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_WIDE;
   1206                         break;
   1207                      case ABBREVIATED :
   1208                      case SHORT : // no month data for this, defaults to ABBREVIATED
   1209                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV;
   1210                         break;
   1211                      case NARROW :
   1212                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_NARROW;
   1213                         break;
   1214                   }
   1215                   break;
   1216                case STANDALONE :
   1217                   switch(width) {
   1218                      case WIDE :
   1219                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE;
   1220                         break;
   1221                      case ABBREVIATED :
   1222                      case SHORT : // no month data for this, defaults to ABBREVIATED
   1223                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV;
   1224                         break;
   1225                      case NARROW :
   1226                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW;
   1227                         break;
   1228                   }
   1229                   break;
   1230                case NUMERIC :
   1231                   leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_NUMERIC;
   1232                   break;
   1233             }
   1234             if (leapMonthPatternIndex < 0) {
   1235                 throw new IllegalArgumentException("Bad context or width argument");
   1236             }
   1237             return leapMonthPatterns[leapMonthPatternIndex];
   1238         }
   1239         return null;
   1240     }
   1241 
   1242     /**
   1243      * Sets a leapMonthPattern, for example: "{0}bis"
   1244      * @param leapMonthPattern  The new leapMonthPattern.
   1245      * @param context   The usage context: FORMAT, STANDALONE, NUMERIC.
   1246      * @param width     The name width: WIDE, ABBREVIATED, NARROW.
   1247      * @internal
   1248      * @deprecated This API is ICU internal only.
   1249      */
   1250     @Deprecated
   1251     public void setLeapMonthPattern(String leapMonthPattern, int context, int width) {
   1252         if (leapMonthPatterns != null) {
   1253             int leapMonthPatternIndex = -1;
   1254             switch (context) {
   1255                case FORMAT :
   1256                   switch(width) {
   1257                      case WIDE :
   1258                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_WIDE;
   1259                         break;
   1260                      case ABBREVIATED :
   1261                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV;
   1262                         break;
   1263                      case NARROW :
   1264                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_NARROW;
   1265                         break;
   1266                      default : // HANDLE SHORT, etc.
   1267                         break;
   1268                   }
   1269                   break;
   1270                case STANDALONE :
   1271                   switch(width) {
   1272                      case WIDE :
   1273                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE;
   1274                         break;
   1275                      case ABBREVIATED :
   1276                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV;
   1277                         break;
   1278                      case NARROW :
   1279                         leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW;
   1280                         break;
   1281                      default : // HANDLE SHORT, etc.
   1282                         break;
   1283                   }
   1284                   break;
   1285                case NUMERIC :
   1286                   leapMonthPatternIndex = DT_LEAP_MONTH_PATTERN_NUMERIC;
   1287                   break;
   1288                default :
   1289                   break;
   1290             }
   1291             if (leapMonthPatternIndex >= 0) {
   1292                 leapMonthPatterns[leapMonthPatternIndex] = leapMonthPattern;
   1293             }
   1294         }
   1295     }
   1296 
   1297     /**
   1298      * Returns am/pm strings. For example: "AM" and "PM".
   1299      * @return the weekday strings.
   1300      * @stable ICU 2.0
   1301      */
   1302     public String[] getAmPmStrings() {
   1303         return duplicate(ampms);
   1304     }
   1305 
   1306     /**
   1307      * Sets am/pm strings. For example: "AM" and "PM".
   1308      * @param newAmpms the new ampm strings.
   1309      * @stable ICU 2.0
   1310      */
   1311     public void setAmPmStrings(String[] newAmpms) {
   1312         ampms = duplicate(newAmpms);
   1313     }
   1314 
   1315     /**
   1316      * Returns the time separator string. For example: ":".
   1317      * @return the time separator string.
   1318      * @internal
   1319      * @deprecated This API is ICU internal only.
   1320      */
   1321     @Deprecated
   1322     public String getTimeSeparatorString() {
   1323         return timeSeparator;
   1324     }
   1325 
   1326     /**
   1327      * Sets the time separator string. For example: ":".
   1328      * @param newTimeSeparator the new time separator string.
   1329      * @internal
   1330      * @deprecated This API is ICU internal only.
   1331      */
   1332     @Deprecated
   1333     public void setTimeSeparatorString(String newTimeSeparator) {
   1334         timeSeparator = newTimeSeparator;
   1335     }
   1336 
   1337     /**
   1338      * Returns time zone strings.
   1339      * <p>
   1340      * The array returned by this API is a two dimensional String array and
   1341      * each row contains at least following strings:
   1342      * <ul>
   1343      * <li>ZoneStrings[n][0] - System time zone ID
   1344      * <li>ZoneStrings[n][1] - Long standard time display name
   1345      * <li>ZoneStrings[n][2] - Short standard time display name
   1346      * <li>ZoneStrings[n][3] - Long daylight saving time display name
   1347      * <li>ZoneStrings[n][4] - Short daylight saving time display name
   1348      * </ul>
   1349      * When a localized display name is not available, the corresponding
   1350      * array element will be <code>null</code>.
   1351      * <p>
   1352      * <b>Note</b>: ICU implements the time zone display name formatting algorithm
   1353      * specified by <a href="http://www.unicode.org/reports/tr35/">UTS#35 Unicode
   1354      * Locale Data Markup Language(LDML)</a>. The algorithm supports historic
   1355      * display name changes and various different types of names not available in
   1356      * {@link java.text.DateFormatSymbols#getZoneStrings()}. For accessing the full
   1357      * set of time zone string data used by ICU implementation, you should use
   1358      * {@link TimeZoneNames} APIs instead.
   1359      *
   1360      * @return the time zone strings.
   1361      * @stable ICU 2.0
   1362      */
   1363     public String[][] getZoneStrings() {
   1364         if (zoneStrings != null) {
   1365             return duplicate(zoneStrings);
   1366         }
   1367 
   1368         String[] tzIDs = TimeZone.getAvailableIDs();
   1369         TimeZoneNames tznames = TimeZoneNames.getInstance(validLocale);
   1370         tznames.loadAllDisplayNames();
   1371         NameType types[] = {
   1372             NameType.LONG_STANDARD, NameType.SHORT_STANDARD,
   1373             NameType.LONG_DAYLIGHT, NameType.SHORT_DAYLIGHT
   1374         };
   1375         long now = System.currentTimeMillis();
   1376         String[][] array = new String[tzIDs.length][5];
   1377         for (int i = 0; i < tzIDs.length; i++) {
   1378             String canonicalID = TimeZone.getCanonicalID(tzIDs[i]);
   1379             if (canonicalID == null) {
   1380                 canonicalID = tzIDs[i];
   1381             }
   1382 
   1383             array[i][0] = tzIDs[i];
   1384             tznames.getDisplayNames(canonicalID, types, now, array[i], 1);
   1385         }
   1386 
   1387         zoneStrings = array;
   1388         return zoneStrings;
   1389     }
   1390 
   1391     /**
   1392      * Sets time zone strings.
   1393      * <p>
   1394      * <b>Note</b>: {@link SimpleDateFormat} no longer uses the
   1395      * zone strings stored in a <code>DateFormatSymbols</code>.
   1396      * Therefore, the time zone strings set by this method have
   1397      * no effects in an instance of <code>SimpleDateFormat</code>
   1398      * for formatting time zones. If you want to customize time
   1399      * zone display names formatted by <code>SimpleDateFormat</code>,
   1400      * you should customize {@link TimeZoneFormat} and set the
   1401      * instance by {@link SimpleDateFormat#setTimeZoneFormat(TimeZoneFormat)}
   1402      * instead.
   1403      *
   1404      * @param newZoneStrings the new time zone strings.
   1405      * @stable ICU 2.0
   1406      */
   1407     public void setZoneStrings(String[][] newZoneStrings) {
   1408         zoneStrings = duplicate(newZoneStrings);
   1409     }
   1410 
   1411     /**
   1412      * Returns localized date-time pattern characters. For example: 'u', 't', etc.
   1413      *
   1414      * <p>Note: ICU no longer provides localized date-time pattern characters for a locale
   1415      * starting ICU 3.8.  This method returns the non-localized date-time pattern
   1416      * characters unless user defined localized data is set by setLocalPatternChars.
   1417      * @return the localized date-time pattern characters.
   1418      * @stable ICU 2.0
   1419      */
   1420     public String getLocalPatternChars() {
   1421         return localPatternChars;
   1422     }
   1423 
   1424     /**
   1425      * Sets localized date-time pattern characters. For example: 'u', 't', etc.
   1426      * @param newLocalPatternChars the new localized date-time
   1427      * pattern characters.
   1428      * @stable ICU 2.0
   1429      */
   1430     public void setLocalPatternChars(String newLocalPatternChars) {
   1431         localPatternChars = newLocalPatternChars;
   1432     }
   1433 
   1434     /**
   1435      * Overrides clone.
   1436      * @stable ICU 2.0
   1437      */
   1438     @Override
   1439     public Object clone()
   1440     {
   1441         try {
   1442             DateFormatSymbols other = (DateFormatSymbols)super.clone();
   1443             return other;
   1444         } catch (CloneNotSupportedException e) {
   1445             ///CLOVER:OFF
   1446             throw new ICUCloneNotSupportedException(e);
   1447             ///CLOVER:ON
   1448         }
   1449     }
   1450 
   1451     /**
   1452      * Override hashCode.
   1453      * Generates a hash code for the DateFormatSymbols object.
   1454      * @stable ICU 2.0
   1455      */
   1456     @Override
   1457     public int hashCode() {
   1458         // Is this sufficient?
   1459         return requestedLocale.toString().hashCode();
   1460     }
   1461 
   1462     /**
   1463      * Overrides equals.
   1464      * @stable ICU 2.0
   1465      */
   1466     @Override
   1467     public boolean equals(Object obj)
   1468     {
   1469         if (this == obj) return true;
   1470         if (obj == null || getClass() != obj.getClass()) return false;
   1471         DateFormatSymbols that = (DateFormatSymbols) obj;
   1472         return (Utility.arrayEquals(eras, that.eras)
   1473                 && Utility.arrayEquals(eraNames, that.eraNames)
   1474                 && Utility.arrayEquals(months, that.months)
   1475                 && Utility.arrayEquals(shortMonths, that.shortMonths)
   1476                 && Utility.arrayEquals(narrowMonths, that.narrowMonths)
   1477                 && Utility.arrayEquals(standaloneMonths, that.standaloneMonths)
   1478                 && Utility.arrayEquals(standaloneShortMonths, that.standaloneShortMonths)
   1479                 && Utility.arrayEquals(standaloneNarrowMonths, that.standaloneNarrowMonths)
   1480                 && Utility.arrayEquals(weekdays, that.weekdays)
   1481                 && Utility.arrayEquals(shortWeekdays, that.shortWeekdays)
   1482                 && Utility.arrayEquals(shorterWeekdays, that.shorterWeekdays)
   1483                 && Utility.arrayEquals(narrowWeekdays, that.narrowWeekdays)
   1484                 && Utility.arrayEquals(standaloneWeekdays, that.standaloneWeekdays)
   1485                 && Utility.arrayEquals(standaloneShortWeekdays, that.standaloneShortWeekdays)
   1486                 && Utility.arrayEquals(standaloneShorterWeekdays, that.standaloneShorterWeekdays)
   1487                 && Utility.arrayEquals(standaloneNarrowWeekdays, that.standaloneNarrowWeekdays)
   1488                 && Utility.arrayEquals(ampms, that.ampms)
   1489                 && Utility.arrayEquals(ampmsNarrow, that.ampmsNarrow)
   1490                 && Utility.arrayEquals(abbreviatedDayPeriods, that.abbreviatedDayPeriods)
   1491                 && Utility.arrayEquals(wideDayPeriods, that.wideDayPeriods)
   1492                 && Utility.arrayEquals(narrowDayPeriods, that.narrowDayPeriods)
   1493                 && Utility.arrayEquals(standaloneAbbreviatedDayPeriods, that.standaloneAbbreviatedDayPeriods)
   1494                 && Utility.arrayEquals(standaloneWideDayPeriods, that.standaloneWideDayPeriods)
   1495                 && Utility.arrayEquals(standaloneNarrowDayPeriods, that.standaloneNarrowDayPeriods)
   1496                 && Utility.arrayEquals(timeSeparator, that.timeSeparator)
   1497                 && arrayOfArrayEquals(zoneStrings, that.zoneStrings)
   1498                 // getDiplayName maps deprecated country and language codes to the current ones
   1499                 // too bad there is no way to get the current codes!
   1500                 // I thought canolicalize() would map the codes but .. alas! it doesn't.
   1501                 && requestedLocale.getDisplayName().equals(that.requestedLocale.getDisplayName())
   1502                 && Utility.arrayEquals(localPatternChars,
   1503                                        that.localPatternChars));
   1504     }
   1505 
   1506     // =======================privates===============================
   1507 
   1508     /**
   1509      * Useful constant for defining timezone offsets.
   1510      */
   1511     static final int millisPerHour = 60*60*1000;
   1512 
   1513     // DateFormatSymbols cache
   1514     private static CacheBase<String, DateFormatSymbols, ULocale> DFSCACHE =
   1515         new SoftCache<String, DateFormatSymbols, ULocale>() {
   1516             @Override
   1517             protected DateFormatSymbols createInstance(String key, ULocale locale) {
   1518                 // Extract the type string from the key.
   1519                 // Otherwise we would have to create a pair object that
   1520                 // carries both the locale and the type.
   1521                 int typeStart = key.indexOf('+') + 1;
   1522                 int typeLimit = key.indexOf('+', typeStart);
   1523                 if (typeLimit < 0) {
   1524                     // no numbers keyword value
   1525                     typeLimit = key.length();
   1526                 }
   1527                 String type = key.substring(typeStart, typeLimit);
   1528                 return new DateFormatSymbols(locale, null, type);
   1529             }
   1530         };
   1531 
   1532     /**
   1533      * Initializes format symbols for the locale and calendar type
   1534      * @param desiredLocale The locale whose symbols are desired.
   1535      * @param type          The calendar type whose date format symbols are desired.
   1536      * @stable ICU 3.0
   1537      */
   1538     //TODO: This protected seems to be marked as @stable accidentally.
   1539     // We may need to deescalate this API to @internal.
   1540     protected void initializeData(ULocale desiredLocale, String type)
   1541     {
   1542         String key = desiredLocale.getBaseName() + '+' + type;
   1543         String ns = desiredLocale.getKeywordValue("numbers");
   1544         if (ns != null && ns.length() > 0) {
   1545             key += '+' + ns;
   1546         }
   1547         DateFormatSymbols dfs = DFSCACHE.getInstance(key, desiredLocale);
   1548         initializeData(dfs);
   1549     }
   1550 
   1551     /**
   1552      * Initializes format symbols using another instance.
   1553      *
   1554      * TODO Clean up initialization methods for subclasses
   1555      */
   1556     void initializeData(DateFormatSymbols dfs) {
   1557         this.eras = dfs.eras;
   1558         this.eraNames = dfs.eraNames;
   1559         this.narrowEras = dfs.narrowEras;
   1560         this.months = dfs.months;
   1561         this.shortMonths = dfs.shortMonths;
   1562         this.narrowMonths = dfs.narrowMonths;
   1563         this.standaloneMonths = dfs.standaloneMonths;
   1564         this.standaloneShortMonths = dfs.standaloneShortMonths;
   1565         this.standaloneNarrowMonths = dfs.standaloneNarrowMonths;
   1566         this.weekdays = dfs.weekdays;
   1567         this.shortWeekdays = dfs.shortWeekdays;
   1568         this.shorterWeekdays = dfs.shorterWeekdays;
   1569         this.narrowWeekdays = dfs.narrowWeekdays;
   1570         this.standaloneWeekdays = dfs.standaloneWeekdays;
   1571         this.standaloneShortWeekdays = dfs.standaloneShortWeekdays;
   1572         this.standaloneShorterWeekdays = dfs.standaloneShorterWeekdays;
   1573         this.standaloneNarrowWeekdays = dfs.standaloneNarrowWeekdays;
   1574         this.ampms = dfs.ampms;
   1575         this.ampmsNarrow = dfs.ampmsNarrow;
   1576         this.timeSeparator = dfs.timeSeparator;
   1577         this.shortQuarters = dfs.shortQuarters;
   1578         this.quarters = dfs.quarters;
   1579         this.standaloneShortQuarters = dfs.standaloneShortQuarters;
   1580         this.standaloneQuarters = dfs.standaloneQuarters;
   1581         this.leapMonthPatterns = dfs.leapMonthPatterns;
   1582         this.shortYearNames = dfs.shortYearNames;
   1583         this.shortZodiacNames = dfs.shortZodiacNames;
   1584         this.abbreviatedDayPeriods = dfs.abbreviatedDayPeriods;
   1585         this.wideDayPeriods = dfs.wideDayPeriods;
   1586         this.narrowDayPeriods = dfs.narrowDayPeriods;
   1587         this.standaloneAbbreviatedDayPeriods = dfs.standaloneAbbreviatedDayPeriods;
   1588         this.standaloneWideDayPeriods = dfs.standaloneWideDayPeriods;
   1589         this.standaloneNarrowDayPeriods = dfs.standaloneNarrowDayPeriods;
   1590 
   1591         this.zoneStrings = dfs.zoneStrings; // always null at initialization time for now
   1592         this.localPatternChars = dfs.localPatternChars;
   1593 
   1594         this.capitalization = dfs.capitalization;
   1595 
   1596         this.actualLocale = dfs.actualLocale;
   1597         this.validLocale = dfs.validLocale;
   1598         this.requestedLocale = dfs.requestedLocale;
   1599     }
   1600 
   1601 
   1602     /**
   1603      * Sink to enumerate the calendar data
   1604      */
   1605     private static final class CalendarDataSink extends UResource.Sink {
   1606 
   1607         // Data structures to store resources from the resource bundle
   1608         Map<String, String[]> arrays = new TreeMap<String, String[]>();
   1609         Map<String, Map<String, String>> maps = new TreeMap<String, Map<String, String>>();
   1610         List<String> aliasPathPairs = new ArrayList<String>();
   1611 
   1612         // Current and next calendar resource table which should be loaded
   1613         String currentCalendarType = null;
   1614         String nextCalendarType = null;
   1615 
   1616         // Resources to visit when enumerating fallback calendars
   1617         private Set<String> resourcesToVisit;
   1618 
   1619         // Alias' relative path populated when an alias is read
   1620         private String aliasRelativePath;
   1621 
   1622         /**
   1623          * Initializes CalendarDataSink with default values
   1624          */
   1625         CalendarDataSink() { }
   1626 
   1627         /**
   1628          * Configure the CalendarSink to visit all the resources
   1629          */
   1630         void visitAllResources() {
   1631             resourcesToVisit = null;
   1632         }
   1633 
   1634         /**
   1635          * Actions to be done before enumerating
   1636          */
   1637         void preEnumerate(String calendarType) {
   1638             currentCalendarType = calendarType;
   1639             nextCalendarType = null;
   1640             aliasPathPairs.clear();
   1641         }
   1642 
   1643         @Override
   1644         public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
   1645             assert currentCalendarType != null && !currentCalendarType.isEmpty();
   1646 
   1647             // Stores the resources to visit on the next calendar.
   1648             Set<String> resourcesToVisitNext = null;
   1649             UResource.Table calendarData = value.getTable();
   1650 
   1651             // Enumerate all resources for this calendar
   1652             for (int i = 0; calendarData.getKeyAndValue(i, key, value); i++) {
   1653                 String keyString = key.toString();
   1654 
   1655                 // == Handle aliases ==
   1656                 AliasType aliasType = processAliasFromValue(keyString, value);
   1657                 if (aliasType == AliasType.GREGORIAN) {
   1658                     // Ignore aliases to the gregorian calendar, all of its resources will be loaded anyways.
   1659                     continue;
   1660 
   1661                 } else if (aliasType == AliasType.DIFFERENT_CALENDAR) {
   1662                     // Whenever an alias to the next calendar (except gregorian) is encountered, register the
   1663                     // calendar type it's pointing to
   1664                     if (resourcesToVisitNext == null) {
   1665                         resourcesToVisitNext = new HashSet<String>();
   1666                     }
   1667                     resourcesToVisitNext.add(aliasRelativePath);
   1668                     continue;
   1669 
   1670                 } else if (aliasType == AliasType.SAME_CALENDAR) {
   1671                     // Register same-calendar alias
   1672                     if (!arrays.containsKey(keyString) && !maps.containsKey(keyString)) {
   1673                         aliasPathPairs.add(aliasRelativePath);
   1674                         aliasPathPairs.add(keyString);
   1675                     }
   1676                     continue;
   1677                 }
   1678 
   1679                 // Only visit the resources that were referenced by an alias on the previous calendar
   1680                 // (AmPmMarkersAbbr is an exception).
   1681                 if (resourcesToVisit != null && !resourcesToVisit.isEmpty() && !resourcesToVisit.contains(keyString)
   1682                         && !keyString.equals("AmPmMarkersAbbr")) { continue; }
   1683 
   1684                 // == Handle data ==
   1685                 if (keyString.startsWith("AmPmMarkers")) {
   1686                     if (!keyString.endsWith("%variant") && !arrays.containsKey(keyString)) {
   1687                         String[] dataArray = value.getStringArray();
   1688                         arrays.put(keyString, dataArray);
   1689                     }
   1690                 } else if (keyString.equals("eras")
   1691                         || keyString.equals("dayNames")
   1692                         || keyString.equals("monthNames")
   1693                         || keyString.equals("quarters")
   1694                         || keyString.equals("dayPeriod")
   1695                         || keyString.equals("monthPatterns")
   1696                         || keyString.equals("cyclicNameSets")) {
   1697                     processResource(keyString, key, value);
   1698                 }
   1699             }
   1700 
   1701             // Apply same-calendar aliases
   1702             boolean modified;
   1703             do {
   1704                 modified = false;
   1705                 for (int i = 0; i < aliasPathPairs.size();) {
   1706                     boolean mod = false;
   1707                     String alias = aliasPathPairs.get(i);
   1708                     if (arrays.containsKey(alias)) {
   1709                         arrays.put(aliasPathPairs.get(i + 1), arrays.get(alias));
   1710                         mod = true;
   1711                     } else if (maps.containsKey(alias)) {
   1712                         maps.put(aliasPathPairs.get(i + 1), maps.get(alias));
   1713                         mod = true;
   1714                     }
   1715                     if (mod) {
   1716                         aliasPathPairs.remove(i + 1);
   1717                         aliasPathPairs.remove(i);
   1718                         modified = true;
   1719                     } else {
   1720                         i += 2;
   1721                     }
   1722                 }
   1723             } while (modified && !aliasPathPairs.isEmpty());
   1724 
   1725             // Set the resources to visit on the next calendar
   1726             if (resourcesToVisitNext != null) {
   1727                 resourcesToVisit = resourcesToVisitNext;
   1728             }
   1729         }
   1730 
   1731         /**
   1732          * Process the nested resource bundle tables
   1733          * @param path Table's relative path to the calendar
   1734          * @param key Resource bundle key
   1735          * @param value Resource bundle value (has to have the table to read)
   1736          */
   1737         protected void processResource(String path, UResource.Key key, UResource.Value value) {
   1738 
   1739             UResource.Table table = value.getTable();
   1740             Map<String, String> stringMap = null;
   1741 
   1742             // Iterate over all the elements of the table and add them to the map
   1743             for(int i = 0; table.getKeyAndValue(i, key, value); i++) {
   1744                 // Ignore '%variant' keys
   1745                 if (key.endsWith("%variant")) { continue; }
   1746 
   1747                 String keyString = key.toString();
   1748 
   1749                 // == Handle String elements ==
   1750                 if (value.getType() == ICUResourceBundle.STRING) {
   1751                     // We are on a leaf, store the map elements into the stringMap
   1752                     if (i == 0) {
   1753                         stringMap = new HashMap<String, String>();
   1754                         maps.put(path, stringMap);
   1755                     }
   1756                     assert stringMap != null;
   1757                     stringMap.put(keyString, value.getString());
   1758                     continue;
   1759                 }
   1760                 assert stringMap == null;
   1761 
   1762                 String currentPath = path + "/" + keyString;
   1763                 // In cyclicNameSets ignore everything but years/format/abbreviated
   1764                 // and zodiacs/format/abbreviated
   1765                 if (currentPath.startsWith("cyclicNameSets")) {
   1766                     if (!"cyclicNameSets/years/format/abbreviated".startsWith(currentPath)
   1767                             && !"cyclicNameSets/zodiacs/format/abbreviated".startsWith(currentPath)
   1768                             && !"cyclicNameSets/dayParts/format/abbreviated".startsWith(currentPath))
   1769                     { continue; }
   1770                 }
   1771 
   1772                 // == Handle aliases ==
   1773                 if (arrays.containsKey(currentPath)
   1774                         || maps.containsKey(currentPath)) { continue; }
   1775 
   1776                 AliasType aliasType = processAliasFromValue(currentPath, value);
   1777                 if (aliasType == AliasType.SAME_CALENDAR) {
   1778                     aliasPathPairs.add(aliasRelativePath);
   1779                     aliasPathPairs.add(currentPath);
   1780                     continue;
   1781                 }
   1782                 assert aliasType == AliasType.NONE;
   1783 
   1784                 // == Handle data ==
   1785                 if (value.getType() == ICUResourceBundle.ARRAY) {
   1786                     // We are on a leaf, store the array
   1787                     String[] dataArray = value.getStringArray();
   1788                     arrays.put(currentPath, dataArray);
   1789                 } else if (value.getType() == ICUResourceBundle.TABLE) {
   1790                     // We are not on a leaf, recursively process the subtable.
   1791                     processResource(currentPath, key, value);
   1792                 }
   1793             }
   1794         }
   1795 
   1796         // Alias' path prefix
   1797         private static final String CALENDAR_ALIAS_PREFIX = "/LOCALE/calendar/";
   1798 
   1799         /**
   1800          * Populates an AliasIdentifier with the alias information contained on the UResource.Value.
   1801          * @param currentRelativePath Relative path of this alias' resource
   1802          * @param value Value which contains the alias
   1803          * @return The AliasType of the alias found on Value
   1804          */
   1805         private AliasType processAliasFromValue(String currentRelativePath, UResource.Value value) {
   1806             if (value.getType() == ICUResourceBundle.ALIAS) {
   1807                 String aliasPath = value.getAliasString();
   1808                 if (aliasPath.startsWith(CALENDAR_ALIAS_PREFIX) &&
   1809                         aliasPath.length() > CALENDAR_ALIAS_PREFIX.length()) {
   1810                     int typeLimit = aliasPath.indexOf('/', CALENDAR_ALIAS_PREFIX.length());
   1811                     if (typeLimit > CALENDAR_ALIAS_PREFIX.length()) {
   1812                         String aliasCalendarType = aliasPath.substring(CALENDAR_ALIAS_PREFIX.length(), typeLimit);
   1813                         aliasRelativePath = aliasPath.substring(typeLimit + 1);
   1814 
   1815                         if (currentCalendarType.equals(aliasCalendarType)
   1816                                 && !currentRelativePath.equals(aliasRelativePath)) {
   1817                             // If we have an alias to the same calendar, the path to the resource must be different
   1818                             return AliasType.SAME_CALENDAR;
   1819 
   1820                         } else if (!currentCalendarType.equals(aliasCalendarType)
   1821                                 && currentRelativePath.equals(aliasRelativePath)) {
   1822                             // If we have an alias to a different calendar, the path to the resource must be the same
   1823                             if (aliasCalendarType.equals("gregorian")) {
   1824                                 return AliasType.GREGORIAN;
   1825                             } else if (nextCalendarType == null || nextCalendarType.equals(aliasCalendarType)) {
   1826                                 nextCalendarType = aliasCalendarType;
   1827                                 return AliasType.DIFFERENT_CALENDAR;
   1828                             }
   1829                         }
   1830                     }
   1831                 }
   1832                 throw new ICUException("Malformed 'calendar' alias. Path: " + aliasPath);
   1833             }
   1834             return AliasType.NONE;
   1835         }
   1836 
   1837         /**
   1838          * Enum which specifies the type of alias received, or no alias
   1839          */
   1840         private enum AliasType {
   1841             SAME_CALENDAR,
   1842             DIFFERENT_CALENDAR,
   1843             GREGORIAN,
   1844             NONE
   1845         }
   1846     }
   1847 
   1848     /** Private, for cache.getInstance(). */
   1849     private DateFormatSymbols(ULocale desiredLocale, ICUResourceBundle b, String calendarType) {
   1850         initializeData(desiredLocale, b, calendarType);
   1851     }
   1852 
   1853     /**
   1854      * Initializes format symbols for the locale and calendar type
   1855      * @param desiredLocale The locale whose symbols are desired.
   1856      * @param b Resource bundle provided externally
   1857      * @param calendarType  The calendar type being used
   1858      * @internal
   1859      * @deprecated This API is ICU internal only.
   1860      */
   1861     @Deprecated
   1862     // This API was accidentally marked as @stable ICU 3.0 formerly.
   1863     protected void initializeData(ULocale desiredLocale, ICUResourceBundle b, String calendarType)
   1864     {
   1865         // Create a CalendarSink to load this data and a resource bundle
   1866         CalendarDataSink calendarSink = new CalendarDataSink();
   1867         if (b == null) {
   1868             b = (ICUResourceBundle) UResourceBundle
   1869                     .getBundleInstance(ICUData.ICU_BASE_NAME, desiredLocale);
   1870         }
   1871 
   1872         // Iterate over the resource bundle data following the fallbacks through different calendar types
   1873         while (calendarType != null) {
   1874 
   1875             // Enumerate this calendar type. If the calendar is not found fallback to gregorian.
   1876             ICUResourceBundle dataForType = b.findWithFallback("calendar/" + calendarType);
   1877             if (dataForType == null) {
   1878                 if (!"gregorian".equals(calendarType)) {
   1879                     calendarType = "gregorian";
   1880                     calendarSink.visitAllResources();
   1881                     continue;
   1882                 }
   1883                 throw new MissingResourceException("The 'gregorian' calendar type wasn't found for the locale: "
   1884                         + desiredLocale.getBaseName(), getClass().getName(), "gregorian");
   1885             }
   1886             calendarSink.preEnumerate(calendarType);
   1887             dataForType.getAllItemsWithFallback("", calendarSink);
   1888 
   1889             // Stop loading when gregorian was loaded
   1890             if (calendarType.equals("gregorian")) {
   1891                 break;
   1892             }
   1893 
   1894             // Get the next calendar type to process from the sink
   1895             calendarType = calendarSink.nextCalendarType;
   1896 
   1897             // Gregorian is always the last fallback
   1898             if (calendarType == null) {
   1899                 calendarType = "gregorian";
   1900                 calendarSink.visitAllResources();
   1901             }
   1902         }
   1903 
   1904         Map<String, String[]> arrays = calendarSink.arrays;
   1905         Map<String, Map<String, String>> maps = calendarSink.maps;
   1906 
   1907         eras = arrays.get("eras/abbreviated");
   1908         eraNames = arrays.get("eras/wide");
   1909         narrowEras = arrays.get("eras/narrow");
   1910 
   1911         months = arrays.get("monthNames/format/wide");
   1912         shortMonths = arrays.get("monthNames/format/abbreviated");
   1913         narrowMonths = arrays.get("monthNames/format/narrow");
   1914 
   1915         standaloneMonths = arrays.get("monthNames/stand-alone/wide");
   1916         standaloneShortMonths = arrays.get("monthNames/stand-alone/abbreviated");
   1917         standaloneNarrowMonths = arrays.get("monthNames/stand-alone/narrow");
   1918 
   1919         String[] lWeekdays = arrays.get("dayNames/format/wide");
   1920         weekdays = new String[8];
   1921         weekdays[0] = "";  // 1-based
   1922         System.arraycopy(lWeekdays, 0, weekdays, 1, lWeekdays.length);
   1923 
   1924         String[] aWeekdays = arrays.get("dayNames/format/abbreviated");
   1925         shortWeekdays = new String[8];
   1926         shortWeekdays[0] = "";  // 1-based
   1927         System.arraycopy(aWeekdays, 0, shortWeekdays, 1, aWeekdays.length);
   1928 
   1929         String[] sWeekdays = arrays.get("dayNames/format/short");
   1930         shorterWeekdays = new String[8];
   1931         shorterWeekdays[0] = "";  // 1-based
   1932         System.arraycopy(sWeekdays, 0, shorterWeekdays, 1, sWeekdays.length);
   1933 
   1934         String [] nWeekdays = arrays.get("dayNames/format/narrow");
   1935         if (nWeekdays == null) {
   1936             nWeekdays = arrays.get("dayNames/stand-alone/narrow");
   1937 
   1938             if (nWeekdays == null) {
   1939                 nWeekdays = arrays.get("dayNames/format/abbreviated");
   1940 
   1941                 if (nWeekdays == null) {
   1942                     throw new MissingResourceException("Resource not found",
   1943                             getClass().getName(), "dayNames/format/abbreviated");
   1944                 }
   1945             }
   1946         }
   1947         narrowWeekdays = new String[8];
   1948         narrowWeekdays[0] = "";  // 1-based
   1949         System.arraycopy(nWeekdays, 0, narrowWeekdays, 1, nWeekdays.length);
   1950 
   1951         String [] swWeekdays = null;
   1952         swWeekdays = arrays.get("dayNames/stand-alone/wide");
   1953         standaloneWeekdays = new String[8];
   1954         standaloneWeekdays[0] = "";  // 1-based
   1955         System.arraycopy(swWeekdays, 0, standaloneWeekdays, 1, swWeekdays.length);
   1956 
   1957         String [] saWeekdays = null;
   1958         saWeekdays = arrays.get("dayNames/stand-alone/abbreviated");
   1959         standaloneShortWeekdays = new String[8];
   1960         standaloneShortWeekdays[0] = "";  // 1-based
   1961         System.arraycopy(saWeekdays, 0, standaloneShortWeekdays, 1, saWeekdays.length);
   1962 
   1963         String [] ssWeekdays = null;
   1964         ssWeekdays = arrays.get("dayNames/stand-alone/short");
   1965         standaloneShorterWeekdays = new String[8];
   1966         standaloneShorterWeekdays[0] = "";  // 1-based
   1967         System.arraycopy(ssWeekdays, 0, standaloneShorterWeekdays, 1, ssWeekdays.length);
   1968 
   1969         String [] snWeekdays = null;
   1970         snWeekdays = arrays.get("dayNames/stand-alone/narrow");
   1971         standaloneNarrowWeekdays = new String[8];
   1972         standaloneNarrowWeekdays[0] = "";  // 1-based
   1973         System.arraycopy(snWeekdays, 0, standaloneNarrowWeekdays, 1, snWeekdays.length);
   1974 
   1975         ampms = arrays.get("AmPmMarkers");
   1976         ampmsNarrow = arrays.get("AmPmMarkersNarrow");
   1977 
   1978         quarters = arrays.get("quarters/format/wide");
   1979         shortQuarters = arrays.get("quarters/format/abbreviated");
   1980 
   1981         standaloneQuarters = arrays.get("quarters/stand-alone/wide");
   1982         standaloneShortQuarters = arrays.get("quarters/stand-alone/abbreviated");
   1983 
   1984         abbreviatedDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/format/abbreviated"));
   1985         wideDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/format/wide"));
   1986         narrowDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/format/narrow"));
   1987         standaloneAbbreviatedDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/stand-alone/abbreviated"));
   1988         standaloneWideDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/stand-alone/wide"));
   1989         standaloneNarrowDayPeriods = loadDayPeriodStrings(maps.get("dayPeriod/stand-alone/narrow"));
   1990 
   1991         for (int i = 0; i < DT_MONTH_PATTERN_COUNT; i++) {
   1992             String monthPatternPath = LEAP_MONTH_PATTERNS_PATHS[i];
   1993             if (monthPatternPath != null) {
   1994                 Map<String, String> monthPatternMap = maps.get(monthPatternPath);
   1995                 if (monthPatternMap != null) {
   1996                     String leapMonthPattern = monthPatternMap.get("leap");
   1997                     if (leapMonthPattern != null) {
   1998                         if (leapMonthPatterns == null) {
   1999                             leapMonthPatterns = new String[DT_MONTH_PATTERN_COUNT];
   2000                         }
   2001                         leapMonthPatterns[i] = leapMonthPattern;
   2002                     }
   2003                 }
   2004             }
   2005         }
   2006 
   2007         shortYearNames = arrays.get("cyclicNameSets/years/format/abbreviated");
   2008         shortZodiacNames = arrays.get("cyclicNameSets/zodiacs/format/abbreviated");
   2009 
   2010         requestedLocale = desiredLocale;
   2011 
   2012         ICUResourceBundle rb =
   2013             (ICUResourceBundle)UResourceBundle.getBundleInstance(
   2014                 ICUData.ICU_BASE_NAME, desiredLocale);
   2015 
   2016         localPatternChars = patternChars;
   2017 
   2018         // TODO: obtain correct actual/valid locale later
   2019         ULocale uloc = rb.getULocale();
   2020         setLocale(uloc, uloc);
   2021 
   2022         capitalization = new HashMap<CapitalizationContextUsage,boolean[]>();
   2023         boolean[] noTransforms = new boolean[2];
   2024         noTransforms[0] = false;
   2025         noTransforms[1] = false;
   2026         CapitalizationContextUsage allUsages[] = CapitalizationContextUsage.values();
   2027         for (CapitalizationContextUsage usage: allUsages) {
   2028             capitalization.put(usage, noTransforms);
   2029         }
   2030         UResourceBundle contextTransformsBundle = null;
   2031         try {
   2032            contextTransformsBundle = rb.getWithFallback("contextTransforms");
   2033         }
   2034         catch (MissingResourceException e) {
   2035             contextTransformsBundle = null; // probably redundant
   2036         }
   2037         if (contextTransformsBundle != null) {
   2038             UResourceBundleIterator ctIterator = contextTransformsBundle.getIterator();
   2039             while ( ctIterator.hasNext() ) {
   2040                 UResourceBundle contextTransformUsage = ctIterator.next();
   2041                 int[] intVector = contextTransformUsage.getIntVector();
   2042                 if (intVector.length >= 2) {
   2043                     String usageKey = contextTransformUsage.getKey();
   2044                     CapitalizationContextUsage usage = contextUsageTypeMap.get(usageKey);
   2045                     if (usage != null) {
   2046                         boolean[] transforms = new boolean[2];
   2047                         transforms[0] = (intVector[0] != 0);
   2048                         transforms[1] = (intVector[1] != 0);
   2049                         capitalization.put(usage, transforms);
   2050                     }
   2051                 }
   2052             }
   2053         }
   2054 
   2055         NumberingSystem ns = NumberingSystem.getInstance(desiredLocale);
   2056         String nsName = ns == null ? "latn" : ns.getName();  // Latin is default.
   2057         String tsPath = "NumberElements/" + nsName + "/symbols/timeSeparator";
   2058         try {
   2059             setTimeSeparatorString(rb.getStringWithFallback(tsPath));
   2060         } catch (MissingResourceException e) {
   2061             setTimeSeparatorString(DEFAULT_TIME_SEPARATOR);
   2062         }
   2063     }
   2064 
   2065     /**
   2066      * Resource bundle paths for each leap month pattern
   2067      */
   2068     private static final String[] LEAP_MONTH_PATTERNS_PATHS = new String[DT_MONTH_PATTERN_COUNT];
   2069     static {
   2070         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_FORMAT_WIDE] = "monthPatterns/format/wide";
   2071         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV] = "monthPatterns/format/abbreviated";
   2072         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_FORMAT_NARROW] = "monthPatterns/format/narrow";
   2073         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE] = "monthPatterns/stand-alone/wide";
   2074         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV] = "monthPatterns/stand-alone/abbreviated";
   2075         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW] = "monthPatterns/stand-alone/narrow";
   2076         LEAP_MONTH_PATTERNS_PATHS[DT_LEAP_MONTH_PATTERN_NUMERIC] = "monthPatterns/numeric/all";
   2077     }
   2078 
   2079     private static final boolean arrayOfArrayEquals(Object[][] aa1, Object[][]aa2) {
   2080         if (aa1 == aa2) { // both are null
   2081             return true;
   2082         }
   2083         if (aa1 == null || aa2 == null) { // one is null and the other is not
   2084             return false;
   2085         }
   2086         if (aa1.length != aa2.length) {
   2087             return false;
   2088         }
   2089         boolean equal = true;
   2090         for (int i = 0; i < aa1.length; i++) {
   2091             equal = Utility.arrayEquals(aa1[i], aa2[i]);
   2092             if (!equal) {
   2093                 break;
   2094             }
   2095         }
   2096         return equal;
   2097     }
   2098 
   2099     /**
   2100      * Keys for dayPeriods
   2101      */
   2102     private static final String[] DAY_PERIOD_KEYS = {"midnight", "noon",
   2103             "morning1", "afternoon1", "evening1", "night1",
   2104             "morning2", "afternoon2", "evening2", "night2"};
   2105 
   2106     /**
   2107      * Loads localized names for day periods in the requested format.
   2108      * @param resourceMap Contains the dayPeriod resource to load
   2109      */
   2110     private String[] loadDayPeriodStrings(Map<String, String> resourceMap) {
   2111         String strings[] = new String[DAY_PERIOD_KEYS.length];
   2112         if (resourceMap != null) {
   2113             for (int i = 0; i < DAY_PERIOD_KEYS.length; ++i) {
   2114                 strings[i] = resourceMap.get(DAY_PERIOD_KEYS[i]);  // Null if string doesn't exist.
   2115             }
   2116         }
   2117         return strings;
   2118     }
   2119 
   2120     /*
   2121      * save the input locale
   2122      */
   2123     private ULocale requestedLocale;
   2124 
   2125     /*
   2126      * Clones an array of Strings.
   2127      * @param srcArray the source array to be cloned.
   2128      * @return a cloned array.
   2129      */
   2130     private final String[] duplicate(String[] srcArray)
   2131     {
   2132         return srcArray.clone();
   2133     }
   2134 
   2135     private final String[][] duplicate(String[][] srcArray)
   2136     {
   2137         String[][] aCopy = new String[srcArray.length][];
   2138         for (int i = 0; i < srcArray.length; ++i)
   2139             aCopy[i] = duplicate(srcArray[i]);
   2140         return aCopy;
   2141     }
   2142 
   2143     /*
   2144      * Compares the equality of the two arrays of String.
   2145      * @param current this String array.
   2146      * @param other that String array.
   2147     private final boolean equals(String[] current, String[] other)
   2148     {
   2149         int count = current.length;
   2150 
   2151         for (int i = 0; i < count; ++i)
   2152             if (!current[i].equals(other[i]))
   2153                 return false;
   2154         return true;
   2155     }
   2156      */
   2157 
   2158     //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   2159 
   2160     /**
   2161      * Returns the {@link DateFormatSymbols} object that should be used to format a
   2162      * calendar system's dates in the given locale.
   2163      * <p>
   2164      * <b>Subclassing:</b><br>
   2165      * When creating a new Calendar subclass, you must create the
   2166      * {@link ResourceBundle ResourceBundle}
   2167      * containing its {@link DateFormatSymbols DateFormatSymbols} in a specific place.
   2168      * The resource bundle name is based on the calendar's fully-specified
   2169      * class name, with ".resources" inserted at the end of the package name
   2170      * (just before the class name) and "Symbols" appended to the end.
   2171      * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
   2172      * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
   2173      * <p>
   2174      * Within the ResourceBundle, this method searches for five keys:
   2175      * <ul>
   2176      * <li><b>DayNames</b> -
   2177      *      An array of strings corresponding to each possible
   2178      *      value of the <code>DAY_OF_WEEK</code> field.  Even though
   2179      *      <code>DAY_OF_WEEK</code> starts with <code>SUNDAY</code> = 1,
   2180      *      This array is 0-based; the name for Sunday goes in the
   2181      *      first position, at index 0.  If this key is not found
   2182      *      in the bundle, the day names are inherited from the
   2183      *      default <code>DateFormatSymbols</code> for the requested locale.
   2184      *
   2185      * <li><b>DayAbbreviations</b> -
   2186      *      An array of abbreviated day names corresponding
   2187      *      to the values in the "DayNames" array.  If this key
   2188      *      is not found in the resource bundle, the "DayNames"
   2189      *      values are used instead.  If neither key is found,
   2190      *      the day abbreviations are inherited from the default
   2191      *      <code>DateFormatSymbols</code> for the locale.
   2192      *
   2193      * <li><b>MonthNames</b> -
   2194      *      An array of strings corresponding to each possible
   2195      *      value of the <code>MONTH</code> field.  If this key is not found
   2196      *      in the bundle, the month names are inherited from the
   2197      *      default <code>DateFormatSymbols</code> for the requested locale.
   2198      *
   2199      * <li><b>MonthAbbreviations</b> -
   2200      *      An array of abbreviated day names corresponding
   2201      *      to the values in the "MonthNames" array.  If this key
   2202      *      is not found in the resource bundle, the "MonthNames"
   2203      *      values are used instead.  If neither key is found,
   2204      *      the day abbreviations are inherited from the default
   2205      *      <code>DateFormatSymbols</code> for the locale.
   2206      *
   2207      * <li><b>Eras</b> -
   2208      *      An array of strings corresponding to each possible
   2209      *      value of the <code>ERA</code> field.  If this key is not found
   2210      *      in the bundle, the era names are inherited from the
   2211      *      default <code>DateFormatSymbols</code> for the requested locale.
   2212      * </ul>
   2213      * <p>
   2214      * @param cal       The calendar system whose date format symbols are desired.
   2215      * @param locale    The locale whose symbols are desired.
   2216      *
   2217      * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale)
   2218      * @stable ICU 2.0
   2219      */
   2220     public DateFormatSymbols(Calendar cal, Locale locale) {
   2221         initializeData(ULocale.forLocale(locale), cal.getType());
   2222     }
   2223 
   2224     /**
   2225      * Returns the {@link DateFormatSymbols} object that should be used to format a
   2226      * calendar system's dates in the given locale.
   2227      * <p>
   2228      * <b>Subclassing:</b><br>
   2229      * When creating a new Calendar subclass, you must create the
   2230      * {@link ResourceBundle ResourceBundle}
   2231      * containing its {@link DateFormatSymbols DateFormatSymbols} in a specific place.
   2232      * The resource bundle name is based on the calendar's fully-specified
   2233      * class name, with ".resources" inserted at the end of the package name
   2234      * (just before the class name) and "Symbols" appended to the end.
   2235      * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
   2236      * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
   2237      * <p>
   2238      * Within the ResourceBundle, this method searches for five keys:
   2239      * <ul>
   2240      * <li><b>DayNames</b> -
   2241      *      An array of strings corresponding to each possible
   2242      *      value of the <code>DAY_OF_WEEK</code> field.  Even though
   2243      *      <code>DAY_OF_WEEK</code> starts with <code>SUNDAY</code> = 1,
   2244      *      This array is 0-based; the name for Sunday goes in the
   2245      *      first position, at index 0.  If this key is not found
   2246      *      in the bundle, the day names are inherited from the
   2247      *      default <code>DateFormatSymbols</code> for the requested locale.
   2248      *
   2249      * <li><b>DayAbbreviations</b> -
   2250      *      An array of abbreviated day names corresponding
   2251      *      to the values in the "DayNames" array.  If this key
   2252      *      is not found in the resource bundle, the "DayNames"
   2253      *      values are used instead.  If neither key is found,
   2254      *      the day abbreviations are inherited from the default
   2255      *      <code>DateFormatSymbols</code> for the locale.
   2256      *
   2257      * <li><b>MonthNames</b> -
   2258      *      An array of strings corresponding to each possible
   2259      *      value of the <code>MONTH</code> field.  If this key is not found
   2260      *      in the bundle, the month names are inherited from the
   2261      *      default <code>DateFormatSymbols</code> for the requested locale.
   2262      *
   2263      * <li><b>MonthAbbreviations</b> -
   2264      *      An array of abbreviated day names corresponding
   2265      *      to the values in the "MonthNames" array.  If this key
   2266      *      is not found in the resource bundle, the "MonthNames"
   2267      *      values are used instead.  If neither key is found,
   2268      *      the day abbreviations are inherited from the default
   2269      *      <code>DateFormatSymbols</code> for the locale.
   2270      *
   2271      * <li><b>Eras</b> -
   2272      *      An array of strings corresponding to each possible
   2273      *      value of the <code>ERA</code> field.  If this key is not found
   2274      *      in the bundle, the era names are inherited from the
   2275      *      default <code>DateFormatSymbols</code> for the requested locale.
   2276      * </ul>
   2277      * <p>
   2278      * @param cal       The calendar system whose date format symbols are desired.
   2279      * @param locale    The ulocale whose symbols are desired.
   2280      *
   2281      * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale)
   2282      * @stable ICU 3.2
   2283      */
   2284     public DateFormatSymbols(Calendar cal, ULocale locale) {
   2285         initializeData(locale, cal.getType());
   2286     }
   2287 
   2288     /**
   2289      * Variant of DateFormatSymbols(Calendar, Locale) that takes the Calendar class
   2290      * instead of a Calendar instance.
   2291      * @see #DateFormatSymbols(Calendar, Locale)
   2292      * @stable ICU 2.2
   2293      */
   2294     public DateFormatSymbols(Class<? extends Calendar> calendarClass, Locale locale) {
   2295         this(calendarClass, ULocale.forLocale(locale));
   2296     }
   2297 
   2298     /**
   2299      * Variant of DateFormatSymbols(Calendar, ULocale) that takes the Calendar class
   2300      * instead of a Calendar instance.
   2301      * @see #DateFormatSymbols(Calendar, Locale)
   2302      * @stable ICU 3.2
   2303      */
   2304     public DateFormatSymbols(Class<? extends Calendar> calendarClass, ULocale locale) {
   2305         String fullName = calendarClass.getName();
   2306         int lastDot = fullName.lastIndexOf('.');
   2307         String className = fullName.substring(lastDot+1);
   2308         String calType = null;
   2309         for (String[] calClassInfo : CALENDAR_CLASSES) {
   2310             if (calClassInfo[0].equals(className)) {
   2311                 calType = calClassInfo[1];
   2312                 break;
   2313             }
   2314         }
   2315         if (calType == null) {
   2316             calType = className.replaceAll("Calendar", "").toLowerCase(Locale.ENGLISH);
   2317         }
   2318 
   2319         initializeData(locale, calType);
   2320     }
   2321 
   2322     // Android patch (http://b/30464240) start: Add constructor taking a calendar type.
   2323     /**
   2324      * Variant of DateFormatSymbols(Calendar, ULocale) that takes the calendar type
   2325      * instead of a Calendar instance.
   2326      * @see #DateFormatSymbols(Calendar, Locale)
   2327      * @internal
   2328      * @deprecated This API is ICU internal only.
   2329      */
   2330     @Deprecated
   2331     public DateFormatSymbols(ULocale locale, String calType) {
   2332         initializeData(locale, calType);
   2333     }
   2334     // Android patch end.
   2335 
   2336     /**
   2337      * Fetches a custom calendar's DateFormatSymbols out of the given resource
   2338      * bundle.  Symbols that are not overridden are inherited from the
   2339      * default DateFormatSymbols for the locale.
   2340      * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale)
   2341      * @stable ICU 2.0
   2342      */
   2343     public DateFormatSymbols(ResourceBundle bundle, Locale locale) {
   2344         this(bundle, ULocale.forLocale(locale));
   2345     }
   2346 
   2347     /**
   2348      * Fetches a custom calendar's DateFormatSymbols out of the given resource
   2349      * bundle.  Symbols that are not overridden are inherited from the
   2350      * default DateFormatSymbols for the locale.
   2351      * @see DateFormatSymbols#DateFormatSymbols(java.util.Locale)
   2352      * @stable ICU 3.2
   2353      */
   2354     public DateFormatSymbols(ResourceBundle bundle, ULocale locale) {
   2355         initializeData(locale, (ICUResourceBundle) bundle, CalendarUtil.getCalendarType(locale));
   2356     }
   2357 
   2358     /**
   2359      * Finds the ResourceBundle containing the date format information for
   2360      * a specified calendar subclass in a given locale.
   2361      * <p>
   2362      * The resource bundle name is based on the calendar's fully-specified
   2363      * class name, with ".resources" inserted at the end of the package name
   2364      * (just before the class name) and "Symbols" appended to the end.
   2365      * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
   2366      * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
   2367      * <p>
   2368      * <b>Note:</b>Because of the structural changes in the ICU locale bundle,
   2369      * this API no longer works as described.  This method always returns null.
   2370      * @deprecated ICU 4.0
   2371      */
   2372     @Deprecated
   2373     // This API was formerly @stable ICU 2.0
   2374     static public ResourceBundle getDateFormatBundle(Class<? extends Calendar> calendarClass,
   2375                                                      Locale locale)
   2376         throws MissingResourceException {
   2377         return null;
   2378     }
   2379 
   2380     /**
   2381      * Finds the ResourceBundle containing the date format information for
   2382      * a specified calendar subclass in a given locale.
   2383      * <p>
   2384      * The resource bundle name is based on the calendar's fully-specified
   2385      * class name, with ".resources" inserted at the end of the package name
   2386      * (just before the class name) and "Symbols" appended to the end.
   2387      * For example, the bundle corresponding to "com.ibm.icu.util.HebrewCalendar"
   2388      * is "com.ibm.icu.impl.data.HebrewCalendarSymbols".
   2389      * <p>
   2390      * <b>Note:</b>Because of the structural changes in the ICU locale bundle,
   2391      * this API no longer works as described.  This method always returns null.
   2392      * @deprecated ICU 4.0
   2393      */
   2394     @Deprecated
   2395     // This API was formerly @stable ICU 3.2
   2396     static public ResourceBundle getDateFormatBundle(Class<? extends Calendar> calendarClass,
   2397                                                      ULocale locale)
   2398         throws MissingResourceException {
   2399         return null;
   2400     }
   2401 
   2402     /**
   2403      * Variant of getDateFormatBundle(java.lang.Class, java.util.Locale) that takes
   2404      * a Calendar instance instead of a Calendar class.
   2405      * <p>
   2406      * <b>Note:</b>Because of the structural changes in the ICU locale bundle,
   2407      * this API no longer works as described.  This method always returns null.
   2408      * @see #getDateFormatBundle(java.lang.Class, java.util.Locale)
   2409      * @deprecated ICU 4.0
   2410      */
   2411     @Deprecated
   2412     // This API was formerly @stable ICU 2.2
   2413     public static ResourceBundle getDateFormatBundle(Calendar cal, Locale locale)
   2414         throws MissingResourceException {
   2415         return null;
   2416     }
   2417 
   2418     /**
   2419      * Variant of getDateFormatBundle(java.lang.Class, java.util.Locale) that takes
   2420      * a Calendar instance instead of a Calendar class.
   2421      * <p>
   2422      * <b>Note:</b>Because of the structural changes in the ICU locale bundle,
   2423      * this API no longer works as described.  This method always returns null.
   2424      * @see #getDateFormatBundle(java.lang.Class, java.util.Locale)
   2425      * @deprecated ICU 4.0
   2426      */
   2427     @Deprecated
   2428     // This API was formerly @stable ICU 3.2
   2429     public static ResourceBundle getDateFormatBundle(Calendar cal, ULocale locale)
   2430         throws MissingResourceException {
   2431         return null;
   2432     }
   2433 
   2434     // -------- BEGIN ULocale boilerplate --------
   2435 
   2436     /**
   2437      * Returns the locale that was used to create this object, or null.
   2438      * This may may differ from the locale requested at the time of
   2439      * this object's creation.  For example, if an object is created
   2440      * for locale <tt>en_US_CALIFORNIA</tt>, the actual data may be
   2441      * drawn from <tt>en</tt> (the <i>actual</i> locale), and
   2442      * <tt>en_US</tt> may be the most specific locale that exists (the
   2443      * <i>valid</i> locale).
   2444      *
   2445      * <p>Note: This method will be implemented in ICU 3.0; ICU 2.8
   2446      * contains a partial preview implementation.  The * <i>actual</i>
   2447      * locale is returned correctly, but the <i>valid</i> locale is
   2448      * not, in most cases.
   2449      * @param type type of information requested, either {@link
   2450      * com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link
   2451      * com.ibm.icu.util.ULocale#ACTUAL_LOCALE}.
   2452      * @return the information specified by <i>type</i>, or null if
   2453      * this object was not constructed from locale data.
   2454      * @see com.ibm.icu.util.ULocale
   2455      * @see com.ibm.icu.util.ULocale#VALID_LOCALE
   2456      * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
   2457      * @draft ICU 2.8 (retain)
   2458      * @provisional This API might change or be removed in a future release.
   2459      */
   2460     public final ULocale getLocale(ULocale.Type type) {
   2461         return type == ULocale.ACTUAL_LOCALE ?
   2462             this.actualLocale : this.validLocale;
   2463     }
   2464 
   2465     /**
   2466      * Sets information about the locales that were used to create this
   2467      * object.  If the object was not constructed from locale data,
   2468      * both arguments should be set to null.  Otherwise, neither
   2469      * should be null.  The actual locale must be at the same level or
   2470      * less specific than the valid locale.  This method is intended
   2471      * for use by factories or other entities that create objects of
   2472      * this class.
   2473      * @param valid the most specific locale containing any resource
   2474      * data, or null
   2475      * @param actual the locale containing data used to construct this
   2476      * object, or null
   2477      * @see com.ibm.icu.util.ULocale
   2478      * @see com.ibm.icu.util.ULocale#VALID_LOCALE
   2479      * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE
   2480      */
   2481     final void setLocale(ULocale valid, ULocale actual) {
   2482         // Change the following to an assertion later
   2483         if ((valid == null) != (actual == null)) {
   2484             ///CLOVER:OFF
   2485             throw new IllegalArgumentException();
   2486             ///CLOVER:ON
   2487         }
   2488         // Another check we could do is that the actual locale is at
   2489         // the same level or less specific than the valid locale.
   2490         this.validLocale = valid;
   2491         this.actualLocale = actual;
   2492     }
   2493 
   2494     /**
   2495      * The most specific locale containing any resource data, or null.
   2496      * @see com.ibm.icu.util.ULocale
   2497      */
   2498     private ULocale validLocale;
   2499 
   2500     /**
   2501      * The locale containing data used to construct this object, or
   2502      * null.
   2503      * @see com.ibm.icu.util.ULocale
   2504      */
   2505     private ULocale actualLocale;
   2506 
   2507     // -------- END ULocale boilerplate --------
   2508 
   2509     /**
   2510      * 3.8 or older version did not have localized GMT format
   2511      * patterns.
   2512      */
   2513     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
   2514         stream.defaultReadObject();
   2515     }
   2516 }
   2517