diff --git a/icu4c/source/common/common.dsp b/icu4c/source/common/common.dsp index 3451fbd2ab6..f8157019a66 100644 --- a/icu4c/source/common/common.dsp +++ b/icu4c/source/common/common.dsp @@ -203,6 +203,22 @@ SOURCE=.\filestrm.c # End Source File # Begin Source File +SOURCE=.\iculserv.cpp +# End Source File +# Begin Source File + +SOURCE=.\icunotif.cpp +# End Source File +# Begin Source File + +SOURCE=.\icurwlck.cpp +# End Source File +# Begin Source File + +SOURCE=.\icuserv.cpp +# End Source File +# Begin Source File + SOURCE=.\locid.cpp # End Source File # Begin Source File @@ -845,6 +861,26 @@ SOURCE=.\hash.h # End Source File # Begin Source File +SOURCE=.\iculdata.h +# End Source File +# Begin Source File + +SOURCE=.\iculserv.h +# End Source File +# Begin Source File + +SOURCE=.\icunotif.h +# End Source File +# Begin Source File + +SOURCE=.\icurwlck.h +# End Source File +# Begin Source File + +SOURCE=.\icuserv.h +# End Source File +# Begin Source File + SOURCE=.\unicode\locid.h !IF "$(CFG)" == "common - Win32 Release" diff --git a/icu4c/source/common/iculserv.cpp b/icu4c/source/common/iculserv.cpp new file mode 100644 index 00000000000..58a977cfdb0 --- /dev/null +++ b/icu4c/source/common/iculserv.cpp @@ -0,0 +1,748 @@ +/** + ******************************************************************************* + * 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" + +U_NAMESPACE_BEGIN + +/* + ****************************************************************** + */ + +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 + + if (cache == NULL) { + Hashtable* result = new Hashtable(); + if (result) { + UErrorCode status = U_ZERO_ERROR; + int32_t count = uloc_countAvailable(); + for (int32_t i = 0; i < count; ++i) { + UnicodeString temp(uloc_getAvailable(i)); + result->put(temp, (void*)result, status); + if (U_FAILURE(status)) { + delete result; + return NULL; + } + } + { + Mutex mutex(&lock); + if (cache == NULL) { + cache = result; + return cache; + } + } + delete result; + } + } + return 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); +} + +Hashtable * LocaleUtility::cache = NULL; + +const UChar LocaleUtility::UNDERSCORE_CHAR = 0x005f; + +UMTX LocaleUtility::lock = 0; + +/* + ****************************************************************** + */ + +const UChar LocaleKey::UNDERSCORE_CHAR = 0x005f; + +const int32_t LocaleKey::KIND_ANY = -1; + +LocaleKey* +LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID) +{ + return LocaleKey::createWithCanonicalFallback(primaryID, canonicalFallbackID, KIND_ANY); +} + +LocaleKey* +LocaleKey::createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind) +{ + if (primaryID == NULL) { + 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) + : Key(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) { + DigitList list; + list.set(_kind); + UnicodeString temp(list.fDigits, list.fCount); + 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); +} + +UnicodeString& +LocaleKey::debug(UnicodeString& result) const +{ + Key::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 "); +} + +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 Key& 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.canonicalLocale(loc); + + return handleCreate(loc, kind, service, status); + } + return NULL; +} + +UBool +LocaleKeyFactory::handlesKey(const Key& 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 { + Locale loc; + LocaleUtility::initLocaleFromName(id, loc); + return loc.getDisplayName(locale, result); +} + +UObject* +LocaleKeyFactory::handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const { + return NULL; +} + +const Hashtable* +LocaleKeyFactory::getSupportedIDs(UErrorCode& status) const { + return NULL; +} + +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"); +} + +const char LocaleKeyFactory::fgClassID = 0; + +/* + ****************************************************************** + */ + +SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt, + const Locale& locale, + int32_t kind, + int32_t coverage) + : LocaleKeyFactory(coverage) + , _obj(objToAdopt) + , _id(locale.getName()) + , _kind(kind) +{ +} + +SimpleLocaleKeyFactory::SimpleLocaleKeyFactory(UObject* objToAdopt, + const Locale& locale, + int32_t kind, + int32_t coverage, + const UnicodeString& name) + : LocaleKeyFactory(coverage, name) + , _obj(objToAdopt) + , _id(locale.getName()) + , _kind(kind) +{ +} + +UObject* +SimpleLocaleKeyFactory::create(const Key& 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; +} + +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); + } + } +} + +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"); +} + +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; +} + +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"); +} + +const char ICUResourceBundleFactory::fgClassID = '\0'; + +/* + ****************************************************************** + */ + +ICULocaleService::ICULocaleService() + : fallbackLocale(Locale::getDefault()) +{ +} + +ICULocaleService::ICULocaleService(const UnicodeString& name) + : ICUService(name) + , fallbackLocale(Locale::getDefault()) +{ +} + +ICULocaleService::~ICULocaleService() +{ +} + +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; + + UnicodeString locName(locale.getName()); + if (locName.isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + Key* key = createKey(&locName, kind); + 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; +} + +const Factory* +ICULocaleService::registerObject(UObject* objToAdopt, const Locale& locale) +{ + return registerObject(objToAdopt, locale, LocaleKey::KIND_ANY, LocaleKeyFactory::VISIBLE); +} + +const Factory* +ICULocaleService::registerObject(UObject* objToAdopt, const Locale& locale, int32_t kind) +{ + return registerObject(objToAdopt, locale, kind, LocaleKeyFactory::VISIBLE); +} + +const Factory* +ICULocaleService::registerObject(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage) +{ + Factory * factory = new SimpleLocaleKeyFactory(objToAdopt, locale, kind, coverage); + if (factory != NULL) { + return registerFactory(factory); + } + delete objToAdopt; + return NULL; +} + +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(UErrorCode& status) { + const UnicodeString* us = snext(status); + if (us) { + int newlen; + for (newlen = us->extract((char*)_bufp, _buflen / sizeof(char), NULL, status); + status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR; + resizeBuffer((newlen + 1) * sizeof(char))) + { + status = U_ZERO_ERROR; + } + + if (U_SUCCESS(status)) { + ((char*)_bufp)[newlen] = 0; + return (const char*)_bufp; + } + } + return NULL; + } + + const UChar* unext(UErrorCode& status) { + const UnicodeString* us = snext(status); + if (us) { + int newlen; + for (newlen = us->extract((UChar*)_bufp, _buflen / sizeof(UChar), status); + status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR; + resizeBuffer((newlen + 1) * sizeof(UChar))) + { + status = U_ZERO_ERROR; + } + + if (U_SUCCESS(status)) { + ((UChar*)_bufp)[newlen] = 0; + return (const UChar*)_bufp; + } + } + 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); + } + } + + 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 (U_SUCCESS(status)) { + _timestamp = _service->getTimestamp(); + _pos = 0; + _service->getVisibleIDs(_ids, status); + } + } +}; + +StringEnumeration* +ICULocaleService::getAvailableLocales(void) const +{ + return ServiceEnumeration::create(this); +} + +const UnicodeString& +ICULocaleService::validateFallbackLocale() const +{ + const Locale& loc = Locale::getDefault(); + if (loc != fallbackLocale) { + ICULocaleService* ncThis = (ICULocaleService*)this; + Mutex mutex(&ncThis->lock); + if (loc != fallbackLocale) { + ncThis->fallbackLocale = loc; + LocaleUtility::initNameFromLocale(loc, ncThis->fallbackLocaleName); + ncThis->clearServiceCache(); + } + } + return fallbackLocaleName; +} + +Key* +ICULocaleService::createKey(const UnicodeString* id) const +{ + return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale()); +} + +Key* +ICULocaleService::createKey(const UnicodeString* id, int32_t kind) const +{ + return LocaleKey::createWithCanonicalFallback(id, &validateFallbackLocale(), kind); +} + +U_NAMESPACE_END + +/* !UCONFIG_NO_SERVICE */ +#endif + + diff --git a/icu4c/source/common/iculserv.h b/icu4c/source/common/iculserv.h new file mode 100644 index 00000000000..fd798d67d59 --- /dev/null +++ b/icu4c/source/common/iculserv.h @@ -0,0 +1,545 @@ +/** + ******************************************************************************* + * Copyright (C) 2001-2002, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + * + ******************************************************************************* + */ +#ifndef ICULSERV_H +#define ICULSERV_H + +#include "unicode/utypes.h" + +#if UCONFIG_NO_SERVICE + +U_NAMESPACE_BEGIN + +/* + * Allow the declaration of APIs with pointers to ICUService + * even when service is removed from the build. + */ +class ICULocaleService; + +U_NAMESPACE_END + +#else + +#include "unicode/uobject.h" +#include "unicode/unistr.h" +#include "unicode/chariter.h" +#include "unicode/locid.h" +#include "unicode/ubrk.h" +#include "unicode/strenum.h" + +#include "hash.h" +#include "uvector.h" +#include "digitlst.h" + +#include "icuserv.h" + +U_NAMESPACE_BEGIN + +class ICULocaleService; + +class LocaleKey; +class LocaleKeyFactory; +class SimpleLocaleKeyFactory; +class ServiceListener; + +/* + ****************************************************************** + */ + +/** + * 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. If a prefix is present, the currentDescriptor + * includes it before the locale proper, separated by "/". This + * is the default key instantiated by ICULocaleService.
+ * + *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.
+ */ + +class U_COMMON_API LocaleKey : public Key { + private: + int32_t _kind; + UnicodeString _primaryID; + UnicodeString _fallbackID; + UnicodeString _currentID; + + static const UChar UNDERSCORE_CHAR; // '_' + + public: + static const int32_t KIND_ANY; // = -1; + + /** + * Create a LocaleKey with canonical primary and fallback IDs. + */ + static LocaleKey* createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID); + + /** + * Create a LocaleKey with canonical primary and fallback IDs. + */ + static LocaleKey* createWithCanonicalFallback(const UnicodeString* primaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind); + + protected: + /** + * PrimaryID is the user's requested locale string, + * canonicalPrimaryID is this string in canonical form, + * fallbackID is the current default locale's string in + * canonical form. + */ + LocaleKey(const UnicodeString& primaryID, + const UnicodeString& canonicalPrimaryID, + const UnicodeString* canonicalFallbackID, + int32_t kind); + + public: + /** + * Append the prefix associated with the kind, or nothing if the kind is KIND_ANY. + */ + virtual UnicodeString& prefix(UnicodeString& result) const; + + /** + * Return the kind code associated with this key. + */ + virtual int32_t kind() const; + + /** + * Return the canonicalID. + */ + virtual UnicodeString& canonicalID(UnicodeString& result) const; + + /** + * Return the currentID. + */ + virtual UnicodeString& currentID(UnicodeString& result) const; + + /** + * Return the (canonical) current descriptor, or null if no current id. + */ + virtual UnicodeString& currentDescriptor(UnicodeString& result) const; + + /** + * Convenience method to return the locale corresponding to the (canonical) original ID. + */ + virtual Locale& canonicalLocale(Locale& result) const; + + /** + * Convenience method to return the locale corresponding to the (canonical) current ID. + */ + virtual Locale& currentLocale(Locale& result) const; + + /** + * If the key has a fallback, modify the key and return true, + * otherwise return false. + * + *First falls back through the primary ID, then through + * the fallbackID. The final fallback is the empty string, + * unless the primary id was the empty string, in which case + * there is no fallback. + */ + virtual UBool fallback(); + + /** + * Return true if a key created from id matches, or would eventually + * fallback to match, the canonical ID of this key. + */ + virtual UBool isFallbackOf(const UnicodeString& id) const; + + public: + /** + * UObject boilerplate. + */ + virtual UClassID getDynamicClassID() const { + return getStaticClassID(); + } + + static UClassID getStaticClassID() { + return (UClassID)&fgClassID; + } + + public: + virtual UnicodeString& debug(UnicodeString& result) const; + virtual UnicodeString& debugClass(UnicodeString& result) const; + + private: + static const char fgClassID; +}; + +/* + ****************************************************************** + */ + +/** + * A subclass of Factory that uses LocaleKeys, and is able to + * 'cover' more specific locales with more general locales that it + * supports. + * + *
Coverage may be either of the values VISIBLE or INVISIBLE. + * + *
'Visible' indicates that the specific locale(s) supported by + * the factory are registered in getSupportedIDs, 'Invisible' + * indicates that they are not. + * + *
Localization of visible ids is handled
+ * by the handling factory, regardless of kind.
+ */
+class U_COMMON_API LocaleKeyFactory : public Factory {
+protected:
+ const UnicodeString _name;
+ const int32_t _coverage;
+
+public:
+ enum {
+ /**
+ * Coverage value indicating that the factory makes
+ * its locales visible, and does not cover more specific
+ * locales.
+ */
+ VISIBLE = 0,
+
+ /**
+ * Coverage value indicating that the factory does not make
+ * its locales visible, and does not cover more specific
+ * locales.
+ */
+ INVISIBLE = 1
+ };
+
+protected:
+ /**
+ * Constructor used by subclasses.
+ */
+ LocaleKeyFactory(int32_t coverage);
+
+ /**
+ * Constructor used by subclasses.
+ */
+ LocaleKeyFactory(int32_t coverage, const UnicodeString& name);
+
+ /**
+ * Destructor.
+ */
+ virtual ~LocaleKeyFactory();
+
+ /**
+ * Implement superclass abstract method. This checks the currentID of
+ * the key against the supported IDs, and passes the canonicalLocale and
+ * kind off to handleCreate (which subclasses must implement).
+ */
+public:
+ virtual UObject* create(const Key& key, const ICUService* service, UErrorCode& status) const;
+
+protected:
+ virtual UBool handlesKey(const Key& key, UErrorCode& status) const;
+
+public:
+ /**
+ * Override of superclass method. This adjusts the result based
+ * on the coverage rule for this factory.
+ */
+ void updateVisibleIDs(Hashtable& result, UErrorCode& status) const;
+
+ /**
+ * Return a localized name for the locale represented by id.
+ */
+ UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const;
+
+protected:
+ /**
+ * Utility method used by create(Key, ICUService). Subclasses can implement
+ * this instead of create. The default returns NULL.
+ */
+ virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const;
+
+ /**
+ * Return the set of ids that this factory supports (visible or
+ * otherwise). This can be called often and might need to be
+ * cached if it is expensive to create.
+ */
+ virtual const Hashtable* getSupportedIDs(UErrorCode& status) const;
+
+ public:
+ /**
+ * UObject boilerplate.
+ */
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ public:
+ virtual UnicodeString& debug(UnicodeString& result) const;
+ virtual UnicodeString& debugClass(UnicodeString& result) const;
+
+ private:
+ static const char fgClassID;
+};
+
+/*
+ ******************************************************************
+ */
+
+/**
+ * A LocaleKeyFactory that just returns a single object for a kind/locale.
+ */
+
+class U_COMMON_API SimpleLocaleKeyFactory : public LocaleKeyFactory {
+ private:
+ UObject* _obj;
+ UnicodeString _id;
+ const int32_t _kind;
+
+ public:
+ SimpleLocaleKeyFactory(UObject* objToAdopt,
+ const Locale& locale,
+ int32_t kind,
+ int32_t coverage);
+
+ SimpleLocaleKeyFactory(UObject* objToAdopt,
+ const Locale& locale,
+ int32_t kind,
+ int32_t coverage,
+ const UnicodeString& name);
+
+ /**
+ * Override of superclass method. Returns the service object if kind/locale match. Service is not used.
+ */
+ UObject* create(const Key& key, const ICUService* service, UErrorCode& status) const;
+
+ /**
+ * Override of superclass method. This adjusts the result based
+ * on the coverage rule for this factory.
+ */
+ void updateVisibleIDs(Hashtable& result, UErrorCode& status) const;
+
+ public:
+ /**
+ * UObject boilerplate.
+ */
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ public:
+ virtual UnicodeString& debug(UnicodeString& result) const;
+ virtual UnicodeString& debugClass(UnicodeString& result) const;
+
+ private:
+ static const char fgClassID;
+};
+
+/*
+ ******************************************************************
+ */
+
+/**
+ * A LocaleKeyFactory that creates a service based on the ICU locale data.
+ * This is a base class for most ICU factories. Subclasses instantiate it
+ * with a constructor that takes a bundle name, which determines the supported
+ * IDs. Subclasses then override handleCreate to create the actual service
+ * object. The default implementation returns a resource bundle.
+ */
+class U_COMMON_API ICUResourceBundleFactory : public LocaleKeyFactory
+{
+ protected:
+ UnicodeString _bundleName;
+
+ public:
+ /**
+ * Convenience constructor that uses the main ICU bundle name.
+ */
+ ICUResourceBundleFactory();
+
+ /**
+ * A service factory based on ICU resource data in resources
+ * with the given name.
+ */
+ ICUResourceBundleFactory(const UnicodeString& bundleName);
+
+protected:
+ /**
+ * Return the supported IDs. This is the set of all locale names in ICULocaleData.
+ */
+ virtual const Hashtable* getSupportedIDs(UErrorCode& status) const;
+
+ /**
+ * Create the service. The default implementation returns the resource bundle
+ * for the locale, ignoring kind, and service.
+ */
+ virtual UObject* handleCreate(const Locale& loc, int32_t kind, const ICUService* service, UErrorCode& status) const;
+
+ public:
+ /**
+ * UObject boilerplate.
+ */
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ public:
+ virtual UnicodeString& debug(UnicodeString& result) const;
+ virtual UnicodeString& debugClass(UnicodeString& result) const;
+
+ private:
+ static const char fgClassID;
+};
+
+/*
+ ******************************************************************
+ */
+
+class U_COMMON_API ICULocaleService : public ICUService
+{
+ private:
+ Locale fallbackLocale;
+ UnicodeString fallbackLocaleName;
+
+ public:
+ /**
+ * Construct an ICULocaleService.
+ */
+ ICULocaleService();
+
+ /**
+ * Construct an ICULocaleService with a name (useful for debugging).
+ */
+ ICULocaleService(const UnicodeString& name);
+
+ /**
+ * Destructor.
+ */
+ ~ICULocaleService();
+
+#if 0
+ // redeclare because of overload resolution rules?
+ UObject* get(const UnicodeString& descriptor, UErrorCode& status) const {
+ return ICUService::get(descriptor, status);
+ }
+
+ UObject* get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const {
+ return ICUService::get(descriptor, actualReturn, status);
+ }
+#endif
+
+ /**
+ * Convenience override for callers using locales. This calls
+ * get(Locale, int, Locale[]) with KIND_ANY for kind and null for
+ * actualReturn.
+ */
+ UObject* get(const Locale& locale, UErrorCode& status) const;
+
+ /**
+ * Convenience override for callers using locales. This calls
+ * get(Locale, int, Locale[]) with a null actualReturn.
+ */
+ UObject* get(const Locale& locale, int32_t kind, UErrorCode& status) const;
+
+ /**
+ * Convenience override for callers using locales. This calls
+ * get(Locale, String, Locale[]) with a null kind.
+ */
+ UObject* get(const Locale& locale, Locale* actualReturn, UErrorCode& status) const;
+
+ /**
+ * Convenience override for callers using locales. This uses
+ * createKey(Locale.toString(), kind) to create a key, calls getKey, and then
+ * if actualReturn is not null, returns the actualResult from
+ * getKey (stripping any prefix) into a Locale.
+ */
+ UObject* get(const Locale& locale, int32_t kind, Locale* actualReturn, UErrorCode& status) const;
+
+ /**
+ * Convenience override for callers using locales. This calls
+ * registerObject(Object, Locale, int32_t kind, int coverage)
+ * passing KIND_ANY for the kind, and VISIBLE for the coverage.
+ */
+ const Factory* registerObject(UObject* objToAdopt, const Locale& locale);
+
+ /**
+ * Convenience function for callers using locales. This calls
+ * registerObject(Object, Locale, int kind, int coverage)
+ * passing VISIBLE for the coverage.
+ */
+ const Factory* registerObject(UObject* objToAdopt, const Locale& locale, int32_t kind);
+
+ /**
+ * Convenience function for callers using locales. This instantiates
+ * a SimpleLocaleKeyFactory, and registers the factory.
+ */
+ virtual const Factory* registerObject(UObject* objToAdopt, const Locale& locale, int32_t kind, int32_t coverage);
+
+ /**
+ * Convenience method for callers using locales. This returns the standard
+ * service ID enumeration.
+ */
+ virtual StringEnumeration* getAvailableLocales(void) const;
+
+ /**
+ * Return the name of the current fallback locale. If it has changed since this was
+ * last accessed, the service cache is cleared.
+ */
+ const UnicodeString& validateFallbackLocale() const;
+
+ /**
+ * Override superclass createKey method.
+ */
+ virtual Key* createKey(const UnicodeString* id) const;
+
+ /**
+ * Additional createKey that takes a kind.
+ */
+ virtual Key* createKey(const UnicodeString* id, int32_t kind) const;
+
+ /*
+ virtual int32_t getTimestamp() const {
+ return ICUService::getTimestamp();
+ }
+ */
+
+ friend class ServiceEnumeration;
+};
+
+// temporary utility functions, till I know where to find them
+// in header so tests can also access them
+
+class U_COMMON_API LocaleUtility {
+ static Hashtable* cache;
+ static UMTX lock;
+ static const UChar UNDERSCORE_CHAR;
+
+public:
+ static UnicodeString& canonicalLocaleString(const UnicodeString* id, UnicodeString& result);
+ static Locale& initLocaleFromName(const UnicodeString& id, Locale& result);
+ static UnicodeString& initNameFromLocale(const Locale& locale, UnicodeString& result);
+ static const Hashtable* getAvailableLocaleNames(const UnicodeString& bundleID);
+ static UBool isFallbackOf(const UnicodeString& root, const UnicodeString& child);
+};
+
+U_NAMESPACE_END
+
+ /* UCONFIG_NO_SERVICE */
+#endif
+
+ /* ICULSERV_H */
+#endif
+
diff --git a/icu4c/source/common/icunotif.cpp b/icu4c/source/common/icunotif.cpp
new file mode 100644
index 00000000000..f3f6e80af61
--- /dev/null
+++ b/icu4c/source/common/icunotif.cpp
@@ -0,0 +1,97 @@
+/**
+ *******************************************************************************
+ * Copyright (C) 2001-2002, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ */
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_SERVICE
+
+#include "icunotif.h"
+#include Abstract implementation of a notification facility. Clients add
+ * EventListeners with addListener and remove them with removeListener.
+ * Notifiers call notifyChanged when they wish to notify listeners.
+ * This queues the listener list on the notification thread, which
+ * eventually dequeues the list and calls notifyListener on each
+ * listener in the list. Subclasses override acceptsListener and notifyListener
+ * to add type-safe notification. AcceptsListener should return
+ * true if the listener is of the appropriate type; ICUNotifier
+ * itself will ensure the listener is non-null and that the
+ * identical listener is not already registered with the Notifier.
+ * NotifyListener should cast the listener to the appropriate
+ * type and call the appropriate method on the listener.
+ */
+
+class U_COMMON_API ICUNotifier : public UMemory {
+ private: UMTX notifyLock;
+ private: UVector* listeners;
+
+ public:
+ ICUNotifier(void)
+ : notifyLock(0), listeners(NULL)
+ {
+ }
+
+ virtual ~ICUNotifier(void) {
+ Mutex lmx(¬ifyLock);
+ delete listeners;
+ listeners = NULL;
+ }
+
+ /**
+ * Add a listener to be notified when notifyChanged is called.
+ * The listener must not be null. AcceptsListener must return
+ * true for the listener. Attempts to concurrently
+ * register the identical listener more than once will be
+ * silently ignored.
+ */
+ virtual void addListener(const EventListener* l, UErrorCode& status);
+
+ /**
+ * Stop notifying this listener. The listener must
+ * not be null. Attemps to remove a listener that is
+ * not registered will be silently ignored.
+ */
+ virtual void removeListener(const EventListener* l, UErrorCode& status);
+
+ /**
+ * ICU doesn't spawn its own threads. All listeners are notified in
+ * the thread of the caller. Misbehaved listeners can therefore
+ * indefinitely block the calling thread. Callers should beware of
+ * deadlock situations.
+ */
+ virtual void notifyChanged(void);
+
+ protected:
+ /**
+ * Subclasses implement this to return TRUE if the listener is
+ * of the appropriate type.
+ */
+ virtual UBool acceptsListener(const EventListener& l) const = 0;
+
+ /**
+ * Subclasses implement this to notify the listener.
+ */
+ virtual void notifyListener(EventListener& l) const = 0;
+};
+
+U_NAMESPACE_END
+
+/* UCONFIG_NO_SERVICE */
+#endif
+
+/* ICUNOTIF_H */
+#endif
diff --git a/icu4c/source/common/icuserv.cpp b/icu4c/source/common/icuserv.cpp
new file mode 100644
index 00000000000..01f534357f1
--- /dev/null
+++ b/icu4c/source/common/icuserv.cpp
@@ -0,0 +1,1170 @@
+ /**
+ *******************************************************************************
+ * Copyright (C) 2001-2002, International Business Machines Corporation. *
+ * All Rights Reserved. *
+ *******************************************************************************
+ */
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_SERVICE
+
+#include "icuserv.h"
+
+#undef SERVICE_REFCOUNT
+
+// in case we use the refcount stuff
+
+U_NAMESPACE_BEGIN
+
+// A reference counted wrapper for an object. Creation and access is
+// through RefHandle.
+
+#ifdef SERVICE_REFCOUNT
+
+#include "unicode/strenum.h"
+
+/*
+ ******************************************************************
+ */
+
+class RefCounted {
+private:
+ int32_t _count;
+ UObject* _obj;
+
+ friend class RefHandle;
+
+ RefCounted(UObject* objectToAdopt) : _count(1), _obj(objectToAdopt) {}
+ ~RefCounted() { delete _obj; }
+ void ref() { umtx_atomic_inc(&_count); }
+ void unref() { if (umtx_atomic_dec(&_count) == 0) { delete this; }}
+};
+
+/*
+ ******************************************************************
+ */
+
+// Reference counted handle for an object
+class RefHandle {
+ RefCounted* _ref;
+
+public:
+ RefHandle() : _ref(NULL) {}
+ RefHandle(UObject* obj) : _ref(new RefCounted(obj)) {}
+ RefHandle(const RefHandle& rhs) : _ref(NULL) { operator=(rhs); }
+ ~RefHandle() { if (_ref) _ref->unref(); }
+ RefHandle& operator=(const RefHandle& rhs) {
+ if (rhs._ref) rhs._ref->ref();
+ if (_ref) _ref->unref();
+ _ref = rhs._ref;
+ }
+ const UObject* get() const { return _ref ? _ref->_obj : NULL; }
+};
+
+/*
+ ******************************************************************
+ */
+
+// Generic enumeration class with fail-fast behavior.
+
+class MapEnumeration : public UObject, public StringEnumeration
+{
+ private:
+ UChar* _buffer;
+ int _buflen;
+
+ protected:
+ const ICUService* _service;
+ uint32_t _timestamp;
+ RefHandle _table;
+ Key* _filter;
+ int32_t _position;
+ int32_t _count;
+
+ protected:
+ MapEnumeration(ICUService* service, int32_t timestamp, RefHandle& table, Key* filter = NULL)
+ : _buffer(NULL)
+ , _buflen(0)
+ , _service(service)
+ , _timestamp(timestamp)
+ , _table(table)
+ , _filter(filter)
+ , _position(0)
+ , _count(((const Hashtable*)table.get())->count())
+ {
+ }
+
+ virtual ~MapEnumeration()
+ {
+ delete _filter;
+ }
+
+ int32_t count(UErrorCode& status) const
+ {
+ return U_SUCCESS(status) ? _count : 0;
+ }
+
+ const char* next(UErrorCode& status) {
+ const UnicodeString* us = snext(status);
+ if (us) {
+ int newlen;
+ for (newlen = us->extract((char*)_buffer, _buflen / sizeof(char), NULL, status);
+ status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR;
+ resizeBuffer((newlen + 1) * sizeof(char)));
+
+ if (U_SUCCESS(status)) {
+ ((char*)_buffer)[newlen] = 0;
+ return (const char*)_buffer;
+ }
+ }
+ return NULL;
+ }
+
+ const UChar* unext(UErrorCode& status) {
+ const UnicodeString* us = snext(status);
+ if (us) {
+ int newlen;
+ for (newlen = us->extract((UChar*)_buffer, _buflen / sizeof(UChar), NULL, status);
+ status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR;
+ resizeBuffer((newlen + 1) * sizeof(UChar)));
+
+ if (U_SUCCESS(status)) {
+ ((UChar*)_buffer)[newlen] = 0;
+ return (const UChar*)_buffer;
+ }
+ }
+ return NULL;
+ }
+
+ const UnicodeString* snext(UErrorCode& status)
+ {
+ if (U_SUCCESS(status)) {
+ if (_timestamp != _service->_timestamp) {
+ status = U_ENUM_OUT_OF_SYNCH_ERROR;
+ } else {
+ return internalNext((Hashtable*)_table.get());
+ }
+ }
+ return NULL;
+ }
+
+ void reset(UErrorCode& status)
+ {
+ if (U_SUCCESS(status)) {
+ service->reset(this);
+ }
+ }
+
+ protected:
+ virtual const UnicodeString* internalNext(Hashtable* table) = 0;
+
+ private:
+ void reset(RefHandle& table, int32_t timestamp)
+ {
+ _table = table;
+ _timestamp = timestamp;
+ _position = 0;
+ _count = ((const Hashtable*)table.get())->count();
+ }
+
+ friend class ICUService;
+};
+
+/*
+ ******************************************************************
+ */
+
+// An enumeration over the visible ids in a service. The ids
+// are in the hashtable, which is refcounted, so it will not
+// disappear as long as the enumeration exists even if the
+// service itself unrefs it. For "fail-fast" behavior the
+// enumeration checks the timestamp of the service, but this
+// is not a guarantee that the result the client receives will
+// still be valid once the function returns.
+
+class IDEnumeration : public MapEnumeration {
+public:
+ IDEnumeration(ICUService* service, int32_t timestamp, RefHandle& table, Key* filter = NULL)
+ : MapEnumeration(service, timestamp, table, filter)
+ {
+ }
+
+protected:
+ const UnicodeString* internalNext(Hashtable* table) {
+ while (TRUE) {
+ const UnicodeString* elem = (const UnicodeString*)(table->nextElement(_position).key.pointer);
+ if (elem == NULL ||
+ _filter == NULL ||
+ _filter->isFallbackOf(*elem)) {
+ return elem;
+ }
+ }
+ return NULL;
+ }
+};
+
+/*
+ ******************************************************************
+ */
+
+class DisplayEnumeration : public MapEnumeration {
+private:
+ Locale _locale;
+ UnicodeString _cache;
+
+public:
+ DisplayEnumeration(ICUService* service, int32_t timestamp, RefHandle& table, Locale& locale, Key* filter = NULL)
+ : MapEnumeration(service, timestamp, table, filter), _locale(locale)
+ {
+ }
+
+protected:
+ const UnicodeString* internalNext(Hashtable* table) {
+ while (TRUE) {
+ UHashElement* elem = table->nextElement(_position);
+ if (elem == NULL) {
+ return NULL;
+ }
+ const UnicodeString* id = (const UnicodeString*)elem->key.pointer;
+ const Factory* factory = (const Factory*)elem->value.pointer;
+ if (_filter == NULL || _filter->isFallbackOf(*id)) {
+ factory->getDisplayName(*id, cache, locale);
+ return &cache;
+ }
+ }
+ return NULL;
+ }
+};
+
+/* SERVICE_REFCOUNT */
+#endif
+
+/*
+ ******************************************************************
+ */
+
+const UChar Key::PREFIX_DELIMITER = '/';
+
+Key::Key(const UnicodeString& id)
+ : _id(id) {
+}
+
+Key::~Key()
+{
+}
+
+const UnicodeString&
+Key::getID() const
+{
+ return _id;
+}
+
+UnicodeString&
+Key::canonicalID(UnicodeString& result) const
+{
+ return result.append(_id);
+}
+
+UnicodeString&
+Key::currentID(UnicodeString& result) const
+{
+ return canonicalID(result);
+}
+
+UnicodeString&
+Key::currentDescriptor(UnicodeString& result) const
+{
+ prefix(result);
+ result.append(PREFIX_DELIMITER);
+ return currentID(result);
+}
+
+UBool
+Key::fallback()
+{
+ return FALSE;
+}
+
+UBool
+Key::isFallbackOf(const UnicodeString& id) const
+{
+ return id == _id;
+}
+
+UnicodeString&
+Key::prefix(UnicodeString& result) const
+{
+ return result;
+}
+
+UnicodeString&
+Key::parsePrefix(UnicodeString& result)
+{
+ int32_t n = result.indexOf(PREFIX_DELIMITER);
+ if (n < 0) {
+ n = 0;
+ }
+ result.remove(n);
+ return result;
+}
+
+UnicodeString&
+Key::parseSuffix(UnicodeString& result)
+{
+ int32_t n = result.indexOf(PREFIX_DELIMITER);
+ if (n >= 0) {
+ result.remove(0, n+1);
+ }
+ return result;
+}
+
+UnicodeString&
+Key::debug(UnicodeString& result) const
+{
+ debugClass(result);
+ result.append(" id: ");
+ result.append(_id);
+ return result;
+}
+
+UnicodeString&
+Key::debugClass(UnicodeString& result) const
+{
+ return result.append("Key");
+}
+
+const char Key::fgClassID = '\0';
+
+/*
+ ******************************************************************
+ */
+
+SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id)
+ : _instance(instanceToAdopt), _id(id), _visible(TRUE)
+{
+}
+
+SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
+ : _instance(instanceToAdopt), _id(id), _visible(visible)
+{
+}
+
+SimpleFactory::~SimpleFactory()
+{
+ delete _instance;
+}
+
+UObject*
+SimpleFactory::create(const Key& key, const ICUService* service, UErrorCode& status) const
+{
+ if (U_SUCCESS(status)) {
+ UnicodeString temp;
+ if (_id == key.currentID(temp)) {
+ return service->cloneInstance(_instance);
+ }
+ }
+ return NULL;
+}
+
+void
+SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const
+{
+ if (_visible) {
+ result.put(_id, (void*)this, status); // cast away const
+ } else {
+ result.remove(_id);
+ }
+}
+
+UnicodeString&
+SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const
+{
+ if (_visible && _id == id) {
+ result = _id;
+ } else {
+ result.setToBogus();
+ }
+ return result;
+}
+
+UnicodeString&
+SimpleFactory::debug(UnicodeString& toAppendTo) const
+{
+ debugClass(toAppendTo);
+ toAppendTo.append(" id: ");
+ toAppendTo.append(_id);
+ toAppendTo.append(", visible: ");
+ toAppendTo.append(_visible ? "T" : "F");
+ return toAppendTo;
+}
+
+UnicodeString&
+SimpleFactory::debugClass(UnicodeString& toAppendTo) const
+{
+ return toAppendTo.append("SimpleFactory");
+}
+
+const char SimpleFactory::fgClassID = '\0';
+
+/*
+ ******************************************************************
+ */
+
+const char ServiceListener::fgClassID = '\0';
+
+/*
+ ******************************************************************
+ */
+
+ // Record the actual id for this service in the cache, so we can return it
+ // even if we succeed later with a different id.
+class CacheEntry {
+private:
+ int32_t refcount;
+
+public:
+ UnicodeString actualDescriptor;
+ UObject* service;
+
+ /**
+ * Releases a reference to the shared resource.
+ */
+ ~CacheEntry() {
+ delete service;
+ }
+
+ CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service)
+ : refcount(1), actualDescriptor(_actualDescriptor), service(_service) {
+ }
+
+ /**
+ * Instantiation creates an initial reference, so don't call this
+ * unless you're creating a new pointer to this. Management of
+ * that pointer will have to know how to deal with refcounts.
+ * Return true if the resource has not already been released.
+ */
+ CacheEntry* ref() {
+ ++refcount;
+ return this;
+ }
+
+ /**
+ * Destructions removes a reference, so don't call this unless
+ * you're removing pointer to this somewhere. Management of that
+ * pointer will have to know how to deal with refcounts. Once
+ * the refcount drops to zero, the resource is released. Return
+ * false if the resouce has been released.
+ */
+ CacheEntry* unref() {
+ if ((--refcount) == 0) {
+ delete this;
+ return NULL;
+ }
+ return this;
+ }
+
+ /**
+ * Return TRUE if there is at least one reference to this and the
+ * resource has not been released.
+ */
+ UBool isShared() const {
+ return refcount > 1;
+ }
+};
+
+// UObjectDeleter for serviceCache
+
+U_CAPI void U_EXPORT2
+cacheDeleter(void* obj) {
+ U_NAMESPACE_USE
+ ((CacheEntry*)obj)->unref();
+}
+
+/**
+ * Deleter for UObjects
+ */
+U_CAPI void U_EXPORT2
+deleteUObject(void *obj) {
+ U_NAMESPACE_USE
+ delete (UObject*) obj;
+}
+
+/*
+ ******************************************************************
+ */
+
+class DNCache {
+public:
+ Hashtable cache;
+ const Locale locale;
+
+ DNCache(const Locale& _locale)
+ : cache(FALSE), locale(_locale)
+ {
+ // cache.setKeyDeleter(uhash_deleteUnicodeString);
+ }
+};
+
+
+/*
+ ******************************************************************
+ */
+
+StringPair*
+StringPair::create(const UnicodeString& displayName,
+ const UnicodeString& id,
+ UErrorCode& status)
+{
+ if (U_SUCCESS(status)) {
+ StringPair* sp = new StringPair(displayName, id);
+ if (sp == NULL || sp->isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete sp;
+ return NULL;
+ }
+ return sp;
+ }
+ return NULL;
+}
+
+UBool
+StringPair::isBogus() const {
+ return displayName.isBogus() || id.isBogus();
+}
+
+StringPair::StringPair(const UnicodeString& _displayName,
+ const UnicodeString& _id)
+ : displayName(_displayName)
+ , id(_id)
+{
+}
+
+U_CAPI void U_EXPORT2
+deleteStringPair(void *obj) {
+ U_NAMESPACE_USE
+ delete (StringPair*) obj;
+}
+
+/*
+ ******************************************************************
+ */
+
+ICUService::ICUService()
+ : name()
+ , lock(0)
+ , timestamp(0)
+ , factories(NULL)
+ , serviceCache(NULL)
+ , idCache(NULL)
+ , dnCache(NULL)
+{
+}
+
+ICUService::ICUService(const UnicodeString& name)
+ : name(name)
+ , lock(0)
+ , timestamp(0)
+ , factories(NULL)
+ , serviceCache(NULL)
+ , idCache(NULL)
+ , dnCache(NULL) {
+}
+
+ICUService::~ICUService()
+ {
+ Mutex mutex(&lock);
+ clearCaches();
+ delete factories;
+ factories = NULL;
+}
+
+UObject*
+ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
+{
+ return get(descriptor, NULL, status);
+}
+
+UObject*
+ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
+{
+ UObject* result = NULL;
+ if (U_SUCCESS(status)) {
+ Key* key = createKey(&descriptor);
+ if (key) {
+ result = getKey(*key, actualReturn, NULL, status);
+ delete key;
+ }
+ }
+ return result;
+}
+
+UObject*
+ICUService::getKey(Key& key, UErrorCode& status) const
+{
+ return getKey(key, NULL, NULL, status);
+}
+
+UObject*
+ICUService::getKey(Key& key, UnicodeString* actualReturn, UErrorCode& status) const
+{
+ return getKey(key, actualReturn, NULL, status);
+}
+
+UObject*
+ICUService::getKey(Key& key, UnicodeString* actualReturn, const Factory* factory, UErrorCode& status) const
+{
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+
+ if (isDefault()) {
+ return handleDefault(key, actualReturn, status);
+ }
+
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, "Service: " + name + " key: " + key);
+#endif
+
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+
+ CacheEntry* result = NULL;
+ {
+ // The factory list can't be modified until we're done,
+ // otherwise we might update the cache with an invalid result.
+ // The cache has to stay in synch with the factory list.
+ // ICU doesn't have monitors so we can't use rw locks, so
+ // we single-thread everything using this service, for now.
+
+ Mutex mutex(&ncthis->lock);
+
+ if (serviceCache == NULL) {
+ ncthis->serviceCache = new Hashtable(FALSE, status);
+ if (U_FAILURE(status)) {
+ delete serviceCache;
+ return NULL;
+ }
+ serviceCache->setValueDeleter(cacheDeleter);
+ }
+
+ UnicodeString currentDescriptor;
+ UVector* cacheDescriptorList = NULL;
+ UBool putInCache = FALSE;
+
+#ifdef DEBUG_SERVICE
+ int32_t NDebug = 0;
+#endif
+
+ int32_t startIndex = 0;
+ int32_t limit = factories->size();
+ UBool cacheResult = TRUE;
+
+ if (factory != NULL) {
+ for (int32_t i = 0; i < limit; ++i) {
+ if (factory == (const Factory*)factories->elementAt(i)) {
+ startIndex = i + 1;
+ break;
+ }
+ }
+ if (startIndex == 0) {
+ // throw new InternalError("Factory " + factory + "not registered with service: " + this);
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ return NULL;
+ }
+ cacheResult = FALSE;
+ }
+
+ do {
+ currentDescriptor.remove();
+ key.currentDescriptor(currentDescriptor);
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, name + "[" + NDebug++ + "] looking for: " + currentDescriptor);
+#endif
+ result = (CacheEntry*)serviceCache->get(currentDescriptor);
+ if (result != NULL) {
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, name + " found with descriptor: " + currentDescriptor);
+#endif
+ break;
+ } else {
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, "did not find: " + currentDescriptor + " in cache");
+#endif
+ }
+
+ // first test of cache failed, so we'll have to update
+ // the cache if we eventually succeed-- that is, if we're
+ // going to update the cache at all.
+ putInCache = cacheResult;
+
+ int32_t n = 0;
+ int32_t index = startIndex;
+ while (index < limit) {
+ Factory* f = (Factory*)factories->elementAt(index++);
+#ifdef DEBUG_SERVICE
+ fprintf("trying factory[" + (index-1) + "] " + f.toString());
+#endif
+ UObject* service = f->create(key, this, status);
+ if (U_FAILURE(status)) {
+ delete cacheDescriptorList;
+ return NULL;
+ }
+ if (service != NULL) {
+ result = new CacheEntry(currentDescriptor, service);
+ if (result == NULL) {
+ delete service;
+ delete cacheDescriptorList;
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, name + " factory supported: " + currentDescriptor + ", caching");
+#endif
+ goto outerEnd;
+ } else {
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, "factory did not support: " + currentDescriptor);
+#endif
+ }
+ }
+
+ // prepare to load the cache with all additional ids that
+ // will resolve to result, assuming we'll succeed. We
+ // 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 (cacheDescriptorList == NULL) {
+ cacheDescriptorList = new UVector(uhash_deleteUnicodeString, NULL, 5, status);
+ if (U_FAILURE(status)) {
+ return NULL;
+ }
+ }
+ UnicodeString* idToCache = new UnicodeString(currentDescriptor);
+ if (idToCache == NULL || idToCache->isBogus()) {
+ delete cacheDescriptorList;
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+
+ cacheDescriptorList->addElement(idToCache, status);
+ if (U_FAILURE(status)) {
+ delete cacheDescriptorList;
+ return NULL;
+ }
+ } while (key.fallback());
+ outerEnd:
+
+ if (result != NULL) {
+ if (putInCache) {
+ serviceCache->put(result->actualDescriptor, result, status);
+ if (U_FAILURE(status)) {
+ delete result;
+ delete cacheDescriptorList;
+ return NULL;
+ }
+
+ if (cacheDescriptorList != NULL) {
+ for (int32_t i = cacheDescriptorList->size(); --i >= 0;) {
+ UnicodeString* desc = (UnicodeString*)cacheDescriptorList->elementAt(i);
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, name + " adding descriptor: '" + desc + "' for actual: '" + result.actualDescriptor + "'");
+#endif
+ serviceCache->put(*desc, result, status);
+ if (U_FAILURE(status)) {
+ delete result;
+ delete cacheDescriptorList;
+ return NULL;
+ }
+
+ result->ref();
+ cacheDescriptorList->removeElementAt(i);
+ }
+
+ delete cacheDescriptorList;
+ cacheDescriptorList = NULL;
+ }
+ }
+
+ if (actualReturn != NULL) {
+ // strip null prefix
+ if (result->actualDescriptor.indexOf("/") == 0) {
+ actualReturn->remove();
+ actualReturn->append(result->actualDescriptor,
+ 1,
+ result->actualDescriptor.length() - 1);
+ } else {
+ *actualReturn = result->actualDescriptor;
+ }
+
+ if (actualReturn->isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ }
+
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, "found in service: " + name);
+#endif
+ return cloneInstance(result->service);
+ }
+ }
+
+#ifdef DEBUG_SERVICE
+ fprintf(stderr, "not found in service: " + name);
+#endif
+
+ return handleDefault(key, actualReturn, status);
+}
+
+UObject*
+ICUService::handleDefault(const Key& key, UnicodeString* actualIDReturn, UErrorCode& status) const
+{
+ return NULL;
+}
+
+
+UVector&
+ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
+ return getVisibleIDs(result, NULL, status);
+}
+
+UVector&
+ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
+{
+ result.removeAllElements();
+
+ if (U_FAILURE(status)) {
+ return result;
+ }
+
+ ICUService * ncthis = (ICUService*)this; // cast away semantic const
+ {
+ Mutex mutex(&ncthis->lock);
+ const Hashtable* map = getVisibleIDMap(status);
+ if (map != NULL) {
+ Key* fallbackKey = createKey(matchID);
+
+ for (int32_t pos = 0;;) {
+ const UHashElement* e = map->nextElement(pos);
+ if (e == NULL) {
+ break;
+ }
+
+ const UnicodeString* id = (const UnicodeString*)e->key.pointer;
+ if (fallbackKey != NULL) {
+ if (!fallbackKey->isFallbackOf(*id)) {
+ continue;
+ }
+ }
+
+ UnicodeString* idClone = new UnicodeString(*id);
+ if (idClone == NULL || idClone->isBogus()) {
+ delete idClone;
+ status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+ result.addElement(idClone, status);
+ if (U_FAILURE(status)) {
+ delete idClone;
+ break;
+ }
+ }
+ delete fallbackKey;
+ }
+ }
+ if (U_FAILURE(status)) {
+ result.removeAllElements();
+ }
+ return result;
+}
+
+const Hashtable*
+ICUService::getVisibleIDMap(UErrorCode& status) const {
+ if (U_SUCCESS(status) && idCache == NULL) {
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+ ncthis->idCache = new Hashtable();
+ if (idCache == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else if (factories != NULL) {
+ for (int32_t pos = factories->size(); --pos >= 0;) {
+ Factory* f = (Factory*)factories->elementAt(pos);
+ f->updateVisibleIDs(*idCache, status);
+ }
+ if (U_FAILURE(status)) {
+ delete idCache;
+ ncthis->idCache = NULL;
+ }
+ }
+ }
+
+ return idCache;
+}
+
+
+UnicodeString&
+ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
+{
+ return getDisplayName(id, result, Locale::getDefault());
+}
+
+UnicodeString&
+ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const
+{
+ result.setToBogus();
+ {
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+ UErrorCode status = U_ZERO_ERROR;
+ Mutex mutex(&ncthis->lock);
+ const Hashtable* map = getVisibleIDMap(status);
+ if (map != NULL) {
+ Factory* f = (Factory*)map->get(id);
+ if (f != NULL) {
+ f->getDisplayName(id, locale, result);
+ }
+ }
+ }
+ return result;
+}
+
+UVector&
+ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
+{
+ return getDisplayNames(result, Locale::getDefault(), NULL, status);
+}
+
+UVector&
+ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
+{
+ return getDisplayNames(result, locale, NULL, status);
+}
+
+UVector&
+ICUService::getDisplayNames(UVector& result,
+ const Locale& locale,
+ const UnicodeString* matchID,
+ UErrorCode& status) const
+{
+ result.removeAllElements();
+ if (U_SUCCESS(status)) {
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+ Mutex mutex(&ncthis->lock);
+
+ if (dnCache != NULL && dnCache->locale != locale) {
+ delete dnCache;
+ ncthis->dnCache = NULL;
+ }
+
+ if (dnCache == NULL) {
+ const Hashtable* m = getVisibleIDMap(status);
+ if (m != NULL) {
+ ncthis->dnCache = new DNCache(locale);
+ if (dnCache == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return result;
+ }
+
+ int32_t pos = 0;
+ const UHashElement* entry = NULL;
+ while (entry = m->nextElement(pos)) {
+ const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
+ Factory* f = (Factory*)entry->value.pointer;
+ UnicodeString name;
+ f->getDisplayName(*id, locale, name);
+ if (name.isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ dnCache->cache.put(name, (void*)id, status); // share pointer with visibleIDMap
+ if (U_SUCCESS(status)) {
+ continue;
+ }
+ }
+ delete dnCache;
+ ncthis->dnCache = NULL;
+ return result;
+ }
+ }
+ }
+ }
+
+ Key* matchKey = createKey(matchID);
+ int32_t pos = 0;
+ const UHashElement *entry = NULL;
+ while (entry = dnCache->cache.nextElement(pos)) {
+ const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
+ if (matchKey != NULL && !matchKey->isFallbackOf(*id)) {
+ continue;
+ }
+ const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
+ StringPair* sp = StringPair::create(*id, *dn, status);
+ result.addElement(sp, status);
+ if (U_FAILURE(status)) {
+ result.removeAllElements();
+ break;
+ }
+ }
+ delete matchKey;
+
+ return result;
+}
+
+const Factory*
+ICUService::registerObject(UObject* objToAdopt, const UnicodeString& id)
+{
+ return registerObject(objToAdopt, id, TRUE);
+}
+
+const Factory*
+ICUService::registerObject(UObject* objToAdopt, const UnicodeString& id, UBool visible)
+{
+ Key* key = createKey(&id);
+ if (key != NULL) {
+ UnicodeString canonicalID;
+ key->canonicalID(canonicalID);
+ delete key;
+
+ Factory* f = createSimpleFactory(objToAdopt, canonicalID, visible);
+ if (f != NULL) {
+ return registerFactory(f);
+ }
+ }
+ delete objToAdopt;
+ return NULL;
+}
+
+Factory*
+ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible)
+{
+ return new SimpleFactory(objToAdopt, id, visible);
+}
+
+const Factory*
+ICUService::registerFactory(Factory* factoryToAdopt)
+{
+ if (factoryToAdopt != NULL) {
+ Mutex mutex(&lock);
+
+ UErrorCode status = U_ZERO_ERROR;
+ if (factories == NULL) {
+ factories = new UVector(deleteUObject, NULL, status);
+ if (U_FAILURE(status)) {
+ delete factories;
+ return NULL;
+ }
+ }
+ factories->insertElementAt(factoryToAdopt, 0, status);
+ if (U_SUCCESS(status)) {
+ clearCaches();
+ } else {
+ delete factoryToAdopt;
+ factoryToAdopt = NULL;
+ }
+ }
+
+ if (factoryToAdopt != NULL) {
+ notifyChanged();
+ }
+
+ return factoryToAdopt;
+}
+
+UBool
+ICUService::unregisterFactory(Factory* factory)
+{
+ UBool result = FALSE;
+ if (factory != NULL && factories != NULL) {
+ Mutex mutex(&lock);
+
+ if (factories->removeElement(factory)) {
+ clearCaches();
+ result = TRUE;
+ }
+ }
+ if (result) {
+ notifyChanged();
+ }
+ return result;
+}
+
+void
+ICUService::reset()
+{
+ {
+ Mutex mutex(&lock);
+ reInitializeFactories();
+ clearCaches();
+ }
+ notifyChanged();
+}
+
+void
+ICUService::reInitializeFactories()
+{
+ if (factories != NULL) {
+ factories->removeAllElements();
+ }
+}
+
+UBool
+ICUService::isDefault() const
+{
+ return factories == NULL || factories->size() == 0;
+}
+
+Key*
+ICUService::createKey(const UnicodeString* id) const
+{
+ return id == NULL ? NULL : new Key(*id);
+}
+
+void
+ICUService::clearCaches()
+{
+ // callers synchronize before use
+ ++timestamp;
+ delete dnCache; dnCache = NULL;
+ delete idCache; idCache = NULL;
+ delete serviceCache; serviceCache = NULL;
+}
+
+void
+ICUService::clearServiceCache()
+{
+ // callers synchronize before use
+ delete serviceCache; serviceCache = NULL;
+}
+
+UBool
+ICUService::acceptsListener(const EventListener& l) const
+{
+ return l.getDynamicClassID() == ServiceListener::getStaticClassID();
+}
+
+void
+ICUService::notifyListener(EventListener& l) const
+{
+ ((ServiceListener&)l).serviceChanged(*this);
+}
+
+UnicodeString&
+ICUService::getName(UnicodeString& result) const
+{
+ return result.append(name);
+}
+
+int32_t
+ICUService::countFactories() const
+{
+ return factories == NULL ? 0 : factories->size();
+}
+
+int32_t
+ICUService::getTimestamp() const
+{
+ return timestamp;
+}
+
+U_NAMESPACE_END
+
+/* UCONFIG_NO_SERVICE */
+#endif
diff --git a/icu4c/source/common/icuserv.h b/icu4c/source/common/icuserv.h
new file mode 100644
index 00000000000..daa8ea724e7
--- /dev/null
+++ b/icu4c/source/common/icuserv.h
@@ -0,0 +1,719 @@
+/**
+ *******************************************************************************
+ * Copyright (C) 2001-2002, International Business Machines Corporation. *
+ * All Rights Reserved. *
+ *******************************************************************************
+ */
+
+#ifndef ICUSERV_H
+#define ICUSERV_H
+
+#include "unicode/utypes.h"
+
+#if UCONFIG_NO_SERVICE
+
+U_NAMESPACE_BEGIN
+
+/*
+ * Allow the declaration of APIs with pointers to ICUService
+ * even when service is removed from the build.
+ */
+class ICUService;
+
+U_NAMESPACE_END
+
+#else
+
+#include "unicode/uobject.h"
+#include "unicode/unistr.h"
+#include "unicode/chariter.h"
+#include "unicode/locid.h"
+#include "unicode/ubrk.h"
+
+#include "hash.h"
+#include "uvector.h"
+#include "icunotif.h"
+
+U_NAMESPACE_BEGIN
+
+class Key;
+class Factory;
+class SimpleFactory;
+class ServiceListener;
+class ICUServiceEnumeration;
+class ICUService;
+class ICUServiceTest;
+
+class DNCache;
+
+/*
+ ******************************************************************
+ */
+
+ /**
+ * Keys are used to communicate with factories to generate an
+ * instance of the service. Keys 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. Keys provide both a currentDescriptor and a currentID.
+ * The descriptor contains an optional prefix, followed by '/'
+ * and the currentID. Factories that handle complex keys,
+ * for example number format factories that generate multiple
+ * kinds of formatters for the same locale, use the descriptor
+ * to provide a fully unique identifier for the service object,
+ * while using the currentID (in this case, the locale string),
+ * as the visible IDs that can be localized.
+ *
+ * The default implementation of Key has no fallbacks and
+ * has no custom descriptors. The current descriptor is used to fully
+ * identify an instance of the service in the cache. A
+ * factory may handle all descriptors for an ID, or just a
+ * particular descriptor. The factory can either parse the
+ * descriptor or use custom API on the key in order to
+ * instantiate the service.
+ */
+ virtual UnicodeString& currentDescriptor(UnicodeString& result) const;
+
+ /**
+ * If the key has a fallback, modify the key and return true,
+ * otherwise return false. The current ID will change if there
+ * is a fallback. No currentIDs should be repeated, and fallback
+ * must eventually return false. This implmentation has no fallbacks
+ * and always returns false.
+ */
+ virtual UBool fallback();
+
+ /**
+ * Return true if a key created from id matches, or would eventually
+ * fallback to match, the canonical ID of this key.
+ */
+ virtual UBool isFallbackOf(const UnicodeString& id) const;
+
+ /**
+ * Append the prefix to result
+ */
+ virtual UnicodeString& prefix(UnicodeString& result) const;
+
+ /**
+ * Parse the prefix out of a descriptor string.
+ */
+ static UnicodeString& parsePrefix(UnicodeString& result);
+
+ /**
+ * Parse the suffix out of a descriptor string.
+ */
+ static UnicodeString& parseSuffix(UnicodeString& result);
+
+ public:
+ /**
+ * UObject boilerplate.
+ */
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ public:
+ virtual UnicodeString& debug(UnicodeString& result) const;
+ virtual UnicodeString& debugClass(UnicodeString& result) const;
+
+ private:
+ static const char fgClassID;
+};
+
+/*
+ ******************************************************************
+ */
+
+ /**
+ * Factories generate the service objects maintained by the
+ * service. A factory generates a service object from a key,
+ * updates id->factory mappings, and returns the display name for
+ * a supported id.
+ */
+class U_COMMON_API Factory : public UObject {
+ public:
+
+ /**
+ * Create a service object from the key, if this factory
+ * supports the key. Otherwise, return null.
+ *
+ * If the factory supports the key, then it can call
+ * defaultCreate(const Key& key, const ICUService*)
+ * the service's getKey(Key, String[], Factory) method
+ * passing itself as the factory to get the object that
+ * the service would have created prior to the factory's
+ * registration with the service. This can change the
+ * key, so any information required from the key should
+ * be extracted before making such a callback.
+ */
+ virtual UObject* create(const Key& key, const ICUService* service, UErrorCode& status) const = 0;
+
+ /**
+ * Update the result IDs (not descriptors) to reflect the IDs
+ * this factory handles. This function and getDisplayName are
+ * used to support ICUService.getDisplayNames. Basically, the
+ * factory has to determine which IDs it will permit to be
+ * available, and of those, which it will provide localized
+ * display names for. In most cases this reflects the IDs that
+ * the factory directly supports.
+ */
+ virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const = 0;
+
+ /**
+ * Return the display name for this id in the provided locale.
+ * This is an localized id, not a descriptor. If the id is
+ * not visible or not defined by the factory, return null.
+ * If locale is null, return id unchanged.
+ */
+ virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const = 0;
+};
+
+/*
+ ******************************************************************
+ */
+
+ /**
+ * A default implementation of factory. This provides default
+ * implementations for subclasses, and implements a singleton
+ * factory that matches a single id and returns a single
+ * (possibly deferred-initialized) instance. This implements
+ * updateVisibleIDs to add a mapping from its ID to itself
+ * if visible is true, or to remove any existing mapping
+ * for its ID if visible is false.
+ */
+class U_COMMON_API SimpleFactory : public Factory {
+ protected:
+ UObject* _instance;
+ const UnicodeString _id;
+ const UBool _visible;
+
+ public:
+ /**
+ * Convenience constructor that calls SimpleFactory(Object, String, boolean)
+ * with visible true.
+ */
+ SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id);
+
+ /**
+ * Construct a simple factory that maps a single id to a single
+ * service instance. If visible is true, the id will be visible.
+ * Neither the instance nor the id can be null.
+ */
+ SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible);
+
+ /**
+ * Virtual destructor.
+ */
+ virtual ~SimpleFactory();
+
+ /**
+ * Return the service instance if the factory's id is equal to
+ * the key's currentID. Service is ignored.
+ */
+ UObject* create(const Key& key, const ICUService* service, UErrorCode& status) const;
+
+ /**
+ * If visible, adds a mapping from id -> this to the result,
+ * otherwise removes id from result.
+ */
+ void updateVisibleIDs(Hashtable& result, UErrorCode& status) const;
+
+ /**
+ * If this.id equals id, returns id regardless of locale,
+ * otherwise returns the empty string. (This default implementation has
+ * no localized id information.)
+ */
+ UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const;
+
+ public:
+
+ /**
+ * UObject boilerplate.
+ */
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ public:
+
+ /**
+ * For debugging.
+ */
+ virtual UnicodeString& debug(UnicodeString& toAppendTo) const;
+ virtual UnicodeString& debugClass(UnicodeString& toAppendTo) const;
+
+ private:
+ static const char fgClassID;
+};
+
+/*
+ ******************************************************************
+ */
+
+/**
+ * ServiceListener is the listener that ICUService provides by default.
+ * ICUService will notifiy this listener when factories are added to
+ * or removed from the service. Subclasses can provide
+ * different listener interfaces that extend EventListener, and modify
+ * acceptsListener and notifyListener as appropriate.
+ */
+class U_COMMON_API ServiceListener : public EventListener {
+ public:
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ virtual void serviceChanged(const ICUService& service) const = 0;
+
+ private:
+ static const char fgClassID;
+};
+
+/*
+ ******************************************************************
+ */
+
+class U_COMMON_API StringPair : public UMemory {
+public:
+ const UnicodeString displayName;
+ const UnicodeString id;
+
+ static StringPair* create(const UnicodeString& displayName,
+ const UnicodeString& id,
+ UErrorCode& status);
+
+ UBool isBogus() const;
+
+private:
+ StringPair(const UnicodeString& displayName, const UnicodeString& id);
+};
+
+/**
+ * Deleter for StringPairs
+ */
+U_CAPI void U_EXPORT2
+deleteStringPair(void *obj);
+
+/*
+ ******************************************************************
+ */
+
+ /**
+ * A Service provides access to service objects that implement a
+ * particular service, e.g. transliterators. Users provide a String
+ * id (for example, a locale string) to the service, and get back an
+ * object for that id. Service objects can be any kind of object.
+ * The service object is cached and returned for later queries, so
+ * generally it should not be mutable, or the caller should clone the
+ * object before modifying it. 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. Service objects are instantiated by Factory objects registered with
+ * the service. The service queries each Factory in turn, from most recently
+ * registered to earliest registered, until one returns a service object.
+ * If none responds with a service object, a fallback id is generated,
+ * and the process repeats until a service object is returned or until
+ * the id has no further fallbacks. Factories can be dynamically registered and unregistered with the
+ * service. When registered, a Factory is installed at the head of
+ * the factory list, and so gets 'first crack' at any keys or fallback
+ * keys. When unregistered, it is removed from the service and can no
+ * longer be located through it. Service objects generated by this
+ * factory and held by the client are unaffected. ICUService uses Keys to query factories and perform
+ * fallback. The Key defines the canonical form of the id, and
+ * implements the fallback strategy. Custom Keys can be defined that
+ * parse complex IDs into components that Factories can more easily
+ * use. The Key can cache the results of this parsing to save
+ * repeated effort. ICUService provides convenience APIs that
+ * take Strings and generate default Keys for use in querying. ICUService provides API to get the list of ids publicly
+ * supported by the service (although queries aren't restricted to
+ * this list). This list contains only 'simple' IDs, and not fully
+ * unique ids. Factories are associated with each simple ID and
+ * the responsible factory can also return a human-readable localized
+ * version of the simple ID, for use in user interfaces. ICUService
+ * can also provide a sorted collection of the all the localized visible
+ * ids. ICUService implements ICUNotifier, so that clients can register
+ * to receive notification when factories are added or removed from
+ * the service. ICUService provides a default EventListener subinterface,
+ * ServiceListener, which can be registered with the service. When
+ * the service changes, the ServiceListener's serviceChanged method
+ * is called, with the service as the only argument. The ICUService API is both rich and generic, and it is expected
+ * that most implementations will statically 'wrap' ICUService to
+ * present a more appropriate API-- for example, to declare the type
+ * of the objects returned from get, to limit the factories that can
+ * be registered with the service, or to define their own listener
+ * interface with a custom callback method. They might also customize
+ * ICUService by overriding it, for example, to customize the Key and
+ * fallback strategy. ICULocaleService is a customized service that
+ * uses Locale names as ids and uses Keys that implement the standard
+ * resource bundle fallback strategy.
+ */
+class U_COMMON_API ICUService : public ICUNotifier {
+ protected:
+ /**
+ * Name useful for debugging.
+ */
+ const UnicodeString name;
+
+ /**
+ * single lock used by this service.
+ */
+ UMTX lock;
+
+ private:
+ /**
+ * Timestamp so iterators can be fail-fast.
+ */
+ uint32_t timestamp;
+
+ /**
+ * All the factories registered with this service.
+ */
+ UVector* factories;
+
+ /**
+ * The service cache.
+ */
+ Hashtable* serviceCache;
+
+ /**
+ * The id cache.
+ */
+ Hashtable* idCache;
+
+ /**
+ * The name cache.
+ */
+ DNCache* dnCache;
+
+ /**
+ * Constructor.
+ */
+ public:
+ ICUService();
+
+ /**
+ * Construct with a name (useful for debugging).
+ */
+ ICUService(const UnicodeString& name);
+
+ /**
+ * Destructor.
+ */
+ virtual ~ICUService();
+
+ /**
+ * Return the name of this service. This will be the empty string if none was assigned.
+ */
+ UnicodeString& getName(UnicodeString& result) const;
+
+ /**
+ * Convenience override for get(Key&, UnicodeString*). This uses
+ * createKey to create a key for the provided descriptor.
+ */
+ UObject* get(const UnicodeString& descriptor, UErrorCode& status) const;
+
+ /**
+ * Convenience override for get(Key&, UnicodeString*). This uses
+ * createKey to create a key from the provided descriptor.
+ */
+ UObject* get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const;
+
+ /**
+ * Convenience override for get(Key&, UnicodeString*).
+ */
+ UObject* getKey(Key& key, UErrorCode& status) const;
+
+ /**
+ * 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. 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. If key is null, just returns null. 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. Return a snapshot of the visible IDs for this service. This
+ * set will not change as Factories are added or removed, but the
+ * supported ids will, so there is no guarantee that all and only
+ * the ids in the returned set are visible and supported by the
+ * service in subsequent calls. matchID is passed to createKey to create a key. If the
+ * key is not null, it is used to filter out ids that don't have
+ * the key as a fallback.
+ *
+ * The IDs are returned as pointers to UnicodeStrings. The
+ * caller owns the IDs.
+ */
+ UVector& getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const;
+
+ /**
+ * Convenience override for getDisplayName(String, Locale) that
+ * uses the current default locale.
+ */
+ UnicodeString& getDisplayName(const UnicodeString& id, UnicodeString& result) const;
+
+ /**
+ * Given a visible id, return the display name in the requested locale.
+ * If there is no directly supported id corresponding to this id, set
+ * result to bogus.
+ */
+ UnicodeString& getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const;
+
+ /**
+ * Convenience override of getDisplayNames(Locale, String) that
+ * uses the current default Locale as the locale and NULL for
+ * the matchID.
+ */
+ UVector& getDisplayNames(UVector& result, UErrorCode& status) const;
+
+ /**
+ * Convenience override of getDisplayNames(Locale, String) that
+ */
+ UVector& getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const;
+
+ /**
+ * Return a snapshot of the mapping from display names to visible
+ * IDs for this service. This set will not change as factories
+ * are added or removed, but the supported ids will, so there is
+ * no guarantee that all and only the ids in the returned map will
+ * be visible and supported by the service in subsequent calls,
+ * nor is there any guarantee that the current display names match
+ * those in the set. This iterates over StringPair instances.
+ */
+ UVector& getDisplayNames(UVector& result,
+ const Locale& locale,
+ const UnicodeString* matchID,
+ UErrorCode& status) const;
+
+ /**
+ * A convenience override of registerObject(Object, String, boolean)
+ * that defaults visible to true. The service adopts the object.
+ */
+ const Factory* registerObject(UObject* objToAdopt, const UnicodeString& id);
+
+ /**
+ * Register an object with the provided id. The id will be
+ * canonicalized. The canonicalized ID will be returned by
+ * getVisibleIDs if visible is true. This wraps the object
+ * using createSimpleFactory and calls registerFactory.
+ */
+ virtual const Factory* registerObject(UObject* obj, const UnicodeString& id, UBool visible);
+
+ /**
+ * Register a Factory. Returns the factory if the service accepts
+ * the factory, otherwise returns null. The default implementation
+ * accepts all factories. The service owns the factories.
+ */
+ virtual const Factory* registerFactory(Factory* factoryToAdopt);
+
+ /**
+ * Unregister a factory. The first matching registered factory will
+ * be deleted and removed from the list. Returns true if a matching factory was
+ * found. If not found, factory is not deleted, but this is typically an
+ * error.
+ */
+ virtual UBool unregisterFactory(Factory* factory);
+
+ /**
+ * Reset the service to the default factories. The factory
+ * lock is acquired and then reInitializeFactories is called.
+ */
+ virtual void reset();
+
+ /**
+ * Return true if the service is in its default state. The default
+ * implementation returns true if there are no factories registered.
+ */
+ virtual UBool isDefault() const;
+
+ /**
+ * Create a key from an id. This creates a Key instance.
+ * Subclasses can override to define more useful keys appropriate
+ * to the factories they accept. If id is null, returns null.
+ */
+ virtual Key* createKey(const UnicodeString* id) const;
+
+ /**
+ * Clone object so that caller can own the copy. UObject doesn't yet implement
+ * clone so we need an instance-aware method that knows how to do this.
+ * This is public so factories can call it, but should really be protected.
+ */
+ virtual UObject* cloneInstance(UObject* instance) const = 0;
+
+ protected:
+
+ /**
+ * Create a factory that wraps a singleton object.
+ * Default is an instance of SimpleFactory.
+ */
+ virtual Factory* createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible);
+
+ /**
+ * Reinitialize the factory list to its default state. By default
+ * this clears the list. Subclasses can override to provide other
+ * default initialization of the factory list. Subclasses must
+ * not call this method directly, as it must only be called while
+ * holding write access to the factory list.
+ */
+ virtual void reInitializeFactories();
+
+ /**
+ * Default handler for this service if no factory in the list
+ * handled the key.
+ */
+ virtual UObject* handleDefault(const Key& key, UnicodeString* actualIDReturn, UErrorCode& status) const;
+
+ /**
+ * Clear caches maintained by this service. Subclasses can
+ * override if they implement additional that need to be cleared
+ * when the service changes. Subclasses should generally not call
+ * this method directly, as it must only be called while
+ * synchronized on this.
+ */
+ virtual void clearCaches();
+
+ /**
+ * Clears only the service cache.
+ * This can be called by subclasses when a change affects the service
+ * cache but not the id caches, e.g., when the default locale changes
+ * the resolution of ids changes, but not the visible ids themselves.
+ */
+ void clearServiceCache();
+
+ /**
+ * Return true if the listener is accepted; by default this
+ * requires a ServiceListener. Subclasses can override to accept
+ * different listeners.
+ */
+ virtual UBool acceptsListener(const EventListener& l) const;
+
+ /**
+ * Notify the listener, which by default is a ServiceListener.
+ * Subclasses can override to use a different listener.
+ */
+ virtual void notifyListener(EventListener& l) const;
+
+ /**
+ * Return a map from visible ids to factories.
+ * This should be only be called when the mutex is held.
+ */
+ const Hashtable* getVisibleIDMap(UErrorCode& status) const;
+
+ /**
+ * Allow subclasses to read the time stamp.
+ */
+ virtual int32_t getTimestamp() const;
+
+ private:
+
+ friend class ICUServiceTest;
+ /**
+ * Return the number of factories, used for testing.
+ */
+ int32_t countFactories() const;
+};
+
+U_NAMESPACE_END
+
+ /* UCONFIG_NO_SERVICE */
+#endif
+
+ /* ICUSERV_H */
+#endif
+
diff --git a/icu4c/source/common/unicode/strenum.h b/icu4c/source/common/unicode/strenum.h
new file mode 100644
index 00000000000..874731690c0
--- /dev/null
+++ b/icu4c/source/common/unicode/strenum.h
@@ -0,0 +1,40 @@
+/*
+*******************************************************************************
+*
+* Copyright (C) 2002, International Business Machines
+* Corporation and others. All Rights Reserved.
+*
+*******************************************************************************
+*/
+
+#ifndef STRENUM_H
+#define STRENUM_H
+
+#include "uobject.h"
+
+U_NAMESPACE_BEGIN
+
+/**
+ * Base class for 'pure' C++ implementations. Adds method that
+ * returns the next UnicodeString since in C++ this might be a
+ * common storage model for strings.
+ */
+class U_COMMON_API StringEnumeration : public UMemory {
+ public:
+ virtual ~StringEnumeration();
+ virtual int32_t count(UErrorCode& status) const = 0;
+
+ virtual const char* next(UErrorCode& status) = 0;
+ virtual const UChar* unext(UErrorCode& status) = 0;
+ virtual const UnicodeString* snext(UErrorCode& status) = 0;
+
+ virtual void reset(UErrorCode& status) = 0;
+};
+
+inline StringEnumeration::~StringEnumeration() {
+}
+
+U_NAMESPACE_END
+
+/* STRENUM_H */
+#endif
diff --git a/icu4c/source/test/intltest/icusvtst.cpp b/icu4c/source/test/intltest/icusvtst.cpp
new file mode 100644
index 00000000000..ccd44c6c5c9
--- /dev/null
+++ b/icu4c/source/test/intltest/icusvtst.cpp
@@ -0,0 +1,1349 @@
+/**
+ *******************************************************************************
+ * Copyright (C) 2001-2002, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ *
+ *******************************************************************************
+ */
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_SERVICE
+
+#include "icusvtst.h"
+
+#include "iculserv.h"
+
+U_NAMESPACE_BEGIN
+
+class MyListener : public EventListener {
+};
+
+class WrongListener : public EventListener {
+};
+
+class ICUNSubclass : public ICUNotifier {
+public:
+ UBool acceptsListener(const EventListener& l) const {
+ return TRUE;
+ // return l instanceof MyListener;
+ }
+
+ void notifyListener(const EventListener& l) const {
+ }
+};
+
+class LKFSubclass : public LocaleKeyFactory {
+public:
+ LKFSubclass(UBool visible)
+ : LocaleKeyFactory(visible ? VISIBLE : INVISIBLE)
+ {
+ }
+
+protected:
+ const Hashtable* getSupportedIDs(void) const {
+ // return Collections.EMPTY_SET;
+ return NULL;
+ }
+};
+
+class Integer : public UObject {
+ const int32_t _val;
+public:
+ Integer(int32_t val) : _val(val) {
+ }
+
+ Integer(const Integer& rhs) : _val(rhs._val) {
+ }
+ virtual ~Integer() {
+ }
+
+ virtual UBool operator==(const UObject& other) const
+ {
+ return other.getDynamicClassID() == getStaticClassID() &&
+ _val == ((Integer&)other)._val;
+ }
+
+ public:
+ /**
+ * UObject boilerplate.
+ */
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+ public:
+ virtual UnicodeString& debug(UnicodeString& result) const {
+ debugClass(result);
+ result.append(" val: ");
+ result.append(_val);
+ return result;
+ }
+
+ virtual UnicodeString& debugClass(UnicodeString& result) const {
+ return result.append("Integer");
+ }
+
+ private:
+ static const char fgClassID;
+};
+
+const char Integer::fgClassID = '\0';
+
+// use locale keys
+class TestIntegerService : public ICUService {
+public:
+ Key* createKey(const UnicodeString* id) const {
+ return LocaleKey::createWithCanonicalFallback(id, NULL); // no fallback locale
+ }
+
+ virtual Factory* createSimpleFactory(UObject* obj, const UnicodeString& id, UBool visible)
+ {
+ if (obj && obj->getDynamicClassID() == Integer::getStaticClassID()) {
+ return new SimpleFactory((Integer*)obj, id, visible);
+ }
+ return NULL;
+ }
+
+ virtual UObject* cloneInstance(UObject* instance) const {
+ return instance ? new Integer(*(Integer*)instance) : NULL;
+ }
+};
+
+
+ICUServiceTest::ICUServiceTest() {
+}
+
+ICUServiceTest::~ICUServiceTest() {
+}
+
+void
+ICUServiceTest::runIndexedTest(int32_t index, UBool exec, const char* &name, char* par)
+{
+ switch (index) {
+ TESTCASE(0,testAPI_One);
+ TESTCASE(1,testAPI_Two);
+ TESTCASE(2,testRBF);
+ TESTCASE(3,testNotification);
+ TESTCASE(4,testLocale);
+ TESTCASE(5,testWrapFactory);
+ TESTCASE(6,testCoverage);
+ default: name = ""; break;
+ }
+}
+
+UnicodeString&
+ICUServiceTest::lrmsg(UnicodeString& result, const UnicodeString& message, const UObject* lhs, const UObject* rhs) const
+{
+ char buffer[128];
+ sprintf(buffer, ", lhs: %x rhs: %x", lhs, rhs);
+ result.append(message);
+ result.append(buffer);
+ return result;
+}
+
+void
+ICUServiceTest::confirmBoolean(const UnicodeString& message, UBool val)
+{
+ if (val) {
+ logln(message);
+ } else {
+ errln(message);
+ }
+}
+
+#if 0
+void
+ICUServiceTest::confirmEqual(const UnicodeString& message, const UObject* lhs, const UObject* rhs)
+{
+ UBool equ = (lhs == NULL)
+ ? (rhs == NULL)
+ : (rhs != NULL && lhs->operator==(*rhs));
+
+ UnicodeString temp;
+ lrmsg(temp, message, lhs, rhs);
+
+ if (equ) {
+ logln(temp);
+ } else {
+ errln(temp);
+ }
+}
+#else
+void
+ICUServiceTest::confirmEqual(const UnicodeString& message, const Integer* lhs, const Integer* rhs)
+{
+ UBool equ = (lhs == NULL)
+ ? (rhs == NULL)
+ : (rhs != NULL && lhs->operator==(*rhs));
+
+ UnicodeString temp;
+ lrmsg(temp, message, lhs, rhs);
+
+ if (equ) {
+ logln(temp);
+ } else {
+ errln(temp);
+ }
+}
+
+void
+ICUServiceTest::confirmEqual(const UnicodeString& message, const UnicodeString* lhs, const UnicodeString* rhs)
+{
+ UBool equ = (lhs == NULL)
+ ? (rhs == NULL)
+ : (rhs != NULL && lhs->operator==(*rhs));
+
+ UnicodeString temp;
+ lrmsg(temp, message, lhs, rhs);
+
+ if (equ) {
+ logln(temp);
+ } else {
+ errln(temp);
+ }
+}
+
+void
+ICUServiceTest::confirmEqual(const UnicodeString& message, const Locale* lhs, const Locale* rhs)
+{
+ UBool equ = (lhs == NULL)
+ ? (rhs == NULL)
+ : (rhs != NULL && lhs->operator==(*rhs));
+
+ UnicodeString temp;
+ lrmsg(temp, message, lhs, rhs);
+
+ if (equ) {
+ logln(temp);
+ } else {
+ errln(temp);
+ }
+}
+#endif
+
+// use these for now
+void
+ICUServiceTest::confirmStringsEqual(const UnicodeString& message, const UnicodeString& lhs, const UnicodeString& rhs)
+{
+ UBool equ = lhs == rhs;
+
+ UnicodeString temp = message;
+ temp.append(" lhs: ");
+ temp.append(lhs);
+ temp.append(" rhs: ");
+ temp.append(rhs);
+
+ if (equ) {
+ logln(temp);
+ } else {
+ errln(temp);
+ }
+}
+
+
+void
+ICUServiceTest::confirmIdentical(const UnicodeString& message, const UObject* lhs, const UObject *rhs)
+{
+ UnicodeString temp;
+ lrmsg(temp, message, lhs, rhs);
+ if (lhs == rhs) {
+ logln(temp);
+ } else {
+ errln(temp);
+ }
+}
+
+void
+ICUServiceTest::confirmIdentical(const UnicodeString& message, int32_t lhs, int32_t rhs)
+{
+ if (lhs == rhs) {
+ logln(message + " lhs: " + lhs + " rhs: " + rhs);
+ } else {
+ errln(message + " lhs: " + lhs + " rhs: " + rhs);
+ }
+}
+
+void
+ICUServiceTest::msgstr(const UnicodeString& message, UObject* obj, UBool err)
+{
+ if (obj) {
+ UnicodeString* str = (UnicodeString*)obj;
+ logln(message + *str);
+ delete str;
+ } else if (err) {
+ errln("Error " + message + "string is NULL");
+ }
+}
+
+void
+ICUServiceTest::testAPI_One()
+{
+ // create a service using locale keys,
+ TestIntegerService service;
+
+ // register an object with one locale,
+ // search for an object with a more specific locale
+ // should return the original object
+ Integer* singleton0 = new Integer(0);
+ service.registerObject(singleton0, "en_US");
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_FOO", status);
+ confirmEqual("1) en_US_FOO -> en_US", result, singleton0);
+ delete result;
+ }
+
+ // register a new object with the more specific locale
+ // search for an object with that locale
+ // should return the new object
+ Integer* singleton1 = new Integer(1);
+ service.registerObject(singleton1, "en_US_FOO");
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_FOO", status);
+ confirmEqual("2) en_US_FOO -> en_US_FOO", result, singleton1);
+ delete result;
+ }
+
+ // search for an object that falls back to the first registered locale
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_BAR", status);
+ confirmEqual("3) en_US_BAR -> en_US", result, singleton0);
+ delete result;
+ }
+
+ // get a list of the factories, should be two
+ {
+ confirmIdentical("4) factory size", service.countFactories(), 2);
+ }
+
+ // register a new object with yet another locale
+ Integer* singleton2 = new Integer(2);
+ service.registerObject(singleton2, "en");
+ {
+ confirmIdentical("5) factory size", service.countFactories(), 3);
+ }
+
+ // search for an object with the new locale
+ // stack of factories is now en, en_US_FOO, en_US
+ // search for en_US should still find en_US object
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_BAR", status);
+ confirmEqual("6) en_US_BAR -> en_US", result, singleton0);
+ delete result;
+ }
+
+ // register a new object with an old id, should hide earlier factory using this id, but leave it there
+ Integer* singleton3 = new Integer(3);
+ const Factory* s3factory = service.registerObject(singleton3, "en_US");
+ {
+ confirmIdentical("9) factory size", service.countFactories(), 4);
+ }
+
+ // should get data from that new factory
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_BAR", status);
+ confirmEqual("10) en_US_BAR -> (3)", result, singleton3);
+ delete result;
+ }
+
+ // remove new factory
+ // should have fewer factories again
+ // singleton3 dead!
+ {
+ service.unregisterFactory((Factory*)s3factory);
+ confirmIdentical("11) factory size", service.countFactories(), 3);
+ }
+
+ // should get original data again after remove factory
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_BAR", status);
+ confirmEqual("12) en_US_BAR -> (3)", result, singleton0);
+ delete result;
+ }
+
+ // shouldn't find unregistered ids
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("foo", status);
+ confirmIdentical("13) foo -> null", result, NULL);
+ delete result;
+ }
+
+ // should find non-canonical strings
+ {
+ UnicodeString resultID;
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("EN_us_fOo", &resultID, status);
+ confirmEqual("14a) find-non-canonical", result, singleton1);
+ confirmStringsEqual("14b) find non-canonical", resultID, "en_US_FOO");
+ delete result;
+ }
+
+ // should be able to register non-canonical strings and get them canonicalized
+ Integer* singleton4 = new Integer(4);
+ service.registerObject(singleton4, "eN_ca_dUde");
+ {
+ UnicodeString resultID;
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("En_Ca_DuDe", &resultID, status);
+ confirmEqual("15a) find-non-canonical", result, singleton4);
+ confirmStringsEqual("15b) register non-canonical", resultID, "en_CA_DUDE");
+ delete result;
+ }
+
+ // should be able to register invisible factories, these will not
+ // be visible by default, but if you know the secret password you
+ // can still access these services...
+ Integer* singleton5 = new Integer(5);
+ service.registerObject(singleton5, "en_US_BAR", FALSE);
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Integer* result = (Integer*)service.get("en_US_BAR", status);
+ confirmEqual("17) get invisible", result, singleton5);
+ delete result;
+ }
+
+ // should not be able to locate invisible services
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, status);
+ service.getVisibleIDs(ids, status);
+ UnicodeString target = "en_US_BAR";
+ confirmBoolean("18) find invisible", !ids.contains(&target));
+ }
+
+ // clear factory and caches
+ service.reset();
+ confirmBoolean("19) is default", service.isDefault());
+}
+
+/*
+ ******************************************************************
+ */
+
+class TestStringService : public ICUService {
+public:
+ Key* createKey(const UnicodeString* id) const {
+ return LocaleKey::createWithCanonicalFallback(id, NULL); // no fallback locale
+ }
+
+ virtual Factory* createSimpleFactory(UObject* obj, const UnicodeString& id, UBool visible)
+ {
+ if (obj && obj->getDynamicClassID() == UnicodeString::getStaticClassID()) {
+ return new SimpleFactory((UnicodeString*)obj, id, visible);
+ }
+ return NULL;
+ }
+
+ virtual UObject* cloneInstance(UObject* instance) const {
+ return instance ? new UnicodeString(*(UnicodeString*)instance) : NULL;
+ }
+};
+
+// this creates a string for any id, but doesn't report anything
+class AnonymousStringFactory : public Factory
+{
+public:
+ virtual UObject* create(const Key& key, const ICUService* service, UErrorCode& status) const {
+ return new UnicodeString(key.getID());
+ }
+
+ virtual void updateVisibleIDs(Hashtable& result, UErrorCode& status) const {
+ // do nothing
+ }
+
+ virtual UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const {
+ // do nothing
+ return result;
+ }
+
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+private:
+ static const char fgClassID;
+};
+
+const char AnonymousStringFactory::fgClassID = '\0';
+
+class TestMultipleKeyStringFactory : public Factory {
+ UErrorCode _status;
+ UVector _ids;
+ UnicodeString _factoryID;
+
+public:
+ TestMultipleKeyStringFactory(const UnicodeString ids[], int32_t count, const UnicodeString& factoryID)
+ : _status(U_ZERO_ERROR)
+ , _ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, count, _status)
+ , _factoryID(factoryID + ": ")
+ {
+ for (int i = 0; i < count; ++i) {
+ _ids.addElement(new UnicodeString(ids[i]), _status);
+ }
+ }
+
+ ~TestMultipleKeyStringFactory() {
+ }
+
+ UObject* create(const Key& key, const ICUService* service, UErrorCode& status) const {
+ UnicodeString temp;
+ key.currentID(temp);
+ if (U_SUCCESS(_status) && _ids.contains(&temp)) {
+ return new UnicodeString(_factoryID + temp);
+ }
+ return NULL;
+ }
+
+ void updateVisibleIDs(Hashtable& result, UErrorCode& status) const {
+ if (U_SUCCESS(_status)) {
+ for (int32_t i = 0; i < _ids.size(); ++i) {
+ result.put(*(UnicodeString*)_ids[i], (void*)this, status);
+ }
+ }
+ }
+
+ UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const {
+ if (U_SUCCESS(_status) && _ids.contains((void*)&id)) {
+ char buffer[128];
+ UErrorCode status = U_ZERO_ERROR;
+ int32_t len = id.extract(buffer, sizeof(buffer), NULL, status);
+ if (U_SUCCESS(status)) {
+ if (len == sizeof(buffer)) {
+ --len;
+ }
+ buffer[len] = 0;
+ Locale loc = Locale::createFromName(buffer);
+ loc.getDisplayName(locale, result);
+ return result;
+ }
+ }
+ result.setToBogus(); // shouldn't happen
+ return result;
+ }
+
+ virtual UClassID getDynamicClassID() const {
+ return getStaticClassID();
+ }
+
+ static UClassID getStaticClassID() {
+ return (UClassID)&fgClassID;
+ }
+
+private:
+ static const char fgClassID;
+};
+
+const char TestMultipleKeyStringFactory::fgClassID = '\0';
+
+void
+ICUServiceTest::testAPI_Two()
+{
+ TestStringService service;
+ service.registerFactory(new AnonymousStringFactory());
+
+ // anonymous factory will still handle the id
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnicodeString en_US = "en_US";
+ UnicodeString* result = (UnicodeString*)service.get(en_US, status);
+ confirmEqual("21) locale", result, &en_US);
+ delete result;
+ }
+
+ // still normalizes id
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ const UnicodeString en_US_BAR = "en_US_BAR";
+ UnicodeString resultID;
+ UnicodeString* result = (UnicodeString*)service.get("EN_us_bar", &resultID, status);
+ confirmEqual("22) locale", &resultID, &en_US_BAR);
+ delete result;
+ }
+
+ // we can override for particular ids
+ UnicodeString* singleton0 = new UnicodeString("Zero");
+ service.registerObject(singleton0, "en_US_BAR");
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* result = (UnicodeString*)service.get("en_US_BAR", status);
+ confirmEqual("23) override super", result, singleton0);
+ delete result;
+ }
+
+ // empty service should not recognize anything
+ service.reset();
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* result = (UnicodeString*)service.get("en_US", status);
+ confirmIdentical("24) empty", result, NULL);
+ }
+
+ // create a custom multiple key factory
+ {
+ UnicodeString xids[] = {
+ "en_US_VALLEY_GIRL",
+ "en_US_VALLEY_BOY",
+ "en_US_SURFER_GAL",
+ "en_US_SURFER_DUDE"
+ };
+ int32_t count = sizeof(xids)/sizeof(UnicodeString);
+
+ Factory* f = new TestMultipleKeyStringFactory(xids, count, "Later");
+ service.registerFactory(f);
+ }
+
+
+ // iterate over the visual ids returned by the multiple factory
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, 0, status);
+ service.getVisibleIDs(ids, status);
+ for (int i = 0; i < ids.size(); ++i) {
+ const UnicodeString* id = (const UnicodeString*)ids[i];
+ UnicodeString* result = (UnicodeString*)service.get(*id, status);
+ if (result) {
+ logln(" " + *id + " --> " + *result);
+ } else {
+ errln("could not find " + *id);
+ }
+ }
+ // four visible ids
+ confirmIdentical("25) visible ids", ids.size(), 4);
+ }
+
+ // iterate over the display names
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector names(deleteStringPair, NULL, status);
+ service.getDisplayNames(names, Locale::getGerman(), status);
+ for (int i = 0; i < names.size(); ++i) {
+ const StringPair* pair = (const StringPair*)names[i];
+ logln(" " + pair->displayName + " --> " + pair->id);
+ }
+ confirmIdentical("26) display names", names.size(), 4);
+ }
+
+ // no valid display name
+ {
+ UnicodeString name;
+ service.getDisplayName("en_US_VALLEY_GEEK", name);
+ confirmBoolean("27) get display name", name.isBogus());
+ }
+
+ {
+ UnicodeString name;
+ service.getDisplayName("en_US_SURFER_DUDE", name, Locale::getEnglish());
+ confirmStringsEqual("28) get display name", name, "English (United States, SURFER_DUDE)");
+ }
+
+ // register another multiple factory
+ {
+ UnicodeString xids[] = {
+ "en_US_SURFER",
+ "en_US_SURFER_GAL",
+ "en_US_SILICON",
+ "en_US_SILICON_GEEK",
+ };
+ int32_t count = sizeof(xids)/sizeof(UnicodeString);
+
+ Factory* f = new TestMultipleKeyStringFactory(xids, count, "Rad dude");
+ service.registerFactory(f);
+ }
+
+ // this time, we have seven display names
+ // Rad dude's surfer gal 'replaces' Later's surfer gal
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector names(deleteStringPair, NULL, status);
+ service.getDisplayNames(names, Locale("es"), status);
+ for (int i = 0; i < names.size(); ++i) {
+ const StringPair* pair = (const StringPair*)names[i];
+ logln(" " + pair->displayName + " --> " + pair->id);
+ }
+ confirmIdentical("26) display names", names.size(), 7);
+ }
+
+ // we should get the display name corresponding to the actual id
+ // returned by the id we used.
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString actualID;
+ UnicodeString id = "en_us_surfer_gal";
+ UnicodeString* gal = (UnicodeString*)service.get(id, &actualID, status);
+ if (gal != NULL) {
+ UnicodeString displayName;
+ logln("actual id: " + actualID);
+ service.getDisplayName(actualID, displayName, Locale::getEnglish());
+ logln("found actual: " + *gal + " with display name: " + displayName);
+ confirmBoolean("30) found display name for actual", !displayName.isBogus());
+
+ service.getDisplayName(id, displayName, Locale::getEnglish());
+ logln("found actual: " + *gal + " with display name: " + displayName);
+ confirmBoolean("31) found display name for query", displayName.isBogus());
+
+ delete gal;
+ } else {
+ errln("30) service could not find entry for " + id);
+ }
+ }
+
+ // this should be handled by the 'dude' factory, since it overrides en_US_SURFER.
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString actualID;
+ UnicodeString id = "en_US_SURFER_BOZO";
+ UnicodeString* bozo = (UnicodeString*)service.get(id, &actualID, status);
+ if (bozo != NULL) {
+ UnicodeString displayName;
+ service.getDisplayName(actualID, displayName, Locale::getEnglish());
+ logln("found actual: " + *bozo + " with display name: " + displayName);
+ confirmBoolean("32) found display name for actual", !displayName.isBogus());
+
+ service.getDisplayName(id, displayName, Locale::getEnglish());
+ logln("found actual: " + *bozo + " with display name: " + displayName);
+ confirmBoolean("33) found display name for query", displayName.isBogus());
+
+ delete bozo;
+ } else {
+ errln("32) service could not find entry for " + id);
+ }
+ }
+
+ // certainly not default...
+ {
+ confirmBoolean("34) is default ", !service.isDefault());
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, 0, status);
+ service.getVisibleIDs(ids, status);
+ for (int i = 0; i < ids.size(); ++i) {
+ const UnicodeString* id = (const UnicodeString*)ids[i];
+ msgstr(*id + "? ", service.get(*id, status));
+ }
+
+ logstr("valleygirl? ", service.get("en_US_VALLEY_GIRL", status));
+ logstr("valleyboy? ", service.get("en_US_VALLEY_BOY", status));
+ logstr("valleydude? ", service.get("en_US_VALLEY_DUDE", status));
+ logstr("surfergirl? ", service.get("en_US_SURFER_GIRL", status));
+ }
+}
+
+
+class CalifornioLanguageFactory : public ICUResourceBundleFactory
+{
+public:
+ static const char* californio; // = "en_US_CA";
+ static const char* valley; // = californio ## "_VALLEY";
+ static const char* surfer; // = californio ## "_SURFER";
+ static const char* geek; // = californio ## "_GEEK";
+ static const Hashtable* supportedIDs; // = NULL;
+
+ const Hashtable* getSupportedIDs(UErrorCode& status) const
+ {
+ if (supportedIDs == NULL) {
+ Hashtable* table = new Hashtable();
+ table->put(UnicodeString(californio), (void*)table, status);
+ table->put(UnicodeString(valley), (void*)table, status);
+ table->put(UnicodeString(surfer), (void*)table, status);
+ table->put(UnicodeString(geek), (void*)table, status);
+
+ // not necessarily atomic, but this is a test...
+ supportedIDs = table;
+ }
+ return supportedIDs;
+ }
+
+ UnicodeString& getDisplayName(const UnicodeString& id, const Locale& locale, UnicodeString& result) const
+ {
+ UnicodeString prefix = "";
+ UnicodeString suffix = "";
+ UnicodeString ls = locale.getName();
+ if (LocaleUtility::isFallbackOf(californio, ls)) {
+ if (!ls.caseCompare(valley, 0)) {
+ prefix = "Like, you know, it's so totally ";
+ } else if (!ls.caseCompare(surfer, 0)) {
+ prefix = "Dude, it's ";
+ } else if (!ls.caseCompare(geek, 0)) {
+ prefix = "I'd estimate it is approximately ";
+ } else {
+ prefix = "Huh? Maybe ";
+ }
+ }
+ if (LocaleUtility::isFallbackOf(californio, id)) {
+ if (!id.caseCompare(valley, 0)) {
+ suffix = "like the Valley, you know? Let's go to the mall!";
+ } else if (!id.caseCompare(surfer, 0)) {
+ suffix = "time to hit those gnarly waves, Dude!!!";
+ } else if (!id.caseCompare(geek, 0)) {
+ suffix = "all systems go. T-Minus 9, 8, 7...";
+ } else {
+ suffix = "No Habla Englais";
+ }
+ } else {
+ suffix = ICUResourceBundleFactory::getDisplayName(id, locale, result);
+ }
+
+ result = prefix + suffix;
+ return result;
+ }
+};
+
+const char* CalifornioLanguageFactory::californio = "en_US_CA";
+const char* CalifornioLanguageFactory::valley = "en_US_CA_VALLEY";
+const char* CalifornioLanguageFactory::surfer = "en_US_CA_SURFER";
+const char* CalifornioLanguageFactory::geek = "en_US_CA_GEEK";
+const Hashtable* CalifornioLanguageFactory::supportedIDs = NULL;
+
+void
+ICUServiceTest::testRBF()
+{
+ // resource bundle factory.
+ TestStringService service;
+ service.registerFactory(new ICUResourceBundleFactory());
+
+ // list all of the resources
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, 0, status);
+ service.getVisibleIDs(ids, status);
+ logln("all visible ids:");
+ for (int i = 0; i < ids.size(); ++i) {
+ const UnicodeString* id = (const UnicodeString*)ids[i];
+ logln(*id);
+ }
+ }
+
+ // get all the display names of these resources
+ // this should be fast since the display names were cached.
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector names(deleteStringPair, NULL, status);
+ service.getDisplayNames(names, Locale::getGermany(), status);
+ logln("service display names for de_DE");
+ for (int i = 0; i < names.size(); ++i) {
+ const StringPair* pair = (const StringPair*)names[i];
+ logln(" " + pair->displayName + " --> " + pair->id);
+ }
+ }
+
+ service.registerFactory(new CalifornioLanguageFactory());
+
+ // get all the display names of these resources
+ {
+ logln("californio language factory:");
+ const char* idNames[] = {
+ CalifornioLanguageFactory::californio,
+ CalifornioLanguageFactory::valley,
+ CalifornioLanguageFactory::surfer,
+ CalifornioLanguageFactory::geek,
+ };
+ int32_t count = sizeof(idNames)/sizeof(idNames[0]);
+
+ for (int i = 0; i < count; ++i) {
+ logln(UnicodeString("\n --- ") + idNames[i] + " ---");
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector names(deleteStringPair, NULL, status);
+ service.getDisplayNames(names, idNames[i], status);
+ for (int i = 0; i < names.size(); ++i) {
+ const StringPair* pair = (const StringPair*)names[i];
+ logln(" " + pair->displayName + " --> " + pair->id);
+ }
+ }
+ }
+ }
+}
+
+class SimpleListener : public ServiceListener {
+ ICUServiceTest* _test;
+ int32_t _n;
+ UnicodeString _name;
+
+public:
+ SimpleListener(ICUServiceTest* test, const UnicodeString& name) : _test(test), _n(0), _name(name) {}
+
+ virtual void serviceChanged(const ICUService& service) const {
+ UnicodeString serviceName = "listener ";
+ serviceName.append(_name);
+ serviceName.append(" n++");
+ serviceName.append(" service changed: " );
+ service.getName(serviceName);
+ _test->logln(serviceName);
+ }
+};
+
+void
+ICUServiceTest::testNotification()
+{
+ SimpleListener one(this, "one");
+ SimpleListener two(this, "two");
+ {
+ UErrorCode status = U_ZERO_ERROR;
+
+ logln("simple registration notification");
+ TestStringService ls;
+ ls.addListener(&one, status);
+ ls.addListener(&two, status);
+
+ logln("registering foo... ");
+ ls.registerObject(new UnicodeString("Foo"), "en_FOO");
+ logln("registering bar... ");
+ ls.registerObject(new UnicodeString("Bar"), "en_BAR");
+ logln("getting foo...");
+ UnicodeString* result = (UnicodeString*)ls.get("en_FOO", status);
+ logln(*result);
+ delete result;
+
+ logln("removing listener 2...");
+ ls.removeListener(&two, status);
+ logln("registering baz...");
+ ls.registerObject(new UnicodeString("Baz"), "en_BAZ");
+ logln("removing listener 1");
+ ls.removeListener(&one, status);
+ logln("registering burp...");
+ ls.registerObject(new UnicodeString("Burp"), "en_BURP");
+
+ // should only get one notification even if register multiple times
+ logln("... trying multiple registration");
+ ls.addListener(&one, status);
+ ls.addListener(&one, status);
+ ls.addListener(&one, status);
+ ls.addListener(&two, status);
+ ls.registerObject(new UnicodeString("Foo"), "en_FOO");
+ logln("... registered foo");
+ }
+#if 0
+ // same thread, so we can't callback within notification, unlike Java
+ ServiceListener l3 = new ServiceListener() {
+private int n;
+public void serviceChanged(ICUService s) {
+ logln("listener 3 report " + n++ + " service changed...");
+ if (s.get("en_BOINK") == null) { // don't recurse on ourselves!!!
+ logln("registering boink...");
+ s.registerObject("boink", "en_BOINK");
+ }
+}
+ };
+ ls.addListener(l3);
+ logln("registering boo...");
+ ls.registerObject("Boo", "en_BOO");
+#endif
+
+ logln("...done");
+}
+
+class TestStringLocaleService : public ICULocaleService {
+public:
+ virtual UObject* cloneInstance(UObject* instance) const {
+ return instance ? new UnicodeString(*(UnicodeString*)instance) : NULL;
+ }
+};
+
+void ICUServiceTest::testLocale() {
+ TestStringLocaleService service;
+
+ UnicodeString* root = new UnicodeString("root");
+ UnicodeString* german = new UnicodeString("german");
+ UnicodeString* germany = new UnicodeString("german_Germany");
+ UnicodeString* japanese = new UnicodeString("japanese");
+ UnicodeString* japan = new UnicodeString("japanese_Japan");
+
+ service.registerObject(root, "");
+ service.registerObject(german, "de");
+ service.registerObject(germany, Locale::getGermany());
+ service.registerObject(japanese, "ja");
+ service.registerObject(japan, Locale::getJapan());
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", status);
+ confirmEqual("test de_US", german, target);
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", LocaleKey::KIND_ANY, status);
+ confirmEqual("test de_US 2", german, target);
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", 1234, status);
+ confirmEqual("test de_US 3", german, target);
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Locale actualReturn;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", &actualReturn, status);
+ confirmEqual("test de_US 5", german, target);
+ confirmEqual("test de_US 6", &actualReturn, &Locale::getGerman());
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Locale actualReturn;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", LocaleKey::KIND_ANY, &actualReturn, status);
+ confirmEqual("test de_US 7", &actualReturn, &Locale::getGerman());
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Locale actualReturn;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", 1234, &actualReturn, status);
+ confirmEqual("test de_US 8", german, target);
+ confirmEqual("test de_US 9", &actualReturn, &Locale::getGerman());
+ delete target;
+ }
+
+ UnicodeString* one = new UnicodeString("one/de_US");
+ UnicodeString* two = new UnicodeString("two/de_US");
+
+ service.registerObject(one, "de_US", 1);
+ service.registerObject(two, "de_US", 2);
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", 1, status);
+ confirmEqual("test de_US kind 1", one, target);
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", 2, status);
+ confirmEqual("test de_US kind 2", two, target);
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("de_US", status);
+ confirmEqual("test de_US kind 3", german, target);
+ delete target;
+ }
+
+ {
+ UnicodeString english = "en";
+ Locale localeResult;
+ UnicodeString result;
+ LocaleKey* lkey = LocaleKey::createWithCanonicalFallback(&english, NULL, 1234);
+ logln("lkey prefix: " + lkey->prefix(result));
+ result.remove();
+ logln("lkey descriptor: " + lkey->currentDescriptor(result));
+ result.remove();
+ logln(UnicodeString("lkey current locale: ") + lkey->currentLocale(localeResult).getName());
+ result.remove();
+
+ lkey->fallback();
+ logln("lkey descriptor 2: " + lkey->currentDescriptor(result));
+ result.remove();
+
+ lkey->fallback();
+ logln("lkey descriptor 3: " + lkey->currentDescriptor(result));
+ result.remove();
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("za_PPP", status);
+ confirmEqual("test zappp", root, target);
+ delete target;
+ }
+
+ Locale loc = Locale::getDefault();
+ UErrorCode status = U_ZERO_ERROR;
+ Locale::setDefault(Locale::getJapanese(), status);
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString* target = (UnicodeString*)service.get("za_PPP", status);
+ confirmEqual("test with ja locale", japanese, target);
+ delete target;
+ }
+
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, 0, status);
+ service.getVisibleIDs(ids, status);
+ logln("all visible ids:");
+ for (int i = 0; i < ids.size(); ++i) {
+ const UnicodeString* id = (const UnicodeString*)ids[i];
+ logln(*id);
+ }
+ }
+
+ Locale::setDefault(loc, status);
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ UVector ids(uhash_deleteUnicodeString, uhash_compareUnicodeString, 0, status);
+ service.getVisibleIDs(ids, status);
+ logln("all visible ids:");
+ for (int i = 0; i < ids.size(); ++i) {
+ const UnicodeString* id = (const UnicodeString*)ids[i];
+ logln(*id);
+ }
+ }
+
+#if 0
+ {
+ target = service.get("za_PPP");
+ confirmEqual("test with en locale", root, target);
+ }
+
+ Locale[] locales = service.getAvailableLocales();
+ confirmIdentical("test available locales", locales.length, 6);
+ logln("locales: ");
+ for (int i = 0; i < locales.length; ++i) {
+ log("\n [" + i + "] " + locales[i]);
+ }
+ logln(" ");
+
+ service.registerFactory(new ICUResourceBundleFactory());
+ target = service.get(Locale.JAPAN);
+
+ {
+ int n = 0;
+ List factories = service.factories();
+ Iterator iter = factories.iterator();
+ while (iter.hasNext()) {
+ logln("[" + n++ + "] " + iter.next());
+ }
+ }
+#endif
+ }
+
+void
+ICUServiceTest::testWrapFactory() {
+#if 0
+ final String greeting = "Hello There";
+ final String greetingID = "greeting";
+
+ ICUService service = new ICUService("wrap");
+ service.registerObject(greeting, greetingID);
+
+ logln("test one: " + service.get(greetingID));
+
+ class WrapFactory implements Factory {
+ public Object create(Key key, ICUService service) {
+ if (key.currentID().equals(greetingID)) {
+ Object previous = service.getKey(key, null, this);
+ return "A different greeting: \"" + previous + "\"";
+ }
+ return null;
+ }
+
+ public void updateVisibleIDs(Map result) {
+ result.put("greeting", this);
+ }
+
+ public String getDisplayName(String id, Locale locale) {
+ return "wrap '" + id + "'";
+ }
+ }
+ service.registerFactory(new WrapFactory());
+
+ confirmEqual("wrap test: ", service.get(greetingID), "A different greeting: \"" + greeting + "\"");
+#endif
+
+}
+
+ // misc coverage tests
+void ICUServiceTest::testCoverage() {
+#if 0
+ // Key
+ Key key = new Key("foobar");
+ logln("ID: " + key.id());
+ logln("canonicalID: " + key.canonicalID());
+ logln("currentID: " + key.currentID());
+ logln("has fallback: " + key.fallback());
+
+ // SimpleFactory
+ Object obj = new Object();
+ SimpleFactory sf = new SimpleFactory(obj, "object");
+ try {
+ sf = new SimpleFactory(null, null);
+ errln("didn't throw exception");
+ }
+ catch (IllegalArgumentException e) {
+ logln("OK: " + e.getMessage());
+ }
+ catch (Exception e) {
+ errln("threw wrong exception" + e);
+ }
+ logln(sf.getDisplayName("object", null));
+
+ // ICUService
+ ICUService service = new ICUService();
+ service.registerFactory(sf);
+
+ try {
+ service.get(null, null);
+ errln("didn't throw exception");
+ }
+ catch (NullPointerException e) {
+ logln("OK: " + e.getMessage());
+ }
+ /*
+ catch (Exception e) {
+ errln("threw wrong exception" + e);
+ }
+ */
+ try {
+ service.registerFactory(null);
+ errln("didn't throw exception");
+ }
+ catch (NullPointerException e) {
+ logln("OK: " + e.getMessage());
+ }
+ catch (Exception e) {
+ errln("threw wrong exception" + e);
+ }
+
+ try {
+ service.unregisterFactory(null);
+ errln("didn't throw exception");
+ }
+ catch (NullPointerException e) {
+ logln("OK: " + e.getMessage());
+ }
+ catch (Exception e) {
+ errln("threw wrong exception" + e);
+ }
+
+ logln("object is: " + service.get("object"));
+
+ logln("stats: " + service.stats());
+
+ // ICURWLock
+
+ ICURWLock rwlock = new ICURWLock();
+ rwlock.acquireRead();
+ rwlock.releaseRead();
+
+ rwlock.acquireWrite();
+ rwlock.releaseWrite();
+ logln("stats: " + rwlock.getStats());
+ logln("stats: " + rwlock.clearStats());
+ rwlock.acquireRead();
+ rwlock.releaseRead();
+ rwlock.acquireWrite();
+ rwlock.releaseWrite();
+ logln("stats: " + rwlock.getStats());
+
+ try {
+ rwlock.releaseRead();
+ errln("no error thrown");
+ }
+ catch (InternalError e) {
+ logln("OK: " + e.getMessage());
+ }
+
+ try {
+ rwlock.releaseWrite();
+ errln("no error thrown");
+ }
+ catch (InternalError e) {
+ logln("OK: " + e.getMessage());
+ }
+
+ // ICULocaleService
+
+ // LocaleKey
+
+ // LocaleKey lkey = LocaleKey.create("en_US", "ja_JP");
+ // lkey = LocaleKey.create(null, null);
+ LocaleKey lkey = LocaleKey.createWithCanonicalFallback("en_US", "ja_JP");
+ logln("lkey: " + lkey);
+
+ lkey = LocaleKey.createWithCanonicalFallback(null, null);
+ logln("lkey from null,null: " + lkey);
+
+ // LocaleKeyFactory
+ LocaleKeyFactory lkf = new LKFSubclass(false);
+ logln("lkf: " + lkf);
+ logln("obj: " + lkf.create(lkey, null));
+ logln(lkf.getDisplayName("foo", null));
+ logln(lkf.getDisplayName("bar", null));
+ lkf.updateVisibleIDs(new HashMap());
+
+ LocaleKeyFactory invisibleLKF = new LKFSubclass(false);
+ logln("obj: " + invisibleLKF.create(lkey, null));
+ logln(invisibleLKF.getDisplayName("foo", null));
+ logln(invisibleLKF.getDisplayName("bar", null));
+ invisibleLKF.updateVisibleIDs(new HashMap());
+
+ // ResourceBundleFactory
+ ICUResourceBundleFactory rbf = new ICUResourceBundleFactory();
+ logln("RB: " + rbf.create(lkey, null));
+
+ // ICUNotifier
+ ICUNotifier nf = new ICUNSubclass();
+ try {
+ nf.addListener(null);
+ errln("added null listener");
+ }
+ catch (NullPointerException e) {
+ logln(e.getMessage());
+ }
+ catch (Exception e) {
+ errln("got wrong exception");
+ }
+
+ try {
+ nf.addListener(new WrongListener());
+ errln("added wrong listener");
+ }
+ catch (InternalError e) {
+ logln(e.getMessage());
+ }
+ catch (Exception e) {
+ errln("got wrong exception");
+ }
+
+ try {
+ nf.removeListener(null);
+ errln("removed null listener");
+ }
+ catch (NullPointerException e) {
+ logln(e.getMessage());
+ }
+ catch (Exception e) {
+ errln("got wrong exception");
+ }
+
+ nf.removeListener(new MyListener());
+ nf.notifyChanged();
+ nf.addListener(new MyListener());
+ nf.removeListener(new MyListener());
+#endif
+ }
+
+U_NAMESPACE_END
+
+/* !UCONFIG_NO_SERVICE */
+#endif
+
+
diff --git a/icu4c/source/test/intltest/icusvtst.h b/icu4c/source/test/intltest/icusvtst.h
new file mode 100644
index 00000000000..01b32059e9c
--- /dev/null
+++ b/icu4c/source/test/intltest/icusvtst.h
@@ -0,0 +1,65 @@
+/**
+ *******************************************************************************
+ * Copyright (C) 2001-2002, International Business Machines Corporation and *
+ * others. All Rights Reserved. *
+ *******************************************************************************
+ *
+ *******************************************************************************
+ */
+
+#ifndef ICUSVTST_H
+#define ICUSVTST_H
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_SERVICE
+
+#include "intltest.h"
+
+U_NAMESPACE_BEGIN
+
+class Integer;
+
+class ICUServiceTest : public IntlTest
+{
+ public:
+ ICUServiceTest();
+ ~ICUServiceTest();
+
+ void runIndexedTest(int32_t index, UBool exec, const char* &name, char* par = NULL);
+
+ void testAPI_One(void);
+ void testAPI_Two(void);
+ void testRBF(void);
+ void testNotification(void);
+ void testLocale(void);
+ void testWrapFactory(void);
+ void testCoverage(void);
+
+ private:
+ UnicodeString& lrmsg(UnicodeString& result, const UnicodeString& message, const UObject* lhs, const UObject* rhs) const;
+ void confirmBoolean(const UnicodeString& message, UBool val);
+#if 0
+ void confirmEqual(const UnicodeString& message, const UObject* lhs, const UObject* rhs);
+#else
+ void confirmEqual(const UnicodeString& message, const Integer* lhs, const Integer* rhs);
+ void confirmEqual(const UnicodeString& message, const UnicodeString* lhs, const UnicodeString* rhs);
+ void confirmEqual(const UnicodeString& message, const Locale* lhs, const Locale* rhs);
+#endif
+ void confirmStringsEqual(const UnicodeString& message, const UnicodeString& lhs, const UnicodeString& rhs);
+ void confirmIdentical(const UnicodeString& message, const UObject* lhs, const UObject* rhs);
+ void confirmIdentical(const UnicodeString& message, int32_t lhs, int32_t rhs);
+
+ void msgstr(const UnicodeString& message, UObject* obj, UBool err = TRUE);
+ void logstr(const UnicodeString& message, UObject* obj) {
+ msgstr(message, obj, FALSE);
+ }
+};
+
+U_NAMESPACE_END
+
+/* UCONFIG_NO_SERVICE */
+#endif
+
+/* ICUSVTST_H */
+#endif
diff --git a/icu4c/source/test/intltest/intltest.cpp b/icu4c/source/test/intltest/intltest.cpp
index d56a3609446..9e9e37c67fb 100644
--- a/icu4c/source/test/intltest/intltest.cpp
+++ b/icu4c/source/test/intltest/intltest.cpp
@@ -1154,7 +1154,7 @@ main(int argc, char* argv[])
/* Call it twice to make sure that the defaults were reset. */
/* Call it before the OK message to verify proper cleanup. */
u_cleanup();
- u_cleanup();
+ u_cleanup();
fprintf(stdout, "OK: All tests passed without error.\n");
}else{
diff --git a/icu4c/source/test/intltest/intltest.dsp b/icu4c/source/test/intltest/intltest.dsp
index 36e598a4ec0..4138e294002 100644
--- a/icu4c/source/test/intltest/intltest.dsp
+++ b/icu4c/source/test/intltest/intltest.dsp
@@ -270,6 +270,10 @@ SOURCE=.\hxuntrts.cpp
# End Source File
# Begin Source File
+SOURCE=.\icusvtst.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\intltest.cpp
# End Source File
# Begin Source File
@@ -647,6 +651,10 @@ SOURCE=.\hxuntrts.h
# End Source File
# Begin Source File
+SOURCE=.\icusvtst.h
+# End Source File
+# Begin Source File
+
SOURCE=.\intltest.h
# End Source File
# Begin Source File
diff --git a/icu4c/source/test/intltest/itmajor.cpp b/icu4c/source/test/intltest/itmajor.cpp
index d1f6ee8b711..08e009bb878 100644
--- a/icu4c/source/test/intltest/itmajor.cpp
+++ b/icu4c/source/test/intltest/itmajor.cpp
@@ -29,6 +29,7 @@
#include "regextst.h"
#include "tstnorm.h"
#include "canittst.h"
+#include "icusvtst.h"
#define CASE_SUITE(id, suite) case id: \
name = #suite; \
@@ -135,6 +136,15 @@ void MajorTestLevel::runIndexedTest( int32_t index, UBool exec, const char* &nam
#endif /* ICU_UNICODECONVERTER_USE_DEPRECATES */
break;
+ case 10: name = "icuserv";
+#if !UCONFIG_NO_SERVICE
+ if (exec) {
+ logln("TestSuite ICUService---"); logln();
+ ICUServiceTest test;
+ callTest(test, par);
+ }
+#endif
+ break;
default: name = ""; break;
}
}