2 *******************************************************************************
\r
3 * Copyright (C) 2001-2007, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.impl;
\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
15 import com.ibm.icu.util.ULocale;
\r
17 public class ICULocaleService extends ICUService {
\r
18 private ULocale fallbackLocale;
\r
19 private String fallbackLocaleName;
\r
22 * Construct an ICULocaleService.
\r
24 public ICULocaleService() {
\r
28 * Construct an ICULocaleService with a name (useful for debugging).
\r
30 public ICULocaleService(String name) {
\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
39 public Object get(ULocale locale) {
\r
40 return get(locale, LocaleKey.KIND_ANY, null);
\r
44 * Convenience override for callers using locales. This calls
\r
45 * get(ULocale, int, ULocale[]) with a null actualReturn.
\r
47 public Object get(ULocale locale, int kind) {
\r
48 return get(locale, kind, null);
\r
52 * Convenience override for callers using locales. This calls
\r
53 * get(ULocale, int, ULocale[]) with KIND_ANY for kind.
\r
55 public Object get(ULocale locale, ULocale[] actualReturn) {
\r
56 return get(locale, LocaleKey.KIND_ANY, actualReturn);
\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
65 public Object get(ULocale locale, int kind, ULocale[] actualReturn) {
\r
66 Key key = createKey(locale, kind);
\r
67 if (actualReturn == null) {
\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
76 temp[0] = temp[0].substring(n+1);
\r
78 actualReturn[0] = new ULocale(temp[0]);
\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
88 public Factory registerObject(Object obj, ULocale locale) {
\r
89 return registerObject(obj, locale, LocaleKey.KIND_ANY, true);
\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
97 public Factory registerObject(Object obj, ULocale locale, boolean visible) {
\r
98 return registerObject(obj, locale, LocaleKey.KIND_ANY, visible);
\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
106 public Factory registerObject(Object obj, ULocale locale, int kind) {
\r
107 return registerObject(obj, locale, kind, true);
\r
111 * Convenience function for callers using locales. This instantiates
\r
112 * a SimpleLocaleKeyFactory, and registers the factory.
\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
120 * Convenience method for callers using locales. This returns the standard
\r
121 * Locale list, built from the Set of visible ids.
\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
129 while (iter.hasNext()) {
\r
130 Locale loc = LocaleUtility.getLocaleFromName((String)iter.next());
\r
131 locales[n++] = loc;
\r
137 * Convenience method for callers using locales. This returns the standard
\r
138 * ULocale list, built from the Set of visible ids.
\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
145 while (iter.hasNext()) {
\r
146 locales[n++] = new ULocale((String)iter.next());
\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
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
163 public static class LocaleKey extends ICUService.Key {
\r
165 private int varstart;
\r
166 private String primaryID;
\r
167 private String fallbackID;
\r
168 private String currentID;
\r
170 public static final int KIND_ANY = -1;
\r
173 * Create a LocaleKey with canonical primary and fallback IDs.
\r
175 public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID) {
\r
176 return createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY);
\r
180 * Create a LocaleKey with canonical primary and fallback IDs.
\r
182 public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID, int kind) {
\r
183 if (primaryID == null) {
\r
186 if (primaryID.length() == 0) {
\r
187 primaryID = "root";
\r
189 String canonicalPrimaryID = ULocale.getName(primaryID);
\r
190 return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID, kind);
\r
194 * Create a LocaleKey with canonical primary and fallback IDs.
\r
196 public static LocaleKey createWithCanonical(ULocale locale, String canonicalFallbackID, int kind) {
\r
197 if (locale == null) {
\r
200 String canonicalPrimaryID = locale.getName();
\r
201 return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID, kind);
\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
210 protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, int kind) {
\r
214 if (canonicalPrimaryID == null) {
\r
215 this.primaryID = "";
\r
217 this.primaryID = canonicalPrimaryID;
\r
218 this.varstart = this.primaryID.indexOf('@');
\r
220 if (this.primaryID == "") {
\r
221 this.fallbackID = null;
\r
223 if (canonicalFallbackID == null || this.primaryID.equals(canonicalFallbackID)) {
\r
224 this.fallbackID = "";
\r
226 this.fallbackID = canonicalFallbackID;
\r
230 this.currentID = varstart == -1 ? this.primaryID : this.primaryID.substring(0, varstart);
\r
234 * Return the prefix associated with the kind, or null if the kind is KIND_ANY.
\r
236 public String prefix() {
\r
237 return kind == KIND_ANY ? null : Integer.toString(kind());
\r
241 * Return the kind code associated with this key.
\r
243 public int kind() {
\r
248 * Return the (canonical) original ID.
\r
250 public String canonicalID() {
\r
255 * Return the (canonical) current ID, or null if no current id.
\r
257 public String currentID() {
\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
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
273 buf.append(result);
\r
274 if (varstart != -1) {
\r
275 buf.append(primaryID.substring(varstart, primaryID.length()));
\r
277 result = buf.toString();
\r
283 * Convenience method to return the locale corresponding to the (canonical) original ID.
\r
285 public ULocale canonicalLocale() {
\r
286 return new ULocale(primaryID);
\r
290 * Convenience method to return the ulocale corresponding to the (canonical) currentID.
\r
292 public ULocale currentLocale() {
\r
293 if (varstart == -1) {
\r
294 return new ULocale(currentID);
\r
296 return new ULocale(currentID + primaryID.substring(varstart));
\r
301 * If the key has a fallback, modify the key and return true,
\r
302 * otherwise return false.</p>
\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
309 public boolean fallback() {
\r
310 int x = currentID.lastIndexOf('_');
\r
312 while (--x >= 0 && currentID.charAt(x) == '_') { // handle zh__PINYIN
\r
314 currentID = currentID.substring(0, x+1);
\r
317 if (fallbackID != null) {
\r
318 if (fallbackID.length() == 0) {
\r
319 currentID = "root";
\r
322 currentID = fallbackID;
\r
332 * If a key created from id would eventually fallback to match the
\r
333 * canonical ID of this key, return true.
\r
335 public boolean isFallbackOf(String id) {
\r
336 return LocaleUtility.isFallbackOf(canonicalID(), id);
\r
341 * A subclass of Factory that uses LocaleKeys. If 'visible' the
\r
342 * factory reports its IDs.
\r
344 public static abstract class LocaleKeyFactory implements Factory {
\r
345 protected final String name;
\r
346 protected final boolean visible;
\r
348 public static final boolean VISIBLE = true;
\r
349 public static final boolean INVISIBLE = false;
\r
352 * Constructor used by subclasses.
\r
354 protected LocaleKeyFactory(boolean visible) {
\r
355 this.visible = visible;
\r
360 * Constructor used by subclasses.
\r
362 protected LocaleKeyFactory(boolean visible, String name) {
\r
363 this.visible = visible;
\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
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
377 ULocale uloc = lkey.currentLocale();
\r
378 return handleCreate(uloc, kind, service);
\r
380 // System.out.println("factory: " + this + " did not support id: " + key.currentID());
\r
381 // System.out.println("supported ids: " + getSupportedIDs());
\r
386 protected boolean handlesKey(Key key) {
\r
388 String id = key.currentID();
\r
389 Set supported = getSupportedIDs();
\r
390 return supported.contains(id);
\r
396 * Override of superclass method.
\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
404 result.put(id, this);
\r
412 * Return a localized name for the locale represented by id.
\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
420 ULocale loc = new ULocale(id);
\r
421 return loc.getDisplayName(locale);
\r
428 * Utility method used by create(Key, ICUService). Subclasses can
\r
429 * implement this instead of create.
\r
431 protected Object handleCreate(ULocale loc, int kind, ICUService service) {
\r
437 * Return true if this id is one the factory supports (visible or
\r
440 protected boolean isSupportedID(String id) {
\r
441 return getSupportedIDs().contains(id);
\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
449 protected Set getSupportedIDs() {
\r
450 return Collections.EMPTY_SET;
\r
456 public String toString() {
\r
457 StringBuffer buf = new StringBuffer(super.toString());
\r
458 if (name != null) {
\r
459 buf.append(", name: ");
\r
462 buf.append(", visible: ");
\r
463 buf.append(visible);
\r
464 return buf.toString();
\r
469 * A LocaleKeyFactory that just returns a single object for a kind/locale.
\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
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
481 public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible, String name) {
\r
482 super(visible, name);
\r
485 this.id = locale.getBaseName();
\r
490 * Returns the service object if kind/locale match. Service is not used.
\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
503 protected boolean isSupportedID(String idToCheck) {
\r
504 return this.id.equals(idToCheck);
\r
507 public void updateVisibleIDs(Map result) {
\r
509 result.put(id, this);
\r
515 public String toString() {
\r
516 StringBuffer buf = new StringBuffer(super.toString());
\r
517 buf.append(", id: ");
\r
519 buf.append(", kind: ");
\r
521 return buf.toString();
\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
532 public static class ICUResourceBundleFactory extends LocaleKeyFactory {
\r
533 protected final String bundleName;
\r
536 * Convenience constructor that uses the main ICU bundle name.
\r
538 public ICUResourceBundleFactory() {
\r
539 this(ICUResourceBundle.ICU_BASE_NAME);
\r
543 * A service factory based on ICU resource data in resources
\r
544 * with the given name.
\r
546 public ICUResourceBundleFactory(String bundleName) {
\r
549 this.bundleName = bundleName;
\r
553 * Return the supported IDs. This is the set of all locale names for the bundleName.
\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
561 * Override of superclass method.
\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
573 * Create the service. The default implementation returns the resource bundle
\r
574 * for the locale, ignoring kind, and service.
\r
576 protected Object handleCreate(ULocale loc, int kind, ICUService service) {
\r
577 return ICUResourceBundle.getBundleInstance(bundleName, loc);
\r
580 public String toString() {
\r
581 return super.toString() + ", bundle: " + bundleName;
\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
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
600 return fallbackLocaleName;
\r
603 public Key createKey(String id) {
\r
604 return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale());
\r
607 public Key createKey(String id, int kind) {
\r
608 return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale(), kind);
\r
611 public Key createKey(ULocale l, int kind) {
\r
612 return LocaleKey.createWithCanonical(l, validateFallbackLocale(), kind);
\r