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