blob: f99462babdf9b241e584f212adc9e98271bfb132 [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 2001-2004, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
*******************************************************************************
*/
#include "unicode/utypes.h"
#if !UCONFIG_NO_SERVICE
#include "unicode/resbund.h"
#include "uresimp.h"
#include "cmemory.h"
#include "iculserv.h"
#include "ustrfmt.h"
#include "uhash.h"
#include "charstr.h"
#include "ucln_cmn.h"
#include "uassert.h"
// see LocaleUtility::getAvailableLocaleNames
static Hashtable * LocaleUtility_cache = NULL;
#define UNDERSCORE_CHAR ((UChar)0x005f)
#define AT_SIGN_CHAR ((UChar)64)
#define PERIOD_CHAR ((UChar)46)
/*
******************************************************************
*/
/**
* Release all static memory held by Locale Utility.
*/
U_CDECL_BEGIN
static UBool U_CALLCONV service_cleanup(void) {
if (LocaleUtility_cache) {
delete LocaleUtility_cache;
LocaleUtility_cache = NULL;
}
return TRUE;
}
U_CDECL_END
U_NAMESPACE_BEGIN
UnicodeString&
LocaleUtility::canonicalLocaleString(const UnicodeString* id, UnicodeString& result)
{
if (id == NULL) {
result.setToBogus();
} else {
// Fix case only (no other changes) up to the first '@' or '.' or
// end of string, whichever comes first. In 3.0 I changed this to
// stop at first '@' or '.'. It used to run out to the end of
// string. My fix makes the tests pass but is probably
// structurally incorrect. See below. [alan 3.0]
// TODO: Doug, you might want to revise this...
result = *id;
int32_t i = 0;
int32_t end = result.indexOf(AT_SIGN_CHAR);
int32_t n = result.indexOf(PERIOD_CHAR);
if (n >= 0 && n < end) {
end = n;
}
if (end < 0) {
end = result.length();
}
n = result.indexOf(UNDERSCORE_CHAR);
if (n < 0) {
n = end;
}
for (; i < n; ++i) {
UChar c = result.charAt(i);
if (c >= 0x0041 && c <= 0x005a) {
c += 0x20;
result.setCharAt(i, c);
}
}
for (n = end; i < n; ++i) {
UChar c = result.charAt(i);
if (c >= 0x0061 && c <= 0x007a) {
c -= 0x20;
result.setCharAt(i, c);
}
}
}
return result;
#if 0
// This code does a proper full level 2 canonicalization of id.
// It's nasty to go from UChar to char to char to UChar -- but
// that's what you have to do to use the uloc_canonicalize
// function on UnicodeStrings.
// I ended up doing the alternate fix (see above) not for
// performance reasons, although performance will certainly be
// better, but because doing a full level 2 canonicalization
// causes some tests to fail. [alan 3.0]
// TODO: Doug, you might want to revisit this...
result.setToBogus();
if (id != 0) {
int32_t buflen = id->length() + 8; // space for NUL
char* buf = (char*) uprv_malloc(buflen);
char* canon = (buf == 0) ? 0 : (char*) uprv_malloc(buflen);
if (buf != 0 && canon != 0) {
U_ASSERT(id->extract(0, INT32_MAX, buf, buflen) < buflen);
UErrorCode ec = U_ZERO_ERROR;
uloc_canonicalize(buf, canon, buflen, &ec);
if (U_SUCCESS(ec)) {
result = UnicodeString(canon);
}
}
uprv_free(buf);
uprv_free(canon);
}
return result;
#endif
}
Locale&
LocaleUtility::initLocaleFromName(const UnicodeString& id, Locale& result)
{
enum { BUFLEN = 128 }; // larger than ever needed
if (id.isBogus() || id.length() >= BUFLEN) {
result.setToBogus();
} else {
/*
* We need to convert from a UnicodeString to char * in order to
* create a Locale.
*
* Problem: Locale ID strings may contain '@' which is a variant
* character and cannot be handled by invariant-character conversion.
*
* Hack: Since ICU code can handle locale IDs with multiple encodings
* of '@' (at least for EBCDIC; it's not known to be a problem for
* ASCII-based systems),
* we use regular invariant-character conversion for everything else
* and manually convert U+0040 into a compiler-char-constant '@'.
* While this compilation-time constant may not match the runtime
* encoding of '@', it should be one of the encodings which ICU
* recognizes.
*
* There should be only at most one '@' in a locale ID.
*/
char buffer[BUFLEN];
int32_t prev, i;
prev = 0;
for(;;) {
i = id.indexOf((UChar)0x40, prev);
if(i < 0) {
// no @ between prev and the rest of the string
id.extract(prev, INT32_MAX, buffer + prev, BUFLEN - prev, US_INV);
break; // done
} else {
// normal invariant-character conversion for text between @s
id.extract(prev, i - prev, buffer + prev, BUFLEN - prev, US_INV);
// manually "convert" U+0040 at id[i] into '@' at buffer[i]
buffer[i] = '@';
prev = i + 1;
}
}
result = Locale::createFromName(buffer);
}
return result;
}
UnicodeString&
LocaleUtility::initNameFromLocale(const Locale& locale, UnicodeString& result)
{
if (locale.isBogus()) {
result.setToBogus();
} else {
result.append(UnicodeString(locale.getName(), -1, US_INV));
}
return result;
}
const Hashtable*
LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID)
{
// LocaleUtility_cache is a hash-of-hashes. The top-level keys
// are path strings ('bundleID') passed to
// ures_openAvailableLocales. The top-level values are
// second-level hashes. The second-level keys are result strings
// from ures_openAvailableLocales. The second-level values are
// garbage ((void*)1 or other random pointer).
UErrorCode status = U_ZERO_ERROR;
Hashtable* cache;
umtx_lock(NULL);
cache = LocaleUtility_cache;
umtx_unlock(NULL);
if (cache == NULL) {
cache = new Hashtable(status);
if (cache == NULL || U_FAILURE(status)) {
return NULL; // catastrophic failure; e.g. out of memory
}
cache->setValueDeleter(uhash_deleteHashtable);
Hashtable* h; // set this to final LocaleUtility_cache value
umtx_lock(NULL);
h = LocaleUtility_cache;
if (h == NULL) {
LocaleUtility_cache = h = cache;
cache = NULL;
ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup);
}
umtx_unlock(NULL);
delete cache;
cache = h;
}
U_ASSERT(cache != NULL);
Hashtable* htp;
umtx_lock(NULL);
htp = (Hashtable*) cache->get(bundleID);
umtx_unlock(NULL);
if (htp == NULL) {
htp = new Hashtable(status);
if (htp && U_SUCCESS(status)) {
CharString cbundleID(bundleID);
const char* path = (const char*) cbundleID;
if (*path == 0) path = NULL; // empty string => NULL
UEnumeration *uenum = ures_openAvailableLocales(path, &status);
for (;;) {
const UChar* id = uenum_unext(uenum, NULL, &status);
if (id == NULL) {
break;
}
htp->put(UnicodeString(id), (void*)htp, status);
}
uenum_close(uenum);
if (U_FAILURE(status)) {
delete htp;
return NULL;
}
umtx_lock(NULL);
cache->put(bundleID, (void*)htp, status);
umtx_unlock(NULL);
}
}
return htp;
}
UBool
LocaleUtility::isFallbackOf(const UnicodeString& root, const UnicodeString& child)
{
return child.indexOf(root) == 0 &&
(child.length() == root.length() ||
child.charAt(root.length()) == UNDERSCORE_CHAR);
}
/*
******************************************************************
*/
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;
}
LocaleKey::~LocaleKey() {}
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
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKey)
/*
******************************************************************
*/
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)) != NULL) {
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;
// assume if this is called on us, we support some fallback of this id
// 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
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(LocaleKeyFactory)
/*
******************************************************************
*/
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
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleLocaleKeyFactory)
/*
******************************************************************
*/
ICUResourceBundleFactory::ICUResourceBundleFactory()
: LocaleKeyFactory(VISIBLE)
, _bundleName()
{
}
ICUResourceBundleFactory::ICUResourceBundleFactory(const UnicodeString& bundleName)
: LocaleKeyFactory(VISIBLE)
, _bundleName(bundleName)
{
}
ICUResourceBundleFactory::~ICUResourceBundleFactory() {}
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)) {
// _bundleName is a package name
// and should only contain invariant characters
char pkg[20];
int32_t length;
length=_bundleName.extract(0, INT32_MAX, pkg, (int32_t)sizeof(pkg), US_INV);
if(length>=(int32_t)sizeof(pkg)) {
return NULL;
}
return new ResourceBundle(pkg, 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
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUResourceBundleFactory)
/*
******************************************************************
*/
ICULocaleService::ICULocaleService()
: fallbackLocale(Locale::getDefault())
, llock(0)
{
umtx_init(&llock);
}
ICULocaleService::ICULocaleService(const UnicodeString& dname)
: ICUService(dname)
, fallbackLocale(Locale::getDefault())
, llock(0)
{
umtx_init(&llock);
}
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(), -1, US_INV);
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;
private:
ServiceEnumeration(const ICULocaleService* service, UErrorCode &status)
: _service(service)
, _timestamp(service->getTimestamp())
, _ids(uhash_deleteUnicodeString, NULL, status)
, _pos(0)
{
_service->getVisibleIDs(_ids, status);
}
ServiceEnumeration(const ServiceEnumeration &other, UErrorCode &status)
: _service(other._service)
, _timestamp(other._timestamp)
, _ids(uhash_deleteUnicodeString, NULL, status)
, _pos(0)
{
if(U_SUCCESS(status)) {
int32_t i, length;
length = other._ids.size();
for(i = 0; i < length; ++i) {
_ids.addElement(((UnicodeString *)other._ids.elementAt(i))->clone(), status);
}
if(U_SUCCESS(status)) {
_pos = other._pos;
}
}
}
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() {}
virtual StringEnumeration *clone() const {
UErrorCode status = U_ZERO_ERROR;
ServiceEnumeration *cl = new ServiceEnumeration(*this, status);
if(U_FAILURE(status)) {
delete cl;
cl = NULL;
}
return cl;
}
UBool upToDate(UErrorCode& status) const {
if (U_SUCCESS(status)) {
if (_timestamp == _service->getTimestamp()) {
return TRUE;
}
status = U_ENUM_OUT_OF_SYNC_ERROR;
}
return FALSE;
}
virtual int32_t count(UErrorCode& status) const {
return upToDate(status) ? _ids.size() : 0;
}
virtual const UnicodeString* snext(UErrorCode& status) {
if (upToDate(status) && (_pos < _ids.size())) {
return (const UnicodeString*)_ids[_pos++];
}
return NULL;
}
virtual 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:
static UClassID U_EXPORT2 getStaticClassID(void);
virtual UClassID getDynamicClassID(void) const;
};
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceEnumeration)
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
/* !UCONFIG_NO_SERVICE */
#endif