2 *******************************************************************************
3 * Copyright (C) 2001-2011, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
7 package com.ibm.icu.impl;
9 import java.util.Collections;
10 import java.util.Locale;
14 import com.ibm.icu.util.ULocale;
16 public class ICULocaleService extends ICUService {
17 private ULocale fallbackLocale;
18 private String fallbackLocaleName;
21 * Construct an ICULocaleService.
23 public ICULocaleService() {
27 * Construct an ICULocaleService with a name (useful for debugging).
29 public ICULocaleService(String name) {
34 * Convenience override for callers using locales. This calls
35 * get(ULocale, int, ULocale[]) with KIND_ANY for kind and null for
38 public Object get(ULocale locale) {
39 return get(locale, LocaleKey.KIND_ANY, null);
43 * Convenience override for callers using locales. This calls
44 * get(ULocale, int, ULocale[]) with a null actualReturn.
46 public Object get(ULocale locale, int kind) {
47 return get(locale, kind, null);
51 * Convenience override for callers using locales. This calls
52 * get(ULocale, int, ULocale[]) with KIND_ANY for kind.
54 public Object get(ULocale locale, ULocale[] actualReturn) {
55 return get(locale, LocaleKey.KIND_ANY, actualReturn);
59 * Convenience override for callers using locales. This uses
60 * createKey(ULocale.toString(), kind) to create a key, calls getKey, and then
61 * if actualReturn is not null, returns the actualResult from
62 * getKey (stripping any prefix) into a ULocale.
64 public Object get(ULocale locale, int kind, ULocale[] actualReturn) {
65 Key key = createKey(locale, kind);
66 if (actualReturn == null) {
70 String[] temp = new String[1];
71 Object result = getKey(key, temp);
73 int n = temp[0].indexOf("/");
75 temp[0] = temp[0].substring(n+1);
77 actualReturn[0] = new ULocale(temp[0]);
83 * Convenience override for callers using locales. This calls
84 * registerObject(Object, ULocale, int kind, boolean visible)
85 * passing KIND_ANY for the kind, and true for the visibility.
87 public Factory registerObject(Object obj, ULocale locale) {
88 return registerObject(obj, locale, LocaleKey.KIND_ANY, true);
92 * Convenience override for callers using locales. This calls
93 * registerObject(Object, ULocale, int kind, boolean visible)
94 * passing KIND_ANY for the kind.
96 public Factory registerObject(Object obj, ULocale locale, boolean visible) {
97 return registerObject(obj, locale, LocaleKey.KIND_ANY, visible);
101 * Convenience function for callers using locales. This calls
102 * registerObject(Object, ULocale, int kind, boolean visible)
103 * passing true for the visibility.
105 public Factory registerObject(Object obj, ULocale locale, int kind) {
106 return registerObject(obj, locale, kind, true);
110 * Convenience function for callers using locales. This instantiates
111 * a SimpleLocaleKeyFactory, and registers the factory.
113 public Factory registerObject(Object obj, ULocale locale, int kind, boolean visible) {
114 Factory factory = new SimpleLocaleKeyFactory(obj, locale, kind, visible);
115 return registerFactory(factory);
119 * Convenience method for callers using locales. This returns the standard
120 * Locale list, built from the Set of visible ids.
122 public Locale[] getAvailableLocales() {
123 // TODO make this wrap getAvailableULocales later
124 Set<String> visIDs = getVisibleIDs();
125 Locale[] locales = new Locale[visIDs.size()];
127 for (String id : visIDs) {
128 Locale loc = LocaleUtility.getLocaleFromName(id);
135 * Convenience method for callers using locales. This returns the standard
136 * ULocale list, built from the Set of visible ids.
138 public ULocale[] getAvailableULocales() {
139 Set<String> visIDs = getVisibleIDs();
140 ULocale[] locales = new ULocale[visIDs.size()];
142 for (String id : visIDs) {
143 locales[n++] = new ULocale(id);
149 * A subclass of Key that implements a locale fallback mechanism.
150 * The first locale to search for is the locale provided by the
151 * client, and the fallback locale to search for is the current
152 * default locale. If a prefix is present, the currentDescriptor
153 * includes it before the locale proper, separated by "/". This
154 * is the default key instantiated by ICULocaleService.</p>
156 * <p>Canonicalization adjusts the locale string so that the
157 * section before the first understore is in lower case, and the rest
158 * is in upper case, with no trailing underscores.</p>
160 public static class LocaleKey extends ICUService.Key {
162 private int varstart;
163 private String primaryID;
164 private String fallbackID;
165 private String currentID;
167 public static final int KIND_ANY = -1;
170 * Create a LocaleKey with canonical primary and fallback IDs.
172 public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID) {
173 return createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY);
177 * Create a LocaleKey with canonical primary and fallback IDs.
179 public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID, int kind) {
180 if (primaryID == null) {
183 String canonicalPrimaryID = ULocale.getName(primaryID);
184 return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID, kind);
188 * Create a LocaleKey with canonical primary and fallback IDs.
190 public static LocaleKey createWithCanonical(ULocale locale, String canonicalFallbackID, int kind) {
191 if (locale == null) {
194 String canonicalPrimaryID = locale.getName();
195 return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID, kind);
199 * PrimaryID is the user's requested locale string,
200 * canonicalPrimaryID is this string in canonical form,
201 * fallbackID is the current default locale's string in
204 protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, int kind) {
208 if (canonicalPrimaryID == null || canonicalPrimaryID.equalsIgnoreCase("root")) {
210 this.fallbackID = null;
212 int idx = canonicalPrimaryID.indexOf('@');
213 if (idx == 4 && canonicalPrimaryID.regionMatches(true, 0, "root", 0, 4)) {
214 this.primaryID = canonicalPrimaryID.substring(4);
216 this.fallbackID = null;
218 this.primaryID = canonicalPrimaryID;
221 if (canonicalFallbackID == null || this.primaryID.equals(canonicalFallbackID)) {
222 this.fallbackID = "";
224 this.fallbackID = canonicalFallbackID;
229 this.currentID = varstart == -1 ? this.primaryID : this.primaryID.substring(0, varstart);
233 * Return the prefix associated with the kind, or null if the kind is KIND_ANY.
235 public String prefix() {
236 return kind == KIND_ANY ? null : Integer.toString(kind());
240 * Return the kind code associated with this key.
247 * Return the (canonical) original ID.
249 public String canonicalID() {
254 * Return the (canonical) current ID, or null if no current id.
256 public String currentID() {
261 * Return the (canonical) current descriptor, or null if no current id.
262 * Includes the keywords, whereas the ID does not include keywords.
264 public String currentDescriptor() {
265 String result = currentID();
266 if (result != null) {
267 StringBuilder buf = new StringBuilder(); // default capacity 16 is usually good enough
268 if (kind != KIND_ANY) {
269 buf.append(prefix());
273 if (varstart != -1) {
274 buf.append(primaryID.substring(varstart, primaryID.length()));
276 result = buf.toString();
282 * Convenience method to return the locale corresponding to the (canonical) original ID.
284 public ULocale canonicalLocale() {
285 return new ULocale(primaryID);
289 * Convenience method to return the ulocale corresponding to the (canonical) currentID.
291 public ULocale currentLocale() {
292 if (varstart == -1) {
293 return new ULocale(currentID);
295 return new ULocale(currentID + primaryID.substring(varstart));
300 * If the key has a fallback, modify the key and return true,
301 * otherwise return false.</p>
303 * <p>First falls back through the primary ID, then through
304 * the fallbackID. The final fallback is "" (root)
305 * unless the primary id was "" (root), in which case
306 * there is no fallback.
308 public boolean fallback() {
309 int x = currentID.lastIndexOf('_');
311 while (--x >= 0 && currentID.charAt(x) == '_') { // handle zh__PINYIN
313 currentID = currentID.substring(0, x+1);
316 if (fallbackID != null) {
317 currentID = fallbackID;
318 if (fallbackID.length() == 0) {
330 * If a key created from id would eventually fallback to match the
331 * canonical ID of this key, return true.
333 public boolean isFallbackOf(String id) {
334 return LocaleUtility.isFallbackOf(canonicalID(), id);
339 * A subclass of Factory that uses LocaleKeys. If 'visible' the
340 * factory reports its IDs.
342 public static abstract class LocaleKeyFactory implements Factory {
343 protected final String name;
344 protected final boolean visible;
346 public static final boolean VISIBLE = true;
347 public static final boolean INVISIBLE = false;
350 * Constructor used by subclasses.
352 protected LocaleKeyFactory(boolean visible) {
353 this.visible = visible;
358 * Constructor used by subclasses.
360 protected LocaleKeyFactory(boolean visible, String name) {
361 this.visible = visible;
366 * Implement superclass abstract method. This checks the currentID of
367 * the key against the supported IDs, and passes the canonicalLocale and
368 * kind off to handleCreate (which subclasses must implement).
370 public Object create(Key key, ICUService service) {
371 if (handlesKey(key)) {
372 LocaleKey lkey = (LocaleKey)key;
373 int kind = lkey.kind();
375 ULocale uloc = lkey.currentLocale();
376 return handleCreate(uloc, kind, service);
378 // System.out.println("factory: " + this + " did not support id: " + key.currentID());
379 // System.out.println("supported ids: " + getSupportedIDs());
384 protected boolean handlesKey(Key key) {
386 String id = key.currentID();
387 Set<String> supported = getSupportedIDs();
388 return supported.contains(id);
394 * Override of superclass method.
396 public void updateVisibleIDs(Map<String, Factory> result) {
397 Set<String> cache = getSupportedIDs();
398 for (String id : cache) {
400 result.put(id, this);
408 * Return a localized name for the locale represented by id.
410 public String getDisplayName(String id, ULocale locale) {
411 // assume if the user called this on us, we must have handled some fallback of this id
412 // if (isSupportedID(id)) {
413 if (locale == null) {
416 ULocale loc = new ULocale(id);
417 return loc.getDisplayName(locale);
424 * Utility method used by create(Key, ICUService). Subclasses can
425 * implement this instead of create.
427 protected Object handleCreate(ULocale loc, int kind, ICUService service) {
433 * Return true if this id is one the factory supports (visible or
436 protected boolean isSupportedID(String id) {
437 return getSupportedIDs().contains(id);
441 * Return the set of ids that this factory supports (visible or
442 * otherwise). This can be called often and might need to be
443 * cached if it is expensive to create.
445 protected Set<String> getSupportedIDs() {
446 return Collections.emptySet();
452 public String toString() {
453 StringBuilder buf = new StringBuilder(super.toString());
455 buf.append(", name: ");
458 buf.append(", visible: ");
460 return buf.toString();
465 * A LocaleKeyFactory that just returns a single object for a kind/locale.
467 public static class SimpleLocaleKeyFactory extends LocaleKeyFactory {
468 private final Object obj;
469 private final String id;
470 private final int kind;
472 // TODO: remove when we no longer need this
473 public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible) {
474 this(obj, locale, kind, visible, null);
477 public SimpleLocaleKeyFactory(Object obj, ULocale locale, int kind, boolean visible, String name) {
478 super(visible, name);
481 this.id = locale.getBaseName();
486 * Returns the service object if kind/locale match. Service is not used.
488 public Object create(Key key, ICUService service) {
489 if (!(key instanceof LocaleKey)) {
493 LocaleKey lkey = (LocaleKey)key;
494 if (kind != LocaleKey.KIND_ANY && kind != lkey.kind()) {
497 if (!id.equals(lkey.currentID())) {
504 protected boolean isSupportedID(String idToCheck) {
505 return this.id.equals(idToCheck);
508 public void updateVisibleIDs(Map<String, Factory> result) {
510 result.put(id, this);
516 public String toString() {
517 StringBuilder buf = new StringBuilder(super.toString());
518 buf.append(", id: ");
520 buf.append(", kind: ");
522 return buf.toString();
527 * A LocaleKeyFactory that creates a service based on the ICU locale data.
528 * This is a base class for most ICU factories. Subclasses instantiate it
529 * with a constructor that takes a bundle name, which determines the supported
530 * IDs. Subclasses then override handleCreate to create the actual service
531 * object. The default implementation returns a resource bundle.
533 public static class ICUResourceBundleFactory extends LocaleKeyFactory {
534 protected final String bundleName;
537 * Convenience constructor that uses the main ICU bundle name.
539 public ICUResourceBundleFactory() {
540 this(ICUResourceBundle.ICU_BASE_NAME);
544 * A service factory based on ICU resource data in resources
545 * with the given name.
547 public ICUResourceBundleFactory(String bundleName) {
550 this.bundleName = bundleName;
554 * Return the supported IDs. This is the set of all locale names for the bundleName.
556 protected Set<String> getSupportedIDs() {
557 return ICUResourceBundle.getFullLocaleNameSet(bundleName, loader());
561 * Override of superclass method.
563 public void updateVisibleIDs(Map<String, Factory> result) {
564 Set<String> visibleIDs = ICUResourceBundle.getAvailableLocaleNameSet(bundleName, loader()); // only visible ids
565 for (String id : visibleIDs) {
566 result.put(id, this);
571 * Create the service. The default implementation returns the resource bundle
572 * for the locale, ignoring kind, and service.
574 protected Object handleCreate(ULocale loc, int kind, ICUService service) {
575 return ICUResourceBundle.getBundleInstance(bundleName, loc, loader());
578 protected ClassLoader loader() {
579 ClassLoader cl = getClass().getClassLoader();
581 cl = Utility.getFallbackClassLoader();
586 public String toString() {
587 return super.toString() + ", bundle: " + bundleName;
592 * Return the name of the current fallback locale. If it has changed since this was
593 * last accessed, the service cache is cleared.
595 public String validateFallbackLocale() {
596 ULocale loc = ULocale.getDefault();
597 if (loc != fallbackLocale) {
598 synchronized (this) {
599 if (loc != fallbackLocale) {
600 fallbackLocale = loc;
601 fallbackLocaleName = loc.getBaseName();
606 return fallbackLocaleName;
609 public Key createKey(String id) {
610 return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale());
613 public Key createKey(String id, int kind) {
614 return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale(), kind);
617 public Key createKey(ULocale l, int kind) {
618 return LocaleKey.createWithCanonical(l, validateFallbackLocale(), kind);