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 *******************************************************************************
6f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert * Copyright (C) 2011-2016, International Business Machines Corporation and
7f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert * others. All Rights Reserved.
82ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *******************************************************************************
92ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpackage android.icu.impl;
112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.IOException;
132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.ObjectInputStream;
142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.io.Serializable;
152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.lang.ref.WeakReference;
162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.text.MessageFormat;
172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Collection;
182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.EnumSet;
192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Iterator;
202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.LinkedList;
212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.MissingResourceException;
222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Set;
232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.concurrent.ConcurrentHashMap;
242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.impl.TextTrieMap.ResultHandler;
262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.text.LocaleDisplayNames;
272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.text.TimeZoneFormat.TimeType;
282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.text.TimeZoneNames;
292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.text.TimeZoneNames.MatchInfo;
302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.text.TimeZoneNames.NameType;
312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.BasicTimeZone;
322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.Freezable;
332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.Output;
342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.TimeZone;
352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.TimeZone.SystemTimeZoneType;
362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.TimeZoneTransition;
372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ULocale;
382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/**
402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * This class interact with TimeZoneNames and LocaleDisplayNames
412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * to format and parse time zone's generic display names.
422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * It is not recommended to use this class directly, instead
431fba789ac68efdd9120a7373f49daef42833e674Neil Fuller * use android.icu.text.TimeZoneFormat.
44836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller * @hide Only a subset of ICU is exposed in Android
452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpublic class TimeZoneGenericNames implements Serializable, Freezable<TimeZoneGenericNames> {
472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Note: This class implements Serializable, but we no longer serialize instance of
491fba789ac68efdd9120a7373f49daef42833e674Neil Fuller    // TimeZoneGenericNames in ICU 49. ICU 4.8 android.icu.text.TimeZoneFormat used to
502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // serialize TimeZoneGenericNames field. TimeZoneFormat no longer read TimeZoneGenericNames
512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // field, we have to keep TimeZoneGenericNames Serializable. Otherwise it fails to read
522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // (unused) TimeZoneGenericNames serialized data.
532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final long serialVersionUID = 2729910342063468417L;
552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Generic name type enum
582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public enum GenericNameType {
602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LOCATION ("LONG", "SHORT"),
612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LONG (),
622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        SHORT ();
632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String[] _fallbackTypeOf;
652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        GenericNameType(String... fallbackTypeOf) {
662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _fallbackTypeOf = fallbackTypeOf;
672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean isFallbackTypeOf(GenericNameType type) {
702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String typeStr = type.toString();
712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (String t : _fallbackTypeOf) {
722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (t.equals(typeStr)) {
732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    return true;
742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Format pattern enum used for composing location and partial location names
822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public enum Pattern {
84f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        // The format pattern such as "{0} Time", where {0} is the country or city.
852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        REGION_FORMAT("regionFormat", "({0})"),
862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Note: FALLBACK_REGION_FORMAT is no longer used since ICU 50/CLDR 22.1
882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // The format pattern such as "{1} Time ({0})", where {1} is the country and {0} is a city.
892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        //FALLBACK_REGION_FORMAT("fallbackRegionFormat", "{1} ({0})"),
902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // The format pattern such as "{1} ({0})", where {1} is the metazone, and {0} is the country or city.
922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        FALLBACK_FORMAT("fallbackFormat", "{1} ({0})");
932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String _key;
952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String _defaultVal;
962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Pattern(String key, String defaultVal) {
982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _key = key;
992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _defaultVal = defaultVal;
1002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String key() {
1032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _key;
1042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String defaultValue() {
1072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _defaultVal;
1082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
111f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    private final ULocale _locale;
1122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private TimeZoneNames _tznames;
1132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient volatile boolean _frozen;
1152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient String _region;
1162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient WeakReference<LocaleDisplayNames> _localeDisplayNamesRef;
1172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient MessageFormat[] _patternFormatters;
1182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient ConcurrentHashMap<String, String> _genericLocationNamesMap;
1202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient ConcurrentHashMap<String, String> _genericPartialLocationNamesMap;
1212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient TextTrieMap<NameInfo> _gnamesTrie;
1222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private transient boolean _gnamesTrieFullyLoaded;
1232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static Cache GENERIC_NAMES_CACHE = new Cache();
1252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Window size used for DST check for a zone in a metazone (about a half year)
1272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final long DST_CHECK_RANGE = 184L*(24*60*60*1000);
1282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final NameType[] GENERIC_NON_LOCATION_TYPES =
1302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                {NameType.LONG_GENERIC, NameType.SHORT_GENERIC};
1312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Constructs a <code>TimeZoneGenericNames</code> with the given locale
1352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and the <code>TimeZoneNames</code>.
1362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale the locale
1372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param tznames the TimeZoneNames
1382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneGenericNames(ULocale locale, TimeZoneNames tznames) {
1402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _locale = locale;
1412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _tznames = tznames;
1422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        init();
1432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private method initializing the instance of <code>TimeZoneGenericName</code>.
1472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * This method should be called from a constructor and readObject.
1482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void init() {
1502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_tznames == null) {
1512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _tznames = TimeZoneNames.getInstance(_locale);
1522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _genericLocationNamesMap = new ConcurrentHashMap<String, String>();
1542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _genericPartialLocationNamesMap = new ConcurrentHashMap<String, String>();
1552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrie = new TextTrieMap<NameInfo>(true);
1572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrieFullyLoaded = false;
1582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Preload zone strings for the default time zone
1602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZone tz = TimeZone.getDefault();
1612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
1622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tzCanonicalID != null) {
1632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            loadStrings(tzCanonicalID);
1642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Constructs a <code>TimeZoneGenericNames</code> with the given locale.
1692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * This constructor is private and called from {@link #getInstance(ULocale)}.
1702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale the locale
1712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private TimeZoneGenericNames(ULocale locale) {
1732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this(locale, null);
1742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * The factory method of <code>TimeZoneGenericNames</code>. This static method
1782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * returns a frozen instance of cached <code>TimeZoneGenericNames</code>.
1792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param locale the locale
1802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return A frozen <code>TimeZoneGenericNames</code>.
1812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static TimeZoneGenericNames getInstance(ULocale locale) {
1832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String key = locale.getBaseName();
1842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return GENERIC_NAMES_CACHE.getInstance(key, locale);
1852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns the display name of the time zone for the given name type
1892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * at the given date, or null if the display name is not available.
190f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
1912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param tz the time zone
1922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param type the generic name type - see {@link GenericNameType}
1932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param date the date
1942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the display name of the time zone for the given name type
1952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * at the given date, or null.
1962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String getDisplayName(TimeZone tz, GenericNameType type, long date) {
1982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String name = null;
1992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String tzCanonicalID = null;
2002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        switch (type) {
2012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case LOCATION:
2022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
2032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (tzCanonicalID != null) {
2042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                name = getGenericLocationName(tzCanonicalID);
2052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            break;
2072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case LONG:
2082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case SHORT:
2092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            name = formatGenericNonLocationName(tz, type, date);
2102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (name == null) {
2112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
2122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (tzCanonicalID != null) {
2132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    name = getGenericLocationName(tzCanonicalID);
2142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            break;
2172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return name;
2192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns the generic location name for the given canonical time zone ID.
223f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
2242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param canonicalTzID the canonical time zone ID
2252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the generic location name for the given canonical time zone ID.
2262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String getGenericLocationName(String canonicalTzID) {
2282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (canonicalTzID == null || canonicalTzID.length() == 0) {
2292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
2302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String name = _genericLocationNamesMap.get(canonicalTzID);
2322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (name != null) {
2332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (name.length() == 0) {
2342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // empty string to indicate the name is not available
2352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return null;
2362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return name;
2382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Output<Boolean> isPrimary = new Output<Boolean>();
2412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String countryCode = ZoneMeta.getCanonicalCountry(canonicalTzID, isPrimary);
2422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (countryCode != null) {
2432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (isPrimary.value) {
2442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // If this is only the single zone in the country, use the country name
2452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String country = getLocaleDisplayNames().regionDisplayName(countryCode);
2462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                name = formatPattern(Pattern.REGION_FORMAT, country);
2472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
2482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // If there are multiple zones including this in the country,
2492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // use the exemplar city name
2502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // getExemplarLocationName should return non-empty String
2522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // if the time zone is associated with a location
2532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String city = _tznames.getExemplarLocationName(canonicalTzID);
2542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                name = formatPattern(Pattern.REGION_FORMAT, city);
2552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (name == null) {
2592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _genericLocationNamesMap.putIfAbsent(canonicalTzID.intern(), "");
2602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
2612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            synchronized (this) {   // we have to sync the name map and the trie
2622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                canonicalTzID = canonicalTzID.intern();
2632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String tmp = _genericLocationNamesMap.putIfAbsent(canonicalTzID, name.intern());
2642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (tmp == null) {
2652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // Also put the name info the to trie
266f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    NameInfo info = new NameInfo(canonicalTzID, GenericNameType.LOCATION);
2672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _gnamesTrie.put(name, info);
2682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
2692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    name = tmp;
2702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
2712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return name;
2742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Sets the pattern string for the pattern type.
2782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Note: This method is designed for CLDR ST - not for common use.
2792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param patType the pattern type
2802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param patStr the pattern string
2812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return this object.
2822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneGenericNames setFormatPattern(Pattern patType, String patStr) {
2842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (isFrozen()) {
2852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new UnsupportedOperationException("Attempt to modify frozen object");
2862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Changing pattern will invalidates cached names
2892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!_genericLocationNamesMap.isEmpty()) {
2902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _genericLocationNamesMap = new ConcurrentHashMap<String, String>();
2912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!_genericPartialLocationNamesMap.isEmpty()) {
2932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _genericPartialLocationNamesMap = new ConcurrentHashMap<String, String>();
2942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrie = null;
2962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrieFullyLoaded = false;
2972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_patternFormatters == null) {
2992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _patternFormatters = new MessageFormat[Pattern.values().length];
3002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _patternFormatters[patType.ordinal()] = new MessageFormat(patStr);
3022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
3032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private method to get a generic string, with fallback logics involved,
3072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * that is,
308f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
3092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * 1. If a generic non-location string is available for the zone, return it.
310f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     * 2. If a generic non-location string is associated with a meta zone and
3112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *    the zone never use daylight time around the given date, use the standard
3122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *    string (if available).
3132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * 3. If a generic non-location string is associated with a meta zone and
3142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *    the offset at the given time is different from the preferred zone for the
3152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *    current locale, then return the generic partial location string (if available)
3162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * 4. If a generic non-location string is not available, use generic location
3172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *    string.
318f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
3192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param tz the requested time zone
3202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param date the date
3212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param type the generic name type, either LONG or SHORT
3222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the name used for a generic name type, which could be the
3232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * generic name, or the standard name (if the zone does not observes DST
3242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * around the date), or the partial location name.
3252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String formatGenericNonLocationName(TimeZone tz, GenericNameType type, long date) {
3272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        assert(type == GenericNameType.LONG || type == GenericNameType.SHORT);
3282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String tzID = ZoneMeta.getCanonicalCLDRID(tz);
3292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tzID == null) {
3312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
3322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Try to get a name from time zone first
3352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        NameType nameType = (type == GenericNameType.LONG) ? NameType.LONG_GENERIC : NameType.SHORT_GENERIC;
3362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String name = _tznames.getTimeZoneDisplayName(tzID, nameType);
3372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (name != null) {
3392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return name;
3402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Try meta zone
3432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String mzID = _tznames.getMetaZoneID(tzID, date);
3442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (mzID != null) {
3452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            boolean useStandard = false;
3462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            int[] offsets = {0, 0};
3472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tz.getOffset(date, false, offsets);
3482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (offsets[1] == 0) {
3502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                useStandard = true;
3512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Check if the zone actually uses daylight saving time around the time
3522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (tz instanceof BasicTimeZone) {
3532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    BasicTimeZone btz = (BasicTimeZone)tz;
3542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    TimeZoneTransition before = btz.getPreviousTransition(date, true);
3552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (before != null
3562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            && (date - before.getTime() < DST_CHECK_RANGE)
3572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            && before.getFrom().getDSTSavings() != 0) {
3582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        useStandard = false;
3592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
3602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        TimeZoneTransition after = btz.getNextTransition(date, false);
3612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (after != null
3622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                && (after.getTime() - date < DST_CHECK_RANGE)
3632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                && after.getTo().getDSTSavings() != 0) {
3642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            useStandard = false;
3652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
3662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
3672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
3682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // If not BasicTimeZone... only if the instance is not an ICU's implementation.
3692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // We may get a wrong answer in edge case, but it should practically work OK.
3702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    int[] tmpOffsets = new int[2];
3712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    tz.getOffset(date - DST_CHECK_RANGE, false, tmpOffsets);
3722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (tmpOffsets[1] != 0) {
3732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        useStandard = false;
3742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
3752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        tz.getOffset(date + DST_CHECK_RANGE, false, tmpOffsets);
3762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (tmpOffsets[1] != 0){
3772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            useStandard = false;
3782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
3792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
3802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
3812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (useStandard) {
3832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                NameType stdNameType = (nameType == NameType.LONG_GENERIC) ?
3842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        NameType.LONG_STANDARD : NameType.SHORT_STANDARD;
3852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String stdName = _tznames.getDisplayName(tzID, stdNameType, date);
3862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (stdName != null) {
3872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    name = stdName;
3882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // TODO: revisit this issue later
3902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // In CLDR, a same display name is used for both generic and standard
3912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // for some meta zones in some locales.  This looks like a data bugs.
3922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // For now, we check if the standard name is different from its generic
3932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // name below.
3942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    String mzGenericName = _tznames.getMetaZoneDisplayName(mzID, nameType);
3952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (stdName.equalsIgnoreCase(mzGenericName)) {
3962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        name = null;
3972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
3982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
3992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (name == null) {
4022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Get a name from meta zone
4032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String mzName = _tznames.getMetaZoneDisplayName(mzID, nameType);
4042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (mzName != null) {
4052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // Check if we need to use a partial location format.
4062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // This check is done by comparing offset with the meta zone's
4072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // golden zone at the given date.
4082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    String goldenID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
4092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (goldenID != null && !goldenID.equals(tzID)) {
4102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        TimeZone goldenZone = TimeZone.getFrozenTimeZone(goldenID);
4112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        int[] offsets1 = {0, 0};
4122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // Check offset in the golden zone with wall time.
4142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // With getOffset(date, false, offsets1),
4152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // you may get incorrect results because of time overlap at DST->STD
4162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // transition.
4172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        goldenZone.getOffset(date + offsets[0] + offsets[1], true, offsets1);
4182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (offsets[0] != offsets1[0] || offsets[1] != offsets1[1]) {
4202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            // Now we need to use a partial location format.
4212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            name = getPartialLocationName(tzID, mzID, (nameType == NameType.LONG_GENERIC), mzName);
4222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        } else {
4232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            name = mzName;
4242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
4252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
4262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        name = mzName;
4272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return name;
4322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
4352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private simple pattern formatter used for formatting generic location names
4362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and partial location names. We intentionally use JDK MessageFormat
4372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * for performance reason.
438f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
4392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param pat the message pattern enum
4402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param args the format argument(s)
4412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the formatted string
4422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
4432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private synchronized String formatPattern(Pattern pat, String... args) {
4442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_patternFormatters == null) {
4452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _patternFormatters = new MessageFormat[Pattern.values().length];
4462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        int idx = pat.ordinal();
4492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_patternFormatters[idx] == null) {
4502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String patText;
4512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            try {
4522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
453f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    ICUData.ICU_ZONE_BASE_NAME, _locale);
4542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                patText = bundle.getStringWithFallback("zoneStrings/" + pat.key());
4552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } catch (MissingResourceException e) {
4562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                patText = pat.defaultValue();
4572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
4582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _patternFormatters[idx] = new MessageFormat(patText);
4602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return _patternFormatters[idx].format(args);
4622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
4652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private method returning LocaleDisplayNames instance for the locale of this
4662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * instance. Because LocaleDisplayNames is only used for generic
4672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * location formant and partial location format, the LocaleDisplayNames
4682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * is instantiated lazily.
469f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
4702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the instance of LocaleDisplayNames for the locale of this object.
4712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
4722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private synchronized LocaleDisplayNames getLocaleDisplayNames() {
4732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LocaleDisplayNames locNames = null;
4742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_localeDisplayNamesRef != null) {
4752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            locNames = _localeDisplayNamesRef.get();
4762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (locNames == null) {
4782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            locNames = LocaleDisplayNames.getInstance(_locale);
4792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _localeDisplayNamesRef = new WeakReference<LocaleDisplayNames>(locNames);
4802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return locNames;
4822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
4832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private synchronized void loadStrings(String tzCanonicalID) {
4852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tzCanonicalID == null || tzCanonicalID.length() == 0) {
4862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return;
4872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
4882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // getGenericLocationName() formats a name and put it into the trie
489f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        getGenericLocationName(tzCanonicalID);
4902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Generic partial location format
4922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Set<String> mzIDs = _tznames.getAvailableMetaZoneIDs(tzCanonicalID);
4932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (String mzID : mzIDs) {
4942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // if this time zone is not the golden zone of the meta zone,
4952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // partial location name (such as "PT (Los Angeles)") might be
4962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // available.
4972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String goldenID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
4982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!tzCanonicalID.equals(goldenID)) {
4992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                for (NameType genNonLocType : GENERIC_NON_LOCATION_TYPES) {
5002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    String mzGenName = _tznames.getMetaZoneDisplayName(mzID, genNonLocType);
5012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (mzGenName != null) {
5022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // getPartialLocationName() formats a name and put it into the trie
5032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        getPartialLocationName(tzCanonicalID, mzID, (genNonLocType == NameType.LONG_GENERIC), mzGenName);
5042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private method returning the target region. The target regions is determined by
5122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the locale of this instance. When a generic name is coming from
5132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * a meta zone, this region is used for checking if the time zone
5142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * is a reference zone of the meta zone.
515f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
5162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the target region
5172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private synchronized String getTargetRegion() {
5192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (_region == null) {
5202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _region = _locale.getCountry();
5212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (_region.length() == 0) {
5222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                ULocale tmp = ULocale.addLikelySubtags(_locale);
5232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _region = tmp.getCountry();
5242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_region.length() == 0) {
5252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _region = "001";
5262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return _region;
5302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Private method for formatting partial location names. This format
5342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * is used when a generic name of a meta zone is available, but the given
5352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * time zone is not a reference zone (golden zone) of the meta zone.
536f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     *
5372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param tzID the canonical time zone ID
5382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param mzID the meta zone ID
5392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param isLong true when long generic name
5402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param mzDisplayName the meta zone generic display name
5412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the partial location format string
5422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private String getPartialLocationName(String tzID, String mzID, boolean isLong, String mzDisplayName) {
5442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String letter = isLong ? "L" : "S";
5452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String key = tzID + "&" + mzID + "#" + letter;
5462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String name = _genericPartialLocationNamesMap.get(key);
5472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (name != null) {
5482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return name;
5492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String location = null;
5512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String countryCode = ZoneMeta.getCanonicalCountry(tzID);
5522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (countryCode != null) {
5532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Is this the golden zone for the region?
5542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String regionalGolden = _tznames.getReferenceZoneID(mzID, countryCode);
5552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (tzID.equals(regionalGolden)) {
5562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Use country name
5572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                location = getLocaleDisplayNames().regionDisplayName(countryCode);
5582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
5592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // Otherwise, use exemplar city name
5602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                location = _tznames.getExemplarLocationName(tzID);
5612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } else {
5632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            location = _tznames.getExemplarLocationName(tzID);
5642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (location == null) {
5652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // This could happen when the time zone is not associated with a country,
5662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // and its ID is not hierarchical, for example, CST6CDT.
5672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // We use the canonical ID itself as the location for this case.
5682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                location = tzID;
5692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        name = formatPattern(Pattern.FALLBACK_FORMAT, location, mzDisplayName);
5722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        synchronized (this) {   // we have to sync the name map and the trie
5732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String tmp = _genericPartialLocationNamesMap.putIfAbsent(key.intern(), name.intern());
5742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (tmp == null) {
575f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                NameInfo info = new NameInfo(tzID.intern(),
576f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        isLong ? GenericNameType.LONG : GenericNameType.SHORT);
5772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _gnamesTrie.put(name, info);
5782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
5792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                name = tmp;
5802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return name;
5832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * A private class used for storing the name information in the local trie.
5872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static class NameInfo {
589f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        final String tzID;
590f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        final GenericNameType type;
591f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
592f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        NameInfo(String tzID, GenericNameType type) {
593f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.tzID = tzID;
594f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.type = type;
595f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        }
5962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * A class used for returning the name search result used by
6002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@link TimeZoneGenericNames#find(String, int, EnumSet)}.
6012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static class GenericMatchInfo {
603f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        final GenericNameType nameType;
604f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        final String tzID;
605f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        final int matchLength;
606f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        final TimeType timeType;
607f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
608f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        private GenericMatchInfo(GenericNameType nameType, String tzID, int matchLength) {
609f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this(nameType, tzID, matchLength, TimeType.UNKNOWN);
610f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        }
611f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
612f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        private GenericMatchInfo(GenericNameType nameType, String tzID, int matchLength, TimeType timeType) {
613f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.nameType = nameType;
614f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.tzID = tzID;
615f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.matchLength = matchLength;
616f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.timeType = timeType;
617f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        }
6182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public GenericNameType nameType() {
6202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return nameType;
6212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String tzID() {
6242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return tzID;
6252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public TimeType timeType() {
6282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return timeType;
6292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public int matchLength() {
6322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return matchLength;
6332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * A private class implementing the search callback interface in
6382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <code>TextTrieMap</code> for collecting match results.
6392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static class GenericNameSearchHandler implements ResultHandler<NameInfo> {
6412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private EnumSet<GenericNameType> _types;
6422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private Collection<GenericMatchInfo> _matches;
6432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private int _maxMatchLen;
6442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        GenericNameSearchHandler(EnumSet<GenericNameType> types) {
6462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _types = types;
6472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /* (non-Javadoc)
6501fba789ac68efdd9120a7373f49daef42833e674Neil Fuller         * @see android.icu.impl.TextTrieMap.ResultHandler#handlePrefixMatch(int, java.util.Iterator)
6512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
652f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
6532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean handlePrefixMatch(int matchLength, Iterator<NameInfo> values) {
6542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            while (values.hasNext()) {
6552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                NameInfo info = values.next();
6562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_types != null && !_types.contains(info.type)) {
6572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    continue;
6582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
659f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                GenericMatchInfo matchInfo = new GenericMatchInfo(info.type, info.tzID, matchLength);
6602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (_matches == null) {
6612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _matches = new LinkedList<GenericMatchInfo>();
6622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                _matches.add(matchInfo);
6642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (matchLength > _maxMatchLen) {
6652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    _maxMatchLen = matchLength;
6662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return true;
6692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
6722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Returns the match results
6732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * @return the match results
6742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
6752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public Collection<GenericMatchInfo> getMatches() {
6762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _matches;
6772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
6802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Returns the maximum match length, or 0 if no match was found
6812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * @return the maximum match length
6822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
6832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public int getMaxMatchLen() {
6842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return _maxMatchLen;
6852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
6882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Resets the match results
6892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
6902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public void resetResults() {
6912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _matches = null;
6922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            _maxMatchLen = 0;
6932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns the best match of time zone display name for the specified types in the
6982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * given text at the given offset.
6992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param text the text
7002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param start the start offset in the text
7012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param genericTypes the set of name types.
7022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return the best matching name info.
7032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public GenericMatchInfo findBestMatch(String text, int start, EnumSet<GenericNameType> genericTypes) {
7052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (text == null || text.length() == 0 || start < 0 || start >= text.length()) {
7062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("bad input text or range");
7072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        GenericMatchInfo bestMatch = null;
7092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Find matches in the TimeZoneNames first
7112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Collection<MatchInfo> tznamesMatches = findTimeZoneNames(text, start, genericTypes);
7122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tznamesMatches != null) {
7132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            MatchInfo longestMatch = null;
7142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (MatchInfo match : tznamesMatches) {
7152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (longestMatch == null || match.matchLength() > longestMatch.matchLength()) {
7162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    longestMatch = match;
7172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
7182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (longestMatch != null) {
7202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                bestMatch = createGenericMatchInfo(longestMatch);
7212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (bestMatch.matchLength() == (text.length() - start)) {
7222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // Full match
7232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    //return bestMatch;
7242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // TODO Some time zone uses a same name for the long standard name
7262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // and the location name. When the match is a long standard name,
7272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // then we need to check if the name is same with the location name.
7282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // This is probably a data error or a design bug.
7292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller//                    if (bestMatch.nameType != GenericNameType.LONG || bestMatch.timeType != TimeType.STANDARD) {
7302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller//                        return bestMatch;
7312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller//                    }
7322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // TODO The deprecation of commonlyUsed flag introduced the name
7342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // conflict not only for long standard names, but short standard names too.
7352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // These short names (found in zh_Hant) should be gone once we clean
7362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // up CLDR time zone display name data. Once the short name conflict
7372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // problem (with location name) is resolved, we should change the condition
7382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // below back to the original one above. -Yoshito (2011-09-14)
7392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (bestMatch.timeType != TimeType.STANDARD) {
7402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        return bestMatch;
7412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
7422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
7432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Find matches in the local trie
7472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Collection<GenericMatchInfo> localMatches = findLocal(text, start, genericTypes);
7482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (localMatches != null) {
7492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (GenericMatchInfo match : localMatches) {
7502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // TODO See the above TODO. We use match.matchLength() >= bestMatch.matcheLength()
7512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // for the reason described above.
7522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                //if (bestMatch == null || match.matchLength() > bestMatch.matchLength()) {
7532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (bestMatch == null || match.matchLength() >= bestMatch.matchLength()) {
7542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    bestMatch = match;
7552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
7562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return bestMatch;
7602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns a collection of time zone display name matches for the specified types in the
7642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * given text at the given offset.
7652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param text the text
7662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param start the start offset in the text
7672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param genericTypes the set of name types.
7682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return A collection of match info.
7692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Collection<GenericMatchInfo> find(String text, int start, EnumSet<GenericNameType> genericTypes) {
7712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (text == null || text.length() == 0 || start < 0 || start >= text.length()) {
7722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("bad input text or range");
7732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Find matches in the local trie
7752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Collection<GenericMatchInfo> results = findLocal(text, start, genericTypes);
7762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Also find matches in the TimeZoneNames
7782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Collection<MatchInfo> tznamesMatches = findTimeZoneNames(text, start, genericTypes);
7792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tznamesMatches != null) {
7802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // transform matches and append them to local matches
7812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (MatchInfo match : tznamesMatches) {
7822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (results == null) {
7832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    results = new LinkedList<GenericMatchInfo>();
7842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
7852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                results.add(createGenericMatchInfo(match));
7862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return results;
7892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns a <code>GenericMatchInfo</code> for the given <code>MatchInfo</code>.
7932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param matchInfo the MatchInfo
7942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return A GenericMatchInfo
7952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private GenericMatchInfo createGenericMatchInfo(MatchInfo matchInfo) {
7972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        GenericNameType nameType = null;
7982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeType timeType = TimeType.UNKNOWN;
7992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        switch (matchInfo.nameType()) {
8002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case LONG_STANDARD:
8012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameType = GenericNameType.LONG;
8022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            timeType = TimeType.STANDARD;
8032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            break;
8042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case LONG_GENERIC:
8052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameType = GenericNameType.LONG;
8062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            break;
8072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case SHORT_STANDARD:
8082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameType = GenericNameType.SHORT;
8092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            timeType = TimeType.STANDARD;
8102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            break;
8112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        case SHORT_GENERIC:
8122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameType = GenericNameType.SHORT;
8132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            break;
8142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        default:
8152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new IllegalArgumentException("Unexpected MatchInfo name type - " + matchInfo.nameType());
8162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String tzID = matchInfo.tzID();
8192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (tzID == null) {
8202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            String mzID = matchInfo.mzID();
8212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            assert(mzID != null);
8222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tzID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
8232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        assert(tzID != null);
8252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
826f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        GenericMatchInfo gmatch = new GenericMatchInfo(nameType, tzID, matchInfo.matchLength(), timeType);
8272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return gmatch;
8292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns a collection of time zone display name matches for the specified types in the
8332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * given text at the given offset. This method only finds matches from the TimeZoneNames
8342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * used by this object.
8352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param text the text
8362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param start the start offset in the text
8372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param types the set of name types.
8382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return A collection of match info.
8392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private Collection<MatchInfo> findTimeZoneNames(String text, int start, EnumSet<GenericNameType> types) {
8412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Collection<MatchInfo> tznamesMatches = null;
8422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Check if the target name type is really in the TimeZoneNames
8442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        EnumSet<NameType> nameTypes = EnumSet.noneOf(NameType.class);
8452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (types.contains(GenericNameType.LONG)) {
8462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameTypes.add(NameType.LONG_GENERIC);
8472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameTypes.add(NameType.LONG_STANDARD);
8482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (types.contains(GenericNameType.SHORT)) {
8502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameTypes.add(NameType.SHORT_GENERIC);
8512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            nameTypes.add(NameType.SHORT_STANDARD);
8522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
853f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
8542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (!nameTypes.isEmpty()) {
8552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // Find matches in the TimeZoneNames
8562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            tznamesMatches = _tznames.find(text, start, nameTypes);
8572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return tznamesMatches;
8592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns a collection of time zone display name matches for the specified types in the
8632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * given text at the given offset. This method only finds matches from the local trie,
8642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * that contains 1) generic location names and 2) long/short generic partial location names,
8652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * used by this object.
8662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param text the text
8672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param start the start offset in the text
8682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @param types the set of name types.
8692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * @return A collection of match info.
8702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private synchronized Collection<GenericMatchInfo> findLocal(String text, int start, EnumSet<GenericNameType> types) {
8722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        GenericNameSearchHandler handler = new GenericNameSearchHandler(types);
8732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrie.find(text, start, handler);
8742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (handler.getMaxMatchLen() == (text.length() - start) || _gnamesTrieFullyLoaded) {
8752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // perfect match
8762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return handler.getMatches();
8772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // All names are not yet loaded into the local trie.
8802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // Load all available names into the trie. This could be very heavy.
8812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Set<String> tzIDs = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
8832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        for (String tzID : tzIDs) {
8842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            loadStrings(tzID);
8852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrieFullyLoaded = true;
8872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // now, try it again
8892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        handler.resetResults();
8902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _gnamesTrie.find(text, start, handler);
8912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return handler.getMatches();
8922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <code>TimeZoneGenericNames</code> cache implementation.
8962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static class Cache extends SoftCache<String, TimeZoneGenericNames, ULocale> {
8982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /* (non-Javadoc)
9001fba789ac68efdd9120a7373f49daef42833e674Neil Fuller         * @see android.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
9012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
9022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        @Override
9032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        protected TimeZoneGenericNames createInstance(String key, ULocale data) {
9042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return new TimeZoneGenericNames(data).freeze();
9052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
906f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
9072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /*
9102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * The custom deserialization method.
9112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * This implementation only read locale used by the object.
9122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
9142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        in.defaultReadObject();
9152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        init();
9162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
9202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
921f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean isFrozen() {
9232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return _frozen;
9242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
9282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
929f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneGenericNames freeze() {
9312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        _frozen = true;
9322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return this;
9332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * {@inheritDoc}
9372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
938f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public TimeZoneGenericNames cloneAsThawed() {
9402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        TimeZoneGenericNames copy = null;
9412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
9422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            copy = (TimeZoneGenericNames)super.clone();
9432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            copy._frozen = false;
9442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        } catch (Throwable t) {
9452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            // This should never happen
9462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
9472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return copy;
9482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller}
950