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