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