17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 2008-2015, International Business Machines Corporation and    *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.                                                *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.text;
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.io.Serializable;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashMap;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.HashSet;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashMap;
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.LinkedHashSet;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map.Entry;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUCache;
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.ICUResourceBundle;
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.SimpleCache;
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.impl.Utility;
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar;
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Freezable;
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ICUCloneNotSupportedException;
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.UResourceBundle;
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalInfo is a public class for encapsulating localizable
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * date time interval patterns. It is used by DateIntervalFormat.
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For most users, ordinary use of DateIntervalFormat does not need to create
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalInfo object directly.
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalFormat will take care of it when creating a date interval
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * formatter when user pass in skeleton and locale.
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For power users, who want to create their own date interval patterns,
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * or want to re-set date interval patterns, they could do so by
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * directly creating DateIntervalInfo and manupulating it.
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Logically, the interval patterns are mappings
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * from (skeleton, the_largest_different_calendar_field)
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * to (date_interval_pattern).
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * A skeleton
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <ol>
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * only keeps the field pattern letter and ignores all other parts
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in a pattern, such as space, punctuations, and string literals.
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * hides the order of fields.
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <li>
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * might hide a field's pattern letter length.
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For those non-digit calendar fields, the pattern letter length is
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * important, such as MMM, MMMM, and MMMMM; EEE and EEEE,
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and the field's pattern letter length is honored.
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For the digit calendar fields,  such as M or MM, d or dd, yy or yyyy,
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the field pattern length is ignored and the best match, which is defined
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in date time patterns, will be returned without honor the field pattern
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * letter length in skeleton.
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </ol>
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The calendar fields we support for interval formatting are:
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * year, month, date, day-of-week, am-pm, hour, hour-of-day, and minute.
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Those calendar fields can be defined in the following order:
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * year >  month > date > am-pm > hour >  minute
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The largest different calendar fields between 2 calendars is the
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * first different calendar field in above order.
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example: the largest different calendar fields between "Jan 10, 2007"
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * and "Feb 20, 2008" is year.
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There is a set of pre-defined static skeleton strings.
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are pre-defined interval patterns for those pre-defined skeletons
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in locales' resource files.
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, for a skeleton YEAR_ABBR_MONTH_DAY, which is  "yMMMd",
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in  en_US, if the largest different calendar field between date1 and date2
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * is "year", the date interval pattern  is "MMM d, yyyy - MMM d, yyyy",
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as "Jan 10, 2007 - Jan 10, 2008".
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the largest different calendar field between date1 and date2 is "month",
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the date interval pattern is "MMM d - MMM d, yyyy",
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as "Jan 10 - Feb 10, 2007".
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the largest different calendar field between date1 and date2 is "day",
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007".
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For date skeleton, the interval patterns when year, or month, or date is
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * different are defined in resource files.
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For time skeleton, the interval patterns when am/pm, or hour, or minute is
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * different are defined in resource files.
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * There are 2 dates in interval pattern. For most locales, the first date
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * in an interval pattern is the earlier date. There might be a locale in which
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the first date in an interval pattern is the later date.
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * We use fallback format for the default order for the locale.
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, if the fallback format is "{0} - {1}", it means
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the first date in the interval pattern for this locale is earlier date.
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * If the fallback format is "{1} - {0}", it means the first date is the
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * later date.
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For a particular interval pattern, the default order can be overriden
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by prefixing "latestFirst:" or "earliestFirst:" to the interval pattern.
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * For example, if the fallback format is "{0}-{1}",
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * but for skeleton "yMMMd", the interval pattern when day is different is
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * "latestFirst:d-d MMM yy", it means by default, the first date in interval
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * pattern is the earlier date. But for skeleton "yMMMd", when day is different,
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the first date in "d-d MMM yy" is the later date.
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * The recommended way to create a DateIntervalFormat object is to pass in
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the locale.
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * By using a Locale parameter, the DateIntervalFormat object is
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * initialized with the pre-defined interval patterns for a given or
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * default locale.
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Users can also create DateIntervalFormat object
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by supplying their own interval patterns.
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * It provides flexibility for power usage.
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * After a DateIntervalInfo object is created, clients may modify
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * the interval patterns using setIntervalPattern function as so desired.
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Currently, users can only set interval patterns when the following
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * calendar fields are different: ERA, YEAR, MONTH, DATE,  DAY_OF_MONTH,
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DAY_OF_WEEK, AM_PM,  HOUR, HOUR_OF_DAY, and MINUTE.
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Interval patterns when other calendar fields are different is not supported.
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalInfo objects are cloneable.
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * When clients obtain a DateIntervalInfo object,
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * they can feel free to modify it as necessary.
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <P>
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DateIntervalInfo are not expected to be subclassed.
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Data for a calendar is loaded out of resource bundles.
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Through ICU 4.4, date interval patterns are only supported in the Gregoria
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * calendar; non-Gregorian calendars are supported from ICU 4.4.1.
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @stable ICU 4.0
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>, Serializable {
153f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
154f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
155f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * Set to pattern for debugging, otherwise make null to strip dead code
156f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
157f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    private static final String DEBUG_SKELETON = null; // "yMMM";
158f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* Save the interval pattern information.
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Interval pattern consists of 2 single date patterns and the separator.
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example, interval pattern "MMM d - MMM d, yyyy" consists
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a single date pattern "MMM d", another single date pattern "MMM d, yyyy",
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and a separator "-".
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Also, the first date appears in an interval pattern could be
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * the earlier date or the later date.
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * And such information is saved in the interval pattern as well.
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final int currentSerialVersion = 1;
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * PatternInfo class saves the first and second part of interval pattern,
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and whether the interval pattern is earlier date first.
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static final class PatternInfo implements Cloneable, Serializable {
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        static final int currentSerialVersion = 1;
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private static final long serialVersionUID = 1;
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final String fIntervalPatternFirstPart;
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final String fIntervalPatternSecondPart;
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /*
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Whether the first date in interval pattern is later date or not.
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Fallback format set the default ordering.
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * And for a particular interval pattern, the order can be
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * overriden by prefixing the interval pattern with "latestFirst:" or
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * "earliestFirst:"
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For example, given 2 date, Jan 10, 2007 to Feb 10, 2007.
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * if the fallback format is "{0} - {1}",
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * and the pattern is "d MMM - d MMM yyyy", the interval format is
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * "10 Jan - 10 Feb, 2007".
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * If the pattern is "latestFirst:d MMM - d MMM yyyy",
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the interval format is "10 Feb - 10 Jan, 2007"
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final boolean fFirstDateInPtnIsLaterDate;
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * constructor
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.0
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public PatternInfo(String firstPart, String secondPart,
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                           boolean firstDateInPtnIsLaterDate) {
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatternFirstPart = firstPart;
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatternSecondPart = secondPart;
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fFirstDateInPtnIsLaterDate = firstDateInPtnIsLaterDate;
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * accessor
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.0
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String getFirstPart() {
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fIntervalPatternFirstPart;
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * accessor
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.0
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String getSecondPart() {
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fIntervalPatternSecondPart;
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * accessor
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.0
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean firstDateInPtnIsLaterDate() {
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fFirstDateInPtnIsLaterDate;
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Override equals
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.0
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean equals(Object a) {
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( a instanceof PatternInfo ) {
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternInfo patternInfo = (PatternInfo)a;
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return Utility.objectEquals(fIntervalPatternFirstPart, patternInfo.fIntervalPatternFirstPart) &&
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                       Utility.objectEquals(fIntervalPatternSecondPart, fIntervalPatternSecondPart) &&
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                       fFirstDateInPtnIsLaterDate == patternInfo.fFirstDateInPtnIsLaterDate;
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Override hashcode
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * @stable ICU 4.0
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int hashCode() {
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int hash = fIntervalPatternFirstPart != null ? fIntervalPatternFirstPart.hashCode() : 0;
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (fIntervalPatternSecondPart != null) {
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                hash ^= fIntervalPatternSecondPart.hashCode();
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (fFirstDateInPtnIsLaterDate) {
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                hash ^= -1;
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return hash;
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
258f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
259f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        /**
260f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * {@inheritDoc}
261f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * @internal
262f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         * @deprecated This API is ICU internal only.
263f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert         */
264f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        @Deprecated
265f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        @Override
266f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        public String toString() {
267f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            return "{first=«" + fIntervalPatternFirstPart + "», second=«" + fIntervalPatternSecondPart + "», reversed:" + fFirstDateInPtnIsLaterDate + "}";
268f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // Following is package protected since
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // it is shared with DateIntervalFormat.
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static final String[] CALENDAR_FIELD_TO_PATTERN_LETTER =
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "G", "y", "M",
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "w", "W", "d",
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "D", "E", "F",
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        "a", "h", "H",
279f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        "m", "s", "S",  // MINUTE, SECOND, MILLISECOND
280f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        "z", " ", "Y",  // ZONE_OFFSET, DST_OFFSET, YEAR_WOY
281f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        "e", "u", "g",  // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY
282f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        "A", " ", " ",  // MILLISECONDS_IN_DAY, IS_LEAP_MONTH.
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    };
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final long serialVersionUID = 1;
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static final int MINIMUM_SUPPORTED_CALENDAR_FIELD =
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                          Calendar.MINUTE;
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    //private static boolean DEBUG = true;
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String FALLBACK_STRING = "fallback";
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String LATEST_FIRST_PREFIX = "latestFirst:";
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static String EARLIEST_FIRST_PREFIX = "earliestFirst:";
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // DateIntervalInfo cache
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private final static ICUCache<String, DateIntervalInfo> DIICACHE = new SimpleCache<String, DateIntervalInfo>();
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
298f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // default interval pattern on the skeleton, {0} - {1}
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String fFallbackIntervalPattern;
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // default order
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private boolean fFirstDateInPtnIsLaterDate = false;
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // HashMap( skeleton, HashMap(largest_different_field, pattern) )
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Map<String, Map<String, PatternInfo>> fIntervalPatterns = null;
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient volatile boolean frozen = false;
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // If true, fIntervalPatterns should not be modified in-place because it
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // is shared with other objects. Unlike frozen which is always true once
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // set to true, this field can go from true to false as long as frozen is
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    // false.
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private transient boolean fIntervalPatternsReadOnly = false;
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Create empty instance.
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It does not initialize any interval patterns except
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * that it initialize default fall-back pattern as "{0} - {1}",
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * which can be reset by setFallbackIntervalPattern().
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It should be followed by setFallbackIntervalPattern() and
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * setIntervalPattern(),
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and is recommended to be used only for power users who
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * wants to create their own interval patterns and use them to create
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * date interval formatter.
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo()
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatterns = new HashMap<String, Map<String, PatternInfo>>();
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFallbackIntervalPattern = "{0} \u2013 {1}";
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct DateIntervalInfo for the given locale,
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale  the interval patterns are loaded from the appropriate
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                calendar data (specified calendar or default calendar)
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                in this locale.
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo(ULocale locale)
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        initializeData(locale);
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct DateIntervalInfo for the given JDK locale,
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale  the interval patterns are loaded from the appropriate
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                calendar data (specified calendar or default calendar)
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                in this locale.
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @draft ICU 54
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @provisional This API might change or be removed in a future release.
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo(Locale locale)
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        this(ULocale.forLocale(locale));
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize the DateIntervalInfo from locale
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param locale   the given locale.
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void initializeData(ULocale locale)
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String key = locale.toString();
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalInfo dii = DIICACHE.get(key);
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( dii == null ) {
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // initialize data from scratch
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            setup(locale);
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Marking fIntervalPatterns read-only makes cloning cheaper.
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatternsReadOnly = true;
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // We freeze what goes in the cache without freezing this object.
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DIICACHE.put(key, ((DateIntervalInfo) clone()).freeze());
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else {
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            initializeFromReadOnlyPatterns(dii);
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize this object
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param dii must have read-only fIntervalPatterns.
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void initializeFromReadOnlyPatterns(DateIntervalInfo dii) {
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFallbackIntervalPattern = dii.fFallbackIntervalPattern;
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFirstDateInPtnIsLaterDate = dii.fFirstDateInPtnIsLaterDate;
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatterns = dii.fIntervalPatterns;
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatternsReadOnly = true;
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Initialize DateIntervalInfo from calendar data
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param calData  calendar data
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void setup(ULocale locale) {
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int DEFAULT_HASH_SIZE = 19;
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatterns = new HashMap<String, Map<String, PatternInfo>>(DEFAULT_HASH_SIZE);
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // initialize to guard if there is no interval date format defined in
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // resource files
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFallbackIntervalPattern = "{0} \u2013 {1}";
408f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        HashSet<String> skeletonKeyPairs = new HashSet<String>();
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // loop through all locales to get all available skeletons'
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // interval format
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale currentLocale = locale;
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // Get the correct calendar type
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String calendarTypeToUse = locale.getKeywordValue("calendar");
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( calendarTypeToUse == null ) {
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String[] preferredCalendarTypes = Calendar.getKeywordValuesForLocale("calendar", locale, true);
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                calendarTypeToUse = preferredCalendarTypes[0]; // the most preferred calendar
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( calendarTypeToUse == null ) {
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                calendarTypeToUse = "gregorian"; // fallback
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            do {
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String name = currentLocale.getName();
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( name.length() == 0 ) {
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle rb = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME,currentLocale);
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Note:
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //      ICU4J getWithFallback does not work well when
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //      1) A nested table is an alias to /LOCALE/...
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //      2) getWithFallback is called multiple times for going down hierarchical resource path
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //      #9987 resolved the issue of alias table when full path is specified in getWithFallback,
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //      but there is no easy solution when the equivalent operation is done by multiple operations.
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                //      This issue is addressed in #9964.
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//                ICUResourceBundle calBundle = rb.getWithFallback("calendar");
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert//                ICUResourceBundle calTypeBundle = calBundle.getWithFallback(calendarTypeToUse);
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ICUResourceBundle itvDtPtnResource =rb.getWithFallback("calendar/" + calendarTypeToUse + "/intervalFormats");
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // look for fallback first, since it establishes the default order
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String fallback = itvDtPtnResource.getStringWithFallback(FALLBACK_STRING);
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                setFallbackIntervalPattern(fallback);
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int size = itvDtPtnResource.getSize();
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                for ( int index = 0; index < size; ++index ) {
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    String skeleton = itvDtPtnResource.get(index).getKey();
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if ( skeleton.compareTo(FALLBACK_STRING) == 0 ) {
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        continue;
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ICUResourceBundle intervalPatterns = (ICUResourceBundle)itvDtPtnResource.get(skeleton);
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    int ptnNum = intervalPatterns.getSize();
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    for ( int ptnIndex = 0; ptnIndex < ptnNum; ++ptnIndex) {
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String key = intervalPatterns.get(ptnIndex).getKey();
452f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
453f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        // hack because Relation isn't available, and it will probably port more easily than Pair<String,String>
454f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        String skeletonKeyPair = skeleton + "\u0001" + key;
455f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        if (skeletonKeyPairs.contains(skeletonKeyPair)) {
456f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            continue;
457f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        }
458f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                        skeletonKeyPairs.add(skeletonKeyPair);
459f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        String pattern = intervalPatterns.get(ptnIndex).getString();
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        int calendarField = -1; // initialize with an invalid value.
4636598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.YEAR])) {
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            calendarField = Calendar.YEAR;
4656598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MONTH])) {
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            calendarField = Calendar.MONTH;
4676598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE])) {
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            calendarField = Calendar.DATE;
4696598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.AM_PM]) ) {
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            calendarField = Calendar.AM_PM;
4716598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR]) ) {
4726598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                            calendarField = Calendar.HOUR;
4736598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                            key = CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR];
4746598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR_OF_DAY]) ) {
4756598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                            // HOUR_OF_DAY is 'H' for 24 hour clock; HOUR is 'h' for 12 hour clock. We use HOUR
4766598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                            // here instead of HOUR_OF_DAY because setIntervalPatternInternally understand HOUR.
4776598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                            calendarField = Calendar.HOUR;
4786598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                            key = CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR];
4796598e9f0af29bfb1f2e0c97cce1a2b4432f9cd62Travis Keep                        } else if ( key.equals(CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.MINUTE]) ) {
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                            calendarField = Calendar.MINUTE;
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        if ( calendarField != -1 ) {
484f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            if (DEBUG_SKELETON != null && DEBUG_SKELETON.equals(skeleton)) {
485f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                Map<String, PatternInfo> oldValue = fIntervalPatterns.get(skeleton);
486f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                setIntervalPatternInternally(skeleton, key, pattern);
487f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                Map<String, PatternInfo> newValue = fIntervalPatterns.get(skeleton);
488f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                if (!Utility.objectEquals(oldValue, newValue)) {
489f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                    System.out.println("\n" + currentLocale + ", skeleton: " + skeleton + ", oldValue: " + oldValue + ", newValue: " + newValue);
490f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                }
491f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            } else {
492f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                                setIntervalPatternInternally(skeleton, key, pattern);
493f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                            }
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        }
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                try {
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    UResourceBundle parentNameBundle = rb.get("%%Parent");
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    currentLocale = new ULocale(parentNameBundle.getString());
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } catch (MissingResourceException e) {
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    currentLocale = currentLocale.getFallback();
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } while (currentLocale != null && !currentLocale.getBaseName().equals("root"));
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch ( MissingResourceException e) {
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // ok, will fallback to {data0} - {date1}
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Split interval patterns into 2 part.
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPattern  interval pattern
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the index in interval pattern which split the pattern into 2 part
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static int splitPatternInto2Part(String intervalPattern) {
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean inQuote = false;
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        char prevCh = 0;
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int count = 0;
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* repeatedPattern used to record whether a pattern has already seen.
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert           It is a pattern applies to first calendar if it is first time seen,
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert           otherwise, it is a pattern applies to the second calendar
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] patternRepeated = new int[58];
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int PATTERN_CHAR_BASE = 0x41;
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /* loop through the pattern string character by character looking for
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the first repeated pattern letter, which breaks the interval pattern
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * into 2 parts.
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int i;
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean foundRepetition = false;
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (i = 0; i < intervalPattern.length(); ++i) {
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            char ch = intervalPattern.charAt(i);
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch != prevCh && count > 0) {
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // check the repeativeness of pattern letter
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int repeated = patternRepeated[prevCh - PATTERN_CHAR_BASE];
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( repeated == 0 ) {
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    patternRepeated[prevCh - PATTERN_CHAR_BASE] = 1;
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    foundRepetition = true;
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    break;
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                count = 0;
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (ch == '\'') {
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // Consecutive single quotes are a single quote literal,
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // either outside of quotes or between quotes
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ((i+1) < intervalPattern.length() &&
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    intervalPattern.charAt(i+1) == '\'') {
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    ++i;
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    inQuote = ! inQuote;
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // ch is a date-time pattern character
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                prevCh = ch;
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ++count;
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // check last pattern char, distinguish
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // "dd MM" ( no repetition ),
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // "d-d"(last char repeated ), and
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // "d-d MM" ( repetition found )
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( count > 0 && foundRepetition == false ) {
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( patternRepeated[prevCh - PATTERN_CHAR_BASE] == 0 ) {
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                count = 0;
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return (i - count);
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Provides a way for client to build interval patterns.
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * User could construct DateIntervalInfo by providing
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a list of skeletons and their patterns.
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <P>
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * For example:
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <pre>
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DateIntervalInfo dIntervalInfo = new DateIntervalInfo();
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * dIntervalInfo.setIntervalPattern("yMd", Calendar.YEAR, "'from' yyyy-M-d 'to' yyyy-M-d");
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * dIntervalInfo.setIntervalPattern("yMMMd", Calendar.MONTH, "'from' yyyy MMM d 'to' MMM d");
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * dIntervalInfo.setIntervalPattern("yMMMd", Calendar.DAY, "yyyy MMM d-d");
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * dIntervalInfo.setFallbackIntervalPattern("{0} ~ {1}");
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * </pre>
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Restriction:
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Currently, users can only set interval patterns when the following
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * calendar fields are different: ERA, YEAR, MONTH, DATE,  DAY_OF_MONTH,
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * DAY_OF_WEEK, AM_PM,  HOUR, HOUR_OF_DAY, and MINUTE.
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Interval patterns when other calendar fields are different are
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * not supported.
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton         the skeleton on which interval pattern based
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param lrgDiffCalUnit   the largest different calendar unit.
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPattern  the interval pattern on the largest different
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                         calendar unit.
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                         For example, if lrgDiffCalUnit is
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                         "year", the interval pattern for en_US when year
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                         is different could be "'from' yyyy 'to' yyyy".
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException  if setting interval pattern on
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                            a calendar field that is smaller
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                            than the MINIMUM_SUPPORTED_CALENDAR_FIELD
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws UnsupportedOperationException  if the object is frozen
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setIntervalPattern(String skeleton,
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                   int lrgDiffCalUnit,
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                   String intervalPattern)
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( frozen ) {
6177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new UnsupportedOperationException("no modification is allowed after DII is frozen");
6187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( lrgDiffCalUnit > MINIMUM_SUPPORTED_CALENDAR_FIELD ) {
6207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("calendar field is larger than MINIMUM_SUPPORTED_CALENDAR_FIELD");
6217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (fIntervalPatternsReadOnly) {
6237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatterns = cloneIntervalPatterns(fIntervalPatterns);
6247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatternsReadOnly = false;
6257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo ptnInfo = setIntervalPatternInternally(skeleton,
6277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          CALENDAR_FIELD_TO_PATTERN_LETTER[lrgDiffCalUnit],
6287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                          intervalPattern);
6297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( lrgDiffCalUnit == Calendar.HOUR_OF_DAY ) {
6307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            setIntervalPattern(skeleton,
6317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.AM_PM],
6327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               ptnInfo);
6337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            setIntervalPattern(skeleton,
6347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR],
6357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               ptnInfo);
6367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( lrgDiffCalUnit == Calendar.DAY_OF_MONTH ||
6377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    lrgDiffCalUnit == Calendar.DAY_OF_WEEK ) {
6387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            setIntervalPattern(skeleton,
6397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE],
6407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                               ptnInfo);
6417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* Set Interval pattern.
6467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It generates the interval pattern info,
6487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * afer which, not only sets the interval pattern info into the hash map,
6497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * but also returns the interval pattern info to the caller
6507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * so that caller can re-use it.
6517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton         skeleton on which the interval pattern based
6537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param lrgDiffCalUnit   the largest different calendar unit.
6547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPattern  the interval pattern on the largest different
6557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                         calendar unit.
6567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return the interval pattern pattern information
6577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private PatternInfo setIntervalPatternInternally(String skeleton,
6597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                String lrgDiffCalUnit,
6607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                                String intervalPattern) {
6617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
6627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean emptyHash = false;
6637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (patternsOfOneSkeleton == null) {
6647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            patternsOfOneSkeleton = new HashMap<String, PatternInfo>();
6657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            emptyHash = true;
6667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean order = fFirstDateInPtnIsLaterDate;
6687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // check for "latestFirst:" or "earliestFirst:" prefix
6697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( intervalPattern.startsWith(LATEST_FIRST_PREFIX) ) {
6707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            order = true;
6717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int prefixLength = LATEST_FIRST_PREFIX.length();
6727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPattern = intervalPattern.substring(prefixLength, intervalPattern.length());
6737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } else if ( intervalPattern.startsWith(EARLIEST_FIRST_PREFIX) ) {
6747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            order = false;
6757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int earliestFirstLength = EARLIEST_FIRST_PREFIX.length();
6767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            intervalPattern = intervalPattern.substring(earliestFirstLength, intervalPattern.length());
6777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        PatternInfo itvPtnInfo = genPatternInfo(intervalPattern, order);
6797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        patternsOfOneSkeleton.put(lrgDiffCalUnit, itvPtnInfo);
6817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( emptyHash == true ) {
6827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fIntervalPatterns.put(skeleton, patternsOfOneSkeleton);
6837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return itvPtnInfo;
6867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /* Set Interval pattern.
6907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
6917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton         skeleton on which the interval pattern based
6927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param lrgDiffCalUnit   the largest different calendar unit.
6937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param ptnInfo          interval pattern infomration
6947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
6957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private void setIntervalPattern(String skeleton,
6967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    String lrgDiffCalUnit,
6977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                    PatternInfo ptnInfo) {
6987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
6997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        patternsOfOneSkeleton.put(lrgDiffCalUnit, ptnInfo);
7007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Break interval patterns as 2 part and save them into pattern info.
7057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param intervalPattern  interval pattern
7067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param laterDateFirst   whether the first date in intervalPattern
7077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                         is earlier date or later date
7087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return                 pattern info object
709f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @internal
710f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @deprecated This API is ICU internal only.
7117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
712f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Deprecated
713f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public static PatternInfo genPatternInfo(String intervalPattern,
7147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                      boolean laterDateFirst) {
7157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int splitPoint = splitPatternInto2Part(intervalPattern);
7167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String firstPart = intervalPattern.substring(0, splitPoint);
7187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String secondPart = null;
7197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( splitPoint < intervalPattern.length() ) {
7207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            secondPart = intervalPattern.substring(splitPoint, intervalPattern.length());
7217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new PatternInfo(firstPart, secondPart, laterDateFirst);
7247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Get the interval pattern given the largest different calendar field.
7297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton   the skeleton
7307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param field      the largest different calendar field
7317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return interval pattern  return null if interval pattern is not found.
7327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException  if getting interval pattern on
7337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                            a calendar field that is smaller
7347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                            than the MINIMUM_SUPPORTED_CALENDAR_FIELD
7357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
7367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public PatternInfo getIntervalPattern(String skeleton, int field)
7387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
7397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( field > MINIMUM_SUPPORTED_CALENDAR_FIELD ) {
7407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("no support for field less than MINUTE");
7417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
7437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( patternsOfOneSkeleton != null ) {
7447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            PatternInfo intervalPattern = patternsOfOneSkeleton.
7457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                get(CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
7467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( intervalPattern != null ) {
7477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return intervalPattern;
7487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
7497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return null;
7517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Get the fallback interval pattern.
7577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return fallback interval pattern
7587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
7597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String getFallbackIntervalPattern()
7617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
7627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return fFallbackIntervalPattern;
7637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Re-set the fallback interval pattern.
7687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * In construction, default fallback pattern is set as "{0} - {1}".
7707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * And constructor taking locale as parameter will set the
7717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * fallback pattern as what defined in the locale resource file.
7727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This method provides a way for user to replace the fallback pattern.
7747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fallbackPattern                 fall-back interval pattern.
7767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws UnsupportedOperationException  if the object is frozen
7777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @throws IllegalArgumentException       if there is no pattern {0} or
7787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                                        pattern {1} in fallbakckPattern
7797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
7807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
7817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
7827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public void setFallbackIntervalPattern(String fallbackPattern)
7837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
7847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( frozen ) {
7857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new UnsupportedOperationException("no modification is allowed after DII is frozen");
7867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int firstPatternIndex = fallbackPattern.indexOf("{0}");
7887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int secondPatternIndex = fallbackPattern.indexOf("{1}");
7897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
7907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("no pattern {0} or pattern {1} in fallbackPattern");
7917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( firstPatternIndex > secondPatternIndex ) {
7937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            fFirstDateInPtnIsLaterDate = true;
7947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
7957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fFallbackIntervalPattern = fallbackPattern;
7967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
7977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
7997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Get default order -- whether the first date in pattern is later date
8017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *                      or not.
8027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
8037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * return default date ordering in interval pattern. TRUE if the first date
8047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        in pattern is later date, FALSE otherwise.
8057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
8067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean getDefaultOrder()
8087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return fFirstDateInPtnIsLaterDate;
8107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate. Clone this object.
8157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return     a copy of the object
8167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU4.0
8177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object clone()
8197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( frozen ) {
8217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this;
8227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return cloneUnfrozenDII();
8247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
8287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Clone an unfrozen DateIntervalInfo object.
8297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return     a copy of the object
8307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private Object cloneUnfrozenDII() //throws IllegalStateException
8327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    {
8337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        try {
8347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateIntervalInfo other = (DateIntervalInfo) super.clone();
8357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            other.fFallbackIntervalPattern=fFallbackIntervalPattern;
8367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            other.fFirstDateInPtnIsLaterDate = fFirstDateInPtnIsLaterDate;
8377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (fIntervalPatternsReadOnly) {
8387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                other.fIntervalPatterns = fIntervalPatterns;
8397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                other.fIntervalPatternsReadOnly = true;
8407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
8417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                other.fIntervalPatterns = cloneIntervalPatterns(fIntervalPatterns);
8427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                other.fIntervalPatternsReadOnly = false;
8437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            other.frozen = false;
8457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return other;
8467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        } catch ( CloneNotSupportedException e ) {
8477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:OFF
8487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new  ICUCloneNotSupportedException("clone is not supported", e);
8497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ///CLOVER:ON
8507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static Map<String, Map<String, PatternInfo>> cloneIntervalPatterns(
8547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, Map<String, PatternInfo>> patterns) {
8557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Map<String, Map<String, PatternInfo>> result = new HashMap<String, Map<String, PatternInfo>>();
8567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Entry<String, Map<String, PatternInfo>> skeletonEntry : patterns.entrySet()) {
8577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String skeleton = skeletonEntry.getKey();
8587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, PatternInfo> patternsOfOneSkeleton = skeletonEntry.getValue();
8597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Map<String, PatternInfo> oneSetPtn = new HashMap<String, PatternInfo>();
8607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (Entry<String, PatternInfo> calEntry : patternsOfOneSkeleton.entrySet()) {
8617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String calField = calEntry.getKey();
8627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                PatternInfo value = calEntry.getValue();
8637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                oneSetPtn.put(calField, value);
8647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
8657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.put(skeleton, oneSetPtn);
8667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
8677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
8687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate for Freezable
8747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
8757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean isFrozen() {
8777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return frozen;
8787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate for Freezable
8827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
8837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo freeze() {
8857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        fIntervalPatternsReadOnly = true;
8867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        frozen = true;
8877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return this;
8887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
8917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Boilerplate for Freezable
8927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.4
8937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public DateIntervalInfo cloneAsThawed() {
8957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        DateIntervalInfo result = (DateIntervalInfo) (this.cloneUnfrozenDII());
8967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
8977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
8987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
8997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
9017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Parse skeleton, save each field's width.
9027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * It is used for looking for best match skeleton,
9037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * and adjust pattern field width.
9047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeleton            skeleton to be parsed
9057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param skeletonFieldWidth  parsed skeleton field width
9067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    static void parseSkeleton(String skeleton, int[] skeletonFieldWidth) {
9087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int PATTERN_CHAR_BASE = 0x41;
9097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for ( int i = 0; i < skeleton.length(); ++i ) {
9107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ++skeletonFieldWidth[skeleton.charAt(i) - PATTERN_CHAR_BASE];
9117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
9177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Check whether one field width is numeric while the other is string.
9187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TODO (xji): make it general
9207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param fieldWidth          one field width
9227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param anotherFieldWidth   another field width
9237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param patternLetter       pattern letter char
9247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return true if one field width is numeric and the other is string,
9257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         false otherwise.
9267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private static boolean stringNumeric(int fieldWidth,
9287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         int anotherFieldWidth,
9297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         char patternLetter) {
9307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( patternLetter == 'M' ) {
9317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( fieldWidth <= 2 && anotherFieldWidth > 2 ||
9327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                 fieldWidth > 2 && anotherFieldWidth <= 2 ) {
9337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
9347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
9377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
9387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /*
9417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * given an input skeleton, get the best match skeleton
9427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * which has pre-defined interval pattern in resource file.
9437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
9447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * TODO (xji): set field weight or
9457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *             isolate the funtionality in DateTimePatternGenerator
9467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @param  inputSkeleton        input skeleton
9477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return 0, if there is exact match for input skeleton
9487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         1, if there is only field width difference between
9497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the best match and the input skeleton
9507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *         2, the only field difference is 'v' and 'z'
9517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *        -1, if there is calendar field difference between
9527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *            the best match and the input skeleton
9537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
9547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    DateIntervalFormat.BestMatchInfo getBestSkeleton(String inputSkeleton) {
9557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String bestSkeleton = inputSkeleton;
9567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] inputSkeletonFieldWidth = new int[58];
9577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int[] skeletonFieldWidth = new int[58];
9587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int DIFFERENT_FIELD = 0x1000;
9607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int STRING_NUMERIC_DIFFERENCE = 0x100;
9617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        final int BASE = 0x41;
9627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // TODO: this is a hack for 'v' and 'z'
9647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // resource bundle only have time skeletons ending with 'v',
9657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // but not for time skeletons ending with 'z'.
9667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        boolean replaceZWithV = false;
9677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( inputSkeleton.indexOf('z') != -1 ) {
9687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            inputSkeleton = inputSkeleton.replace('z', 'v');
9697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            replaceZWithV = true;
9707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
9717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
9727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
9737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int bestDistance = Integer.MAX_VALUE;
9747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 0 means exact the same skeletons;
9757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 1 means having the same field, but with different length,
9767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // 2 means only z/v differs
9777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // -1 means having different field.
9787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int bestFieldDifference = 0;
9797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String skeleton : fIntervalPatterns.keySet()) {
9807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // clear skeleton field width
9817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for ( int i = 0; i < skeletonFieldWidth.length; ++i ) {
9827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                skeletonFieldWidth[i] = 0;
9837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
9847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            parseSkeleton(skeleton, skeletonFieldWidth);
9857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // calculate distance
9867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int distance = 0;
9877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int fieldDifference = 1;
9887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for ( int i = 0; i < inputSkeletonFieldWidth.length; ++i ) {
9897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int inputFieldWidth = inputSkeletonFieldWidth[i];
9907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int fieldWidth = skeletonFieldWidth[i];
9917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( inputFieldWidth == fieldWidth ) {
9927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    continue;
9937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
9947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if ( inputFieldWidth == 0 ) {
9957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fieldDifference = -1;
9967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distance += DIFFERENT_FIELD;
9977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if ( fieldWidth == 0 ) {
9987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fieldDifference = -1;
9997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distance += DIFFERENT_FIELD;
10007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else if (stringNumeric(inputFieldWidth, fieldWidth,
10017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                                         (char)(i+BASE) ) ) {
10027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distance += STRING_NUMERIC_DIFFERENCE;
10037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
10047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    distance += Math.abs(inputFieldWidth - fieldWidth);
10057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
10067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( distance < bestDistance ) {
10087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestSkeleton = skeleton;
10097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestDistance = distance;
10107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestFieldDifference = fieldDifference;
10117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if ( distance == 0 ) {
10137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                bestFieldDifference = 0;
10147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                break;
10157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
10167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( replaceZWithV && bestFieldDifference != -1 ) {
10187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            bestFieldDifference = 2;
10197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return new DateIntervalFormat.BestMatchInfo(bestSkeleton, bestFieldDifference);
10217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Override equals
10257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
10267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public boolean equals(Object a) {
10287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if ( a instanceof DateIntervalInfo ) {
10297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            DateIntervalInfo dtInfo = (DateIntervalInfo)a;
10307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return fIntervalPatterns.equals(dtInfo.fIntervalPatterns);
10317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return false;
10337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Override hashcode
10377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @stable ICU 4.0
10387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int hashCode() {
10407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return fIntervalPatterns.hashCode();
10417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
10427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
10437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
10447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @internal CLDR
10457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @deprecated This API is ICU internal only.
10467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
10477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    @Deprecated
10487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Map<String,Set<String>> getPatterns() {
10497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        LinkedHashMap<String,Set<String>> result = new LinkedHashMap<String,Set<String>>();
10507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (Entry<String, Map<String, PatternInfo>> entry : fIntervalPatterns.entrySet()) {
10517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            result.put(entry.getKey(), new LinkedHashSet<String>(entry.getValue().keySet()));
10527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
10537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
10547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1055f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert
1056f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
1057f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * Get the internal patterns, with a deep clone for safety.
1058f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @internal CLDR
1059f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @deprecated This API is ICU internal only.
1060f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
1061f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Deprecated
1062f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public Map<String, Map<String, PatternInfo>> getRawPatterns() {
1063f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        LinkedHashMap<String, Map<String, PatternInfo>> result = new LinkedHashMap<String, Map<String, PatternInfo>>();
1064f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        for (Entry<String, Map<String, PatternInfo>> entry : fIntervalPatterns.entrySet()) {
1065f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            result.put(entry.getKey(), new LinkedHashMap<String, PatternInfo>(entry.getValue()));
1066f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
1067f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        return result;
1068f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
10697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}// end class DateIntervalInfo
1070