summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/common/serv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu/source/common/serv.cpp')
-rw-r--r--intl/icu/source/common/serv.cpp968
1 files changed, 968 insertions, 0 deletions
diff --git a/intl/icu/source/common/serv.cpp b/intl/icu/source/common/serv.cpp
new file mode 100644
index 0000000000..5248f7c192
--- /dev/null
+++ b/intl/icu/source/common/serv.cpp
@@ -0,0 +1,968 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+/**
+*******************************************************************************
+* Copyright (C) 2001-2014, International Business Machines Corporation.
+* All Rights Reserved.
+*******************************************************************************
+*/
+
+#include "unicode/utypes.h"
+#include "unicode/localpointer.h"
+
+#if !UCONFIG_NO_SERVICE
+
+#include "serv.h"
+#include "umutex.h"
+
+#undef SERVICE_REFCOUNT
+
+// in case we use the refcount stuff
+
+U_NAMESPACE_BEGIN
+
+/*
+******************************************************************
+*/
+
+const char16_t ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */
+
+ICUServiceKey::ICUServiceKey(const UnicodeString& id)
+: _id(id) {
+}
+
+ICUServiceKey::~ICUServiceKey()
+{
+}
+
+const UnicodeString&
+ICUServiceKey::getID() const
+{
+ return _id;
+}
+
+UnicodeString&
+ICUServiceKey::canonicalID(UnicodeString& result) const
+{
+ return result.append(_id);
+}
+
+UnicodeString&
+ICUServiceKey::currentID(UnicodeString& result) const
+{
+ return canonicalID(result);
+}
+
+UnicodeString&
+ICUServiceKey::currentDescriptor(UnicodeString& result) const
+{
+ prefix(result);
+ result.append(PREFIX_DELIMITER);
+ return currentID(result);
+}
+
+UBool
+ICUServiceKey::fallback()
+{
+ return false;
+}
+
+UBool
+ICUServiceKey::isFallbackOf(const UnicodeString& id) const
+{
+ return id == _id;
+}
+
+UnicodeString&
+ICUServiceKey::prefix(UnicodeString& result) const
+{
+ return result;
+}
+
+UnicodeString&
+ICUServiceKey::parsePrefix(UnicodeString& result)
+{
+ int32_t n = result.indexOf(PREFIX_DELIMITER);
+ if (n < 0) {
+ n = 0;
+ }
+ result.remove(n);
+ return result;
+}
+
+UnicodeString&
+ICUServiceKey::parseSuffix(UnicodeString& result)
+{
+ int32_t n = result.indexOf(PREFIX_DELIMITER);
+ if (n >= 0) {
+ result.remove(0, n+1);
+ }
+ return result;
+}
+
+#ifdef SERVICE_DEBUG
+UnicodeString&
+ICUServiceKey::debug(UnicodeString& result) const
+{
+ debugClass(result);
+ result.append((UnicodeString)" id: ");
+ result.append(_id);
+ return result;
+}
+
+UnicodeString&
+ICUServiceKey::debugClass(UnicodeString& result) const
+{
+ return result.append((UnicodeString)"ICUServiceKey");
+}
+#endif
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
+
+/*
+******************************************************************
+*/
+
+ICUServiceFactory::~ICUServiceFactory() {}
+
+SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
+: _instance(instanceToAdopt), _id(id), _visible(visible)
+{
+}
+
+SimpleFactory::~SimpleFactory()
+{
+ delete _instance;
+}
+
+UObject*
+SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
+{
+ if (U_SUCCESS(status)) {
+ UnicodeString temp;
+ if (_id == key.currentID(temp)) {
+ return service->cloneInstance(_instance);
+ }
+ }
+ return nullptr;
+}
+
+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;
+}
+
+#ifdef SERVICE_DEBUG
+UnicodeString&
+SimpleFactory::debug(UnicodeString& toAppendTo) const
+{
+ debugClass(toAppendTo);
+ toAppendTo.append((UnicodeString)" id: ");
+ toAppendTo.append(_id);
+ toAppendTo.append((UnicodeString)", visible: ");
+ toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F");
+ return toAppendTo;
+}
+
+UnicodeString&
+SimpleFactory::debugClass(UnicodeString& toAppendTo) const
+{
+ return toAppendTo.append((UnicodeString)"SimpleFactory");
+}
+#endif
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
+
+/*
+******************************************************************
+*/
+
+ServiceListener::~ServiceListener() {}
+
+UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener)
+
+/*
+******************************************************************
+*/
+
+// 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 : public UMemory {
+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 resource has been released.
+ */
+ CacheEntry* unref() {
+ if ((--refcount) == 0) {
+ delete this;
+ return nullptr;
+ }
+ 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;
+ }
+};
+
+// Deleter for serviceCache
+U_CDECL_BEGIN
+static void U_CALLCONV
+cacheDeleter(void* obj) {
+ U_NAMESPACE_USE ((CacheEntry*)obj)->unref();
+}
+
+U_CDECL_END
+
+/*
+******************************************************************
+*/
+
+class DNCache : public UMemory {
+public:
+ Hashtable cache;
+ const Locale locale;
+
+ DNCache(const Locale& _locale)
+ : cache(), locale(_locale)
+ {
+ // cache.setKeyDeleter(uprv_deleteUObject);
+ }
+};
+
+
+/*
+******************************************************************
+*/
+
+StringPair*
+StringPair::create(const UnicodeString& displayName,
+ const UnicodeString& id,
+ UErrorCode& status)
+{
+ if (U_SUCCESS(status)) {
+ StringPair* sp = new StringPair(displayName, id);
+ if (sp == nullptr || sp->isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete sp;
+ return nullptr;
+ }
+ return sp;
+ }
+ return nullptr;
+}
+
+UBool
+StringPair::isBogus() const {
+ return displayName.isBogus() || id.isBogus();
+}
+
+StringPair::StringPair(const UnicodeString& _displayName,
+ const UnicodeString& _id)
+: displayName(_displayName)
+, id(_id)
+{
+}
+
+U_CDECL_BEGIN
+static void U_CALLCONV
+userv_deleteStringPair(void *obj) {
+ U_NAMESPACE_USE delete (StringPair*) obj;
+}
+U_CDECL_END
+
+/*
+******************************************************************
+*/
+
+static UMutex lock;
+
+ICUService::ICUService()
+: name()
+, timestamp(0)
+, factories(nullptr)
+, serviceCache(nullptr)
+, idCache(nullptr)
+, dnCache(nullptr)
+{
+}
+
+ICUService::ICUService(const UnicodeString& newName)
+: name(newName)
+, timestamp(0)
+, factories(nullptr)
+, serviceCache(nullptr)
+, idCache(nullptr)
+, dnCache(nullptr)
+{
+}
+
+ICUService::~ICUService()
+{
+ {
+ Mutex mutex(&lock);
+ clearCaches();
+ delete factories;
+ factories = nullptr;
+ }
+}
+
+UObject*
+ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
+{
+ return get(descriptor, nullptr, status);
+}
+
+UObject*
+ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
+{
+ UObject* result = nullptr;
+ ICUServiceKey* key = createKey(&descriptor, status);
+ if (key) {
+ result = getKey(*key, actualReturn, status);
+ delete key;
+ }
+ return result;
+}
+
+UObject*
+ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const
+{
+ return getKey(key, nullptr, status);
+}
+
+// this is a vector that subclasses of ICUService can override to further customize the result object
+// before returning it. All other public get functions should call this one.
+
+UObject*
+ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const
+{
+ return getKey(key, actualReturn, nullptr, status);
+}
+
+// make it possible to call reentrantly on systems that don't have reentrant mutexes.
+// we can use this simple approach since we know the situation where we're calling
+// reentrantly even without knowing the thread.
+class XMutex : public UMemory {
+public:
+ inline XMutex(UMutex *mutex, UBool reentering)
+ : fMutex(mutex)
+ , fActive(!reentering)
+ {
+ if (fActive) umtx_lock(fMutex);
+ }
+ inline ~XMutex() {
+ if (fActive) umtx_unlock(fMutex);
+ }
+
+private:
+ UMutex *fMutex;
+ UBool fActive;
+};
+
+// called only by factories, treat as private
+UObject*
+ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
+{
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+
+ if (isDefault()) {
+ return handleDefault(key, actualReturn, status);
+ }
+
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+
+ CacheEntry* result = nullptr;
+ {
+ // 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.
+
+ // if factory is not null, we're calling from within the mutex,
+ // and since some unix machines don't have reentrant mutexes we
+ // need to make sure not to try to lock it again.
+ XMutex mutex(&lock, factory != nullptr);
+
+ if (serviceCache == nullptr) {
+ ncthis->serviceCache = new Hashtable(status);
+ if (ncthis->serviceCache == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return nullptr;
+ }
+ if (U_FAILURE(status)) {
+ delete serviceCache;
+ return nullptr;
+ }
+ serviceCache->setValueDeleter(cacheDeleter);
+ }
+
+ UnicodeString currentDescriptor;
+ LocalPointer<UVector> cacheDescriptorList;
+ UBool putInCache = false;
+
+ int32_t startIndex = 0;
+ int32_t limit = factories->size();
+ UBool cacheResult = true;
+
+ if (factory != nullptr) {
+ for (int32_t i = 0; i < limit; ++i) {
+ if (factory == (const ICUServiceFactory*)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 nullptr;
+ }
+ cacheResult = false;
+ }
+
+ do {
+ currentDescriptor.remove();
+ key.currentDescriptor(currentDescriptor);
+ result = (CacheEntry*)serviceCache->get(currentDescriptor);
+ if (result != nullptr) {
+ break;
+ }
+
+ // 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 = true;
+
+ int32_t index = startIndex;
+ while (index < limit) {
+ ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++);
+ LocalPointer<UObject> service(f->create(key, this, status));
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ if (service.isValid()) {
+ result = new CacheEntry(currentDescriptor, service.getAlias());
+ if (result == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return nullptr;
+ }
+ service.orphan(); // result now owns service.
+
+ goto outerEnd;
+ }
+ }
+
+ // 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.isNull()) {
+ cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, nullptr, 5, status), status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ }
+
+ LocalPointer<UnicodeString> idToCache(new UnicodeString(currentDescriptor), status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ if (idToCache->isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return nullptr;
+ }
+ cacheDescriptorList->adoptElement(idToCache.orphan(), status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ } while (key.fallback());
+outerEnd:
+
+ if (result != nullptr) {
+ if (putInCache && cacheResult) {
+ serviceCache->put(result->actualDescriptor, result, status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+
+ if (cacheDescriptorList.isValid()) {
+ for (int32_t i = cacheDescriptorList->size(); --i >= 0;) {
+ UnicodeString* desc = (UnicodeString*)cacheDescriptorList->elementAt(i);
+
+ serviceCache->put(*desc, result, status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+
+ result->ref();
+ cacheDescriptorList->removeElementAt(i);
+ }
+ }
+ }
+
+ if (actualReturn != nullptr) {
+ // strip null prefix
+ if (result->actualDescriptor.indexOf((char16_t)0x2f) == 0) { // U+002f=slash (/)
+ actualReturn->remove();
+ actualReturn->append(result->actualDescriptor,
+ 1,
+ result->actualDescriptor.length() - 1);
+ } else {
+ *actualReturn = result->actualDescriptor;
+ }
+
+ if (actualReturn->isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ delete result;
+ return nullptr;
+ }
+ }
+
+ UObject* service = cloneInstance(result->service);
+ if (putInCache && !cacheResult) {
+ delete result;
+ }
+ return service;
+ }
+ }
+
+ return handleDefault(key, actualReturn, status);
+}
+
+UObject*
+ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
+{
+ return nullptr;
+}
+
+UVector&
+ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
+ return getVisibleIDs(result, nullptr, status);
+}
+
+UVector&
+ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
+{
+ result.removeAllElements();
+
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ UObjectDeleter *savedDeleter = result.setDeleter(uprv_deleteUObject);
+
+ {
+ Mutex mutex(&lock);
+ const Hashtable* map = getVisibleIDMap(status);
+ if (map != nullptr) {
+ ICUServiceKey* fallbackKey = createKey(matchID, status);
+
+ for (int32_t pos = UHASH_FIRST; U_SUCCESS(status); ) {
+ const UHashElement* e = map->nextElement(pos);
+ if (e == nullptr) {
+ break;
+ }
+
+ const UnicodeString* id = (const UnicodeString*)e->key.pointer;
+ if (fallbackKey != nullptr) {
+ if (!fallbackKey->isFallbackOf(*id)) {
+ continue;
+ }
+ }
+
+ LocalPointer<UnicodeString> idClone(id->clone(), status);
+ result.adoptElement(idClone.orphan(), status);
+ }
+ delete fallbackKey;
+ }
+ }
+ if (U_FAILURE(status)) {
+ result.removeAllElements();
+ }
+ result.setDeleter(savedDeleter);
+ return result;
+}
+
+const Hashtable*
+ICUService::getVisibleIDMap(UErrorCode& status) const {
+ if (U_FAILURE(status)) return nullptr;
+
+ // must only be called when lock is already held
+
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+ if (idCache == nullptr) {
+ ncthis->idCache = new Hashtable(status);
+ if (idCache == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else if (factories != nullptr) {
+ for (int32_t pos = factories->size(); --pos >= 0;) {
+ ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos);
+ f->updateVisibleIDs(*idCache, status);
+ }
+ if (U_FAILURE(status)) {
+ delete idCache;
+ ncthis->idCache = nullptr;
+ }
+ }
+ }
+
+ 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
+{
+ {
+ UErrorCode status = U_ZERO_ERROR;
+ Mutex mutex(&lock);
+ const Hashtable* map = getVisibleIDMap(status);
+ if (map != nullptr) {
+ ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
+ if (f != nullptr) {
+ f->getDisplayName(id, locale, result);
+ return result;
+ }
+
+ // fallback
+ status = U_ZERO_ERROR;
+ ICUServiceKey* fallbackKey = createKey(&id, status);
+ while (fallbackKey != nullptr && fallbackKey->fallback()) {
+ UnicodeString us;
+ fallbackKey->currentID(us);
+ f = (ICUServiceFactory*)map->get(us);
+ if (f != nullptr) {
+ f->getDisplayName(id, locale, result);
+ delete fallbackKey;
+ return result;
+ }
+ }
+ delete fallbackKey;
+ }
+ }
+ result.setToBogus();
+ return result;
+}
+
+UVector&
+ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
+{
+ return getDisplayNames(result, Locale::getDefault(), nullptr, status);
+}
+
+
+UVector&
+ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
+{
+ return getDisplayNames(result, locale, nullptr, status);
+}
+
+UVector&
+ICUService::getDisplayNames(UVector& result,
+ const Locale& locale,
+ const UnicodeString* matchID,
+ UErrorCode& status) const
+{
+ result.removeAllElements();
+ result.setDeleter(userv_deleteStringPair);
+ if (U_SUCCESS(status)) {
+ ICUService* ncthis = (ICUService*)this; // cast away semantic const
+ Mutex mutex(&lock);
+
+ if (dnCache != nullptr && dnCache->locale != locale) {
+ delete dnCache;
+ ncthis->dnCache = nullptr;
+ }
+
+ if (dnCache == nullptr) {
+ const Hashtable* m = getVisibleIDMap(status);
+ if (U_FAILURE(status)) {
+ return result;
+ }
+ ncthis->dnCache = new DNCache(locale);
+ if (dnCache == nullptr) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return result;
+ }
+
+ int32_t pos = UHASH_FIRST;
+ const UHashElement* entry = nullptr;
+ while ((entry = m->nextElement(pos)) != nullptr) {
+ const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
+ ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer;
+ UnicodeString dname;
+ f->getDisplayName(*id, locale, dname);
+ if (dname.isBogus()) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
+ if (U_SUCCESS(status)) {
+ continue;
+ }
+ }
+ delete dnCache;
+ ncthis->dnCache = nullptr;
+ return result;
+ }
+ }
+ }
+
+ ICUServiceKey* matchKey = createKey(matchID, status);
+ /* To ensure that all elements in the hashtable are iterated, set pos to -1.
+ * nextElement(pos) will skip the position at pos and begin the iteration
+ * at the next position, which in this case will be 0.
+ */
+ int32_t pos = UHASH_FIRST;
+ const UHashElement *entry = nullptr;
+ while ((entry = dnCache->cache.nextElement(pos)) != nullptr) {
+ const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
+ if (matchKey != nullptr && !matchKey->isFallbackOf(*id)) {
+ continue;
+ }
+ const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
+ StringPair* sp = StringPair::create(*id, *dn, status);
+ result.adoptElement(sp, status);
+ if (U_FAILURE(status)) {
+ result.removeAllElements();
+ break;
+ }
+ }
+ delete matchKey;
+
+ return result;
+}
+
+URegistryKey
+ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
+{
+ return registerInstance(objToAdopt, id, true, status);
+}
+
+URegistryKey
+ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
+{
+ ICUServiceKey* key = createKey(&id, status);
+ if (key != nullptr) {
+ UnicodeString canonicalID;
+ key->canonicalID(canonicalID);
+ delete key;
+
+ ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
+ if (f != nullptr) {
+ return registerFactory(f, status);
+ }
+ }
+ delete objToAdopt;
+ return nullptr;
+}
+
+ICUServiceFactory*
+ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
+{
+ if (U_SUCCESS(status)) {
+ if ((objToAdopt != nullptr) && (!id.isBogus())) {
+ return new SimpleFactory(objToAdopt, id, visible);
+ }
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+ return nullptr;
+}
+
+URegistryKey
+ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status)
+{
+ LocalPointer<ICUServiceFactory>lpFactoryToAdopt(factoryToAdopt);
+ if (U_FAILURE(status) || factoryToAdopt == nullptr) {
+ return nullptr;
+ }
+ {
+ Mutex mutex(&lock);
+
+ if (factories == nullptr) {
+ LocalPointer<UVector> lpFactories(new UVector(uprv_deleteUObject, nullptr, status), status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ factories = lpFactories.orphan();
+ }
+ factories->insertElementAt(lpFactoryToAdopt.orphan(), 0, status);
+ if (U_SUCCESS(status)) {
+ clearCaches();
+ }
+ } // Close of mutex lock block.
+
+ if (U_SUCCESS(status)) {
+ notifyChanged();
+ return (URegistryKey)factoryToAdopt;
+ } else {
+ return nullptr;
+ }
+}
+
+UBool
+ICUService::unregister(URegistryKey rkey, UErrorCode& status)
+{
+ ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
+ UBool result = false;
+ if (factory != nullptr && factories != nullptr) {
+ Mutex mutex(&lock);
+
+ if (factories->removeElement(factory)) {
+ clearCaches();
+ result = true;
+ } else {
+ status = U_ILLEGAL_ARGUMENT_ERROR;
+ delete factory;
+ }
+ }
+ if (result) {
+ notifyChanged();
+ }
+ return result;
+}
+
+void
+ICUService::reset()
+{
+ {
+ Mutex mutex(&lock);
+ reInitializeFactories();
+ clearCaches();
+ }
+ notifyChanged();
+}
+
+void
+ICUService::reInitializeFactories()
+{
+ if (factories != nullptr) {
+ factories->removeAllElements();
+ }
+}
+
+UBool
+ICUService::isDefault() const
+{
+ return countFactories() == 0;
+}
+
+ICUServiceKey*
+ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
+{
+ return (U_FAILURE(status) || id == nullptr) ? nullptr : new ICUServiceKey(*id);
+}
+
+void
+ICUService::clearCaches()
+{
+ // callers synchronize before use
+ ++timestamp;
+ delete dnCache;
+ dnCache = nullptr;
+ delete idCache;
+ idCache = nullptr;
+ delete serviceCache; serviceCache = nullptr;
+}
+
+void
+ICUService::clearServiceCache()
+{
+ // callers synchronize before use
+ delete serviceCache; serviceCache = nullptr;
+}
+
+UBool
+ICUService::acceptsListener(const EventListener& l) const
+{
+ return dynamic_cast<const ServiceListener*>(&l) != nullptr;
+}
+
+void
+ICUService::notifyListener(EventListener& l) const
+{
+ (static_cast<ServiceListener&>(l)).serviceChanged(*this);
+}
+
+UnicodeString&
+ICUService::getName(UnicodeString& result) const
+{
+ return result.append(name);
+}
+
+int32_t
+ICUService::countFactories() const
+{
+ return factories == nullptr ? 0 : factories->size();
+}
+
+int32_t
+ICUService::getTimestamp() const
+{
+ return timestamp;
+}
+
+U_NAMESPACE_END
+
+/* UCONFIG_NO_SERVICE */
+#endif