ICU-2203 (also 1949) service registration for number format, and

associated changes to service APIs

X-SVN-Rev: 9860
This commit is contained in:
Doug Felt 2002-09-14 21:36:30 +00:00
parent 821c8a97b5
commit daf555a88b
8 changed files with 705 additions and 239 deletions

View File

@ -1,7 +1,7 @@
/*****************************************************************************************
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/dev/test/format/NumberRegression.java,v $
* $Date: 2002/08/21 00:05:59 $
* $Revision: 1.11 $
* $Date: 2002/09/14 21:36:28 $
* $Revision: 1.12 $
*
*****************************************************************************************
**/
@ -634,7 +634,7 @@ public class NumberRegression extends com.ibm.icu.dev.test.TestFmwk {
if (tempString.equals(expectedDefault)) {
logln ("Bug 4071859 default test passed.");
} else {
errln("Failed:" +
errln("a) Failed:" +
" Expected " + expectedDefault +
" Received " + tempString );
}
@ -645,7 +645,7 @@ public class NumberRegression extends com.ibm.icu.dev.test.TestFmwk {
if (tempString.equals(expectedCurrency) ) {
logln ("Bug 4071859 currency test passed.");
} else {
errln("Failed:" +
errln("b) Failed:" +
" Expected " + expectedCurrency +
" Received " + tempString );
}
@ -656,7 +656,7 @@ public class NumberRegression extends com.ibm.icu.dev.test.TestFmwk {
if (tempString.equals(expectedPercent) ) {
logln ("Bug 4071859 percentage test passed.");
} else {
errln("Failed:" +
errln("c) Failed:" +
" Expected " + expectedPercent +
" Received " + tempString );
}

View File

@ -5,8 +5,8 @@
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/dev/test/util/ICUServiceTest.java,v $
* $Date: 2002/08/13 22:10:20 $
* $Revision: 1.4 $
* $Date: 2002/09/14 21:36:30 $
* $Revision: 1.5 $
*
*******************************************************************************
*/
@ -30,6 +30,7 @@ import com.ibm.icu.impl.ICULocaleService.ICUResourceBundleFactory;
import java.util.Arrays;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@ -479,11 +480,10 @@ public class ICUServiceTest extends TestFmwk
return null;
}
protected void handleUpdateVisibleIDs(Set result) {
for (int i = 0; i < ids.length; ++i) {
result.add(ids[i]);
}
protected Set handleGetSupportedIDs() {
return new HashSet(Arrays.asList(ids));
}
protected String handleGetDisplayName(String id, Locale locale) {
return factoryID + LocaleUtility.getLocaleFromName(id).getDisplayName(locale);
}
@ -544,13 +544,15 @@ public class ICUServiceTest extends TestFmwk
private static String surfer = californio + "_SURFER";
private static String geek = californio + "_GEEK";
public void handleUpdateVisibleIDs(Set result) {
super.handleUpdateVisibleIDs(result);
public Set handleGetSupportedIDs() {
Set result = super.handleGetSupportedIDs();
result.add(californio);
result.add(valley);
result.add(surfer);
result.add(geek);
return result;
}
protected String handleGetDisplayName(String id, Locale locale) {
@ -620,6 +622,11 @@ public class ICUServiceTest extends TestFmwk
confirmEqual("test with en locale", "root", target);
}
public void errln(String msg) {
System.out.println(msg);
(new String[0])[1] = "foo";
}
// misc coverage tests
public void TestCoverage() {
// Key
@ -640,7 +647,7 @@ public class ICUServiceTest extends TestFmwk
logln("OK: " + e.getMessage());
}
catch (Exception e) {
errln("threw wrong exception");
errln("threw wrong exception" + e);
}
logln(sf.getDisplayName("object", null));
@ -655,10 +662,11 @@ public class ICUServiceTest extends TestFmwk
catch (NullPointerException e) {
logln("OK: " + e.getMessage());
}
/*
catch (Exception e) {
errln("threw wrong exception");
errln("threw wrong exception" + e);
}
*/
try {
service.registerFactory(null);
errln("didn't throw exception");
@ -667,7 +675,7 @@ public class ICUServiceTest extends TestFmwk
logln("OK: " + e.getMessage());
}
catch (Exception e) {
errln("threw wrong exception");
errln("threw wrong exception" + e);
}
try {
@ -678,7 +686,7 @@ public class ICUServiceTest extends TestFmwk
logln("OK: " + e.getMessage());
}
catch (Exception e) {
errln("threw wrong exception");
errln("threw wrong exception" + e);
}
logln("object is: " + service.get("object"));
@ -718,9 +726,9 @@ public class ICUServiceTest extends TestFmwk
}
// LocaleKey
LocaleKey lkey = LocaleKey.create("en_US", "ja_JP");
lkey = LocaleKey.create(null, null);
lkey = LocaleKey.createWithCanonical("en_US", "ja_JP");
// LocaleKey lkey = LocaleKey.create("en_US", "ja_JP");
// lkey = LocaleKey.create(null, null);
LocaleKey lkey = LocaleKey.createWithCanonicalFallback("en_US", "ja_JP");
// MultipleKeyFactory
MultipleKeyFactory mkf = new MKFSubclass(false);
@ -736,10 +744,10 @@ public class ICUServiceTest extends TestFmwk
invisibleMKF.updateVisibleIDs(new HashMap());
// ResourceBundleFactory
ICUResourceBundleFactory rbf = new ICUResourceBundleFactory(null, true);
ICUResourceBundleFactory rbf = new ICUResourceBundleFactory(true);
logln("RB: " + rbf.create(lkey));
LocaleKey nokey = LocaleKey.create(null, null);
logln("RB: " + rbf.create(nokey));
// LocaleKey nokey = LocaleKey.create(null, null);
// logln("RB: " + rbf.create(nokey));
rbf = new ICUResourceBundleFactory("foobar", true);
logln("RB: " + rbf.create(lkey));
@ -814,7 +822,8 @@ public class ICUServiceTest extends TestFmwk
return null;
}
public void handleUpdateVisibleIDs(Set result) {
public Set handleGetSupportedIDs() {
return null;
}
}
}

View File

@ -185,7 +185,7 @@ public class ICULocaleData {
* returns an 'unparented' bundle that exactly matches the bundle name and locale name.
*/
public static ResourceBundle loadResourceBundle(String bundleName, String localeName) {
if (localeName != null) {
if (localeName != null && localeName.length() > 0) {
bundleName = bundleName + "_" + localeName;
}
for (int i = 0; i < packageNames.length; ++i) {

View File

@ -5,8 +5,8 @@
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/impl/ICULocaleService.java,v $
* $Date: 2002/09/07 00:15:33 $
* $Revision: 1.6 $
* $Date: 2002/09/14 21:36:30 $
* $Revision: 1.7 $
*
*******************************************************************************
*/
@ -14,25 +14,31 @@ package com.ibm.icu.impl;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;
public class ICULocaleService extends ICUService {
Locale fallbackLocale;
String fallbackLocaleName;
private Locale fallbackLocale;
private String fallbackLocaleName;
/**
* Construct an ICULocaleService with a fallback locale string based on the current
* default locale at the time of construction.
* Construct an ICULocaleService. This uses the current default locale as a fallback.
*/
public ICULocaleService() {
fallbackLocale = Locale.getDefault();
fallbackLocaleName = LocaleUtility.canonicalLocaleString(fallbackLocale.toString());
}
/**
* Construct an ICULocaleService with a name (useful for debugging).
*/
public ICULocaleService(String name) {
super(name);
}
/**
@ -45,13 +51,15 @@ public class ICULocaleService extends ICUService {
/**
* Convenience override for callers using locales.
*/
public Object get(Locale locale, Locale[] actualLocaleReturn) {
if (actualLocaleReturn == null) {
public Object get(Locale locale, Locale[] actualReturn) {
if (actualReturn == null) {
return get(locale.toString());
}
String[] temp = new String[1];
Object result = get(locale.toString(), temp);
actualLocaleReturn[0] = LocaleUtility.getLocaleFromName(temp[0]);
if (result != null) {
actualReturn[0] = LocaleUtility.getLocaleFromName(temp[0]);
}
return result;
}
@ -70,14 +78,13 @@ public class ICULocaleService extends ICUService {
}
/**
* Convenience method for callers using locales. This is the typical
* current API for this operation.
* Convenience method for callers using locales. This is the
* current typical API for this operation, though perhaps it should change.
*/
public Locale[] getAvailableLocales() {
TreeSet sort = new TreeSet(String.CASE_INSENSITIVE_ORDER);
sort.addAll(getVisibleIDs());
Iterator iter = sort.iterator();
Locale[] locales = new Locale[sort.size()];
Set visIDs = getVisibleIDs();
Iterator iter = visIDs.iterator();
Locale[] locales = new Locale[visIDs.size()];
int n = 0;
while (iter.hasNext()) {
Locale loc = LocaleUtility.getLocaleFromName((String)iter.next());
@ -89,42 +96,34 @@ public class ICULocaleService extends ICUService {
/**
* A subclass of Key that implements a locale fallback mechanism.
* The first locale to search for is the locale provided by the
* client, and the fallback locale to search for is the current default
* locale. This is instantiated by ICULocaleService.</p>
* client, and the fallback locale to search for is the current
* default locale. If a prefix is present, the currentDescriptor
* includes it before the locale proper, separated by "/". This
* is the default key instantiated by ICULocaleService.</p>
*
* <p>Canonicalization adjusts the locale string so that the
* section before the first understore is in lower case, and the rest
* is in upper case, with no trailing underscores.</p>
*/
public static class LocaleKey extends ICUService.Key {
private String prefix;
private String primaryID;
private String fallbackID;
private String currentID;
/**
* Convenience method for createWithCanonical that canonicalizes both the
* primary and fallback IDs first.
*/
public static LocaleKey create(String primaryID, String fallbackID) {
String canonicalPrimaryID = LocaleUtility.canonicalLocaleString(primaryID);
String canonicalFallbackID = LocaleUtility.canonicalLocaleString(fallbackID);
return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID);
}
/**
* Convenience method for createWithCanonical that canonicalizes the
* primary ID first, the fallback is assumed to already be canonical.
* Create a LocaleKey with canonical primary and fallback IDs.
*/
public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID) {
String canonicalPrimaryID = LocaleUtility.canonicalLocaleString(primaryID);
return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID);
return createWithCanonicalFallback(primaryID, canonicalFallbackID, null);
}
/**
* Create a LocaleKey with canonical primary and fallback IDs.
*/
public static LocaleKey createWithCanonical(String canonicalPrimaryID, String canonicalFallbackID) {
return new LocaleKey(canonicalPrimaryID, canonicalPrimaryID, canonicalFallbackID);
public static LocaleKey createWithCanonicalFallback(String primaryID, String canonicalFallbackID, String prefix) {
String canonicalPrimaryID = LocaleUtility.canonicalLocaleString(primaryID);
return new LocaleKey(primaryID, canonicalPrimaryID, canonicalFallbackID, prefix);
}
/**
@ -133,9 +132,11 @@ public class ICULocaleService extends ICUService {
* fallbackID is the current default locale's string in
* canonical form.
*/
protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID) {
protected LocaleKey(String primaryID, String canonicalPrimaryID, String canonicalFallbackID, String prefix) {
super(primaryID);
this.prefix = prefix;
if (canonicalPrimaryID == null) {
this.primaryID = "";
} else {
@ -154,6 +155,13 @@ public class ICULocaleService extends ICUService {
this.currentID = this.primaryID;
}
/**
* Return the prefix, or null if none was defined.
*/
public String prefix() {
return prefix;
}
/**
* Return the (canonical) original ID.
*/
@ -162,12 +170,37 @@ public class ICULocaleService extends ICUService {
}
/**
* Return the (canonical) current ID.
* Return the (canonical) current ID, or null if no current id.
*/
public String currentID() {
return currentID;
}
/**
* Return the (canonical) current descriptor, or null if no current id.
*/
public String currentDescriptor() {
String result = currentID();
if (result != null && prefix != null) {
result = prefix + "/" + result;
}
return result;
}
/**
* Convenience method to return the locale corresponding to the (canonical) original ID.
*/
public Locale canonicalLocale() {
return LocaleUtility.getLocaleFromName(primaryID);
}
/**
* Convenience method to return the locale corresponding to the (canonical) current ID.
*/
public Locale currentLocale() {
return LocaleUtility.getLocaleFromName(currentID);
}
/**
* If the key has a fallback, modify the key and return true,
* otherwise return false.</p>
@ -178,10 +211,9 @@ public class ICULocaleService extends ICUService {
* there is no fallback.
*/
public boolean fallback() {
String current = currentID();
int x = current.lastIndexOf('_');
int x = currentID.lastIndexOf('_');
if (x != -1) {
currentID = current.substring(0, x);
currentID = currentID.substring(0, x);
return true;
}
if (fallbackID != null) {
@ -207,7 +239,41 @@ public class ICULocaleService extends ICUService {
public static abstract class MultipleKeyFactory implements ICUService.Factory {
protected final boolean visible;
private SoftReference cacheref;
private boolean included;
private static final class CacheInfo {
final Set cache;
final boolean included;
CacheInfo() {
this.cache = new HashSet();
this.included = false;
}
CacheInfo(Set cache) {
this.cache = cache;
this.included = true;
}
/**
* Return true if we're known to support id, or not known to not support id.
*/
boolean tryCreate(String id) {
boolean result = cache.contains(id) == included;
return result;
}
/**
* Update information about whether we support this id. Since if we are storing
* information on included ids, we already know all of them, we only need to
* update if we're storing information on ids we don't support and we don't
* support the id (the result is null).
*/
void addCreate(String id, Object result) {
if (!included && result == null) {
cache.add(id);
}
}
}
/**
* Convenience overload of MultipleKeyFactory(boolean) that defaults
@ -227,62 +293,58 @@ public class ICULocaleService extends ICUService {
/**
* Get the cache of IDs. These are either the ids that we know we
* don't understand, if included is false, or the entire set of ids
* we do know we understand, if included is true. Note that if
* the cache has been freed by gc, we reset the included flag, so
* it must not be tested before this method is called.
* we do know we understand, if included is true. If the cache has
* been flushed, included is false.
*/
private HashSet getCache() {
HashSet cache = null;
private CacheInfo getCache() {
CacheInfo result = null;
if (cacheref != null) {
cache = (HashSet)cacheref.get();
result = (CacheInfo)cacheref.get();
}
if (cache == null) {
cache = new HashSet();
cacheref = new SoftReference(cache);
included = false;
if (result == null) {
result = new CacheInfo();
cacheref = new SoftReference(result);
}
return cache;
return result;
}
/**
* Get the cache of IDs we understand.
*/
private HashSet getIncludedCache() {
HashSet cache = getCache();
if (!included) {
cache.clear();
handleUpdateVisibleIDs(cache);
included = true;
}
return cache;
protected Set getSupportedIDs() {
CacheInfo ci = getCache();
Set result = ci.cache;
if (!ci.included) {
result = handleGetSupportedIDs();
cacheref = new SoftReference(new CacheInfo(result));
}
public final Object create(Key key) {
return result;
}
public Object create(Key key) {
Object result = null;
String id = key.currentID();
HashSet cache = getCache();
if (cache.contains(id) == included) {
CacheInfo ci = getCache();
if (ci.tryCreate(id)) {
result = handleCreate(key);
if (!included && result == null) {
cache.add(id);
}
ci.addCreate(id, result);
}
return result;
}
public final void updateVisibleIDs(Map result) {
public void updateVisibleIDs(Map result) {
if (visible) {
Set cache = getIncludedCache();
Iterator iter = cache.iterator();
Iterator iter = getSupportedIDs().iterator();
while (iter.hasNext()) {
result.put((String)iter.next(), this);
result.put(iter.next(), this);
}
}
}
public final String getDisplayName(String id, Locale locale) {
public String getDisplayName(String id, Locale locale) {
if (visible) {
Set cache = getIncludedCache();
Set cache = getSupportedIDs();
if (cache.contains(id)) {
return handleGetDisplayName(id, locale);
}
@ -296,11 +358,11 @@ public class ICULocaleService extends ICUService {
protected abstract Object handleCreate(Key key);
/**
* Subclasses implement this instead of updateVisibleIDs. Any
* id known to and handled by this class should be added to
* result.
* Subclasses implement this instead of getSupportedIDs. Any
* id known to and handled by this class should be included in
* the returned Set.
*/
protected abstract void handleUpdateVisibleIDs(Set result);
protected abstract Set handleGetSupportedIDs();
/**
* Subclasses implement this instead of getDisplayName.
@ -313,6 +375,64 @@ public class ICULocaleService extends ICUService {
}
}
/**
* A subclass of MultipleKeyFactory that uses LocaleKeys. It is
* able to optionally 'hide' more specific locales with more general
* locales that it supports.
*/
public static abstract class LocaleKeyFactory extends MultipleKeyFactory {
protected final boolean hides;
/**
* Create a LocaleKeyFactory.
*/
public LocaleKeyFactory(boolean visible, boolean hides) {
super(visible);
this.hides = hides;
}
/**
* Override of superclass method. If this is visible, it will update
* result with the ids it supports. If this hides ids, more specific
* ids already in result will be remapped to this.
*/
public void updateVisibleIDs(Map result) {
if (visible) {
Set cache = getSupportedIDs();
Map toRemap = new HashMap();
Iterator iter = cache.iterator();
while (iter.hasNext()) {
String id = (String)iter.next();
if (hides) {
int idlen = id.length();
Iterator miter = result.keySet().iterator();
while (miter.hasNext()) {
String mid = (String)miter.next();
if (mid.startsWith(id) &&
(mid.length() == idlen ||
mid.charAt(idlen) == '_')) {
toRemap.put(mid, this);
miter.remove();
}
}
}
toRemap.put(id, this);
}
result.putAll(toRemap);
}
}
/**
* Return a localized name for the locale represented by id.
*/
protected String handleGetDisplayName(String id, Locale locale) {
// use java's display name formatting for now
return LocaleUtility.getLocaleFromName(id).getDisplayName(locale);
}
}
/**
* A factory that creates a service based on the ICU locale data.
* Subclasses specify a prefix (default is LocaleElements), a
@ -325,7 +445,11 @@ public class ICULocaleService extends ICUService {
*/
public static class ICUResourceBundleFactory extends MultipleKeyFactory {
protected final String name;
protected final String[] requiredContents;
protected final String[][] requiredContents;
public ICUResourceBundleFactory(boolean visible) {
this((String)null, visible);
}
/**
* A service factory based on ICU resource data in the LocaleElements resources.
@ -340,29 +464,89 @@ public class ICULocaleService extends ICUService {
* listed resources must come directly from the same bundle.
*/
public ICUResourceBundleFactory(String name, String requiredContents, boolean visible) {
super(visible);
this(name, buildRcAndOr(requiredContents), true, visible);
}
this.name = name;
private static class Node {
public boolean test(ResourceBundle rb) {
return rb != null;
}
}
private static class ResourceNode {
String name;
}
private static class BoolNode extends Node {
BoolNode car;
BoolNode cdr;
}
private static String[][] buildRcAndOr(String requiredContents) {
String[][] rcAndOr = null;
if (requiredContents != null) {
rcAndOr = new String[][] { parseDelimitedString(requiredContents) };
}
return rcAndOr;
}
public ICUResourceBundleFactory(String[] rcOr, boolean visible) {
this(ICULocaleData.LOCALE_ELEMENTS, rcOr, visible);
}
public ICUResourceBundleFactory(String name, String[] rcOr, boolean visible) {
this(name, buildRcAndOr(rcOr), true, visible);
}
private static String[][] buildRcAndOr(String[] rcOr) {
String[][] rcOrAnd = null;
if (rcOr != null) {
rcOrAnd = new String[rcOr.length][];
for (int i = 0; i < rcOr.length; ++i) {
rcOrAnd[i] = parseDelimitedString(rcOr[i]);
}
}
return rcOrAnd;
}
public ICUResourceBundleFactory(String[][] rcOrAnd, boolean adopt, boolean visible) {
this(ICULocaleData.LOCALE_ELEMENTS, rcOrAnd, adopt, visible);
}
private static String[] parseDelimitedString(String str) {
if (str != null) {
ArrayList list = new ArrayList();
for (int i = 0, len = requiredContents.length();;) {
while (i < len && requiredContents.charAt(i) == ';') {
for (int i = 0, len = str.length();;) {
while (i < len && str.charAt(i) == ';') {
++i;
}
if (i == len) {
break;
}
int j = requiredContents.indexOf(';', i);
int j = str.indexOf(';', i);
if (j == -1) {
j = len;
}
list.add(requiredContents.substring(i, j));
list.add(str.substring(i, j));
i = j;
}
this.requiredContents = (String[])list.toArray(new String[list.size()]);
} else {
this.requiredContents = null;
return (String[])list.toArray(new String[list.size()]);
}
return null;
}
public ICUResourceBundleFactory(String name, String[][] rcOrAnd, boolean adopt, boolean visible) {
super(visible);
this.name = name;
if (!adopt && rcOrAnd != null) {
rcOrAnd = (String[][])rcOrAnd.clone();
for (int i = 0; i < rcOrAnd.length; ++i) {
rcOrAnd[i] = (String[])(rcOrAnd[i].clone());
}
}
this.requiredContents = rcOrAnd;
}
/**
@ -384,21 +568,23 @@ public class ICULocaleService extends ICUService {
* time-consuming so we don't want to do it more than once if
* we have to. This is only called if we are visible.
*/
protected void handleUpdateVisibleIDs(Set result) {
protected Set handleGetSupportedIDs() {
Set result = new TreeSet(String.CASE_INSENSITIVE_ORDER);
Locale[] locales = ICULocaleData.getAvailableLocales(name);
for (int i = 0; i < locales.length; ++i) {
Locale locale = locales[i];
if (acceptsLocale(locale)) {
result.add(LocaleUtility.canonicalLocaleString(locale.toString()));
String str = LocaleUtility.canonicalLocaleString(locale.toString());
result.add(str);
}
}
return result;
}
/**
* Return a localized name for the locale represented by id.
*/
protected String handleGetDisplayName(String id, Locale locale) {
// use java's display name formatting for now
return LocaleUtility.getLocaleFromName(id).getDisplayName(locale);
}
@ -408,18 +594,41 @@ public class ICULocaleService extends ICUService {
* inherited bundle);
*/
protected boolean acceptsLocale(Locale loc) {
boolean debug = false;
if (debug) System.out.println("al name: " + name + " loc: '" + loc + "'");
try {
ResourceBundle bundle = ICULocaleData.loadResourceBundle(name, loc); // single resource bundle lookup
if (requiredContents != null) {
for (int i = 0; i < requiredContents.length; ++i) {
if (bundle.getObject(requiredContents[i]) == null) {
ResourceBundle bundle = ICULocaleData.loadResourceBundle(name, loc);
if (bundle == null) {
if (debug) System.out.println("no bundle");
return false;
}
}
}
if (requiredContents == null) {
if (debug) System.out.println("always accepts");
return true;
}
loop:
for (int i = 0; i < requiredContents.length; ++i) {
String[] andRC = requiredContents[i];
for (int j = 0; j < andRC.length; ++j) {
try {
if (debug) System.out.println("al["+i+"]["+j+"] " + andRC[j]);
bundle.getObject(andRC[j]);
}
catch (MissingResourceException ex) {
if (debug) System.out.println("nope");
continue loop;
}
}
if (debug) System.out.println("ok");
return true;
}
}
catch (Exception e) {
Thread.dumpStack();
if (debug) System.out.println("whoops: " + e);
System.exit(0);
}
return false;
}
@ -433,7 +642,11 @@ public class ICULocaleService extends ICUService {
}
}
protected Key createKey(String id) {
/**
* Return the name of the current fallback locale. If it has changed since this was
* last accessed, the service cache is cleared.
*/
public String validateFallbackLocale() {
Locale loc = Locale.getDefault();
if (loc != fallbackLocale) {
synchronized (this) {
@ -444,7 +657,10 @@ public class ICULocaleService extends ICUService {
}
}
}
return fallbackLocaleName;
}
return LocaleKey.createWithCanonicalFallback(id, fallbackLocaleName);
protected Key createKey(String id) {
return LocaleKey.createWithCanonicalFallback(id, validateFallbackLocale());
}
}

View File

@ -5,8 +5,8 @@
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/impl/ICUService.java,v $
* $Date: 2002/08/13 23:40:52 $
* $Revision: 1.7 $
* $Date: 2002/09/14 21:36:29 $
* $Revision: 1.8 $
*
*******************************************************************************
*/
@ -38,11 +38,11 @@ import java.util.TreeMap;
* generally it should not be mutable, or the caller should clone the
* object before modifying it.</p>
*
* <p>Services 'canonicalize' the query id and use the canonical id
* to query for the service. The service also defines a mechanism
* to 'fallback' the id multiple times. Clients can optionally
* request the actual id that was matched by a query when they
* use an id to retrieve a service object.</p>
* <p>Services 'canonicalize' the query id and use the canonical id to
* query for the service. The service also defines a mechanism to
* 'fallback' the id multiple times. Clients can optionally request
* the actual id that was matched by a query when they use an id to
* retrieve a service object.</p>
*
* <p>Service objects are instantiated by Factory objects registered with
* the service. The service queries each Factory in turn, from most recently
@ -84,6 +84,22 @@ import java.util.TreeMap;
* resource bundle fallback strategy.<p>
*/
public class ICUService extends ICUNotifier {
protected final String name;
/**
* Constructor.
*/
public ICUService() {
name = "";
}
/**
* Construct with a name (useful for debugging).
*/
public ICUService(String name) {
this.name = name;
}
/**
* Access to factories is protected by a read-write lock. This is
* to allow multiple threads to read concurrently, but keep
@ -97,10 +113,12 @@ public class ICUService extends ICUNotifier {
private final List factories = new ArrayList();
/**
* Keys define how ids are canonicalized, and determine the
* fallback strategy used when querying the factories. The default
* key just takes as its canonicalID the id assigned to it when it
* is constructed, and has no fallbacks.
* Keys are used to communicate with factories to generate an
* instance of the service. They define how ids are
* canonicalized, provide both a current id and a current
* descriptor to use in querying the cache and factories, and
* determine the fallback strategy. The default key has no
* fallbacks.
*/
public static class Key {
private final String id;
@ -113,7 +131,7 @@ public class ICUService extends ICUNotifier {
}
/**
* Return the original ID.
* Return the original ID used to construct this key.
*/
public final String id() {
return id;
@ -128,13 +146,26 @@ public class ICUService extends ICUNotifier {
}
/**
* Return the (canonical) current ID. This implementation returns the
* canonical ID.
* Return the (canonical) current ID. This implementation
* returns the canonical ID.
*/
public String currentID() {
return canonicalID();
}
/**
* Return the current descriptor. This implementation returns
* the current ID. The current descriptor is used to fully
* identify an instance of the service in the cache. The
* current ID is that part of the descriptor that a factory
* can examine to identify whether it handles the key. The
* factory can either parse the descriptor or use custom API
* on the key in order to instantiate the service.
*/
public String currentDescriptor() {
return currentID();
}
/**
* If the key has a fallback, modify the key and return true,
* otherwise return false. The current ID will change if there
@ -161,17 +192,18 @@ public class ICUService extends ICUNotifier {
public Object create(Key key);
/**
* Add IDs understood by this factory to the result map, with
* this factory as the value. If this factory hides IDs
* Add IDs this factory publicly handles to the result map,
* with this factory as the value. If this factory hides IDs
* currently in result, it should remove or reset the mappings
* for those IDs.
* for those IDs. Result should contain only ids, not
* descriptors.
*/
public void updateVisibleIDs(Map result);
/**
* Return the display name for this id in the provided locale.
* If the id is not visible or not defined by the factory,
* return null.
* return null. This is an id, not a descriptor.
*/
public String getDisplayName(String id, Locale locale);
}
@ -182,7 +214,8 @@ public class ICUService extends ICUNotifier {
* factory that matches a single id and returns a single
* (possibly deferred-initialized) instance. If visible is
* true, updates the map passed to updateVisibleIDs with a
* mapping from id to itself.
* mapping from id to itself. This ignores the key descriptor
* and only examines the id.
*/
public static class SimpleFactory implements Factory {
protected Object instance;
@ -238,33 +271,55 @@ public class ICUService extends ICUNotifier {
}
/**
* Convenience override for get(String, String[]).
* Convenience override for get(String, String[]). This uses
* createKey to create a key for the provided descriptor.
*/
public Object get(String id) {
return get(id, null);
public Object get(String descriptor) {
return getKey(createKey(descriptor), null);
}
/**
* <p>Given an id, return a service object, and, if actualIDReturn
* is not null, the actual id under which it was found in the
* first element of actualIDReturn. If no service object matches
* this id, return null, and leave actualIDReturn unchanged.</p>
*
* <p>This tries each registered factory in order, and if none can
* generate a service object for the key, repeats the process with
* each fallback of the key until one returns a service object, or
* the key has no fallback.</p>
* Convenience override for get(Key, String[]). This uses
* createKey to create a key from the provided descriptor.
*/
public Object get(String id, String[] actualIDReturn) {
if (id == null) {
throw new NullPointerException();
public Object get(String descriptor, String[] actualReturn) {
if (descriptor == null) {
throw new NullPointerException("descriptor must not be null");
}
return getKey(createKey(descriptor), actualReturn);
}
/**
* Convenience override for get(Key, String[]).
*/
public Object getKey(Key key) {
return getKey(key, null);
}
/**
* <p>Given a key, return a service object, and, if actualReturn
* is not null, the descriptor with which it was found in the
* first element of actualReturn. If no service object matches
* this key, return null, and leave actualReturn unchanged.</p>
*
* <p>This queries the cache using the key's descriptor, and if no
* object in the cache matches it, tries the key on each
* registered factory, in order. If none generates a service
* object for the key, repeats the process with each fallback of
* the key, until either one returns a service object, or the key
* has no fallback.</p>
*
* <p>If key is null, just returns null.</p>
*/
public Object getKey(Key key, String[] actualReturn) {
if (factories.size() == 0) {
return null;
}
boolean debug = false;
if (debug) System.out.println("Service: " + name + " key: " + key);
CacheEntry result = null;
Key key = createKey(id);
if (key != null) {
try {
// The factory list can't be modified until we're done,
@ -284,15 +339,19 @@ public class ICUService extends ICUNotifier {
cref = new SoftReference(cache);
}
String currentID = null;
ArrayList cacheIDList = null;
String currentDescriptor = null;
ArrayList cacheDescriptorList = null;
boolean putInCache = false;
int NDebug = 0;
outer:
do {
currentID = key.currentID();
result = (CacheEntry)cache.get(currentID);
currentDescriptor = key.currentDescriptor();
if (debug) System.out.println(name + "[" + NDebug++ + "] looking for: " + currentDescriptor);
result = (CacheEntry)cache.get(currentDescriptor);
if (result != null) {
if (debug) System.out.println(name + " found with descriptor: " + currentDescriptor);
break outer;
}
@ -300,11 +359,13 @@ public class ICUService extends ICUNotifier {
// the cache if we eventually succeed.
putInCache = true;
int n = 0;
Iterator fi = factories.iterator();
while (fi.hasNext()) {
Object service = ((Factory)fi.next()).create(key);
if (service != null) {
result = new CacheEntry(currentID, service);
result = new CacheEntry(currentDescriptor, service);
if (debug) System.out.println(name + " factory cache with descriptor: " + currentDescriptor);
break outer;
}
}
@ -314,20 +375,23 @@ public class ICUService extends ICUNotifier {
// don't want to keep querying on an id that's going to
// fallback to the one that succeeded, we want to hit the
// cache the first time next goaround.
if (cacheIDList == null) {
cacheIDList = new ArrayList(5);
if (cacheDescriptorList == null) {
cacheDescriptorList = new ArrayList(5);
}
cacheIDList.add(currentID);
cacheDescriptorList.add(currentDescriptor);
} while (key.fallback());
if (result != null) {
if (putInCache) {
cache.put(result.actualID, result);
if (cacheIDList != null) {
Iterator iter = cacheIDList.iterator();
cache.put(result.actualDescriptor, result);
if (cacheDescriptorList != null) {
Iterator iter = cacheDescriptorList.iterator();
while (iter.hasNext()) {
cache.put((String)iter.next(), result);
String desc = (String)iter.next();
if (debug) System.out.println(name + " adding descriptor: '" + desc + "' for actual: '" + result.actualDescriptor + "'");
cache.put(desc, result);
}
}
// Atomic update. We held the read lock all this time
@ -337,10 +401,12 @@ public class ICUService extends ICUNotifier {
cacheref = cref;
}
if (actualIDReturn != null) {
actualIDReturn[0] = result.actualID;
if (actualReturn != null) {
actualReturn[0] = result.actualDescriptor;
}
if (debug) System.out.println("found in service: " + name);
return result.service;
}
}
@ -349,6 +415,8 @@ public class ICUService extends ICUNotifier {
}
}
if (debug) System.out.println("not found in service: " + name);
return null;
}
private SoftReference cacheref;
@ -356,10 +424,10 @@ public class ICUService extends ICUNotifier {
// Record the actual id for this service in the cache, so we can return it
// even if we succeed later with a different id.
private static final class CacheEntry {
String actualID;
Object service;
CacheEntry(String actualID, Object service) {
this.actualID = actualID;
final String actualDescriptor;
final Object service;
CacheEntry(String actualDescriptor, Object service) {
this.actualDescriptor = actualDescriptor;
this.service = service;
}
}
@ -391,9 +459,7 @@ public class ICUService extends ICUNotifier {
// grab the factory list and update it ourselves
try {
factoryLock.acquireRead();
idcache = new TreeMap(String.CASE_INSENSITIVE_ORDER);
ListIterator lIter = factories.listIterator(factories.size());
while (lIter.hasPrevious()) {
Factory f = (Factory)lIter.previous();
@ -490,8 +556,8 @@ public class ICUService extends ICUNotifier {
// we define a class so we get atomic simultaneous access to both the
// locale and corresponding map
private static class LocaleRef {
Locale locale;
SoftReference ref;
final Locale locale;
final SoftReference ref;
LocaleRef(Map dnCache, Locale locale) {
this.locale = locale;
@ -690,5 +756,19 @@ public class ICUService extends ICUNotifier {
}
return "no stats";
}
/**
* Return the name of this service. This will be the empty string if none was assigned.
*/
public String getName() {
return name;
}
/**
* Returns the result of super.toString, appending the name in curly braces.
*/
public String toString() {
return super.toString() + "{" + name + "}";
}
}

View File

@ -5,8 +5,8 @@
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/impl/LocaleUtility.java,v $
* $Date: 2002/06/19 21:03:12 $
* $Revision: 1.4 $
* $Date: 2002/09/14 21:36:29 $
* $Revision: 1.5 $
* *****************************************************************************************
*/
@ -97,27 +97,4 @@ public class LocaleUtility {
}
return id;
}
/*
public static String getDisplayLanguage(String languageID, Locale l) {
}
public static String getDisplayRegion(String regionID, Locale l) {
}
public static String getDisplayVariant(String variantID, Locale l) {
}
public static String getDisplayName(String localeName) {
return getDisplayName(getLocaleFromString(localeName));
}
public static String getDisplayName(Locale locale) {
String lang = locale.getLanguage();
String region = locale.getCountry();
String var = locale.getVariant();
StringBuffer buf = new StringBuffer(lang);
}
*/
}

View File

@ -5,15 +5,13 @@
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/text/NumberFormat.java,v $
* $Date: 2002/03/20 05:11:15 $
* $Revision: 1.13 $
* $Date: 2002/09/14 21:36:28 $
* $Revision: 1.14 $
*
*****************************************************************************************
*/
package com.ibm.icu.text;
import com.ibm.icu.impl.ICULocaleData;
import java.io.InvalidObjectException; //Bug 4185761 [Richard/GCL]
import java.io.IOException;
import java.io.ObjectInputStream;
@ -23,9 +21,22 @@ import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import com.ibm.icu.impl.ICULocaleData;
import com.ibm.icu.impl.ICUService;
import com.ibm.icu.impl.ICUService.Key;
import com.ibm.icu.impl.ICUService.Factory;
import com.ibm.icu.impl.ICULocaleService;
import com.ibm.icu.impl.ICULocaleService.ICUResourceBundleFactory;
import com.ibm.icu.impl.ICULocaleService.LocaleKey;
import com.ibm.icu.impl.ICULocaleService.LocaleKeyFactory;
import com.ibm.icu.impl.LocaleUtility;
/**
* <code>NumberFormat</code> is the abstract base class for all number
@ -151,13 +162,20 @@ import java.util.ResourceBundle;
*
* see DecimalFormat
* see java.text.ChoiceFormat
* @version $Revision: 1.13 $
* @version $Revision: 1.14 $
* @author Mark Davis
* @author Helena Shih
* @author Alan Liu
*/
public abstract class NumberFormat extends Format{
// Constants used by factory methods to specify a style of format.
private static final int NUMBERSTYLE = 0;
private static final int CURRENCYSTYLE = 1;
private static final int PERCENTSTYLE = 2;
private static final int SCIENTIFICSTYLE = 3;
private static final int INTEGERSTYLE = 4;
/**
* Field constant used to construct a FieldPosition object. Signifies that
* the position of the integer part of a formatted number should be returned.
@ -455,13 +473,168 @@ public abstract class NumberFormat extends Format{
return getInstance(inLocale, SCIENTIFICSTYLE);
}
// ===== Factory stuff =====
public static abstract class NumberFormatFactory {
public static final int FORMAT_NUMBER = NUMBERSTYLE;
public static final int FORMAT_CURRENCY = CURRENCYSTYLE;
public static final int FORMAT_PERCENT = PERCENTSTYLE;
public static final int FORMAT_SCIENTIFIC = SCIENTIFICSTYLE;
public static final int FORMAT_INTEGER = INTEGERSTYLE;
/**
* Return true if this factory will 'hide' other locales that
* are more specific than the ones in this factory. E.g., if
* this 'hides' other locales, then by supporting the 'en'
* locale, this also supports 'en_US', 'en_US_ETC' and so on,
* even though only the 'en' locale is listed.
*/
public abstract boolean hasGenericLocales();
/**
* Return an array of the locales directly supported by this factory.
*/
public abstract Locale[] getSupportedLocales();
/**
* Return a number format of the appropriate type. If the locale
* is not supported, return null. All the defined types must be
* supported.
*/
public abstract NumberFormat createFormat(Locale loc, int formatType);
}
public static abstract class SimpleNumberFormatFactory extends NumberFormatFactory {
final Locale locale;
final boolean isGeneric;
public SimpleNumberFormatFactory(Locale locale, boolean isGeneric) {
this.locale = locale;
this.isGeneric = isGeneric;
}
public final boolean hasGenericLocales() {
return isGeneric;
}
public final Locale[] getSupportedLocales() {
return new Locale[] { locale };
}
}
private static final class NFFactory extends LocaleKeyFactory {
private NumberFormatFactory delegate;
NFFactory(NumberFormatFactory delegate) {
super(true, delegate.hasGenericLocales());
this.delegate = delegate;
}
protected Object handleCreate(Key key) {
NFKey nfkey = (NFKey)key;
Locale loc = nfkey.canonicalLocale();
int choice = nfkey.choice();
return delegate.createFormat(loc, choice);
}
protected Set handleGetSupportedIDs() {
Locale[] locales = delegate.getSupportedLocales();
Set result = new HashSet();
for (int i = 0; i < locales.length; ++i) {
result.add(locales.toString());
}
return result;
}
}
/**
* Get the set of Locales for which NumberFormats are installed
* @return available locales
*/
public static Locale[] getAvailableLocales() {
return ICULocaleData.getAvailableLocales("NumberPatterns");
// return ICULocaleData.getAvailableLocales("NumberPatterns");
return getService().getAvailableLocales();
}
private static final class NFKey extends LocaleKey {
private int choice;
private static String[] CHOICE_NAMES = {
"number", "currency", "percent", "scientific", "integer"
};
private NFKey(String id, String canonicalID, String canonicalFallback, int choice) {
super(id, canonicalID, canonicalFallback, CHOICE_NAMES[choice]);
this.choice = choice;
}
int choice() {
return choice;
}
static NFKey createKey(ICULocaleService service, Locale locale, int choice) {
String id = LocaleUtility.canonicalLocaleString(locale.toString());
String fallback = service.validateFallbackLocale();
return new NFKey(id, id, fallback, choice);
}
}
/**
* Registers a new NumberFormat for the provided locale of the defined
* type. The returned object is a key that can be used to unregister this
* NumberFormat object.
*/
public static Object register(NumberFormatFactory factory) {
if (factory == null) {
throw new IllegalArgumentException("factory must not be null");
}
return getService().registerFactory(new NFFactory(factory));
}
/**
* Unregister the currency associated with this key (obtained from
* registerInstance).
*/
public static boolean unregister(Object registryKey) {
return getService().unregisterFactory((Factory)registryKey);
}
private static ICULocaleService service = null;
private static ICULocaleService getService() {
if (service == null) {
final String[][] pattern = {
new String[] { "NumberPatterns" },
new String[] { "NumberElements" },
new String[] { "CurrencyElements" },
};
class RBNumberFormatFactory extends ICUResourceBundleFactory {
RBNumberFormatFactory() {
super ("LocaleElements", pattern, true, true);
}
protected Object handleCreate(Key key) {
NFKey nfkey = (NFKey)key;
Locale locale = nfkey.currentLocale();
// System.out.println("testing locale: " + locale);
if (acceptsLocale(locale)) {
// System.out.println("creating with locale: " + nfkey.canonicalLocale());
return createInstance(nfkey.canonicalLocale(), nfkey.choice());
}
// System.out.println("did not support locale");
return null;
}
}
ICULocaleService newService = new ICULocaleService("NumberFormat");
newService.registerFactory(new RBNumberFormatFactory());
service = newService; // atomic
}
return service;
}
/**
@ -624,11 +797,28 @@ public abstract class NumberFormat extends Format{
// =======================privates===============================
// Hook for service
private static NumberFormat getInstance(Locale desiredLocale, int choice) {
ICULocaleService service = getService();
NFKey key = NFKey.createKey(service, desiredLocale, choice);
NumberFormat result = (NumberFormat)service.getKey(key);
//System.out.println("desired locale: " + desiredLocale + " service result:" + result);
//NumberFormat result2 = createInstance(desiredLocale, choice);
//System.out.println("defaultResult: " + result2);
return (NumberFormat)result.clone();
}
// [NEW]
private static NumberFormat getInstance(Locale desiredLocale,
private static NumberFormat createInstance(Locale desiredLocale,
int choice) {
DecimalFormat format = new DecimalFormat(getPattern(desiredLocale, choice),
new DecimalFormatSymbols(desiredLocale));
String pattern = getPattern(desiredLocale, choice);
DecimalFormatSymbols symbols = new DecimalFormatSymbols(desiredLocale);
DecimalFormat format = new DecimalFormat(pattern, symbols);
// System.out.println("loc: " + desiredLocale + " choice: " + choice + " pat: " + pattern + " sym: " + symbols + " result: " + format);
/*Bug 4408066
Add codes for the new method getIntegerInstance() [Richard/GCL]
*/
@ -763,15 +953,9 @@ public abstract class NumberFormat extends Format{
*/
private static final Hashtable cachedLocaleData = new Hashtable(3);
// Constants used by factory methods to specify a style of format.
private static final int NUMBERSTYLE = 0;
private static final int CURRENCYSTYLE = 1;
private static final int PERCENTSTYLE = 2;
private static final int SCIENTIFICSTYLE = 3;
/*Bug 4408066
Add Field for the new method getIntegerInstance() [Richard/GCL]
*/
private static final int INTEGERSTYLE = 4;
/**
* True if the the grouping (i.e. thousands) separator is used when

View File

@ -5,8 +5,8 @@
*******************************************************************************
*
* $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/util/Currency.java,v $
* $Date: 2002/09/07 00:15:35 $
* $Revision: 1.5 $
* $Date: 2002/09/14 21:36:30 $
* $Revision: 1.6 $
*
*******************************************************************************
*/
@ -56,11 +56,11 @@ public class Currency implements Serializable {
private static ICULocaleService getService() {
if (service == null) {
service = new ICULocaleService();
service = new ICULocaleService("Currency");
class CurrencyFactory extends ICUResourceBundleFactory {
CurrencyFactory() {
super ("LocaleElements", "CurrencyElements", true);
super("CurrencyElements", true);
}
protected Object createFromBundle(ResourceBundle bundle, Key key) {