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) 2001-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.util.ArrayList;
132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Collections;
142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Comparator;
152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.EventListener;
162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashMap;
172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.HashSet;
182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Iterator;
192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.List;
202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.ListIterator;
212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Map;
222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Map.Entry;
232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.Set;
242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.SortedMap;
252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport java.util.TreeMap;
26f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubertimport java.util.concurrent.ConcurrentHashMap;
272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ULocale;
292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerimport android.icu.util.ULocale.Category;
302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller/**
322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>A Service provides access to service objects that implement a
332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * particular service, e.g. transliterators.  Users provide a String
342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * id (for example, a locale string) to the service, and get back an
352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * object for that id.  Service objects can be any kind of object.
362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * The service object is cached and returned for later queries, so
372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * generally it should not be mutable, or the caller should clone the
382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * object before modifying it.</p>
392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>Services 'canonicalize' the query id and use the canonical id to
412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * query for the service.  The service also defines a mechanism to
422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * 'fallback' the id multiple times.  Clients can optionally request
432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the actual id that was matched by a query when they use an id to
442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * retrieve a service object.</p>
452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>Service objects are instantiated by Factory objects registered with
472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the service.  The service queries each Factory in turn, from most recently
482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * registered to earliest registered, until one returns a service object.
492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * If none responds with a service object, a fallback id is generated,
502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * and the process repeats until a service object is returned or until
512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the id has no further fallbacks.</p>
522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>Factories can be dynamically registered and unregistered with the
542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * service.  When registered, a Factory is installed at the head of
552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the factory list, and so gets 'first crack' at any keys or fallback
562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * keys.  When unregistered, it is removed from the service and can no
572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * longer be located through it.  Service objects generated by this
582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * factory and held by the client are unaffected.</p>
592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>ICUService uses Keys to query factories and perform
612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * fallback.  The Key defines the canonical form of the id, and
622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * implements the fallback strategy.  Custom Keys can be defined that
632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * parse complex IDs into components that Factories can more easily
642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * use.  The Key can cache the results of this parsing to save
652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * repeated effort.  ICUService provides convenience APIs that
662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * take Strings and generate default Keys for use in querying.</p>
672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>ICUService provides API to get the list of ids publicly
692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * supported by the service (although queries aren't restricted to
702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * this list).  This list contains only 'simple' IDs, and not fully
712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * unique ids.  Factories are associated with each simple ID and
722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the responsible factory can also return a human-readable localized
732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * version of the simple ID, for use in user interfaces.  ICUService
742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * can also provide a sorted collection of the all the localized visible
752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * ids.</p>
762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>ICUService implements ICUNotifier, so that clients can register
782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * to receive notification when factories are added or removed from
792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the service.  ICUService provides a default EventListener subinterface,
802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * ServiceListener, which can be registered with the service.  When
812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * the service changes, the ServiceListener's serviceChanged method
822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * is called, with the service as the only argument.</p>
832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller *
842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * <p>The ICUService API is both rich and generic, and it is expected
852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * that most implementations will statically 'wrap' ICUService to
862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * present a more appropriate API-- for example, to declare the type
872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * of the objects returned from get, to limit the factories that can
882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * be registered with the service, or to define their own listener
892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * interface with a custom callback method.  They might also customize
902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * ICUService by overriding it, for example, to customize the Key and
912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * fallback strategy.  ICULocaleService is a customized service that
922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * uses Locale names as ids and uses Keys that implement the standard
932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller * resource bundle fallback strategy.<p>
94836e6b40a94ec3fb7545a76cb072960442b7eee9Neil Fuller * @hide Only a subset of ICU is exposed in Android
952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller */
962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fullerpublic class ICUService extends ICUNotifier {
972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Name used for debugging.
992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected final String name;
1012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Constructor.
1042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public ICUService() {
1062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        name = "";
1072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final boolean DEBUG = ICUDebug.enabled("service");
1102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Construct with a name (useful for debugging).
1122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public ICUService(String name) {
1142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        this.name = name;
1152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
1162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Access to factories is protected by a read-write lock.  This is
1192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * to allow multiple threads to read concurrently, but keep
1202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * changes to the factory list atomic with respect to all readers.
1212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final ICURWLock factoryLock = new ICURWLock();
1232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * All the factories registered with this service.
1262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private final List<Factory> factories = new ArrayList<Factory>();
1282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Record the default number of factories for this service.
1312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Can be set by markDefault.
1322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private int defaultSize = 0;
1342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
1362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Keys are used to communicate with factories to generate an
1372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * instance of the service.  Keys define how ids are
1382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * canonicalized, provide both a current id and a current
1392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * descriptor to use in querying the cache and factories, and
1402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * determine the fallback strategy.</p>
1412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
1422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p>Keys provide both a currentDescriptor and a currentID.
1432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * The descriptor contains an optional prefix, followed by '/'
1442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * and the currentID.  Factories that handle complex keys,
1452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * for example number format factories that generate multiple
1462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * kinds of formatters for the same locale, use the descriptor
1472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * to provide a fully unique identifier for the service object,
1482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * while using the currentID (in this case, the locale string),
1492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * as the visible IDs that can be localized.
1502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
1512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p> The default implementation of Key has no fallbacks and
1522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * has no custom descriptors.</p>
1532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
1542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static class Key {
1552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final String id;
1562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Construct a key from an id.
1592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
1602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public Key(String id) {
1612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.id = id;
1622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Return the original ID used to construct this key.
1662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
1672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public final String id() {
1682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return id;
1692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Return the canonical version of the original ID.  This implementation
1732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * returns the original ID unchanged.
1742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
1752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String canonicalID() {
1762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return id;
1772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Return the (canonical) current ID.  This implementation
1812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * returns the canonical ID.
1822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
1832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String currentID() {
1842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return canonicalID();
1852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
1872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
1882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Return the current descriptor.  This implementation returns
1892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the current ID.  The current descriptor is used to fully
1902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * identify an instance of the service in the cache.  A
1912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * factory may handle all descriptors for an ID, or just a
1922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * particular descriptor.  The factory can either parse the
1932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * descriptor or use custom API on the key in order to
1942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * instantiate the service.
1952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
1962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String currentDescriptor() {
1972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return "/" + currentID();
1982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
1992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * If the key has a fallback, modify the key and return true,
2022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * otherwise return false.  The current ID will change if there
2032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * is a fallback.  No currentIDs should be repeated, and fallback
2042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * must eventually return false.  This implmentation has no fallbacks
2052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * and always returns false.
2062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean fallback() {
2082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return false;
2092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * If a key created from id would eventually fallback to match the
2132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * canonical ID of this key, return true.
2142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public boolean isFallbackOf(String idToCheck) {
2162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return canonicalID().equals(idToCheck);
2172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Factories generate the service objects maintained by the
2222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * service.  A factory generates a service object from a key,
2232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * updates id->factory mappings, and returns the display name for
2242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * a supported id.
2252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static interface Factory {
2272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Create a service object from the key, if this factory
2302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * supports the key.  Otherwise, return null.
2312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         *
2322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * <p>If the factory supports the key, then it can call
2332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the service's getKey(Key, String[], Factory) method
2342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * passing itself as the factory to get the object that
2352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the service would have created prior to the factory's
2362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * registration with the service.  This can change the
2372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * key, so any information required from the key should
2382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * be extracted before making such a callback.
2392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public Object create(Key key, ICUService service);
2412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Update the result IDs (not descriptors) to reflect the IDs
2442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * this factory handles.  This function and getDisplayName are
2452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * used to support ICUService.getDisplayNames.  Basically, the
2462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * factory has to determine which IDs it will permit to be
2472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * available, and of those, which it will provide localized
2482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * display names for.  In most cases this reflects the IDs that
2492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the factory directly supports.
2502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public void updateVisibleIDs(Map<String, Factory> result);
2522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Return the display name for this id in the provided locale.
2552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * This is an localized id, not a descriptor.  If the id is
2562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * not visible or not defined by the factory, return null.
2572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * If locale is null, return id unchanged.
2582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String getDisplayName(String id, ULocale locale);
2602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
2612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
2632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * A default implementation of factory.  This provides default
2642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * implementations for subclasses, and implements a singleton
2652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * factory that matches a single id  and returns a single
2662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * (possibly deferred-initialized) instance.  This implements
2672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * updateVisibleIDs to add a mapping from its ID to itself
2682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * if visible is true, or to remove any existing mapping
2692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * for its ID if visible is false.
2702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
2712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static class SimpleFactory implements Factory {
2722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        protected Object instance;
2732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        protected String id;
2742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        protected boolean visible;
2752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Convenience constructor that calls SimpleFactory(Object, String, boolean)
2782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * with visible true.
2792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public SimpleFactory(Object instance, String id) {
2812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this(instance, id, true);
2822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Construct a simple factory that maps a single id to a single
2862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * service instance.  If visible is true, the id will be visible.
2872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Neither the instance nor the id can be null.
2882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
2892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public SimpleFactory(Object instance, String id, boolean visible) {
2902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (instance == null || id == null) {
2912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                throw new IllegalArgumentException("Instance or id is null");
2922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
2932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.instance = instance;
2942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.id = id;
2952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.visible = visible;
2962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
2972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
2982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
2992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * Return the service instance if the factory's id is equal to
3002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * the key's currentID.  Service is ignored.
3012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
302f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
3032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public Object create(Key key, ICUService service) {
3042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (id.equals(key.currentID())) {
3052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return instance;
3062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
3082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
3112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * If visible, adds a mapping from id -> this to the result,
3122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * otherwise removes id from result.
3132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
314f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
3152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public void updateVisibleIDs(Map<String, Factory> result) {
3162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (visible) {
3172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result.put(id, this);
3182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            } else {
3192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result.remove(id);
3202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
3212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
3242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * If this.id equals id, returns id regardless of locale,
3252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * otherwise returns null.  (This default implementation has
3262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * no localized id information.)
3272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
328f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
3292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String getDisplayName(String identifier, ULocale locale) {
3302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return (visible && id.equals(identifier)) ? identifier : null;
3312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        /**
3342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         * For debugging.
3352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller         */
336f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        @Override
3372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public String toString() {
3382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            StringBuilder buf = new StringBuilder(super.toString());
3392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            buf.append(", id: ");
3402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            buf.append(id);
3412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            buf.append(", visible: ");
3422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            buf.append(visible);
3432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return buf.toString();
3442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override for get(String, String[]). This uses
3492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * createKey to create a key for the provided descriptor.
3502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Object get(String descriptor) {
3522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getKey(createKey(descriptor), null);
3532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override for get(Key, String[]).  This uses
3572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * createKey to create a key from the provided descriptor.
3582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Object get(String descriptor, String[] actualReturn) {
3602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (descriptor == null) {
3612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new NullPointerException("descriptor must not be null");
3622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getKey(createKey(descriptor), actualReturn);
3642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override for get(Key, String[]).
3682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Object getKey(Key key) {
3702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getKey(key, null);
3712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
3742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p>Given a key, return a service object, and, if actualReturn
3752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * is not null, the descriptor with which it was found in the
3762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * first element of actualReturn.  If no service object matches
3772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * this key, return null, and leave actualReturn unchanged.</p>
3782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
3792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p>This queries the cache using the key's descriptor, and if no
3802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * object in the cache matches it, tries the key on each
3812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * registered factory, in order.  If none generates a service
3822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * object for the key, repeats the process with each fallback of
3832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the key, until either one returns a service object, or the key
3842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * has no fallback.</p>
3852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
3862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p>If key is null, just returns null.</p>
3872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
3882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Object getKey(Key key, String[] actualReturn) {
3892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getKey(key, actualReturn, null);
3902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
3912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // debugging
3932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Map hardRef;
3942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
3952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Object getKey(Key key, String[] actualReturn, Factory factory) {
3962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (factories.size() == 0) {
3972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return handleDefault(key, actualReturn);
3982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
3992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (DEBUG) System.out.println("Service: " + name + " key: " + key.canonicalID());
4012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CacheEntry result = null;
4032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (key != null) {
4042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            try {
4052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // The factory list can't be modified until we're done,
4062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // otherwise we might update the cache with an invalid result.
4072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                // The cache has to stay in synch with the factory list.
4082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                factoryLock.acquireRead();
4092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
410f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                Map<String, CacheEntry> cache = this.cache; // copy so we don't need to sync on this
4112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (cache == null) {
4122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (DEBUG) System.out.println("Service " + name + " cache was empty");
4132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // synchronized since additions and queries on the cache must be atomic
4142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // they can be interleaved, though
415f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    cache = new ConcurrentHashMap<String, CacheEntry>();
4162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                String currentDescriptor = null;
4192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                ArrayList<String> cacheDescriptorList = null;
4202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                boolean putInCache = false;
4212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int NDebug = 0;
4232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int startIndex = 0;
4252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                int limit = factories.size();
4262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                boolean cacheResult = true;
4272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (factory != null) {
4282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    for (int i = 0; i < limit; ++i) {
4292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (factory == factories.get(i)) {
4302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            startIndex = i + 1;
4312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            break;
4322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
4332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (startIndex == 0) {
4352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        throw new IllegalStateException("Factory " + factory + "not registered with service: " + this);
4362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    cacheResult = false;
4382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
4392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            outer:
4412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                do {
4422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    currentDescriptor = key.currentDescriptor();
4432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (DEBUG) System.out.println(name + "[" + NDebug++ + "] looking for: " + currentDescriptor);
4442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    result = cache.get(currentDescriptor);
4452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (result != null) {
4462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (DEBUG) System.out.println(name + " found with descriptor: " + currentDescriptor);
4472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        break outer;
4482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    } else {
4492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (DEBUG) System.out.println("did not find: " + currentDescriptor + " in cache");
4502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // first test of cache failed, so we'll have to update
4532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // the cache if we eventually succeed-- that is, if we're
4542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // going to update the cache at all.
4552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    putInCache = cacheResult;
4562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    //  int n = 0;
4582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    int index = startIndex;
4592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    while (index < limit) {
4602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        Factory f = factories.get(index++);
4612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (DEBUG) System.out.println("trying factory[" + (index-1) + "] " + f.toString());
4622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        Object service = f.create(key, this);
4632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (service != null) {
4642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            result = new CacheEntry(currentDescriptor, service);
4652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (DEBUG) System.out.println(name + " factory supported: " + currentDescriptor + ", caching");
4662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            break outer;
4672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        } else {
4682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            if (DEBUG) System.out.println("factory did not support: " + currentDescriptor);
4692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
4702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // prepare to load the cache with all additional ids that
4732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // will resolve to result, assuming we'll succeed.  We
4742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // don't want to keep querying on an id that's going to
4752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // fallback to the one that succeeded, we want to hit the
4762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    // cache the first time next goaround.
4772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (cacheDescriptorList == null) {
4782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        cacheDescriptorList = new ArrayList<String>(5);
4792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
4802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    cacheDescriptorList.add(currentDescriptor);
4812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } while (key.fallback());
4832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (result != null) {
4852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (putInCache) {
4862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (DEBUG) System.out.println("caching '" + result.actualDescriptor + "'");
4872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        cache.put(result.actualDescriptor, result);
4882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (cacheDescriptorList != null) {
4892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            for (String desc : cacheDescriptorList) {
4902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                if (DEBUG) System.out.println(name + " adding descriptor: '" + desc + "' for actual: '" + result.actualDescriptor + "'");
4912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
4922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                                cache.put(desc, result);
4932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            }
4942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
4952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // Atomic update.  We held the read lock all this time
4962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // so we know our cache is consistent with the factory list.
4972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // We might stomp over a cache that some other thread
4982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // rebuilt, but that's the breaks.  They're both good.
499f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        this.cache = cache;
5002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (actualReturn != null) {
5032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        // strip null prefix
5042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        if (result.actualDescriptor.indexOf("/") == 0) {
5052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            actualReturn[0] = result.actualDescriptor.substring(1);
5062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        } else {
5072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                            actualReturn[0] = result.actualDescriptor;
5082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        }
5092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
5102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    if (DEBUG) System.out.println("found in service: " + name);
5122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    return result.service;
5142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            finally {
5172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                factoryLock.releaseRead();
5182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (DEBUG) System.out.println("not found in service: " + name);
5222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return handleDefault(key, actualReturn);
5242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
525f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    private Map<String, CacheEntry> cache;
5262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // Record the actual id for this service in the cache, so we can return it
5282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // even if we succeed later with a different id.
5292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static final class CacheEntry {
5302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final String actualDescriptor;
5312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        final Object service;
5322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        CacheEntry(String actualDescriptor, Object service) {
5332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.actualDescriptor = actualDescriptor;
5342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.service = service;
5352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Default handler for this service if no factory in the list
5412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * handled the key.
5422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected Object handleDefault(Key key, String[] actualIDReturn) {
5442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return null;
5452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override for getVisibleIDs(String) that passes null
5492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * as the fallback, thus returning all visible IDs.
5502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Set<String> getVisibleIDs() {
5522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getVisibleIDs(null);
5532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p>Return a snapshot of the visible IDs for this service.  This
5572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * set will not change as Factories are added or removed, but the
5582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * supported ids will, so there is no guarantee that all and only
5592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the ids in the returned set are visible and supported by the
5602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * service in subsequent calls.</p>
5612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     *
5622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * <p>matchID is passed to createKey to create a key.  If the
5632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * key is not null, it is used to filter out ids that don't have
5642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the key as a fallback.
5652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Set<String> getVisibleIDs(String matchID) {
5672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Set<String> result = getVisibleIDMap().keySet();
5682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Key fallbackKey = createKey(matchID);
5702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (fallbackKey != null) {
5722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Set<String> temp = new HashSet<String>(result.size());
5732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            for (String id : result) {
5742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (fallbackKey.isFallbackOf(id)) {
5752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    temp.add(id);
5762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
5772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
5782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            result = temp;
5792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
5802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
5812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
5822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
5832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
5842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return a map from visible ids to factories.
5852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
5862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private Map<String, Factory> getVisibleIDMap() {
587f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        synchronized (this) { // or idcache-only lock?
588f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            if (idcache == null) {
589f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                try {
590f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    factoryLock.acquireRead();
591f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    Map<String, Factory> mutableMap = new HashMap<String, Factory>();
592f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    ListIterator<Factory> lIter = factories.listIterator(factories.size());
593f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    while (lIter.hasPrevious()) {
594f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        Factory f = lIter.previous();
595f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                        f.updateVisibleIDs(mutableMap);
5962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
597f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    this.idcache = Collections.unmodifiableMap(mutableMap);
598f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                } finally {
599f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert                    factoryLock.releaseRead();
6002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
6012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return idcache;
6042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
605f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    private Map<String, Factory> idcache;
6062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override for getDisplayName(String, ULocale) that
6092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * uses the current default locale.
6102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String getDisplayName(String id) {
6122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getDisplayName(id, ULocale.getDefault(Category.DISPLAY));
6132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Given a visible id, return the display name in the requested locale.
6172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * If there is no directly supported id corresponding to this id, return
6182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * null.
6192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String getDisplayName(String id, ULocale locale) {
6212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Map<String, Factory> m = getVisibleIDMap();
6222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Factory f = m.get(id);
6232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (f != null) {
6242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return f.getDisplayName(id, locale);
6252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Key key = createKey(id);
6282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (key.fallback()) {
6292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            f = m.get(key.currentID());
6302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (f != null) {
6312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return f.getDisplayName(id, locale);
6322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
6332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
634f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
6352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return null;
6362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
639f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert     * Convenience override of getDisplayNames(ULocale, Comparator, String) that
6402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * uses the current default Locale as the locale, null as
6412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the comparator, and null for the matchID.
6422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public SortedMap<String, String> getDisplayNames() {
6442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ULocale locale = ULocale.getDefault(Category.DISPLAY);
6452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getDisplayNames(locale, null, null);
6462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override of getDisplayNames(ULocale, Comparator, String) that
6502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * uses null for the comparator, and null for the matchID.
6512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public SortedMap<String, String> getDisplayNames(ULocale locale) {
6532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getDisplayNames(locale, null, null);
6542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override of getDisplayNames(ULocale, Comparator, String) that
6582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * uses null for the matchID, thus returning all display names.
6592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public SortedMap<String, String> getDisplayNames(ULocale locale, Comparator<Object> com) {
6612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getDisplayNames(locale, com, null);
6622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Convenience override of getDisplayNames(ULocale, Comparator, String) that
6662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * uses null for the comparator.
6672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public SortedMap<String, String> getDisplayNames(ULocale locale, String matchID) {
6692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return getDisplayNames(locale, null, matchID);
6702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
6712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
6732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return a snapshot of the mapping from display names to visible
6742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * IDs for this service.  This set will not change as factories
6752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * are added or removed, but the supported ids will, so there is
6762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * no guarantee that all and only the ids in the returned map will
6772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * be visible and supported by the service in subsequent calls,
6782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * nor is there any guarantee that the current display names match
6792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * those in the set.  The display names are sorted based on the
6802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * comparator provided.
6812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
6822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public SortedMap<String, String> getDisplayNames(ULocale locale, Comparator<Object> com, String matchID) {
6832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        SortedMap<String, String> dncache = null;
6842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LocaleRef ref = dnref;
6852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (ref != null) {
6872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            dncache = ref.get(locale, com);
6882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
6892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
6902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (dncache == null) {
6912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            synchronized (this) {
6922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                if (ref == dnref || dnref == null) {
6932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    dncache = new TreeMap<String, String>(com); // sorted
694f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert
6952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Map<String, Factory> m = getVisibleIDMap();
6962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    Iterator<Entry<String, Factory>> ei = m.entrySet().iterator();
6972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    while (ei.hasNext()) {
6982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        Entry<String, Factory> e = ei.next();
6992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        String id = e.getKey();
7002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        Factory f = e.getValue();
7012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                        dncache.put(f.getDisplayName(id, locale), id);
7022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    }
7032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    dncache = Collections.unmodifiableSortedMap(dncache);
7052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    dnref = new LocaleRef(dncache, locale, com);
7062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                } else {
7072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    ref = dnref;
7082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                    dncache = ref.get(locale, com);
7092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                }
7102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Key matchKey = createKey(matchID);
7142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (matchKey == null) {
7152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return dncache;
7162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        SortedMap<String, String> result = new TreeMap<String, String>(dncache);
7192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        Iterator<Entry<String, String>> iter = result.entrySet().iterator();
7202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        while (iter.hasNext()) {
7212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            Entry<String, String> e = iter.next();
7222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (!matchKey.isFallbackOf(e.getValue())) {
7232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                iter.remove();
7242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
7272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // we define a class so we get atomic simultaneous access to the
7302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    // locale, comparator, and corresponding map.
7312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private static class LocaleRef {
7322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private final ULocale locale;
733f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        private SortedMap<String, String> dnCache;
7342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        private Comparator<Object> com;
7352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        LocaleRef(SortedMap<String, String> dnCache, ULocale locale, Comparator<Object> com) {
7372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.locale = locale;
7382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            this.com = com;
739f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            this.dnCache = dnCache;
7402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        SortedMap<String, String> get(ULocale loc, Comparator<Object> comp) {
744f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert            SortedMap<String, String> m = dnCache;
7452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (m != null &&
7462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                this.locale.equals(loc) &&
7472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                (this.com == comp || (this.com != null && this.com.equals(comp)))) {
7482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                return m;
7502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
7512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return null;
7522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    private LocaleRef dnref;
7552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return a snapshot of the currently registered factories.  There
7582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * is no guarantee that the list will still match the current
7592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * factory list of the service subsequent to this call.
7602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final List<Factory> factories() {
7622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
7632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.acquireRead();
7642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return new ArrayList<Factory>(factories);
7652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        finally{
7672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.releaseRead();
7682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * A convenience override of registerObject(Object, String, boolean)
7732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * that defaults visible to true.
7742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Factory registerObject(Object obj, String id) {
7762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return registerObject(obj, id, true);
7772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Register an object with the provided id.  The id will be
7812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * canonicalized.  The canonicalized ID will be returned by
7822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * getVisibleIDs if visible is true.
7832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Factory registerObject(Object obj, String id, boolean visible) {
7852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        String canonicalID = createKey(id).canonicalID();
7862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return registerFactory(new SimpleFactory(obj, canonicalID, visible));
7872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
7882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
7892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
7902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Register a Factory.  Returns the factory if the service accepts
7912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the factory, otherwise returns null.  The default implementation
7922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * accepts all factories.
7932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
7942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final Factory registerFactory(Factory factory) {
7952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (factory == null) {
7962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new NullPointerException();
7972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
7982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
7992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.acquireWrite();
8002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factories.add(0, factory);
8012ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            clearCaches();
8022ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        finally {
8042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.releaseWrite();
8052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        notifyChanged();
8072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return factory;
8082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Unregister a factory.  The first matching registered factory will
8122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * be removed from the list.  Returns true if a matching factory was
8132ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * removed.
8142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final boolean unregisterFactory(Factory factory) {
8162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (factory == null) {
8172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            throw new NullPointerException();
8182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        boolean result = false;
8212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
8222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.acquireWrite();
8232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            if (factories.remove(factory)) {
8242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                result = true;
8252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller                clearCaches();
8262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            }
8272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        finally {
8292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.releaseWrite();
8302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8322ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (result) {
8332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            notifyChanged();
8342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return result;
8362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Reset the service to the default factories.  The factory
8402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * lock is acquired and then reInitializeFactories is called.
8412ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public final void reset() {
8432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        try {
8442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.acquireWrite();
8452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            reInitializeFactories();
8462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            clearCaches();
8472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        finally {
8492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            factoryLock.releaseWrite();
8502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
8512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        notifyChanged();
8522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Reinitialize the factory list to its default state.  By default
8562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * this clears the list.  Subclasses can override to provide other
8572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * default initialization of the factory list.  Subclasses must
8582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * not call this method directly, as it must only be called while
8592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * holding write access to the factory list.
8602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected void reInitializeFactories() {
8622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        factories.clear();
8632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return true if the service is in its default state.  The default
8672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * implementation returns true if there are no factories registered.
8682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public boolean isDefault() {
8702ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return factories.size() == defaultSize;
8712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Set the default size to the current number of registered factories.
8752ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Used by subclasses to customize the behavior of isDefault.
8762ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8772ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected void markDefault() {
8782ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        defaultSize = factories.size();
8792ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8802ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8812ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8822ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Create a key from an id.  This creates a Key instance.
8832ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Subclasses can override to define more useful keys appropriate
8842ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * to the factories they accept.  If id is null, returns null.
8852ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8862ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public Key createKey(String id) {
8872ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return id == null ? null : new Key(id);
8882ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
8892ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
8902ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
8912ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Clear caches maintained by this service.  Subclasses can
8922ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * override if they implement additional that need to be cleared
8932ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * when the service changes. Subclasses should generally not call
8942ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * this method directly, as it must only be called while
8952ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * synchronized on this.
8962ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
8972ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected void clearCaches() {
8982ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // we don't synchronize on these because methods that use them
8992ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // copy before use, and check for changes if they modify the
9002ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        // caches.
901f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        cache = null;
902f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        idcache = null;
9032ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        dnref = null;
9042ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9052ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9062ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9072ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Clears only the service cache.
9082ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * This can be called by subclasses when a change affects the service
9092ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * cache but not the id caches, e.g., when the default locale changes
9102ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the resolution of ids changes, but not the visible ids themselves.
9112ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9122ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected void clearServiceCache() {
913f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert        cache = null;
9142ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9152ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9162ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9172ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * ServiceListener is the listener that ICUService provides by default.
9182ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * ICUService will notifiy this listener when factories are added to
9192ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * or removed from the service.  Subclasses can provide
9202ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * different listener interfaces that extend EventListener, and modify
9212ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * acceptsListener and notifyListener as appropriate.
9222ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9232ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public static interface ServiceListener extends EventListener {
9242ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        public void serviceChanged(ICUService service);
9252ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9262ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9272ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9282ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return true if the listener is accepted; by default this
9292ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * requires a ServiceListener.  Subclasses can override to accept
9302ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * different listeners.
9312ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
932f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9332ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected boolean acceptsListener(EventListener l) {
9342ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return l instanceof ServiceListener;
9352ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9362ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9372ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9382ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Notify the listener, which by default is a ServiceListener.
9392ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Subclasses can override to use a different listener.
9402ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
941f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9422ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    protected void notifyListener(EventListener l) {
9432ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ((ServiceListener)l).serviceChanged(this);
9442ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9452ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9462ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9472ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * When the statistics for this service is already enabled,
9482ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * return the log and resets he statistics.
9492ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * When the statistics is not enabled, this method enable
9502ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * the statistics. Used for debugging purposes.
9512ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9522ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String stats() {
9532ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        ICURWLock.Stats stats = factoryLock.resetStats();
9542ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        if (stats != null) {
9552ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller            return stats.toString();
9562ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        }
9572ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return "no stats";
9582ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9592ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9602ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9612ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Return the name of this service. This will be the empty string if none was assigned.
9622ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
9632ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String getName() {
9642ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return name;
9652ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9662ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller
9672ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    /**
9682ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     * Returns the result of super.toString, appending the name in curly braces.
9692ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller     */
970f86f25d102340da66b9c7cb6b2d5ecdc0de43ecfFredrik Roubert    @Override
9712ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    public String toString() {
9722ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller        return super.toString() + "{" + name + "}";
9732ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller    }
9742ae130017183d2f66d55bf0ca51f8da3294644fdNeil Fuller}
975