Home | History | Annotate | Download | only in impl
      1 /* GENERATED SOURCE. DO NOT MODIFY. */
      2 //  2016 and later: Unicode, Inc. and others.
      3 // License & terms of use: http://www.unicode.org/copyright.html#License
      4 /**
      5  *******************************************************************************
      6  * Copyright (C) 2001-2016, International Business Machines Corporation and
      7  * others. All Rights Reserved.
      8  *******************************************************************************
      9  */
     10 package android.icu.impl;
     11 
     12 import java.util.Collections;
     13 import java.util.Locale;
     14 import java.util.Map;
     15 import java.util.Set;
     16 
     17 import android.icu.util.ULocale;
     18 
     19 /**
     20  * @hide Only a subset of ICU is exposed in Android
     21  */
     22 public class ICULocaleService extends ICUService {
     23     private ULocale fallbackLocale;
     24     private String fallbackLocaleName;
     25 
     26     /**
     27      * Construct an ICULocaleService.
     28      */
     29     public ICULocaleService() {
     30     }
     31 
     32     /**
     33      * Construct an ICULocaleService with a name (useful for debugging).
     34      */
     35     public ICULocaleService(String name) {
     36         super(name);
     37     }
     38 
     39     /**
     40      * Convenience override for callers using locales.  This calls
     41      * get(ULocale, int, ULocale[]) with KIND_ANY for kind and null for
     42      * actualReturn.
     43      */
     44     public Object get(ULocale locale) {
     45         return get(locale, LocaleKey.KIND_ANY, null);
     46     }
     47 
     48     /**
     49      * Convenience override for callers using locales.  This calls
     50      * get(ULocale, int, ULocale[]) with a null actualReturn.
     51      */
     52     public Object get(ULocale locale, int kind) {
     53         return get(locale, kind, null);
     54     }
     55 
     56     /**
     57      * Convenience override for callers using locales.  This calls
     58      * get(ULocale, int, ULocale[]) with KIND_ANY for kind.
     59      */
     60     public Object get(ULocale locale, ULocale[] actualReturn) {
     61         return get(locale, LocaleKey.KIND_ANY, actualReturn);
     62     }
     63 
     64     /**
     65      * Convenience override for callers using locales.  This uses
     66      * createKey(ULocale.toString(), kind) to create a key, calls getKey, and then
     67      * if actualReturn is not null, returns the actualResult from
     68      * getKey (stripping any prefix) into a ULocale.
     69      */
     70     public Object get(ULocale locale, int kind, ULocale[] actualReturn) {
     71         Key key = createKey(locale, kind);
     72         if (actualReturn == null) {
     73             return getKey(key);
     74         }
     75 
     76         String[] temp = new String[1];
     77         Object result = getKey(key, temp);
     78         if (result != null) {
     79             int n = temp[0].indexOf("/");
     80             if (n >= 0) {
     81                 temp[0] = temp[0].substring(n+1);
     82             }
     83             actualReturn[0] = new ULocale(temp[0]);
     84         }
     85         return result;
     86     }
     87 
     88     /**
     89      * Convenience override for callers using locales.  This calls
     90      * registerObject(Object, ULocale, int kind, boolean visible)
     91      * passing KIND_ANY for the kind, and true for the visibility.
     92      */
     93     public Factory registerObject(Object obj, ULocale locale) {
     94         return registerObject(obj, locale, LocaleKey.KIND_ANY, true);
     95     }
     96 
     97     /**
     98      * Convenience override for callers using locales.  This calls
     99      * registerObject(Object, ULocale, int kind, boolean visible)
    100      * passing KIND_ANY for the kind.
    101      */
    102     public Factory registerObject(Object obj, ULocale locale, boolean visible) {
    103         return registerObject(obj, locale, LocaleKey.KIND_ANY, visible);
    104     }
    105 
    106     /**
    107      * Convenience function for callers using locales.  This calls
    108      * registerObject(Object, ULocale, int kind, boolean visible)
    109      * passing true for the visibility.
    110      */
    111     public Factory registerObject(Object obj, ULocale locale, int kind) {
    112         return registerObject(obj, locale, kind, true);
    113     }
    114 
    115     /**
    116      * Convenience function for callers using locales.  This  instantiates
    117      * a SimpleLocaleKeyFactory, and registers the factory.
    118      */
    119     public Factory registerObject(Object obj, ULocale locale, int kind, boolean visible) {
    120         Factory factory = new SimpleLocaleKeyFactory(obj, locale, kind, visible);
    121         return registerFactory(factory);
    122     }
    123 
    124     /**
    125      * Convenience method for callers using locales.  This returns the standard
    126      * Locale list, built from the Set of visible ids.
    127      */
    128     public Locale[] getAvailableLocales() {
    129         // TODO make this wrap getAvailableULocales later
    130         Set<String> visIDs = getVisibleIDs();
    131         Locale[] locales = new Locale[visIDs.size()];
    132         int n = 0;
    133         for (String id : visIDs) {
    134             Locale loc = LocaleUtility.getLocaleFromName(id);
    135             locales[n++] = loc;
    136         }
    137         return locales;
    138     }
    139 
    140     /**
    141      * Convenience method for callers using locales.  This returns the standard
    142      * ULocale list, built from the Set of visible ids.
    143      */
    144     public ULocale[] getAvailableULocales() {
    145         Set<String> visIDs = getVisibleIDs();
    146         ULocale[] locales = new ULocale[visIDs.size()];
    147         int n = 0;
    148         for (String id : visIDs) {
    149             locales[n++] = new ULocale(id);
    150         }
    151         return locales;
    152     }
    153 
    154     /**
    155      * A subclass of Key that implements a locale fallback mechanism.
    156      * The first locale to search for is the locale provided by the
    157      * client, and the fallback locale to search for is the current
    158      * default locale.  If a prefix is present, the currentDescriptor
    159      * includes it before the locale proper, separated by "/".  This
    160      * is the default key instantiated by ICULocaleService.</p>
    161      *
    162      * <p>Canonicalization adjusts the locale string so that the
    163      * section before the first understore is in lower case, and the rest
    164      * is in upper case, with no trailing underscores.</p>
    165      */
    166     public static class LocaleKey extends ICUService.Key {
    167         private int kind;
    168         private int varstart;
    169         private String primaryID;
    170         private String fallbackID;
    171         private String currentID;
    172 
    173         public static final int KIND_ANY = -1;
    174 
    175         /**
    176          * Create a LocaleKey with canonical primary and fallback IDs.
    177          */
    178         public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID) {
    179             return createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY);
    180         }
    181 
    182         /**
    183          * Create a LocaleKey with canonical primary and fallback IDs.
    184          */
    185         public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID, int kind) {
    186             if (primaryID == null) {
    187                 return null;
    188             }
    189             String canonicalPrimaryID = ULocale.getName(primaryID);
    190             return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID, kind);
    191         }
    192 
    193         /**
    194          * Create a LocaleKey with canonical primary and fallback IDs.
    195          */
    196         public static LocaleKey createWithCanonical(ULocale locale, String canonicalFallbackID, int kind) {
    197             if (locale == null) {
    198                 return null;
    199             }
    200             String canonicalPrimaryID = locale.getName();
    201             return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID, kind);
    202         }
    203 
    204         /**
    205          * PrimaryID is the user's requested locale string,
    206          * canonicalPrimaryID is this string in canonical form,
    207          * fallbackID is the current default locale's string in
    208          * canonical form.
    209          */
    210         protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, int kind) {
    211             super(primaryID);
    212             this.kind = kind;
    213 
    214             if (canonicalPrimaryID == null || canonicalPrimaryID.equalsIgnoreCase("root")) {
    215                 this.primaryID = "";
    216                 this.fallbackID = null;
    217             } else {
    218                 int idx = canonicalPrimaryID.indexOf('@');
    219                 if (idx == 4 && canonicalPrimaryID.regionMatches(true, 0, "root", 0, 4)) {
    220                     this.primaryID = canonicalPrimaryID.substring(4);
    221                     this.varstart = 0;
    222                     this.fallbackID = null;
    223                 } else {
    224                     this.primaryID = canonicalPrimaryID;
    225                     this.varstart = idx;
    226 
    227                     if (canonicalFallbackID == null || this.primaryID.equals(canonicalFallbackID)) {
    228                         this.fallbackID = "";
    229                     } else {
    230                         this.fallbackID = canonicalFallbackID;
    231                     }
    232                 }
    233             }
    234 
    235             this.currentID = varstart == -1 ? this.primaryID : this.primaryID.substring(0, varstart);
    236         }
    237 
    238         /**
    239          * Return the prefix associated with the kind, or null if the kind is KIND_ANY.
    240          */
    241         public String prefix() {
    242             return kind == KIND_ANY ? null : Integer.toString(kind());
    243         }
    244 
    245         /**
    246          * Return the kind code associated with this key.
    247          */
    248         public int kind() {
    249             return kind;
    250         }
    251 
    252         /**
    253          * Return the (canonical) original ID.
    254          */
    255         @Override
    256         public String canonicalID() {
    257             return primaryID;
    258         }
    259 
    260         /**
    261          * Return the (canonical) current ID, or null if no current id.
    262          */
    263         @Override
    264         public String currentID() {
    265             return currentID;
    266         }
    267 
    268         /**
    269          * Return the (canonical) current descriptor, or null if no current id.
    270          * Includes the keywords, whereas the ID does not include keywords.
    271          */
    272         @Override
    273         public String currentDescriptor() {
    274             String result = currentID();
    275             if (result != null) {
    276                 StringBuilder buf = new StringBuilder(); // default capacity 16 is usually good enough
    277                 if (kind != KIND_ANY) {
    278                     buf.append(prefix());
    279                 }
    280                 buf.append('/');
    281                 buf.append(result);
    282                 if (varstart != -1) {
    283                     buf.append(primaryID.substring(varstart, primaryID.length()));
    284                 }
    285                 result = buf.toString();
    286             }
    287             return result;
    288         }
    289 
    290         /**
    291          * Convenience method to return the locale corresponding to the (canonical) original ID.
    292          */
    293         public ULocale canonicalLocale() {
    294             return new ULocale(primaryID);
    295         }
    296 
    297         /**
    298          * Convenience method to return the ulocale corresponding to the (canonical) currentID.
    299          */
    300         public ULocale currentLocale() {
    301             if (varstart == -1) {
    302                 return new ULocale(currentID);
    303             } else {
    304                 return new ULocale(currentID + primaryID.substring(varstart));
    305             }
    306         }
    307 
    308         /**
    309          * If the key has a fallback, modify the key and return true,
    310          * otherwise return false.</p>
    311          *
    312          * <p>First falls back through the primary ID, then through
    313          * the fallbackID.  The final fallback is "" (root)
    314          * unless the primary id was "" (root), in which case
    315          * there is no fallback.
    316          */
    317         @Override
    318         public boolean fallback() {
    319             int x = currentID.lastIndexOf('_');
    320             if (x != -1) {
    321                 while (--x >= 0 && currentID.charAt(x) == '_') { // handle zh__PINYIN
    322                 }
    323                 currentID = currentID.substring(0, x+1);
    324                 return true;
    325             }
    326             if (fallbackID != null) {
    327                 currentID = fallbackID;
    328                 if (fallbackID.length() == 0) {
    329                     fallbackID = null;
    330                 } else {
    331                     fallbackID = "";
    332                 }
    333                 return true;
    334             }
    335             currentID = null;
    336             return false;
    337         }
    338 
    339         /**
    340          * If a key created from id would eventually fallback to match the
    341          * canonical ID of this key, return true.
    342          */
    343         @Override
    344         public boolean isFallbackOf(String id) {
    345             return LocaleUtility.isFallbackOf(canonicalID(), id);
    346         }
    347     }
    348 
    349     /**
    350      * A subclass of Factory that uses LocaleKeys.  If 'visible' the
    351      * factory reports its IDs.
    352      */
    353     public static abstract class LocaleKeyFactory implements Factory {
    354         protected final String name;
    355         protected final boolean visible;
    356 
    357         public static final boolean VISIBLE = true;
    358         public static final boolean INVISIBLE = false;
    359 
    360         /**
    361          * Constructor used by subclasses.
    362          */
    363         protected LocaleKeyFactory(boolean visible) {
    364             this.visible = visible;
    365             this.name = null;
    366         }
    367 
    368         /**
    369          * Constructor used by subclasses.
    370          */
    371         protected LocaleKeyFactory(boolean visible, String name) {
    372             this.visible = visible;
    373             this.name = name;
    374         }
    375 
    376         /**
    377          * Implement superclass abstract method.  This checks the currentID of
    378          * the key against the supported IDs, and passes the canonicalLocale and
    379          * kind off to handleCreate (which subclasses must implement).
    380          */
    381         @Override
    382         public Object create(Key key, ICUService service) {
    383             if (handlesKey(key)) {
    384                 LocaleKey lkey = (LocaleKey)key;
    385                 int kind = lkey.kind();
    386 
    387                 ULocale uloc = lkey.currentLocale();
    388                 return handleCreate(uloc, kind, service);
    389             } else {
    390                 // System.out.println("factory: " + this + " did not support id: " + key.currentID());
    391                 // System.out.println("supported ids: " + getSupportedIDs());
    392             }
    393             return null;
    394         }
    395 
    396         protected boolean handlesKey(Key key) {
    397             if (key != null) {
    398                 String id = key.currentID();
    399                 Set<String> supported = getSupportedIDs();
    400                 return supported.contains(id);
    401             }
    402             return false;
    403         }
    404 
    405         /**
    406          * Override of superclass method.
    407          */
    408         @Override
    409         public void updateVisibleIDs(Map<String, Factory> result) {
    410             Set<String> cache = getSupportedIDs();
    411             for (String id : cache) {
    412                 if (visible) {
    413                     result.put(id, this);
    414                 } else {
    415                     result.remove(id);
    416                 }
    417             }
    418        }
    419 
    420         /**
    421          * Return a localized name for the locale represented by id.
    422          */
    423         @Override
    424         public String getDisplayName(String id, ULocale locale) {
    425             // assume if the user called this on us, we must have handled some fallback of this id
    426             //          if (isSupportedID(id)) {
    427             if (locale == null) {
    428                 return id;
    429             }
    430             ULocale loc = new ULocale(id);
    431             return loc.getDisplayName(locale);
    432             //              }
    433             //          return null;
    434         }
    435 
    436         ///CLOVER:OFF
    437         /**
    438          * Utility method used by create(Key, ICUService).  Subclasses can
    439          * implement this instead of create.
    440          */
    441         protected Object handleCreate(ULocale loc, int kind, ICUService service) {
    442             return null;
    443         }
    444         ///CLOVER:ON
    445 
    446         /**
    447          * Return true if this id is one the factory supports (visible or
    448          * otherwise).
    449          */
    450         protected boolean isSupportedID(String id) {
    451             return getSupportedIDs().contains(id);
    452         }
    453 
    454         /**
    455          * Return the set of ids that this factory supports (visible or
    456          * otherwise).  This can be called often and might need to be
    457          * cached if it is expensive to create.
    458          */
    459         protected Set<String> getSupportedIDs() {
    460             return Collections.emptySet();
    461         }
    462 
    463         /**
    464          * For debugging.
    465          */
    466         @Override
    467         public String toString() {
    468             StringBuilder buf = new StringBuilder(super.toString());
    469             if (name != null) {
    470                 buf.append(", name: ");
    471                 buf.append(name);
    472             }
    473             buf.append(", visible: ");
    474             buf.append(visible);
    475             return buf.toString();
    476         }
    477     }
    478 
    479     /**
    480      * A LocaleKeyFactory that just returns a single object for a kind/locale.
    481      */
    482     public static class SimpleLocaleKeyFactory extends LocaleKeyFactory {
    483         private final Object obj;
    484         private final String id;
    485         private final int kind;
    486 
    487         // TODO: remove when we no longer need this
    488         public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible) {
    489             this(obj, locale, kind, visible, null);
    490         }
    491 
    492         public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible, String name) {
    493             super(visible, name);
    494 
    495             this.obj = obj;
    496             this.id = locale.getBaseName();
    497             this.kind = kind;
    498         }
    499 
    500         /**
    501          * Returns the service object if kind/locale match.  Service is not used.
    502          */
    503         @Override
    504         public Object create(Key key, ICUService service) {
    505             if (!(key instanceof LocaleKey)) {
    506                 return null;
    507             }
    508 
    509             LocaleKey lkey = (LocaleKey)key;
    510             if (kind != LocaleKey.KIND_ANY && kind != lkey.kind()) {
    511                 return null;
    512             }
    513             if (!id.equals(lkey.currentID())) {
    514                 return null;
    515             }
    516 
    517             return obj;
    518         }
    519 
    520         @Override
    521         protected boolean isSupportedID(String idToCheck) {
    522             return this.id.equals(idToCheck);
    523         }
    524 
    525         @Override
    526         public void updateVisibleIDs(Map<String, Factory> result) {
    527             if (visible) {
    528                 result.put(id, this);
    529             } else {
    530                 result.remove(id);
    531             }
    532         }
    533 
    534         @Override
    535         public String toString() {
    536             StringBuilder buf = new StringBuilder(super.toString());
    537             buf.append(", id: ");
    538             buf.append(id);
    539             buf.append(", kind: ");
    540             buf.append(kind);
    541             return buf.toString();
    542         }
    543     }
    544 
    545     /**
    546      * A LocaleKeyFactory that creates a service based on the ICU locale data.
    547      * This is a base class for most ICU factories.  Subclasses instantiate it
    548      * with a constructor that takes a bundle name, which determines the supported
    549      * IDs.  Subclasses then override handleCreate to create the actual service
    550      * object.  The default implementation returns a resource bundle.
    551      */
    552     public static class ICUResourceBundleFactory extends LocaleKeyFactory {
    553         protected final String bundleName;
    554 
    555         /**
    556          * Convenience constructor that uses the main ICU bundle name.
    557          */
    558         public ICUResourceBundleFactory() {
    559             this(ICUData.ICU_BASE_NAME);
    560         }
    561 
    562         /**
    563          * A service factory based on ICU resource data in resources
    564          * with the given name.
    565          */
    566         public ICUResourceBundleFactory(String bundleName) {
    567             super(true);
    568 
    569             this.bundleName = bundleName;
    570         }
    571 
    572         /**
    573          * Return the supported IDs.  This is the set of all locale names for the bundleName.
    574          */
    575         @Override
    576         protected Set<String> getSupportedIDs() {
    577             return ICUResourceBundle.getFullLocaleNameSet(bundleName, loader());
    578         }
    579 
    580         /**
    581          * Override of superclass method.
    582          */
    583         @Override
    584         public void updateVisibleIDs(Map<String, Factory> result) {
    585           Set<String> visibleIDs = ICUResourceBundle.getAvailableLocaleNameSet(bundleName, loader()); // only visible ids
    586             for (String id : visibleIDs) {
    587                 result.put(id, this);
    588             }
    589         }
    590 
    591         /**
    592          * Create the service.  The default implementation returns the resource bundle
    593          * for the locale, ignoring kind, and service.
    594          */
    595         @Override
    596         protected Object handleCreate(ULocale loc, int kind, ICUService service) {
    597             return ICUResourceBundle.getBundleInstance(bundleName, loc, loader());
    598         }
    599 
    600         protected ClassLoader loader() {
    601             return ClassLoaderUtil.getClassLoader(getClass());
    602         }
    603 
    604         @Override
    605         public String toString() {
    606             return super.toString() + ", bundle: " + bundleName;
    607         }
    608     }
    609 
    610     /**
    611      * Return the name of the current fallback locale.  If it has changed since this was
    612      * last accessed, the service cache is cleared.
    613      */
    614     public String validateFallbackLocale() {
    615         ULocale loc = ULocale.getDefault();
    616         if (loc != fallbackLocale) {
    617             synchronized (this) {
    618                 if (loc != fallbackLocale) {
    619                     fallbackLocale = loc;
    620                     fallbackLocaleName = loc.getBaseName();
    621                     clearServiceCache();
    622                 }
    623             }
    624         }
    625         return fallbackLocaleName;
    626     }
    627 
    628     @Override
    629     public Key createKey(String id) {
    630         return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale());
    631     }
    632 
    633     public Key createKey(String id, int kind) {
    634         return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale(), kind);
    635     }
    636 
    637     public Key createKey(ULocale l, int kind) {
    638         return LocaleKey.createWithCanonical(l, validateFallbackLocale(), kind);
    639     }
    640 }
    641