• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
ICULocaleService()29     public ICULocaleService() {
30     }
31 
32     /**
33      * Construct an ICULocaleService with a name (useful for debugging).
34      */
ICULocaleService(String name)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      */
get(ULocale locale)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      */
get(ULocale locale, int kind)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      */
get(ULocale locale, ULocale[] actualReturn)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      */
get(ULocale locale, int kind, ULocale[] actualReturn)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      */
registerObject(Object obj, ULocale locale)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      */
registerObject(Object obj, ULocale locale, boolean visible)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      */
registerObject(Object obj, ULocale locale, int kind)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      */
registerObject(Object obj, ULocale locale, int kind, boolean visible)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      */
getAvailableLocales()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      */
getAvailableULocales()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          */
createWithCanonicalFallback(String primaryID, String canonicalFallbackID)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          */
createWithCanonicalFallback(String primaryID, String canonicalFallbackID, int kind)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          */
createWithCanonical(ULocale locale, String canonicalFallbackID, int kind)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          */
LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, int kind)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          */
prefix()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          */
kind()248         public int kind() {
249             return kind;
250         }
251 
252         /**
253          * Return the (canonical) original ID.
254          */
255         @Override
canonicalID()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
currentID()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
currentDescriptor()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          */
canonicalLocale()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          */
currentLocale()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
fallback()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
isFallbackOf(String id)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          */
LocaleKeyFactory(boolean visible)363         protected LocaleKeyFactory(boolean visible) {
364             this.visible = visible;
365             this.name = null;
366         }
367 
368         /**
369          * Constructor used by subclasses.
370          */
LocaleKeyFactory(boolean visible, String name)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
create(Key key, ICUService service)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 
handlesKey(Key key)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
updateVisibleIDs(Map<String, Factory> result)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
getDisplayName(String id, ULocale locale)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          */
handleCreate(ULocale loc, int kind, ICUService service)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          */
isSupportedID(String id)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          */
getSupportedIDs()459         protected Set<String> getSupportedIDs() {
460             return Collections.emptySet();
461         }
462 
463         /**
464          * For debugging.
465          */
466         @Override
toString()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
SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible)488         public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible) {
489             this(obj, locale, kind, visible, null);
490         }
491 
SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible, String name)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
create(Key key, ICUService service)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
isSupportedID(String idToCheck)521         protected boolean isSupportedID(String idToCheck) {
522             return this.id.equals(idToCheck);
523         }
524 
525         @Override
updateVisibleIDs(Map<String, Factory> result)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
toString()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          */
ICUResourceBundleFactory()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          */
ICUResourceBundleFactory(String bundleName)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
getSupportedIDs()576         protected Set<String> getSupportedIDs() {
577             return ICUResourceBundle.getFullLocaleNameSet(bundleName, loader());
578         }
579 
580         /**
581          * Override of superclass method.
582          */
583         @Override
updateVisibleIDs(Map<String, Factory> result)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
handleCreate(ULocale loc, int kind, ICUService service)596         protected Object handleCreate(ULocale loc, int kind, ICUService service) {
597             return ICUResourceBundle.getBundleInstance(bundleName, loc, loader());
598         }
599 
loader()600         protected ClassLoader loader() {
601             return ClassLoaderUtil.getClassLoader(getClass());
602         }
603 
604         @Override
toString()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      */
validateFallbackLocale()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
createKey(String id)629     public Key createKey(String id) {
630         return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale());
631     }
632 
createKey(String id, int kind)633     public Key createKey(String id, int kind) {
634         return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale(), kind);
635     }
636 
createKey(ULocale l, int kind)637     public Key createKey(ULocale l, int kind) {
638         return LocaleKey.createWithCanonical(l, validateFallbackLocale(), kind);
639     }
640 }
641