17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
3f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert * Copyright (C) 2001-2015, International Business Machines Corporation and    *
47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved.                                                *
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Collections;
107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Locale;
117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Map;
127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Set;
137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale;
157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class ICULocaleService extends ICUService {
177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private ULocale fallbackLocale;
187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    private String fallbackLocaleName;
197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct an ICULocaleService.
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ICULocaleService() {
247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Construct an ICULocaleService with a name (useful for debugging).
287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ICULocaleService(String name) {
307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        super(name);
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience override for callers using locales.  This calls
357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * get(ULocale, int, ULocale[]) with KIND_ANY for kind and null for
367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * actualReturn.
377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object get(ULocale locale) {
397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return get(locale, LocaleKey.KIND_ANY, null);
407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience override for callers using locales.  This calls
447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * get(ULocale, int, ULocale[]) with a null actualReturn.
457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object get(ULocale locale, int kind) {
477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return get(locale, kind, null);
487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience override for callers using locales.  This calls
527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * get(ULocale, int, ULocale[]) with KIND_ANY for kind.
537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object get(ULocale locale, ULocale[] actualReturn) {
557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return get(locale, LocaleKey.KIND_ANY, actualReturn);
567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience override for callers using locales.  This uses
607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * createKey(ULocale.toString(), kind) to create a key, calls getKey, and then
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * if actualReturn is not null, returns the actualResult from
627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * getKey (stripping any prefix) into a ULocale.
637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Object get(ULocale locale, int kind, ULocale[] actualReturn) {
657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Key key = createKey(locale, kind);
667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (actualReturn == null) {
677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return getKey(key);
687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        String[] temp = new String[1];
717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Object result = getKey(key, temp);
727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (result != null) {
737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int n = temp[0].indexOf("/");
747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (n >= 0) {
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                temp[0] = temp[0].substring(n+1);
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            actualReturn[0] = new ULocale(temp[0]);
787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return result;
807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience override for callers using locales.  This calls
847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * registerObject(Object, ULocale, int kind, boolean visible)
857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * passing KIND_ANY for the kind, and true for the visibility.
867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Factory registerObject(Object obj, ULocale locale) {
887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return registerObject(obj, locale, LocaleKey.KIND_ANY, true);
897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience override for callers using locales.  This calls
937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * registerObject(Object, ULocale, int kind, boolean visible)
947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * passing KIND_ANY for the kind.
957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Factory registerObject(Object obj, ULocale locale, boolean visible) {
977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return registerObject(obj, locale, LocaleKey.KIND_ANY, visible);
987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience function for callers using locales.  This calls
1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * registerObject(Object, ULocale, int kind, boolean visible)
1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * passing true for the visibility.
1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Factory registerObject(Object obj, ULocale locale, int kind) {
1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return registerObject(obj, locale, kind, true);
1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience function for callers using locales.  This  instantiates
1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * a SimpleLocaleKeyFactory, and registers the factory.
1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Factory registerObject(Object obj, ULocale locale, int kind, boolean visible) {
1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Factory factory = new SimpleLocaleKeyFactory(obj, locale, kind, visible);
1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return registerFactory(factory);
1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience method for callers using locales.  This returns the standard
1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Locale list, built from the Set of visible ids.
1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Locale[] getAvailableLocales() {
1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // TODO make this wrap getAvailableULocales later
1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<String> visIDs = getVisibleIDs();
1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Locale[] locales = new Locale[visIDs.size()];
1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int n = 0;
1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String id : visIDs) {
1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Locale loc = LocaleUtility.getLocaleFromName(id);
1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            locales[n++] = loc;
1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return locales;
1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Convenience method for callers using locales.  This returns the standard
1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * ULocale list, built from the Set of visible ids.
1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public ULocale[] getAvailableULocales() {
1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        Set<String> visIDs = getVisibleIDs();
1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale[] locales = new ULocale[visIDs.size()];
1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        int n = 0;
1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        for (String id : visIDs) {
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            locales[n++] = new ULocale(id);
1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return locales;
1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A subclass of Key that implements a locale fallback mechanism.
1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * The first locale to search for is the locale provided by the
1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * client, and the fallback locale to search for is the current
1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * default locale.  If a prefix is present, the currentDescriptor
1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * includes it before the locale proper, separated by "/".  This
1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is the default key instantiated by ICULocaleService.</p>
1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     *
1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * <p>Canonicalization adjusts the locale string so that the
1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * section before the first understore is in lower case, and the rest
1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * is in upper case, with no trailing underscores.</p>
1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class LocaleKey extends ICUService.Key {
1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private int kind;
1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private int varstart;
1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String primaryID;
1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String fallbackID;
1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private String currentID;
1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static final int KIND_ANY = -1;
1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create a LocaleKey with canonical primary and fallback IDs.
1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID) {
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY);
1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create a LocaleKey with canonical primary and fallback IDs.
1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID, int kind) {
1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (primaryID == null) {
1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String canonicalPrimaryID = ULocale.getName(primaryID);
1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID, kind);
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create a LocaleKey with canonical primary and fallback IDs.
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static LocaleKey createWithCanonical(ULocale locale, String canonicalFallbackID, int kind) {
1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (locale == null) {
1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String canonicalPrimaryID = locale.getName();
1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID, kind);
1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * PrimaryID is the user's requested locale string,
2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * canonicalPrimaryID is this string in canonical form,
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * fallbackID is the current default locale's string in
2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * canonical form.
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, int kind) {
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(primaryID);
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.kind = kind;
2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (canonicalPrimaryID == null || canonicalPrimaryID.equalsIgnoreCase("root")) {
2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.primaryID = "";
2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                this.fallbackID = null;
2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int idx = canonicalPrimaryID.indexOf('@');
2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (idx == 4 && canonicalPrimaryID.regionMatches(true, 0, "root", 0, 4)) {
2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.primaryID = canonicalPrimaryID.substring(4);
2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.varstart = 0;
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.fallbackID = null;
2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.primaryID = canonicalPrimaryID;
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    this.varstart = idx;
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    if (canonicalFallbackID == null || this.primaryID.equals(canonicalFallbackID)) {
2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        this.fallbackID = "";
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    } else {
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                        this.fallbackID = canonicalFallbackID;
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    }
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.currentID = varstart == -1 ? this.primaryID : this.primaryID.substring(0, varstart);
2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the prefix associated with the kind, or null if the kind is KIND_ANY.
2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String prefix() {
2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return kind == KIND_ANY ? null : Integer.toString(kind());
2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the kind code associated with this key.
2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public int kind() {
2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return kind;
2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the (canonical) original ID.
2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String canonicalID() {
2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return primaryID;
2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the (canonical) current ID, or null if no current id.
2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String currentID() {
2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return currentID;
2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the (canonical) current descriptor, or null if no current id.
2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Includes the keywords, whereas the ID does not include keywords.
2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String currentDescriptor() {
2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            String result = currentID();
2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (result != null) {
2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                StringBuilder buf = new StringBuilder(); // default capacity 16 is usually good enough
2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (kind != KIND_ANY) {
2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(prefix());
2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append('/');
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(result);
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (varstart != -1) {
2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    buf.append(primaryID.substring(varstart, primaryID.length()));
2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result = buf.toString();
2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return result;
2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Convenience method to return the locale corresponding to the (canonical) original ID.
2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ULocale canonicalLocale() {
2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return new ULocale(primaryID);
2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Convenience method to return the ulocale corresponding to the (canonical) currentID.
2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ULocale currentLocale() {
2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (varstart == -1) {
2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return new ULocale(currentID);
2947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
2957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return new ULocale(currentID + primaryID.substring(varstart));
2967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
2977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * If the key has a fallback, modify the key and return true,
3017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * otherwise return false.</p>
3027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         *
3037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * <p>First falls back through the primary ID, then through
3047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the fallbackID.  The final fallback is "" (root)
3057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * unless the primary id was "" (root), in which case
3067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * there is no fallback.
3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean fallback() {
3097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            int x = currentID.lastIndexOf('_');
3107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (x != -1) {
3117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                while (--x >= 0 && currentID.charAt(x) == '_') { // handle zh__PINYIN
3127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                currentID = currentID.substring(0, x+1);
3147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
3157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (fallbackID != null) {
3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                currentID = fallbackID;
3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (fallbackID.length() == 0) {
3197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fallbackID = null;
3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fallbackID = "";
3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return true;
3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            currentID = null;
3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * If a key created from id would eventually fallback to match the
3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * canonical ID of this key, return true.
3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public boolean isFallbackOf(String id) {
3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return LocaleUtility.isFallbackOf(canonicalID(), id);
3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A subclass of Factory that uses LocaleKeys.  If 'visible' the
3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * factory reports its IDs.
3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static abstract class LocaleKeyFactory implements Factory {
3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected final String name;
3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected final boolean visible;
3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static final boolean VISIBLE = true;
3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public static final boolean INVISIBLE = false;
3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Constructor used by subclasses.
3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected LocaleKeyFactory(boolean visible) {
3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.visible = visible;
3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.name = null;
3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Constructor used by subclasses.
3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected LocaleKeyFactory(boolean visible, String name) {
3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.visible = visible;
3627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.name = name;
3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Implement superclass abstract method.  This checks the currentID of
3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * the key against the supported IDs, and passes the canonicalLocale and
3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * kind off to handleCreate (which subclasses must implement).
3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Object create(Key key, ICUService service) {
3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (handlesKey(key)) {
3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                LocaleKey lkey = (LocaleKey)key;
3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                int kind = lkey.kind();
3747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                ULocale uloc = lkey.currentLocale();
3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return handleCreate(uloc, kind, service);
3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
3787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // System.out.println("factory: " + this + " did not support id: " + key.currentID());
3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                // System.out.println("supported ids: " + getSupportedIDs());
3807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
3827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean handlesKey(Key key) {
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (key != null) {
3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                String id = key.currentID();
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                Set<String> supported = getSupportedIDs();
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return supported.contains(id);
3897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return false;
3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Override of superclass method.
3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void updateVisibleIDs(Map<String, Factory> result) {
3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            Set<String> cache = getSupportedIDs();
3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (String id : cache) {
3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (visible) {
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.put(id, this);
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    result.remove(id);
4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert       }
4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return a localized name for the locale represented by id.
4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String getDisplayName(String id, ULocale locale) {
4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            // assume if the user called this on us, we must have handled some fallback of this id
4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //          if (isSupportedID(id)) {
4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (locale == null) {
4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return id;
4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            ULocale loc = new ULocale(id);
4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return loc.getDisplayName(locale);
4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //              }
4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            //          return null;
4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ///CLOVER:OFF
4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Utility method used by create(Key, ICUService).  Subclasses can
4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * implement this instead of create.
4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected Object handleCreate(ULocale loc, int kind, ICUService service) {
4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return null;
4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ///CLOVER:ON
4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return true if this id is one the factory supports (visible or
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * otherwise).
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean isSupportedID(String id) {
4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return getSupportedIDs().contains(id);
4387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the set of ids that this factory supports (visible or
4427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * otherwise).  This can be called often and might need to be
4437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * cached if it is expensive to create.
4447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected Set<String> getSupportedIDs() {
4467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return Collections.emptySet();
4477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * For debugging.
4517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
4537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder buf = new StringBuilder(super.toString());
4547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (name != null) {
4557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(", name: ");
4567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                buf.append(name);
4577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(", visible: ");
4597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(visible);
4607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return buf.toString();
4617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
4657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A LocaleKeyFactory that just returns a single object for a kind/locale.
4667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
4677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class SimpleLocaleKeyFactory extends LocaleKeyFactory {
4687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final Object obj;
4697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final String id;
4707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        private final int kind;
4717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        // TODO: remove when we no longer need this
4737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible) {
4747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(obj, locale, kind, visible, null);
4757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible, String name) {
4787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(visible, name);
4797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.obj = obj;
4817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.id = locale.getBaseName();
4827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.kind = kind;
4837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
4847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
4867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Returns the service object if kind/locale match.  Service is not used.
4877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
4887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public Object create(Key key, ICUService service) {
4897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!(key instanceof LocaleKey)) {
4907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
4917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
4937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            LocaleKey lkey = (LocaleKey)key;
4947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (kind != LocaleKey.KIND_ANY && kind != lkey.kind()) {
4957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
4967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (!id.equals(lkey.currentID())) {
4987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                return null;
4997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return obj;
5027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected boolean isSupportedID(String idToCheck) {
5057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return this.id.equals(idToCheck);
5067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void updateVisibleIDs(Map<String, Factory> result) {
5097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            if (visible) {
5107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.put(id, this);
5117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            } else {
5127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.remove(id);
5137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
5177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder buf = new StringBuilder(super.toString());
5187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(", id: ");
5197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(id);
5207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(", kind: ");
5217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            buf.append(kind);
5227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return buf.toString();
5237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * A LocaleKeyFactory that creates a service based on the ICU locale data.
5287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * This is a base class for most ICU factories.  Subclasses instantiate it
5297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * with a constructor that takes a bundle name, which determines the supported
5307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * IDs.  Subclasses then override handleCreate to create the actual service
5317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * object.  The default implementation returns a resource bundle.
5327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public static class ICUResourceBundleFactory extends LocaleKeyFactory {
5347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected final String bundleName;
5357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Convenience constructor that uses the main ICU bundle name.
5387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ICUResourceBundleFactory() {
5407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this(ICUResourceBundle.ICU_BASE_NAME);
5417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * A service factory based on ICU resource data in resources
5457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * with the given name.
5467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public ICUResourceBundleFactory(String bundleName) {
5487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            super(true);
5497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            this.bundleName = bundleName;
5517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Return the supported IDs.  This is the set of all locale names for the bundleName.
5557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected Set<String> getSupportedIDs() {
5577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return ICUResourceBundle.getFullLocaleNameSet(bundleName, loader());
5587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Override of superclass method.
5627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public void updateVisibleIDs(Map<String, Factory> result) {
5647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert          Set<String> visibleIDs = ICUResourceBundle.getAvailableLocaleNameSet(bundleName, loader()); // only visible ids
5657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            for (String id : visibleIDs) {
5667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                result.put(id, this);
5677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
5687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        /**
5717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * Create the service.  The default implementation returns the resource bundle
5727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         * for the locale, ignoring kind, and service.
5737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert         */
5747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected Object handleCreate(ULocale loc, int kind, ICUService service) {
5757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return ICUResourceBundle.getBundleInstance(bundleName, loc, loader());
5767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        protected ClassLoader loader() {
579f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            return ClassLoaderUtil.getClassLoader(getClass());
5807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        public String toString() {
5837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            return super.toString() + ", bundle: " + bundleName;
5847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
5857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
5867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
5877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
5887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Return the name of the current fallback locale.  If it has changed since this was
5897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * last accessed, the service cache is cleared.
5907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
5917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String validateFallbackLocale() {
5927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        ULocale loc = ULocale.getDefault();
5937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        if (loc != fallbackLocale) {
5947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            synchronized (this) {
5957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                if (loc != fallbackLocale) {
5967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fallbackLocale = loc;
5977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    fallbackLocaleName = loc.getBaseName();
5987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                    clearServiceCache();
5997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
6007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
6017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
6027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return fallbackLocaleName;
6037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Key createKey(String id) {
6067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale());
6077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Key createKey(String id, int kind) {
6107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale(), kind);
6117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public Key createKey(ULocale l, int kind) {
6147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        return LocaleKey.createWithCanonical(l, validateFallbackLocale(), kind);
6157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
6167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
617