scuffed-code/icu4c/source/common/iculserv.cpp
Andy Heninger bdd7442d57 ICU-2787 Locale thread safety.
X-SVN-Rev: 11804
2003-05-05 23:21:48 +00:00

864 lines
23 KiB
C++

/**
*******************************************************************************
* Copyright (C) 2001-2002, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
*******************************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_SERVICE
#include "unicode/resbund.h"
#include "cmemory.h"
#include "iculserv.h"
#include "ustrfmt.h"
U_NAMESPACE_BEGIN
static Hashtable * LocaleUtility_cache = NULL;
#define UNDERSCORE_CHAR ((UChar)0x005f)
/*
******************************************************************
*/
UnicodeString&
LocaleUtility::canonicalLocaleString(const UnicodeString* id, UnicodeString& result)
{
if (id == NULL) {
result.setToBogus();
} else {
result = *id;
int32_t i = 0;
int32_t n = result.indexOf(UNDERSCORE_CHAR);
if (n < 0) {
n = result.length();
}
for (; i < n; ++i) {
UChar c = result.charAt(i);
if (c >= 0x0041 && c <= 0x005a) {
c += 0x20;
result.setCharAt(i, c);
}
}
for (n = result.length(); i < n; ++i) {
UChar c = result.charAt(i);
if (c >= 0x0061 && c <= 0x007a) {
c -= 0x20;
result.setCharAt(i, c);
}
}
}
return result;
}
Locale&
LocaleUtility::initLocaleFromName(const UnicodeString& id, Locale& result)
{
if (id.isBogus()) {
result.setToBogus();
} else {
const int32_t BUFLEN = 128; // larger than ever needed
char buffer[BUFLEN];
int len = id.extract(0, BUFLEN, buffer);
if (len >= BUFLEN) {
result.setToBogus();
} else {
buffer[len] = '\0';
result = Locale::createFromName(buffer);
}
}
return result;
}
UnicodeString&
LocaleUtility::initNameFromLocale(const Locale& locale, UnicodeString& result)
{
if (locale.isBogus()) {
result.setToBogus();
} else {
result.append(locale.getName());
}
return result;
}
const Hashtable*
LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID)
{
// have to ignore bundleID for the moment, since we don't have easy C++ api.
// assume it's the default bundle
Hashtable* htp;
umtx_lock(NULL);
htp = LocaleUtility_cache;
umtx_unlock(NULL);
if (htp == NULL) {
htp = new Hashtable();
if (htp) {
UErrorCode status = U_ZERO_ERROR;
int32_t count = uloc_countAvailable();
for (int32_t i = 0; i < count; ++i) {
UnicodeString temp(uloc_getAvailable(i), "");
htp->put(temp, (void*)htp, status);
if (U_FAILURE(status)) {
delete htp;
return NULL;
}
}
umtx_lock(NULL);
if (LocaleUtility_cache == NULL) {
LocaleUtility_cache = htp;
htp = NULL;
}
umtx_unlock(NULL);
delete htp;
}
}
return LocaleUtility_cache;
}
UBool
LocaleUtility::isFallbackOf(const UnicodeString& root, const UnicodeString& child)
{
return child.indexOf(root) == 0 &&
(child.length() == root.length() ||
child.charAt(root.length()) == UNDERSCORE_CHAR);
}
UBool
LocaleUtility::cleanup(void) {
if (LocaleUtility_cache) {
delete LocaleUtility_cache;
LocaleUtility_cache = NULL;
}
return TRUE;
}
/*
******************************************************************
*/
const int32_t LocaleKey::KIND_ANY = -1;
LocaleKey*
LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID,
const UnicodeString* canonicalFallbackID,
UErrorCode& status)
{
return LocaleKey::createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY, status);
}
LocaleKey*
LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID,
const UnicodeString* canonicalFallbackID,
int32_t kind,
UErrorCode& status)
{
if (primaryID == NULL || U_FAILURE(status)) {
return NULL;
}
UnicodeString canonicalPrimaryID;
LocaleUtility::canonicalLocaleString(primaryID, canonicalPrimaryID);
return new LocaleKey(*primaryID, canonicalPrimaryID, canonicalFallbackID, kind);
}
LocaleKey::LocaleKey(const UnicodeString& primaryID,
const UnicodeString& canonicalPrimaryID,
const UnicodeString* canonicalFallbackID,
int32_t kind)
: ICUServiceKey(primaryID)
, _kind(kind)
, _primaryID(canonicalPrimaryID)
, _fallbackID()
, _currentID()
{
_fallbackID.setToBogus();
if (_primaryID.length() != 0) {
if (canonicalFallbackID != NULL && _primaryID != *canonicalFallbackID) {
_fallbackID = *canonicalFallbackID;
}
}
_currentID = _primaryID;
}
UnicodeString&
LocaleKey::prefix(UnicodeString& result) const {
if (_kind != KIND_ANY) {
UChar buffer[64];
uprv_itou(buffer, 64, _kind, 10, 0);
UnicodeString temp(buffer);
result.append(temp);
}
return result;
}
int32_t
LocaleKey::kind() const {
return _kind;
}
UnicodeString&
LocaleKey::canonicalID(UnicodeString& result) const {
return result.append(_primaryID);
}
UnicodeString&
LocaleKey::currentID(UnicodeString& result) const {
if (!_currentID.isBogus()) {
result.append(_currentID);
}
return result;
}
UnicodeString&
LocaleKey::currentDescriptor(UnicodeString& result) const {
if (!_currentID.isBogus()) {
prefix(result).append(PREFIX_DELIMITER).append(_currentID);
} else {
result.setToBogus();
}
return result;
}
Locale&
LocaleKey::canonicalLocale(Locale& result) const {
return LocaleUtility::initLocaleFromName(_primaryID, result);
}
Locale&
LocaleKey::currentLocale(Locale& result) const {
return LocaleUtility::initLocaleFromName(_currentID, result);
}
UBool
LocaleKey::fallback() {
if (!_currentID.isBogus()) {
int x = _currentID.lastIndexOf(UNDERSCORE_CHAR);
if (x != -1) {
_currentID.remove(x); // truncate current or fallback, whichever we're pointing to
return TRUE;
}
if (!_fallbackID.isBogus()) {
_currentID = _fallbackID;
_fallbackID.setToBogus();
return TRUE;
}
if (_currentID.length() > 0) {
_currentID.remove(0); // completely truncate
return TRUE;
}
_currentID.setToBogus();
}
return FALSE;
}
UBool
LocaleKey::isFallbackOf(const UnicodeString& id) const {
UnicodeString temp(id);
parseSuffix(temp);
return temp.indexOf(_primaryID) == 0 &&
(temp.length() == _primaryID.length() ||
temp.charAt(_primaryID.length()) == UNDERSCORE_CHAR);
}
#ifdef SERVICE_DEBUG
UnicodeString&
LocaleKey::debug(UnicodeString& result) const
{
ICUServiceKey::debug(result);
result.append(" kind: ");
result.append(_kind);
result.append(" primaryID: ");
result.append(_primaryID);
result.append(" fallbackID: ");
result.append(_fallbackID);
result.append(" currentID: ");
result.append(_currentID);
return result;
}
UnicodeString&
LocaleKey::debugClass(UnicodeString& result) const
{
return result.append("LocaleKey ");
}
#endif
const char LocaleKey::fgClassID = 0;
/*
******************************************************************
*/
LocaleKeyFactory::LocaleKeyFactory(int32_t coverage)
: _name()
, _coverage(coverage)
{
}
LocaleKeyFactory::LocaleKeyFactory(int32_t coverage, const UnicodeString& name)
: _name(name)
, _coverage(coverage)
{
}
LocaleKeyFactory::~LocaleKeyFactory() {
}
UObject*
LocaleKeyFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const {
if (handlesKey(key, status)) {
const LocaleKey& lkey = (const LocaleKey&)key;
int32_t kind = lkey.kind();
Locale loc;
lkey.currentLocale(loc);
return handleCreate(loc, kind, service, status);
}
return NULL;
}
UBool
LocaleKeyFactory::handlesKey(const ICUServiceKey& key, UErrorCode& status) const {
const Hashtable* supported = getSupportedIDs(status);
if (supported) {
UnicodeString id;
key.currentID(id);
return supported->get(id) != NULL;
}
return FALSE;
}
void
LocaleKeyFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const {
const Hashtable* supported = getSupportedIDs(status);
if (supported) {
UBool visible = (_coverage & 0x1) == 0;
const UHashElement* elem = NULL;
int32_t pos = 0;
while (elem = supported->nextElement(pos)) {
const UnicodeString& id = *((const UnicodeString*)elem->key.pointer);
if (!visible) {
result.remove(id);
} else {
result.put(id, (void*)this, status); // this is dummy non-void marker used for set semantics
if (U_FAILURE(status)) {
break;
}
}
}
}
}
UnicodeString&
LocaleKeyFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const {
if ((_coverage & 0x1) == 0) {
UErrorCode status = U_ZERO_ERROR;
if (isSupportedID(id, status)) {
Locale loc;
LocaleUtility::initLocaleFromName(id, loc);
return loc.getDisplayName(locale, result);
}
}
result.setToBogus();
return result;
}
UObject*
LocaleKeyFactory::handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const {
return NULL;
}
UBool
LocaleKeyFactory::isSupportedID(const UnicodeString& id, UErrorCode& status) const {
const Hashtable* ids = getSupportedIDs(status);
return ids && ids->get(id);
}
const Hashtable*
LocaleKeyFactory::getSupportedIDs(UErrorCode& status) const {
return NULL;
}
#ifdef SERVICE_DEBUG
UnicodeString&
LocaleKeyFactory::debug(UnicodeString& result) const
{
debugClass(result);
result.append(", name: ");
result.append(_name);
result.append(", coverage: ");
result.append(_coverage);
return result;
}
UnicodeString&
LocaleKeyFactory::debugClass(UnicodeString& result) const
{
return result.append("LocaleKeyFactory");
}
#endif
const char LocaleKeyFactory::fgClassID = 0;
/*
******************************************************************
*/
SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt,
const UnicodeString& locale,
int32_t kind,
int32_t coverage)
: LocaleKeyFactory(coverage)
, _obj(objToAdopt)
, _id(locale)
, _kind(kind)
{
}
SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt,
const Locale& locale,
int32_t kind,
int32_t coverage)
: LocaleKeyFactory(coverage)
, _obj(objToAdopt)
, _id()
, _kind(kind)
{
LocaleUtility::initNameFromLocale(locale, _id);
}
SimpleLocaleKeyFactory::~SimpleLocaleKeyFactory()
{
delete _obj;
_obj = NULL;
}
UObject*
SimpleLocaleKeyFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
{
if (U_SUCCESS(status)) {
const LocaleKey& lkey = (const LocaleKey&)key;
if (_kind == LocaleKey::KIND_ANY || _kind == lkey.kind()) {
UnicodeString keyID;
lkey.currentID(keyID);
if (_id == keyID) {
return service->cloneInstance(_obj);
}
}
}
return NULL;
}
UBool
SimpleLocaleKeyFactory::isSupportedID(const UnicodeString& id, UErrorCode& status) const
{
return id == _id;
}
void
SimpleLocaleKeyFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const
{
if (U_SUCCESS(status)) {
if (_coverage & 0x1) {
result.remove(_id);
} else {
result.put(_id, (void*)this, status);
}
}
}
#ifdef SERVICE_DEBUG
UnicodeString&
SimpleLocaleKeyFactory::debug(UnicodeString& result) const
{
LocaleKeyFactory::debug(result);
result.append(", id: ");
result.append(_id);
result.append(", kind: ");
result.append(_kind);
return result;
}
UnicodeString&
SimpleLocaleKeyFactory::debugClass(UnicodeString& result) const
{
return result.append("SimpleLocaleKeyFactory");
}
#endif
const char SimpleLocaleKeyFactory::fgClassID = 0;
/*
******************************************************************
*/
ICUResourceBundleFactory::ICUResourceBundleFactory()
: LocaleKeyFactory(VISIBLE)
, _bundleName()
{
}
ICUResourceBundleFactory::ICUResourceBundleFactory(const UnicodeString& bundleName)
: LocaleKeyFactory(VISIBLE)
, _bundleName(bundleName)
{
}
const Hashtable*
ICUResourceBundleFactory::getSupportedIDs(UErrorCode& status) const
{
if (U_SUCCESS(status)) {
return LocaleUtility::getAvailableLocaleNames(_bundleName);
}
return NULL;
}
UObject*
ICUResourceBundleFactory::handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const
{
if (U_SUCCESS(status)) {
return new ResourceBundle(_bundleName, loc, status);
}
return NULL;
}
#ifdef SERVICE_DEBUG
UnicodeString&
ICUResourceBundleFactory::debug(UnicodeString& result) const
{
LocaleKeyFactory::debug(result);
result.append(", bundle: ");
return result.append(_bundleName);
}
UnicodeString&
ICUResourceBundleFactory::debugClass(UnicodeString& result) const
{
return result.append("ICUResourceBundleFactory");
}
#endif
const char ICUResourceBundleFactory::fgClassID = '\0';
/*
******************************************************************
*/
ICULocaleService::ICULocaleService()
: fallbackLocale(Locale::getDefault())
, llock(0)
{
}
ICULocaleService::ICULocaleService(const UnicodeString& dname)
: ICUService(dname)
, fallbackLocale(Locale::getDefault())
, llock(0)
{
}
ICULocaleService::~ICULocaleService()
{
umtx_destroy(&llock);
}
UObject*
ICULocaleService::get(const Locale& locale, UErrorCode& status) const
{
return get(locale, LocaleKey::KIND_ANY, NULL, status);
}
UObject*
ICULocaleService::get(const Locale& locale, int32_t kind, UErrorCode& status) const
{
return get(locale, kind, NULL, status);
}
UObject*
ICULocaleService::get(const Locale& locale, Locale* actualReturn, UErrorCode& status) const
{
return get(locale, LocaleKey::KIND_ANY, actualReturn, status);
}
UObject*
ICULocaleService::get(const Locale& locale, int32_t kind, Locale* actualReturn, UErrorCode& status) const
{
UObject* result = NULL;
if (U_FAILURE(status)) {
return result;
}
UnicodeString locName(locale.getName(), "");
if (locName.isBogus()) {
status = U_MEMORY_ALLOCATION_ERROR;
} else {
ICUServiceKey* key = createKey(&locName, kind, status);
if (key) {
if (actualReturn == NULL) {
result = getKey(*key, status);
} else {
UnicodeString temp;
result = getKey(*key, &temp, status);
if (result != NULL) {
key->parseSuffix(temp);
LocaleUtility::initLocaleFromName(temp, *actualReturn);
}
}
delete key;
}
}
return result;
}
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale,
UBool visible, UErrorCode& status)
{
Locale loc;
LocaleUtility::initLocaleFromName(locale, loc);
return registerInstance(objToAdopt, loc, LocaleKey::KIND_ANY,
visible ? LocaleKeyFactory::VISIBLE : LocaleKeyFactory::INVISIBLE, status);
}
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, UErrorCode& status)
{
return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE, status);
}
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, UErrorCode& status)
{
return registerInstance(objToAdopt, locale, kind, LocaleKeyFactory::VISIBLE, status);
}
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage, UErrorCode& status)
{
ICUServiceFactory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage);
if (factory != NULL) {
return registerFactory(factory, status);
}
delete objToAdopt;
return NULL;
}
#if 0
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, UErrorCode& status)
{
return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE, status);
}
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, UBool visible, UErrorCode& status)
{
return registerInstance(objToAdopt, locale, LocaleKey::KIND_ANY,
visible ? LocaleKeyFactory::VISIBLE : LocaleKeyFactory::INVISIBLE,
status);
}
URegistryKey
ICULocaleService::registerInstance(UObject* objToAdopt, const UnicodeString& locale, int32_t kind, int32_t coverage, UErrorCode& status)
{
ICUServiceFactory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage);
if (factory != NULL) {
return registerFactory(factory, status);
}
delete objToAdopt;
return NULL;
}
#endif
class ServiceEnumeration : public StringEnumeration {
private:
const ICULocaleService* _service;
int32_t _timestamp;
UVector _ids;
int32_t _pos;
void* _bufp;
int32_t _buflen;
private:
ServiceEnumeration(const ICULocaleService* service, UErrorCode status)
: _service(service)
, _timestamp(service->getTimestamp())
, _ids(uhash_deleteUnicodeString, NULL, status)
, _pos(0)
, _bufp(NULL)
, _buflen(0)
{
_service->getVisibleIDs(_ids, status);
}
public:
static ServiceEnumeration* create(const ICULocaleService* service) {
UErrorCode status = U_ZERO_ERROR;
ServiceEnumeration* result = new ServiceEnumeration(service, status);
if (U_SUCCESS(status)) {
return result;
}
delete result;
return NULL;
}
virtual ~ServiceEnumeration() {
uprv_free(_bufp);
}
virtual int32_t count(UErrorCode& status) const {
return upToDate(status) ? _ids.size() : 0;
}
const char* next(int32_t* resultLength, UErrorCode& status) {
const UnicodeString* us = snext(status);
if (us) {
while (TRUE) {
int32_t newlen = us->extract((char*)_bufp, _buflen / sizeof(char), NULL, status);
if (status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR) {
resizeBuffer((newlen + 1) * sizeof(char));
status = U_ZERO_ERROR;
} else if (U_SUCCESS(status)) {
((char*)_bufp)[newlen] = 0;
if (resultLength) {
resultLength[0] = newlen;
}
return (const char*)_bufp;
} else {
break;
}
}
}
return NULL;
}
const UChar* unext(int32_t* resultLength, UErrorCode& status) {
const UnicodeString* us = snext(status);
if (us) {
while (TRUE) {
int32_t newlen = us->extract((UChar*)_bufp, _buflen / sizeof(UChar), status);
if (status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR) {
resizeBuffer((newlen + 1) * sizeof(UChar));
} else if (U_SUCCESS(status)) {
((UChar*)_bufp)[newlen] = 0;
if (resultLength) {
resultLength[0] = newlen;
}
return (const UChar*)_bufp;
} else {
break;
}
}
}
return NULL;
}
const UnicodeString* snext(UErrorCode& status) {
if (upToDate(status) && (_pos < _ids.size())) {
return (const UnicodeString*)_ids[_pos++];
}
return NULL;
}
void resizeBuffer(int32_t newlen) {
if (_bufp) {
_bufp = uprv_realloc(_bufp, newlen);
} else {
_bufp = uprv_malloc(newlen);
}
_buflen = newlen;
}
UBool upToDate(UErrorCode& status) const {
if (U_SUCCESS(status)) {
if (_timestamp == _service->getTimestamp()) {
return TRUE;
}
status = U_ENUM_OUT_OF_SYNC_ERROR;
}
return FALSE;
}
void reset(UErrorCode& status) {
if (status == U_ENUM_OUT_OF_SYNC_ERROR) {
status = U_ZERO_ERROR;
}
if (U_SUCCESS(status)) {
_timestamp = _service->getTimestamp();
_pos = 0;
_service->getVisibleIDs(_ids, status);
}
}
public:
virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); }
static UClassID getStaticClassID(void) { return (UClassID)&fgClassID; }
private:
static const char fgClassID;
};
const char ServiceEnumeration::fgClassID = '\0';
StringEnumeration*
ICULocaleService::getAvailableLocales(void) const
{
return ServiceEnumeration::create(this);
}
const UnicodeString&
ICULocaleService::validateFallbackLocale() const
{
const Locale& loc = Locale::getDefault();
ICULocaleService* ncThis = (ICULocaleService*)this;
{
Mutex mutex(&ncThis->llock);
if (loc != fallbackLocale) {
ncThis->fallbackLocale = loc;
LocaleUtility::initNameFromLocale(loc, ncThis->fallbackLocaleName);
ncThis->clearServiceCache();
}
}
return fallbackLocaleName;
}
ICUServiceKey*
ICULocaleService::createKey(const UnicodeString* id, UErrorCode& status) const
{
return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), status);
}
ICUServiceKey*
ICULocaleService::createKey(const UnicodeString* id, int32_t kind, UErrorCode& status) const
{
return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), kind, status);
}
U_NAMESPACE_END
// defined in ucln_cmn.h
/**
* Release all static memory held by Locale Utility.
*/
U_CFUNC UBool service_cleanup(void) {
return LocaleUtility::cleanup();
}
/* !UCONFIG_NO_SERVICE */
#endif