12ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/* GENERATED SOURCE. DO NOT MODIFY. */
2f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert// © 2016 and later: Unicode, Inc. and others.
3f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
42ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/*
52ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
61c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * Copyright (C) 2008-2016, International Business Machines Corporation and
71c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * others. All Rights Reserved.
82ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
92ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpackage android.icu.text;
122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.Serializable;
142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashMap;
152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashSet;
162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.LinkedHashMap;
172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.LinkedHashSet;
182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Locale;
192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Map;
202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Map.Entry;
212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.MissingResourceException;
222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Set;
232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.ICUCache;
251c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubertimport android.icu.impl.ICUData;
262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.ICUResourceBundle;
272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.SimpleCache;
281c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubertimport android.icu.impl.UResource;
291c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubertimport android.icu.impl.UResource.Key;
301c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubertimport android.icu.impl.UResource.Value;
312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.Utility;
322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.Calendar;
332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.Freezable;
342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ICUCloneNotSupportedException;
351c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubertimport android.icu.util.ICUException;
362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ULocale;
372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.UResourceBundle;
382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/**
402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * DateIntervalInfo is a public class for encapsulating localizable
412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * date time interval patterns. It is used by DateIntervalFormat.
422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For most users, ordinary use of DateIntervalFormat does not need to create
452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * DateIntervalInfo object directly.
462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * DateIntervalFormat will take care of it when creating a date interval
472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * formatter when user pass in skeleton and locale.
482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For power users, who want to create their own date interval patterns,
512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * or want to re-set date interval patterns, they could do so by
525cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert * directly creating DateIntervalInfo and manipulating it.
532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Logically, the interval patterns are mappings
562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * from (skeleton, the_largest_different_calendar_field)
572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * to (date_interval_pattern).
582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
601c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * A skeleton
612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <ol>
622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <li>
631c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * only keeps the field pattern letter and ignores all other parts
642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * in a pattern, such as space, punctuations, and string literals.
652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <li>
661c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * hides the order of fields.
672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <li>
682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * might hide a field's pattern letter length.
692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
701c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * For those non-digit calendar fields, the pattern letter length is
711c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * important, such as MMM, MMMM, and MMMMM; EEE and EEEE,
722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * and the field's pattern letter length is honored.
731c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert *
741c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * For the digit calendar fields,  such as M or MM, d or dd, yy or yyyy,
751c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * the field pattern length is ignored and the best match, which is defined
762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * in date time patterns, will be returned without honor the field pattern
772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * letter length in skeleton.
782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * </ol>
792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * The calendar fields we support for interval formatting are:
822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * year, month, date, day-of-week, am-pm, hour, hour-of-day, minute, and
832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * second (though we do not currently have specific intervalFormat data for
841c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * skeletons with seconds).
852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Those calendar fields can be defined in the following order:
8608ae9f2909b2ec37f755dac4372553437e9d7cf6Paul Duffin * year &gt; month &gt; date &gt; am-pm &gt; hour &gt;  minute &gt; second
871c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert *
882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * The largest different calendar fields between 2 calendars is the
892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * first different calendar field in above order.
902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
911c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * For example: the largest different calendar fields between "Jan 10, 2007"
922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * and "Feb 20, 2008" is year.
931c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert *
942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * There is a set of pre-defined static skeleton strings.
962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * There are pre-defined interval patterns for those pre-defined skeletons
972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * in locales' resource files.
982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For example, for a skeleton YEAR_ABBR_MONTH_DAY, which is  "yMMMd",
991c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * in  en_US, if the largest different calendar field between date1 and date2
1001c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * is "year", the date interval pattern  is "MMM d, yyyy - MMM d, yyyy",
1012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * such as "Jan 10, 2007 - Jan 10, 2008".
1022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * If the largest different calendar field between date1 and date2 is "month",
1032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the date interval pattern is "MMM d - MMM d, yyyy",
1042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * such as "Jan 10 - Feb 10, 2007".
1052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * If the largest different calendar field between date1 and date2 is "day",
1062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the date interval pattern is ""MMM d-d, yyyy", such as "Jan 10-20, 2007".
1072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
1081c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * For date skeleton, the interval patterns when year, or month, or date is
1092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * different are defined in resource files.
1102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For time skeleton, the interval patterns when am/pm, or hour, or minute is
1112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * different are defined in resource files.
1122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
1132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
1142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
1152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * There are 2 dates in interval pattern. For most locales, the first date
1162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * in an interval pattern is the earlier date. There might be a locale in which
1172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the first date in an interval pattern is the later date.
1182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * We use fallback format for the default order for the locale.
1192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For example, if the fallback format is "{0} - {1}", it means
1202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the first date in the interval pattern for this locale is earlier date.
1211c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * If the fallback format is "{1} - {0}", it means the first date is the
1222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * later date.
1232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For a particular interval pattern, the default order can be overriden
1242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * by prefixing "latestFirst:" or "earliestFirst:" to the interval pattern.
1252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * For example, if the fallback format is "{0}-{1}",
1261c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * but for skeleton "yMMMd", the interval pattern when day is different is
1272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * "latestFirst:d-d MMM yy", it means by default, the first date in interval
1282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * pattern is the earlier date. But for skeleton "yMMMd", when day is different,
1292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the first date in "d-d MMM yy" is the later date.
1301c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert *
1312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
1321c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * The recommended way to create a DateIntervalFormat object is to pass in
1331c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * the locale.
1341c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * By using a Locale parameter, the DateIntervalFormat object is
1351c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * initialized with the pre-defined interval patterns for a given or
1362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * default locale.
1372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
1381c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * Users can also create DateIntervalFormat object
1392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * by supplying their own interval patterns.
1402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * It provides flexibility for power usage.
1412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
1422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
1432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * After a DateIntervalInfo object is created, clients may modify
1442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the interval patterns using setIntervalPattern function as so desired.
1451c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * Currently, users can only set interval patterns when the following
1461c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * calendar fields are different: ERA, YEAR, MONTH, DATE,  DAY_OF_MONTH,
1472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * DAY_OF_WEEK, AM_PM,  HOUR, HOUR_OF_DAY, MINUTE and SECOND.
1482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Interval patterns when other calendar fields are different is not supported.
1492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
1501c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * DateIntervalInfo objects are cloneable.
1511c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * When clients obtain a DateIntervalInfo object,
1522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * they can feel free to modify it as necessary.
1532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <P>
1541c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * DateIntervalInfo are not expected to be subclassed.
1551c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * Data for a calendar is loaded out of resource bundles.
1562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * Through ICU 4.4, date interval patterns are only supported in the Gregoria
1571c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert * calendar; non-Gregorian calendars are supported from ICU 4.4.1.
1582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
1592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpublic class DateIntervalInfo implements Cloneable, Freezable<DateIntervalInfo>, Serializable {
1612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /* Save the interval pattern information.
1632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Interval pattern consists of 2 single date patterns and the separator.
1642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * For example, interval pattern "MMM d - MMM d, yyyy" consists
1652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * a single date pattern "MMM d", another single date pattern "MMM d, yyyy",
1662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and a separator "-".
1671c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * Also, the first date appears in an interval pattern could be
1682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the earlier date or the later date.
1692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * And such information is saved in the interval pattern as well.
1702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static final int currentSerialVersion = 1;
1722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * PatternInfo class saves the first and second part of interval pattern,
1752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and whether the interval pattern is earlier date first.
1762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static final class PatternInfo implements Cloneable, Serializable {
1782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        static final int currentSerialVersion = 1;
1792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private static final long serialVersionUID = 1;
1802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final String fIntervalPatternFirstPart;
1812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final String fIntervalPatternSecondPart;
1822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /*
1832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Whether the first date in interval pattern is later date or not.
1842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Fallback format set the default ordering.
1851c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * And for a particular interval pattern, the order can be
1861c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * overriden by prefixing the interval pattern with "latestFirst:" or
1872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * "earliestFirst:"
1882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * For example, given 2 date, Jan 10, 2007 to Feb 10, 2007.
1891c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * if the fallback format is "{0} - {1}",
1902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * and the pattern is "d MMM - d MMM yyyy", the interval format is
1912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * "10 Jan - 10 Feb, 2007".
1921c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * If the pattern is "latestFirst:d MMM - d MMM yyyy",
1932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the interval format is "10 Feb - 10 Jan, 2007"
1942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
1952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final boolean fFirstDateInPtnIsLaterDate;
1962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1985cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * Constructs a <code>PatternInfo</code> object.
1995cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @param firstPart     The first part of interval pattern.
2005cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @param secondPart    The second part of interval pattern.
2015cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @param firstDateInPtnIsLaterDate Whether the first date in interval patter is later date or not.
2022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public PatternInfo(String firstPart, String secondPart,
2042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                           boolean firstDateInPtnIsLaterDate) {
2052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fIntervalPatternFirstPart = firstPart;
2062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fIntervalPatternSecondPart = secondPart;
2072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fFirstDateInPtnIsLaterDate = firstDateInPtnIsLaterDate;
2082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2115cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * Returns the first part of interval pattern.
2125cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @return The first part of interval pattern.
2132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String getFirstPart() {
2152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return fIntervalPatternFirstPart;
2162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2195cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * Returns the second part of interval pattern.
2205cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @return The second part of interval pattern.
2212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String getSecondPart() {
2232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return fIntervalPatternSecondPart;
2242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2275cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * Returns whether the first date in interval patter is later date or not.
2285cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @return Whether the first date in interval patter is later date or not.
2292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean firstDateInPtnIsLaterDate() {
2312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return fFirstDateInPtnIsLaterDate;
2322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2355cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * Compares the specified object with this <code>PatternInfo</code> for equality.
2365cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @param a The object to be compared.
2375cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @return <code>true</code> if the specified object is equal to this <code>PatternInfo</code>.
2382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
239f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
2402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean equals(Object a) {
241f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (a instanceof PatternInfo) {
2422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                PatternInfo patternInfo = (PatternInfo)a;
2431c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                return Utility.objectEquals(fIntervalPatternFirstPart, patternInfo.fIntervalPatternFirstPart) &&
244f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                       Utility.objectEquals(fIntervalPatternSecondPart, patternInfo.fIntervalPatternSecondPart) &&
2452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                       fFirstDateInPtnIsLaterDate == patternInfo.fFirstDateInPtnIsLaterDate;
2462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
2482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2515cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * Returns the hash code of this <code>PatternInfo</code>.
2525cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert         * @return A hash code value for this object.
2532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
254f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
2552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public int hashCode() {
2562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int hash = fIntervalPatternFirstPart != null ? fIntervalPatternFirstPart.hashCode() : 0;
2572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (fIntervalPatternSecondPart != null) {
2582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                hash ^= fIntervalPatternSecondPart.hashCode();
2592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (fFirstDateInPtnIsLaterDate) {
2612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                hash ^= -1;
2622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return hash;
2642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * {@inheritDoc}
2682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * @deprecated This API is ICU internal only.
269836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller         * @hide draft / provisional / internal are hidden on Android
2702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        @Deprecated
2722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        @Override
2732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String toString() {
2742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return "{first=«" + fIntervalPatternFirstPart + "», second=«" + fIntervalPatternSecondPart + "», reversed:" + fFirstDateInPtnIsLaterDate + "}";
2752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2781c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    // Following is package protected since
2792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // it is shared with DateIntervalFormat.
2801c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    static final String[] CALENDAR_FIELD_TO_PATTERN_LETTER =
2812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
2822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "G", "y", "M",
2831c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        "w", "W", "d",
2842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "D", "E", "F",
2852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "a", "h", "H",
2862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "m", "s", "S",  // MINUTE, SECOND, MILLISECOND
2872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "z", " ", "Y",  // ZONE_OFFSET, DST_OFFSET, YEAR_WOY
2882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "e", "u", "g",  // DOW_LOCAL, EXTENDED_YEAR, JULIAN_DAY
2892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        "A", " ", " ",  // MILLISECONDS_IN_DAY, IS_LEAP_MONTH.
2902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    };
2912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final long serialVersionUID = 1;
2941c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    private static final int MINIMUM_SUPPORTED_CALENDAR_FIELD =
2952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                                          Calendar.SECOND;
2962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    //private static boolean DEBUG = true;
2972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2981c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    private static String CALENDAR_KEY = "calendar";
2991c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    private static String INTERVAL_FORMATS_KEY = "intervalFormats";
3002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static String FALLBACK_STRING = "fallback";
3012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static String LATEST_FIRST_PREFIX = "latestFirst:";
3022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static String EARLIEST_FIRST_PREFIX = "earliestFirst:";
3032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // DateIntervalInfo cache
3052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final static ICUCache<String, DateIntervalInfo> DIICACHE = new SimpleCache<String, DateIntervalInfo>();
3062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // default interval pattern on the skeleton, {0} - {1}
3092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String fFallbackIntervalPattern;
3102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // default order
3112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private boolean fFirstDateInPtnIsLaterDate = false;
3122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // HashMap( skeleton, HashMap(largest_different_field, pattern) )
3142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private Map<String, Map<String, PatternInfo>> fIntervalPatterns = null;
3152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient volatile boolean frozen = false;
3171c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
3182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // If true, fIntervalPatterns should not be modified in-place because it
3192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // is shared with other objects. Unlike frozen which is always true once
3202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // set to true, this field can go from true to false as long as frozen is
3212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // false.
3222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient boolean fIntervalPatternsReadOnly = false;
3232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Create empty instance.
3272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * It does not initialize any interval patterns except
3282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * that it initialize default fall-back pattern as "{0} - {1}",
3292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * which can be reset by setFallbackIntervalPattern().
3302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
3311c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * It should be followed by setFallbackIntervalPattern() and
3321c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * setIntervalPattern(),
3332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and is recommended to be used only for power users who
3342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * wants to create their own interval patterns and use them to create
3352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * date interval formatter.
3362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
33793cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller     * @hide original deprecated declaration
338836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
3392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
3411c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public DateIntervalInfo()
3422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
3432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fIntervalPatterns = new HashMap<String, Map<String, PatternInfo>>();
3442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fFallbackIntervalPattern = "{0} \u2013 {1}";
3452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3481c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    /**
3492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Construct DateIntervalInfo for the given locale,
3501c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * @param locale  the interval patterns are loaded from the appropriate
3512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                calendar data (specified calendar or default calendar)
3522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                in this locale.
3532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3541c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public DateIntervalInfo(ULocale locale)
3552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
3562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        initializeData(locale);
3572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3601c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    /**
3617241d93e30f4c7c527c019cd6ca3a791ca22d9b2Fredrik Roubert     * Construct DateIntervalInfo for the given {@link java.util.Locale}.
3621c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * @param locale  the interval patterns are loaded from the appropriate
3632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                calendar data (specified calendar or default calendar)
3642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                in this locale.
3652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3661c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public DateIntervalInfo(Locale locale)
3672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
3682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this(ULocale.forLocale(locale));
3692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
3722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Initialize the DateIntervalInfo from locale
3732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale   the given locale.
3742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void initializeData(ULocale locale)
3762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
3772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String key = locale.toString();
3782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        DateIntervalInfo dii = DIICACHE.get(key);
3792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( dii == null ) {
3802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // initialize data from scratch
3812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            setup(locale);
3822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Marking fIntervalPatterns read-only makes cloning cheaper.
3832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fIntervalPatternsReadOnly = true;
3842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // We freeze what goes in the cache without freezing this object.
3852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            DIICACHE.put(key, ((DateIntervalInfo) clone()).freeze());
3862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
3872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            initializeFromReadOnlyPatterns(dii);
3882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3911c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
3922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Initialize this object
3952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param dii must have read-only fIntervalPatterns.
3962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void initializeFromReadOnlyPatterns(DateIntervalInfo dii) {
3982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fFallbackIntervalPattern = dii.fFallbackIntervalPattern;
3992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fFirstDateInPtnIsLaterDate = dii.fFirstDateInPtnIsLaterDate;
4002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fIntervalPatterns = dii.fIntervalPatterns;
4012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fIntervalPatternsReadOnly = true;
4022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4051c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4061c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    /**
4071c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * Sink for enumerating all of the date interval skeletons.
4081c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     */
409f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    private static final class DateIntervalSink extends UResource.Sink {
4101c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4111c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        /**
4121c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Accepted pattern letters:
4131c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.YEAR
4141c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.MONTH
4151c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.DATE
4161c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.AM_PM
4171c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.HOUR
4181c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.HOUR_OF_DAY
4191c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.MINUTE
4201c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Calendar.SECOND
4211c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         */
4221c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        private static final String ACCEPTED_PATTERN_LETTERS = "yMdahHms";
4231c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4241c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        // Output data
4251c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        DateIntervalInfo dateIntervalInfo;
4261c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4271c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        // Alias handling
4281c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        String nextCalendarType;
4291c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4301c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        // Constructor
4311c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        public DateIntervalSink(DateIntervalInfo dateIntervalInfo) {
4321c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            this.dateIntervalInfo = dateIntervalInfo;
4331c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        }
4341c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4351c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        @Override
436f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        public void put(Key key, Value value, boolean noFallback) {
437f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // Iterate over all the calendar entries and only pick the 'intervalFormats' table.
438f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            UResource.Table dateIntervalData = value.getTable();
439f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            for (int i = 0; dateIntervalData.getKeyAndValue(i, key, value); i++) {
440f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                if (!key.contentEquals(INTERVAL_FORMATS_KEY)) {
441f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    continue;
442f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                }
4431c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
444f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                // Handle aliases and tables. Ignore the rest.
445f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                if (value.getType() == ICUResourceBundle.ALIAS) {
446f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    // Get the calendar type from the alias path.
447f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    nextCalendarType = getCalendarTypeFromPath(value.getAliasString());
448f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    break;
449f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
450f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                } else if (value.getType() == ICUResourceBundle.TABLE) {
451f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    // Iterate over all the skeletons in the 'intervalFormat' table.
452f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    UResource.Table skeletonData = value.getTable();
453f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    for (int j = 0; skeletonData.getKeyAndValue(j, key, value); j++) {
454f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        if (value.getType() == ICUResourceBundle.TABLE) {
455f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                            // Process the skeleton
456f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                            processSkeletonTable(key, value);
457f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        }
458f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    }
459f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    break;
460f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                }
461f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            }
4621c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        }
4631c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
464f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        /** Processes the patterns for a skeleton table. */
465f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        public void processSkeletonTable(Key key, Value value) {
466f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // Iterate over all the patterns in the current skeleton table
467f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            String currentSkeleton = key.toString();
468f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            UResource.Table patternData = value.getTable();
469f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            for (int k = 0; patternData.getKeyAndValue(k, key, value); k++) {
470f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                if (value.getType() == ICUResourceBundle.STRING) {
471f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    // Process the key
472f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    CharSequence patternLetter = validateAndProcessPatternLetter(key);
473f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
474f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    // If the calendar field has a valid value
475f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    if (patternLetter != null) {
476f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        // Get the largest different calendar unit
477f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        String lrgDiffCalUnit = patternLetter.toString();
478f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
479f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        // Set the interval pattern
480f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        setIntervalPatternIfAbsent(currentSkeleton, lrgDiffCalUnit, value);
481f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    }
482f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                }
4831c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            }
4841c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        }
4851c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
4861c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        /**
4871c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Returns and resets the next calendar type.
4881c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * @return Next calendar type
4891c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         */
4901c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        public String getAndResetNextCalendarType() {
4911c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            String tmpCalendarType = nextCalendarType;
4921c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            nextCalendarType = null;
4931c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            return tmpCalendarType;
4941c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        }
4951c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
496f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        // Alias' path prefix and suffix.
4971c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        private static final String DATE_INTERVAL_PATH_PREFIX =
4981c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            "/LOCALE/" + CALENDAR_KEY + "/";
499f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        private static final String DATE_INTERVAL_PATH_SUFFIX =
5001c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            "/" + INTERVAL_FORMATS_KEY;
5011c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5021c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        /**
5031c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * Extracts the calendar type from the path
5041c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * @param path
5051c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * @return Calendar Type
5061c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         */
5071c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        private String getCalendarTypeFromPath(String path) {
5081c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            if (path.startsWith(DATE_INTERVAL_PATH_PREFIX) &&
509f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    path.endsWith(DATE_INTERVAL_PATH_SUFFIX)) {
5101c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                return path.substring(DATE_INTERVAL_PATH_PREFIX.length(),
511f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    path.length() - DATE_INTERVAL_PATH_SUFFIX.length());
5121c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            }
5131c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            throw new ICUException("Malformed 'intervalFormat' alias path: " + path);
5141c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        }
515f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
516f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        /**
517f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * Processes the pattern letter
518f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * @param patternLetter
519f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * @return Pattern letter
520f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         */
521f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        private CharSequence validateAndProcessPatternLetter(CharSequence patternLetter) {
522f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // Check that patternLetter is just one letter
523f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (patternLetter.length() != 1) { return null; }
524f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
525f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // Check that the pattern letter is accepted
526f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            char letter = patternLetter.charAt(0);
527f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (ACCEPTED_PATTERN_LETTERS.indexOf(letter) < 0) {
528f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                return null;
529f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            }
530f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
531f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // Replace 'h' for 'H'
532f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (letter == CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR_OF_DAY].charAt(0)) {
533f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                patternLetter = CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR];
534f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            }
535f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
536f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            return patternLetter;
537f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        }
538f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
539f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        /**
540f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * Stores the interval pattern for the current skeleton in the internal data structure
541f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * if it's not present.
542f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * @param lrgDiffCalUnit
543f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         * @param intervalPattern
544f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert         */
545f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        private void setIntervalPatternIfAbsent(String currentSkeleton, String lrgDiffCalUnit, Value intervalPattern) {
546f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            // Check if the pattern has already been stored on the data structure.
547f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            Map<String, PatternInfo> patternsOfOneSkeleton =
548f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    dateIntervalInfo.fIntervalPatterns.get(currentSkeleton);
549f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (patternsOfOneSkeleton == null || !patternsOfOneSkeleton.containsKey(lrgDiffCalUnit)) {
550f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                // Store the pattern
551f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                dateIntervalInfo.setIntervalPatternInternally(currentSkeleton, lrgDiffCalUnit,
552f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        intervalPattern.toString());
553f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            }
554f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        }
5551c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    }
5561c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5571c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
5592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Initialize DateIntervalInfo from calendar data
5602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param calData  calendar data
5612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5621c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    private void setup(ULocale locale) {
5632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int DEFAULT_HASH_SIZE = 19;
5642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fIntervalPatterns = new HashMap<String, Map<String, PatternInfo>>(DEFAULT_HASH_SIZE);
5651c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        // initialize to guard if there is no interval date format defined in
5662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // resource files
5672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fFallbackIntervalPattern = "{0} \u2013 {1}";
5681c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
5702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Get the correct calendar type
5712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String calendarTypeToUse = locale.getKeywordValue("calendar");
5722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( calendarTypeToUse == null ) {
5731c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                String[] preferredCalendarTypes =
5741c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                        Calendar.getKeywordValuesForLocale("calendar", locale, true);
5752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                calendarTypeToUse = preferredCalendarTypes[0]; // the most preferred calendar
5762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( calendarTypeToUse == null ) {
5782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                calendarTypeToUse = "gregorian"; // fallback
5792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5811c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            // Instantiate the sink to process the data and the resource bundle
5821c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            DateIntervalSink sink = new DateIntervalSink(this);
5831c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            ICUResourceBundle resource =
5841c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                    (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
5851c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5861c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            // Get the fallback pattern
5871c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            String fallbackPattern = resource.getStringWithFallback(CALENDAR_KEY + "/" + calendarTypeToUse
5881c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                    + "/" + INTERVAL_FORMATS_KEY + "/" + FALLBACK_STRING);
5891c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            setFallbackIntervalPattern(fallbackPattern);
5901c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5911c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            // Already loaded calendar types
5921c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            Set<String> loadedCalendarTypes = new HashSet<String>();
5931c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
5941c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            while (calendarTypeToUse != null) {
5951c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                // Throw an exception when a loop is detected
5961c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                if (loadedCalendarTypes.contains(calendarTypeToUse)) {
5971c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                    throw new ICUException("Loop in calendar type fallback: " + calendarTypeToUse);
5982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5991c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
6001c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                // Register the calendar type to avoid loops
6011c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                loadedCalendarTypes.add(calendarTypeToUse);
6021c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
6031c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                // Get all resources for this calendar type
6041c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                String pathToIntervalFormats = CALENDAR_KEY + "/" + calendarTypeToUse;
605f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                resource.getAllItemsWithFallback(pathToIntervalFormats, sink);
6061c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
6071c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                // Get next calendar type to load if there was an alias pointing at it
6081c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                calendarTypeToUse = sink.getAndResetNextCalendarType();
6091c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            }
6102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } catch ( MissingResourceException e) {
6111c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            // Will fallback to {data0} - {date1}
6122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
6172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Split interval patterns into 2 part.
6182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param intervalPattern  interval pattern
6192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the index in interval pattern which split the pattern into 2 part
6202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static int splitPatternInto2Part(String intervalPattern) {
6222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean inQuote = false;
6232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        char prevCh = 0;
6242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int count = 0;
6251c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
6262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /* repeatedPattern used to record whether a pattern has already seen.
6272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller           It is a pattern applies to first calendar if it is first time seen,
6282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller           otherwise, it is a pattern applies to the second calendar
6292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
6302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] patternRepeated = new int[58];
6312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int PATTERN_CHAR_BASE = 0x41;
6331c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
6342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /* loop through the pattern string character by character looking for
6352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the first repeated pattern letter, which breaks the interval pattern
6361c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert         * into 2 parts.
6372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
6382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int i;
6392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean foundRepetition = false;
6402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (i = 0; i < intervalPattern.length(); ++i) {
6412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            char ch = intervalPattern.charAt(i);
6421c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
6432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (ch != prevCh && count > 0) {
6442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // check the repeativeness of pattern letter
6452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int repeated = patternRepeated[prevCh - PATTERN_CHAR_BASE];
6462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if ( repeated == 0 ) {
6472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    patternRepeated[prevCh - PATTERN_CHAR_BASE] = 1;
6482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
6492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    foundRepetition = true;
6502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    break;
6512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                count = 0;
6532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (ch == '\'') {
6552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Consecutive single quotes are a single quote literal,
6562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // either outside of quotes or between quotes
6571c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                if ((i+1) < intervalPattern.length() &&
6582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    intervalPattern.charAt(i+1) == '\'') {
6592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    ++i;
6602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
6612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    inQuote = ! inQuote;
6622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6631c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            }
6642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            else if (!inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
6652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
6661c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                // ch is a date-time pattern character
6672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                prevCh = ch;
6682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                ++count;
6692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // check last pattern char, distinguish
6721c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        // "dd MM" ( no repetition ),
6731c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        // "d-d"(last char repeated ), and
6742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // "d-d MM" ( repetition found )
6752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( count > 0 && foundRepetition == false ) {
6762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( patternRepeated[prevCh - PATTERN_CHAR_BASE] == 0 ) {
6772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                count = 0;
6782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return (i - count);
6812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6841c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    /**
6852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Provides a way for client to build interval patterns.
6861c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * User could construct DateIntervalInfo by providing
6872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * a list of skeletons and their patterns.
6882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <P>
6892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * For example:
6902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <pre>
6912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * DateIntervalInfo dIntervalInfo = new DateIntervalInfo();
6921c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * dIntervalInfo.setIntervalPattern("yMd", Calendar.YEAR, "'from' yyyy-M-d 'to' yyyy-M-d");
6932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * dIntervalInfo.setIntervalPattern("yMMMd", Calendar.MONTH, "'from' yyyy MMM d 'to' MMM d");
6942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * dIntervalInfo.setIntervalPattern("yMMMd", Calendar.DAY, "yyyy MMM d-d");
6952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * dIntervalInfo.setFallbackIntervalPattern("{0} ~ {1}");
6962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * </pre>
6972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
6981c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * Restriction:
6991c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * Currently, users can only set interval patterns when the following
7001c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * calendar fields are different: ERA, YEAR, MONTH, DATE,  DAY_OF_MONTH,
7012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * DAY_OF_WEEK, AM_PM,  HOUR, HOUR_OF_DAY, MINUTE, and SECOND.
7021c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * Interval patterns when other calendar fields are different are
7032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * not supported.
7042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
7052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param skeleton         the skeleton on which interval pattern based
7062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param lrgDiffCalUnit   the largest different calendar unit.
7072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param intervalPattern  the interval pattern on the largest different
7082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                         calendar unit.
7091c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     *                         For example, if lrgDiffCalUnit is
7102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                         "year", the interval pattern for en_US when year
7112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                         is different could be "'from' yyyy 'to' yyyy".
7121c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * @throws IllegalArgumentException  if setting interval pattern on
7132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                            a calendar field that is smaller
7141c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     *                            than the MINIMUM_SUPPORTED_CALENDAR_FIELD
7152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @throws UnsupportedOperationException  if the object is frozen
7162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7171c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public void setIntervalPattern(String skeleton,
7181c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                                   int lrgDiffCalUnit,
7192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                   String intervalPattern)
7202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
7212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( frozen ) {
7222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new UnsupportedOperationException("no modification is allowed after DII is frozen");
7232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( lrgDiffCalUnit > MINIMUM_SUPPORTED_CALENDAR_FIELD ) {
7252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("calendar field is larger than MINIMUM_SUPPORTED_CALENDAR_FIELD");
7262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (fIntervalPatternsReadOnly) {
7282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fIntervalPatterns = cloneIntervalPatterns(fIntervalPatterns);
7292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fIntervalPatternsReadOnly = false;
7302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        PatternInfo ptnInfo = setIntervalPatternInternally(skeleton,
7321c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                          CALENDAR_FIELD_TO_PATTERN_LETTER[lrgDiffCalUnit],
7332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                          intervalPattern);
7342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( lrgDiffCalUnit == Calendar.HOUR_OF_DAY ) {
7351c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            setIntervalPattern(skeleton,
7362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                               CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.AM_PM],
7372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                               ptnInfo);
7381c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            setIntervalPattern(skeleton,
7392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                               CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.HOUR],
7402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                               ptnInfo);
7412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if ( lrgDiffCalUnit == Calendar.DAY_OF_MONTH ||
7422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    lrgDiffCalUnit == Calendar.DAY_OF_WEEK ) {
7431c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert            setIntervalPattern(skeleton,
7442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                               CALENDAR_FIELD_TO_PATTERN_LETTER[Calendar.DATE],
7452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                               ptnInfo);
7462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /* Set Interval pattern.
7512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
7521c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * It generates the interval pattern info,
7532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * afer which, not only sets the interval pattern info into the hash map,
7542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * but also returns the interval pattern info to the caller
7552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * so that caller can re-use it.
7562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
7572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param skeleton         skeleton on which the interval pattern based
7582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param lrgDiffCalUnit   the largest different calendar unit.
7592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param intervalPattern  the interval pattern on the largest different
7602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                         calendar unit.
7612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the interval pattern pattern information
7622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private PatternInfo setIntervalPatternInternally(String skeleton,
7642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                                String lrgDiffCalUnit,
7652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                                String intervalPattern) {
7662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
7672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean emptyHash = false;
7682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (patternsOfOneSkeleton == null) {
7692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            patternsOfOneSkeleton = new HashMap<String, PatternInfo>();
7702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            emptyHash = true;
7712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean order = fFirstDateInPtnIsLaterDate;
7732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // check for "latestFirst:" or "earliestFirst:" prefix
7742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( intervalPattern.startsWith(LATEST_FIRST_PREFIX) ) {
7752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            order = true;
7762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int prefixLength = LATEST_FIRST_PREFIX.length();
7772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            intervalPattern = intervalPattern.substring(prefixLength, intervalPattern.length());
7782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else if ( intervalPattern.startsWith(EARLIEST_FIRST_PREFIX) ) {
7792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            order = false;
7802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int earliestFirstLength = EARLIEST_FIRST_PREFIX.length();
7812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            intervalPattern = intervalPattern.substring(earliestFirstLength, intervalPattern.length());
7822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        PatternInfo itvPtnInfo = genPatternInfo(intervalPattern, order);
7842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        patternsOfOneSkeleton.put(lrgDiffCalUnit, itvPtnInfo);
7862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( emptyHash == true ) {
7872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fIntervalPatterns.put(skeleton, patternsOfOneSkeleton);
7882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return itvPtnInfo;
7912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /* Set Interval pattern.
7952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
7962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param skeleton         skeleton on which the interval pattern based
7972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param lrgDiffCalUnit   the largest different calendar unit.
7981c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * @param ptnInfo          interval pattern infomration
7992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void setIntervalPattern(String skeleton,
8012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                    String lrgDiffCalUnit,
8022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                    PatternInfo ptnInfo) {
8032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
8042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        patternsOfOneSkeleton.put(lrgDiffCalUnit, ptnInfo);
8052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Break interval patterns as 2 part and save them into pattern info.
8102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param intervalPattern  interval pattern
8112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param laterDateFirst   whether the first date in intervalPattern
8122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                         is earlier date or later date
8132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return                 pattern info object
8142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
81593cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller     * @hide original deprecated declaration
816836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
8172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
8191c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public static PatternInfo genPatternInfo(String intervalPattern,
8202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                      boolean laterDateFirst) {
8212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int splitPoint = splitPatternInto2Part(intervalPattern);
8221c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
8232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String firstPart = intervalPattern.substring(0, splitPoint);
8242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String secondPart = null;
8252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( splitPoint < intervalPattern.length() ) {
8262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            secondPart = intervalPattern.substring(splitPoint, intervalPattern.length());
8272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new PatternInfo(firstPart, secondPart, laterDateFirst);
8302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get the interval pattern given the largest different calendar field.
8352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param skeleton   the skeleton
8362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param field      the largest different calendar field
8372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return interval pattern  return null if interval pattern is not found.
8381c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * @throws IllegalArgumentException  if getting interval pattern on
8392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                            a calendar field that is smaller
8401c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     *                            than the MINIMUM_SUPPORTED_CALENDAR_FIELD
8412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8421c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public PatternInfo getIntervalPattern(String skeleton, int field)
8432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
8442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( field > MINIMUM_SUPPORTED_CALENDAR_FIELD ) {
8452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("no support for field less than SECOND");
8462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Map<String, PatternInfo> patternsOfOneSkeleton = fIntervalPatterns.get(skeleton);
8482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( patternsOfOneSkeleton != null ) {
8492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            PatternInfo intervalPattern = patternsOfOneSkeleton.
8502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                get(CALENDAR_FIELD_TO_PATTERN_LETTER[field]);
8512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( intervalPattern != null ) {
8522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return intervalPattern;
8532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
8542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return null;
8562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get the fallback interval pattern.
8622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return fallback interval pattern
8632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String getFallbackIntervalPattern()
8652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
8662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return fFallbackIntervalPattern;
8672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Re-set the fallback interval pattern.
8722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
8732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * In construction, default fallback pattern is set as "{0} - {1}".
8742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * And constructor taking locale as parameter will set the
8752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * fallback pattern as what defined in the locale resource file.
8762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
8772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * This method provides a way for user to replace the fallback pattern.
8782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
8792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param fallbackPattern                 fall-back interval pattern.
8802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @throws UnsupportedOperationException  if the object is frozen
8811c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * @throws IllegalArgumentException       if there is no pattern {0} or
8822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                                        pattern {1} in fallbakckPattern
8832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public void setFallbackIntervalPattern(String fallbackPattern)
8852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
8862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( frozen ) {
8872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new UnsupportedOperationException("no modification is allowed after DII is frozen");
8882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int firstPatternIndex = fallbackPattern.indexOf("{0}");
8902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int secondPatternIndex = fallbackPattern.indexOf("{1}");
8912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( firstPatternIndex == -1 || secondPatternIndex == -1 ) {
8922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("no pattern {0} or pattern {1} in fallbackPattern");
8932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( firstPatternIndex > secondPatternIndex ) {
8952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            fFirstDateInPtnIsLaterDate = true;
8962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fFallbackIntervalPattern = fallbackPattern;
8982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get default order -- whether the first date in pattern is later date
9032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *                      or not.
9042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
9051c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * return default date ordering in interval pattern. TRUE if the first date
9062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *        in pattern is later date, FALSE otherwise.
9072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean getDefaultOrder()
9092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
9102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return fFirstDateInPtnIsLaterDate;
9112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9155cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert     * Clone this object.
9162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return     a copy of the object
9172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
918f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9191c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert    public Object clone()
9202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
9212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( frozen ) {
9222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return this;
9232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
9242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return cloneUnfrozenDII();
9252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
9292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Clone an unfrozen DateIntervalInfo object.
9302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return     a copy of the object
9312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private Object cloneUnfrozenDII() //throws IllegalStateException
9332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    {
9342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
9352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            DateIntervalInfo other = (DateIntervalInfo) super.clone();
9362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            other.fFallbackIntervalPattern=fFallbackIntervalPattern;
9372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            other.fFirstDateInPtnIsLaterDate = fFirstDateInPtnIsLaterDate;
9382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (fIntervalPatternsReadOnly) {
9392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                other.fIntervalPatterns = fIntervalPatterns;
9402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                other.fIntervalPatternsReadOnly = true;
9412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
9422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                other.fIntervalPatterns = cloneIntervalPatterns(fIntervalPatterns);
9432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                other.fIntervalPatternsReadOnly = false;
9442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
9452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            other.frozen = false;
9462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return other;
9472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } catch ( CloneNotSupportedException e ) {
9482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ///CLOVER:OFF
9492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new  ICUCloneNotSupportedException("clone is not supported", e);
9502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ///CLOVER:ON
9512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
9522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9531c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
9542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static Map<String, Map<String, PatternInfo>> cloneIntervalPatterns(
9552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Map<String, Map<String, PatternInfo>> patterns) {
9562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Map<String, Map<String, PatternInfo>> result = new HashMap<String, Map<String, PatternInfo>>();
9572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (Entry<String, Map<String, PatternInfo>> skeletonEntry : patterns.entrySet()) {
9582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String skeleton = skeletonEntry.getKey();
9592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Map<String, PatternInfo> patternsOfOneSkeleton = skeletonEntry.getValue();
9602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Map<String, PatternInfo> oneSetPtn = new HashMap<String, PatternInfo>();
9612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (Entry<String, PatternInfo> calEntry : patternsOfOneSkeleton.entrySet()) {
9622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String calField = calEntry.getKey();
9632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                PatternInfo value = calEntry.getValue();
9642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                oneSetPtn.put(calField, value);
9652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
9662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.put(skeleton, oneSetPtn);
9672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
9682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
9692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9711c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
9721c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
9732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9745cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert     * {@inheritDoc}
9752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
976f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean isFrozen() {
9782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return frozen;
9792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9801c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
9812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9825cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert     * {@inheritDoc}
9832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
984f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public DateIntervalInfo freeze() {
9862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        fIntervalPatternsReadOnly = true;
9872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        frozen = true;
9882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
9892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9901c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
9912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9925cf9d333bc032f332a812665fc8a9824765f01c9Fredrik Roubert     * {@inheritDoc}
9932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
994f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public DateIntervalInfo cloneAsThawed() {
9962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        DateIntervalInfo result = (DateIntervalInfo) (this.cloneUnfrozenDII());
9972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
9982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
10022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Parse skeleton, save each field's width.
10032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * It is used for looking for best match skeleton,
10042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and adjust pattern field width.
10052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param skeleton            skeleton to be parsed
10062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param skeletonFieldWidth  parsed skeleton field width
10072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
10082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    static void parseSkeleton(String skeleton, int[] skeletonFieldWidth) {
10092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int PATTERN_CHAR_BASE = 0x41;
10102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for ( int i = 0; i < skeleton.length(); ++i ) {
10112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            ++skeletonFieldWidth[skeleton.charAt(i) - PATTERN_CHAR_BASE];
10122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
10142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
10182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Check whether one field width is numeric while the other is string.
10192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
10202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * TODO (xji): make it general
10212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
10222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param fieldWidth          one field width
10232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param anotherFieldWidth   another field width
10242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param patternLetter       pattern letter char
10252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return true if one field width is numeric and the other is string,
10262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *         false otherwise.
10272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
10282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static boolean stringNumeric(int fieldWidth,
10292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                         int anotherFieldWidth,
10302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                         char patternLetter) {
10312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( patternLetter == 'M' ) {
10322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( fieldWidth <= 2 && anotherFieldWidth > 2 ||
10332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                 fieldWidth > 2 && anotherFieldWidth <= 2 ) {
10342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return true;
10352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
10361c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert        }
10372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return false;
10382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
10392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
10421c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     * given an input skeleton, get the best match skeleton
10432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * which has pre-defined interval pattern in resource file.
10442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
10452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * TODO (xji): set field weight or
10462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *             isolate the funtionality in DateTimePatternGenerator
10472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param  inputSkeleton        input skeleton
10482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return 0, if there is exact match for input skeleton
10491c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert     *         1, if there is only field width difference between
10502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *            the best match and the input skeleton
10512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *         2, the only field difference is 'v' and 'z'
10522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *        -1, if there is calendar field difference between
10532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *            the best match and the input skeleton
10542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
10552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    DateIntervalFormat.BestMatchInfo getBestSkeleton(String inputSkeleton) {
10562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String bestSkeleton = inputSkeleton;
10572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] inputSkeletonFieldWidth = new int[58];
10582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int[] skeletonFieldWidth = new int[58];
10592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final int DIFFERENT_FIELD = 0x1000;
10612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final int STRING_NUMERIC_DIFFERENCE = 0x100;
10622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final int BASE = 0x41;
10632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // TODO: this is a hack for 'v' and 'z'
10652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // resource bundle only have time skeletons ending with 'v',
10662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // but not for time skeletons ending with 'z'.
10672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean replaceZWithV = false;
10682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( inputSkeleton.indexOf('z') != -1 ) {
10692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            inputSkeleton = inputSkeleton.replace('z', 'v');
10702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            replaceZWithV = true;
10712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
10722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
10732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        parseSkeleton(inputSkeleton, inputSkeletonFieldWidth);
10742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int bestDistance = Integer.MAX_VALUE;
10752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // 0 means exact the same skeletons;
10762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // 1 means having the same field, but with different length,
10772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // 2 means only z/v differs
10782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // -1 means having different field.
10792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int bestFieldDifference = 0;
10802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (String skeleton : fIntervalPatterns.keySet()) {
10812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // clear skeleton field width
10822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for ( int i = 0; i < skeletonFieldWidth.length; ++i ) {
10831c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                skeletonFieldWidth[i] = 0;
10842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
10852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            parseSkeleton(skeleton, skeletonFieldWidth);
10862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // calculate distance
10872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int distance = 0;
10882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int fieldDifference = 1;
10892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for ( int i = 0; i < inputSkeletonFieldWidth.length; ++i ) {
10902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int inputFieldWidth = inputSkeletonFieldWidth[i];
10912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int fieldWidth = skeletonFieldWidth[i];
10922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if ( inputFieldWidth == fieldWidth ) {
10932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    continue;
10942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
10952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if ( inputFieldWidth == 0 ) {
10962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    fieldDifference = -1;
10972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    distance += DIFFERENT_FIELD;
10982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else if ( fieldWidth == 0 ) {
10992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    fieldDifference = -1;
11002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    distance += DIFFERENT_FIELD;
11011c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert                } else if (stringNumeric(inputFieldWidth, fieldWidth,
11022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                         (char)(i+BASE) ) ) {
11032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    distance += STRING_NUMERIC_DIFFERENCE;
11042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
11052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    distance += Math.abs(inputFieldWidth - fieldWidth);
11062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
11072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( distance < bestDistance ) {
11092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                bestSkeleton = skeleton;
11102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                bestDistance = distance;
11112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                bestFieldDifference = fieldDifference;
11122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if ( distance == 0 ) {
11142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                bestFieldDifference = 0;
11152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                break;
11162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
11172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( replaceZWithV && bestFieldDifference != -1 ) {
11192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            bestFieldDifference = 2;
11202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return new DateIntervalFormat.BestMatchInfo(bestSkeleton, bestFieldDifference);
11222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
11252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Override equals
11262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1127f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
11282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean equals(Object a) {
11292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if ( a instanceof DateIntervalInfo ) {
11302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            DateIntervalInfo dtInfo = (DateIntervalInfo)a;
11312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return fIntervalPatterns.equals(dtInfo.fIntervalPatterns);
11322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return false;
11342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
11362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
11372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Override hashcode
11382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1139f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
11402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public int hashCode() {
11412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return fIntervalPatterns.hashCode();
11422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11431c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
11442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
11452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
114693cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller     * @hide original deprecated declaration
1147836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
11482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
11492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
11502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Map<String,Set<String>> getPatterns() {
11512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LinkedHashMap<String,Set<String>> result = new LinkedHashMap<String,Set<String>>();
11522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (Entry<String, Map<String, PatternInfo>> entry : fIntervalPatterns.entrySet()) {
11532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.put(entry.getKey(), new LinkedHashSet<String>(entry.getValue().keySet()));
11542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
11562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11571c8a530973739aafa823d758240d2cd5dad96fe3Fredrik Roubert
11582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
11592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Get the internal patterns, with a deep clone for safety.
11602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @deprecated This API is ICU internal only.
116193cf604e9dd0525f15bc0a7450b2a35f3884c298Neil Fuller     * @hide original deprecated declaration
1162836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller     * @hide draft / provisional / internal are hidden on Android
11632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
11642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    @Deprecated
11652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Map<String, Map<String, PatternInfo>> getRawPatterns() {
11662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LinkedHashMap<String, Map<String, PatternInfo>> result = new LinkedHashMap<String, Map<String, PatternInfo>>();
11672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (Entry<String, Map<String, PatternInfo>> entry : fIntervalPatterns.entrySet()) {
11682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result.put(entry.getKey(), new LinkedHashMap<String, PatternInfo>(entry.getValue()));
11692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
11702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
11712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
11722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller}// end class DateIntervalInfo
1173