michael@0: /** michael@0: ******************************************************************************* michael@0: * Copyright (C) 2001-2012, International Business Machines Corporation. michael@0: * All Rights Reserved. michael@0: ******************************************************************************* michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_SERVICE michael@0: michael@0: #include "serv.h" michael@0: #include "umutex.h" michael@0: michael@0: #undef SERVICE_REFCOUNT michael@0: michael@0: // in case we use the refcount stuff michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */ michael@0: michael@0: ICUServiceKey::ICUServiceKey(const UnicodeString& id) michael@0: : _id(id) { michael@0: } michael@0: michael@0: ICUServiceKey::~ICUServiceKey() michael@0: { michael@0: } michael@0: michael@0: const UnicodeString& michael@0: ICUServiceKey::getID() const michael@0: { michael@0: return _id; michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::canonicalID(UnicodeString& result) const michael@0: { michael@0: return result.append(_id); michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::currentID(UnicodeString& result) const michael@0: { michael@0: return canonicalID(result); michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::currentDescriptor(UnicodeString& result) const michael@0: { michael@0: prefix(result); michael@0: result.append(PREFIX_DELIMITER); michael@0: return currentID(result); michael@0: } michael@0: michael@0: UBool michael@0: ICUServiceKey::fallback() michael@0: { michael@0: return FALSE; michael@0: } michael@0: michael@0: UBool michael@0: ICUServiceKey::isFallbackOf(const UnicodeString& id) const michael@0: { michael@0: return id == _id; michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::prefix(UnicodeString& result) const michael@0: { michael@0: return result; michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::parsePrefix(UnicodeString& result) michael@0: { michael@0: int32_t n = result.indexOf(PREFIX_DELIMITER); michael@0: if (n < 0) { michael@0: n = 0; michael@0: } michael@0: result.remove(n); michael@0: return result; michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::parseSuffix(UnicodeString& result) michael@0: { michael@0: int32_t n = result.indexOf(PREFIX_DELIMITER); michael@0: if (n >= 0) { michael@0: result.remove(0, n+1); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: #ifdef SERVICE_DEBUG michael@0: UnicodeString& michael@0: ICUServiceKey::debug(UnicodeString& result) const michael@0: { michael@0: debugClass(result); michael@0: result.append(" id: "); michael@0: result.append(_id); michael@0: return result; michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUServiceKey::debugClass(UnicodeString& result) const michael@0: { michael@0: return result.append("ICUServiceKey"); michael@0: } michael@0: #endif michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey) michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: ICUServiceFactory::~ICUServiceFactory() {} michael@0: michael@0: SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) michael@0: : _instance(instanceToAdopt), _id(id), _visible(visible) michael@0: { michael@0: } michael@0: michael@0: SimpleFactory::~SimpleFactory() michael@0: { michael@0: delete _instance; michael@0: } michael@0: michael@0: UObject* michael@0: SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const michael@0: { michael@0: if (U_SUCCESS(status)) { michael@0: UnicodeString temp; michael@0: if (_id == key.currentID(temp)) { michael@0: return service->cloneInstance(_instance); michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: void michael@0: SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const michael@0: { michael@0: if (_visible) { michael@0: result.put(_id, (void*)this, status); // cast away const michael@0: } else { michael@0: result.remove(_id); michael@0: } michael@0: } michael@0: michael@0: UnicodeString& michael@0: SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const michael@0: { michael@0: if (_visible && _id == id) { michael@0: result = _id; michael@0: } else { michael@0: result.setToBogus(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: #ifdef SERVICE_DEBUG michael@0: UnicodeString& michael@0: SimpleFactory::debug(UnicodeString& toAppendTo) const michael@0: { michael@0: debugClass(toAppendTo); michael@0: toAppendTo.append(" id: "); michael@0: toAppendTo.append(_id); michael@0: toAppendTo.append(", visible: "); michael@0: toAppendTo.append(_visible ? "T" : "F"); michael@0: return toAppendTo; michael@0: } michael@0: michael@0: UnicodeString& michael@0: SimpleFactory::debugClass(UnicodeString& toAppendTo) const michael@0: { michael@0: return toAppendTo.append("SimpleFactory"); michael@0: } michael@0: #endif michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory) michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: ServiceListener::~ServiceListener() {} michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener) michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: // Record the actual id for this service in the cache, so we can return it michael@0: // even if we succeed later with a different id. michael@0: class CacheEntry : public UMemory { michael@0: private: michael@0: int32_t refcount; michael@0: michael@0: public: michael@0: UnicodeString actualDescriptor; michael@0: UObject* service; michael@0: michael@0: /** michael@0: * Releases a reference to the shared resource. michael@0: */ michael@0: ~CacheEntry() { michael@0: delete service; michael@0: } michael@0: michael@0: CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) michael@0: : refcount(1), actualDescriptor(_actualDescriptor), service(_service) { michael@0: } michael@0: michael@0: /** michael@0: * Instantiation creates an initial reference, so don't call this michael@0: * unless you're creating a new pointer to this. Management of michael@0: * that pointer will have to know how to deal with refcounts. michael@0: * Return true if the resource has not already been released. michael@0: */ michael@0: CacheEntry* ref() { michael@0: ++refcount; michael@0: return this; michael@0: } michael@0: michael@0: /** michael@0: * Destructions removes a reference, so don't call this unless michael@0: * you're removing pointer to this somewhere. Management of that michael@0: * pointer will have to know how to deal with refcounts. Once michael@0: * the refcount drops to zero, the resource is released. Return michael@0: * false if the resouce has been released. michael@0: */ michael@0: CacheEntry* unref() { michael@0: if ((--refcount) == 0) { michael@0: delete this; michael@0: return NULL; michael@0: } michael@0: return this; michael@0: } michael@0: michael@0: /** michael@0: * Return TRUE if there is at least one reference to this and the michael@0: * resource has not been released. michael@0: */ michael@0: UBool isShared() const { michael@0: return refcount > 1; michael@0: } michael@0: }; michael@0: michael@0: // UObjectDeleter for serviceCache michael@0: U_CDECL_BEGIN michael@0: static void U_CALLCONV michael@0: cacheDeleter(void* obj) { michael@0: U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); michael@0: } michael@0: michael@0: /** michael@0: * Deleter for UObjects michael@0: */ michael@0: static void U_CALLCONV michael@0: deleteUObject(void *obj) { michael@0: U_NAMESPACE_USE delete (UObject*) obj; michael@0: } michael@0: U_CDECL_END michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: class DNCache : public UMemory { michael@0: public: michael@0: Hashtable cache; michael@0: const Locale locale; michael@0: michael@0: DNCache(const Locale& _locale) michael@0: : cache(), locale(_locale) michael@0: { michael@0: // cache.setKeyDeleter(uprv_deleteUObject); michael@0: } michael@0: }; michael@0: michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: StringPair* michael@0: StringPair::create(const UnicodeString& displayName, michael@0: const UnicodeString& id, michael@0: UErrorCode& status) michael@0: { michael@0: if (U_SUCCESS(status)) { michael@0: StringPair* sp = new StringPair(displayName, id); michael@0: if (sp == NULL || sp->isBogus()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: delete sp; michael@0: return NULL; michael@0: } michael@0: return sp; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: UBool michael@0: StringPair::isBogus() const { michael@0: return displayName.isBogus() || id.isBogus(); michael@0: } michael@0: michael@0: StringPair::StringPair(const UnicodeString& _displayName, michael@0: const UnicodeString& _id) michael@0: : displayName(_displayName) michael@0: , id(_id) michael@0: { michael@0: } michael@0: michael@0: U_CDECL_BEGIN michael@0: static void U_CALLCONV michael@0: userv_deleteStringPair(void *obj) { michael@0: U_NAMESPACE_USE delete (StringPair*) obj; michael@0: } michael@0: U_CDECL_END michael@0: michael@0: /* michael@0: ****************************************************************** michael@0: */ michael@0: michael@0: static UMutex lock = U_MUTEX_INITIALIZER; michael@0: michael@0: ICUService::ICUService() michael@0: : name() michael@0: , timestamp(0) michael@0: , factories(NULL) michael@0: , serviceCache(NULL) michael@0: , idCache(NULL) michael@0: , dnCache(NULL) michael@0: { michael@0: } michael@0: michael@0: ICUService::ICUService(const UnicodeString& newName) michael@0: : name(newName) michael@0: , timestamp(0) michael@0: , factories(NULL) michael@0: , serviceCache(NULL) michael@0: , idCache(NULL) michael@0: , dnCache(NULL) michael@0: { michael@0: } michael@0: michael@0: ICUService::~ICUService() michael@0: { michael@0: { michael@0: Mutex mutex(&lock); michael@0: clearCaches(); michael@0: delete factories; michael@0: factories = NULL; michael@0: } michael@0: } michael@0: michael@0: UObject* michael@0: ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const michael@0: { michael@0: return get(descriptor, NULL, status); michael@0: } michael@0: michael@0: UObject* michael@0: ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const michael@0: { michael@0: UObject* result = NULL; michael@0: ICUServiceKey* key = createKey(&descriptor, status); michael@0: if (key) { michael@0: result = getKey(*key, actualReturn, status); michael@0: delete key; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: UObject* michael@0: ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const michael@0: { michael@0: return getKey(key, NULL, status); michael@0: } michael@0: michael@0: // this is a vector that subclasses of ICUService can override to further customize the result object michael@0: // before returning it. All other public get functions should call this one. michael@0: michael@0: UObject* michael@0: ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const michael@0: { michael@0: return getKey(key, actualReturn, NULL, status); michael@0: } michael@0: michael@0: // make it possible to call reentrantly on systems that don't have reentrant mutexes. michael@0: // we can use this simple approach since we know the situation where we're calling michael@0: // reentrantly even without knowing the thread. michael@0: class XMutex : public UMemory { michael@0: public: michael@0: inline XMutex(UMutex *mutex, UBool reentering) michael@0: : fMutex(mutex) michael@0: , fActive(!reentering) michael@0: { michael@0: if (fActive) umtx_lock(fMutex); michael@0: } michael@0: inline ~XMutex() { michael@0: if (fActive) umtx_unlock(fMutex); michael@0: } michael@0: michael@0: private: michael@0: UMutex *fMutex; michael@0: UBool fActive; michael@0: }; michael@0: michael@0: struct UVectorDeleter { michael@0: UVector* _obj; michael@0: UVectorDeleter() : _obj(NULL) {} michael@0: ~UVectorDeleter() { delete _obj; } michael@0: }; michael@0: michael@0: // called only by factories, treat as private michael@0: UObject* michael@0: ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const michael@0: { michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: michael@0: if (isDefault()) { michael@0: return handleDefault(key, actualReturn, status); michael@0: } michael@0: michael@0: ICUService* ncthis = (ICUService*)this; // cast away semantic const michael@0: michael@0: CacheEntry* result = NULL; michael@0: { michael@0: // The factory list can't be modified until we're done, michael@0: // otherwise we might update the cache with an invalid result. michael@0: // The cache has to stay in synch with the factory list. michael@0: // ICU doesn't have monitors so we can't use rw locks, so michael@0: // we single-thread everything using this service, for now. michael@0: michael@0: // if factory is not null, we're calling from within the mutex, michael@0: // and since some unix machines don't have reentrant mutexes we michael@0: // need to make sure not to try to lock it again. michael@0: XMutex mutex(&lock, factory != NULL); michael@0: michael@0: if (serviceCache == NULL) { michael@0: ncthis->serviceCache = new Hashtable(status); michael@0: if (ncthis->serviceCache == NULL) { michael@0: return NULL; michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: delete serviceCache; michael@0: return NULL; michael@0: } michael@0: serviceCache->setValueDeleter(cacheDeleter); michael@0: } michael@0: michael@0: UnicodeString currentDescriptor; michael@0: UVectorDeleter cacheDescriptorList; michael@0: UBool putInCache = FALSE; michael@0: michael@0: int32_t startIndex = 0; michael@0: int32_t limit = factories->size(); michael@0: UBool cacheResult = TRUE; michael@0: michael@0: if (factory != NULL) { michael@0: for (int32_t i = 0; i < limit; ++i) { michael@0: if (factory == (const ICUServiceFactory*)factories->elementAt(i)) { michael@0: startIndex = i + 1; michael@0: break; michael@0: } michael@0: } michael@0: if (startIndex == 0) { michael@0: // throw new InternalError("Factory " + factory + "not registered with service: " + this); michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return NULL; michael@0: } michael@0: cacheResult = FALSE; michael@0: } michael@0: michael@0: do { michael@0: currentDescriptor.remove(); michael@0: key.currentDescriptor(currentDescriptor); michael@0: result = (CacheEntry*)serviceCache->get(currentDescriptor); michael@0: if (result != NULL) { michael@0: break; michael@0: } michael@0: michael@0: // first test of cache failed, so we'll have to update michael@0: // the cache if we eventually succeed-- that is, if we're michael@0: // going to update the cache at all. michael@0: putInCache = TRUE; michael@0: michael@0: int32_t index = startIndex; michael@0: while (index < limit) { michael@0: ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++); michael@0: UObject* service = f->create(key, this, status); michael@0: if (U_FAILURE(status)) { michael@0: delete service; michael@0: return NULL; michael@0: } michael@0: if (service != NULL) { michael@0: result = new CacheEntry(currentDescriptor, service); michael@0: if (result == NULL) { michael@0: delete service; michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: goto outerEnd; michael@0: } michael@0: } michael@0: michael@0: // prepare to load the cache with all additional ids that michael@0: // will resolve to result, assuming we'll succeed. We michael@0: // don't want to keep querying on an id that's going to michael@0: // fallback to the one that succeeded, we want to hit the michael@0: // cache the first time next goaround. michael@0: if (cacheDescriptorList._obj == NULL) { michael@0: cacheDescriptorList._obj = new UVector(uprv_deleteUObject, NULL, 5, status); michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: } michael@0: UnicodeString* idToCache = new UnicodeString(currentDescriptor); michael@0: if (idToCache == NULL || idToCache->isBogus()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: michael@0: cacheDescriptorList._obj->addElement(idToCache, status); michael@0: if (U_FAILURE(status)) { michael@0: return NULL; michael@0: } michael@0: } while (key.fallback()); michael@0: outerEnd: michael@0: michael@0: if (result != NULL) { michael@0: if (putInCache && cacheResult) { michael@0: serviceCache->put(result->actualDescriptor, result, status); michael@0: if (U_FAILURE(status)) { michael@0: delete result; michael@0: return NULL; michael@0: } michael@0: michael@0: if (cacheDescriptorList._obj != NULL) { michael@0: for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) { michael@0: UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i); michael@0: serviceCache->put(*desc, result, status); michael@0: if (U_FAILURE(status)) { michael@0: delete result; michael@0: return NULL; michael@0: } michael@0: michael@0: result->ref(); michael@0: cacheDescriptorList._obj->removeElementAt(i); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (actualReturn != NULL) { michael@0: // strip null prefix michael@0: if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/) michael@0: actualReturn->remove(); michael@0: actualReturn->append(result->actualDescriptor, michael@0: 1, michael@0: result->actualDescriptor.length() - 1); michael@0: } else { michael@0: *actualReturn = result->actualDescriptor; michael@0: } michael@0: michael@0: if (actualReturn->isBogus()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: delete result; michael@0: return NULL; michael@0: } michael@0: } michael@0: michael@0: UObject* service = cloneInstance(result->service); michael@0: if (putInCache && !cacheResult) { michael@0: delete result; michael@0: } michael@0: return service; michael@0: } michael@0: } michael@0: michael@0: return handleDefault(key, actualReturn, status); michael@0: } michael@0: michael@0: UObject* michael@0: ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const michael@0: { michael@0: return NULL; michael@0: } michael@0: michael@0: UVector& michael@0: ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { michael@0: return getVisibleIDs(result, NULL, status); michael@0: } michael@0: michael@0: UVector& michael@0: ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const michael@0: { michael@0: result.removeAllElements(); michael@0: michael@0: if (U_FAILURE(status)) { michael@0: return result; michael@0: } michael@0: michael@0: { michael@0: Mutex mutex(&lock); michael@0: const Hashtable* map = getVisibleIDMap(status); michael@0: if (map != NULL) { michael@0: ICUServiceKey* fallbackKey = createKey(matchID, status); michael@0: michael@0: for (int32_t pos = -1;;) { michael@0: const UHashElement* e = map->nextElement(pos); michael@0: if (e == NULL) { michael@0: break; michael@0: } michael@0: michael@0: const UnicodeString* id = (const UnicodeString*)e->key.pointer; michael@0: if (fallbackKey != NULL) { michael@0: if (!fallbackKey->isFallbackOf(*id)) { michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: UnicodeString* idClone = new UnicodeString(*id); michael@0: if (idClone == NULL || idClone->isBogus()) { michael@0: delete idClone; michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: break; michael@0: } michael@0: result.addElement(idClone, status); michael@0: if (U_FAILURE(status)) { michael@0: delete idClone; michael@0: break; michael@0: } michael@0: } michael@0: delete fallbackKey; michael@0: } michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: result.removeAllElements(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: const Hashtable* michael@0: ICUService::getVisibleIDMap(UErrorCode& status) const { michael@0: if (U_FAILURE(status)) return NULL; michael@0: michael@0: // must only be called when lock is already held michael@0: michael@0: ICUService* ncthis = (ICUService*)this; // cast away semantic const michael@0: if (idCache == NULL) { michael@0: ncthis->idCache = new Hashtable(status); michael@0: if (idCache == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } else if (factories != NULL) { michael@0: for (int32_t pos = factories->size(); --pos >= 0;) { michael@0: ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); michael@0: f->updateVisibleIDs(*idCache, status); michael@0: } michael@0: if (U_FAILURE(status)) { michael@0: delete idCache; michael@0: ncthis->idCache = NULL; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return idCache; michael@0: } michael@0: michael@0: michael@0: UnicodeString& michael@0: ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const michael@0: { michael@0: return getDisplayName(id, result, Locale::getDefault()); michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const michael@0: { michael@0: { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: Mutex mutex(&lock); michael@0: const Hashtable* map = getVisibleIDMap(status); michael@0: if (map != NULL) { michael@0: ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); michael@0: if (f != NULL) { michael@0: f->getDisplayName(id, locale, result); michael@0: return result; michael@0: } michael@0: michael@0: // fallback michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: ICUServiceKey* fallbackKey = createKey(&id, status); michael@0: while (fallbackKey->fallback()) { michael@0: UnicodeString us; michael@0: fallbackKey->currentID(us); michael@0: f = (ICUServiceFactory*)map->get(us); michael@0: if (f != NULL) { michael@0: f->getDisplayName(id, locale, result); michael@0: delete fallbackKey; michael@0: return result; michael@0: } michael@0: } michael@0: delete fallbackKey; michael@0: } michael@0: } michael@0: result.setToBogus(); michael@0: return result; michael@0: } michael@0: michael@0: UVector& michael@0: ICUService::getDisplayNames(UVector& result, UErrorCode& status) const michael@0: { michael@0: return getDisplayNames(result, Locale::getDefault(), NULL, status); michael@0: } michael@0: michael@0: michael@0: UVector& michael@0: ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const michael@0: { michael@0: return getDisplayNames(result, locale, NULL, status); michael@0: } michael@0: michael@0: UVector& michael@0: ICUService::getDisplayNames(UVector& result, michael@0: const Locale& locale, michael@0: const UnicodeString* matchID, michael@0: UErrorCode& status) const michael@0: { michael@0: result.removeAllElements(); michael@0: result.setDeleter(userv_deleteStringPair); michael@0: if (U_SUCCESS(status)) { michael@0: ICUService* ncthis = (ICUService*)this; // cast away semantic const michael@0: Mutex mutex(&lock); michael@0: michael@0: if (dnCache != NULL && dnCache->locale != locale) { michael@0: delete dnCache; michael@0: ncthis->dnCache = NULL; michael@0: } michael@0: michael@0: if (dnCache == NULL) { michael@0: const Hashtable* m = getVisibleIDMap(status); michael@0: if (U_FAILURE(status)) { michael@0: return result; michael@0: } michael@0: ncthis->dnCache = new DNCache(locale); michael@0: if (dnCache == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return result; michael@0: } michael@0: michael@0: int32_t pos = -1; michael@0: const UHashElement* entry = NULL; michael@0: while ((entry = m->nextElement(pos)) != NULL) { michael@0: const UnicodeString* id = (const UnicodeString*)entry->key.pointer; michael@0: ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; michael@0: UnicodeString dname; michael@0: f->getDisplayName(*id, locale, dname); michael@0: if (dname.isBogus()) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } else { michael@0: dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap michael@0: if (U_SUCCESS(status)) { michael@0: continue; michael@0: } michael@0: } michael@0: delete dnCache; michael@0: ncthis->dnCache = NULL; michael@0: return result; michael@0: } michael@0: } michael@0: } michael@0: michael@0: ICUServiceKey* matchKey = createKey(matchID, status); michael@0: /* To ensure that all elements in the hashtable are iterated, set pos to -1. michael@0: * nextElement(pos) will skip the position at pos and begin the iteration michael@0: * at the next position, which in this case will be 0. michael@0: */ michael@0: int32_t pos = -1; michael@0: const UHashElement *entry = NULL; michael@0: while ((entry = dnCache->cache.nextElement(pos)) != NULL) { michael@0: const UnicodeString* id = (const UnicodeString*)entry->value.pointer; michael@0: if (matchKey != NULL && !matchKey->isFallbackOf(*id)) { michael@0: continue; michael@0: } michael@0: const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; michael@0: StringPair* sp = StringPair::create(*id, *dn, status); michael@0: result.addElement(sp, status); michael@0: if (U_FAILURE(status)) { michael@0: result.removeAllElements(); michael@0: break; michael@0: } michael@0: } michael@0: delete matchKey; michael@0: michael@0: return result; michael@0: } michael@0: michael@0: URegistryKey michael@0: ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) michael@0: { michael@0: return registerInstance(objToAdopt, id, TRUE, status); michael@0: } michael@0: michael@0: URegistryKey michael@0: ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) michael@0: { michael@0: ICUServiceKey* key = createKey(&id, status); michael@0: if (key != NULL) { michael@0: UnicodeString canonicalID; michael@0: key->canonicalID(canonicalID); michael@0: delete key; michael@0: michael@0: ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); michael@0: if (f != NULL) { michael@0: return registerFactory(f, status); michael@0: } michael@0: } michael@0: delete objToAdopt; michael@0: return NULL; michael@0: } michael@0: michael@0: ICUServiceFactory* michael@0: ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) michael@0: { michael@0: if (U_SUCCESS(status)) { michael@0: if ((objToAdopt != NULL) && (!id.isBogus())) { michael@0: return new SimpleFactory(objToAdopt, id, visible); michael@0: } michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: URegistryKey michael@0: ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) michael@0: { michael@0: if (U_SUCCESS(status) && factoryToAdopt != NULL) { michael@0: Mutex mutex(&lock); michael@0: michael@0: if (factories == NULL) { michael@0: factories = new UVector(deleteUObject, NULL, status); michael@0: if (U_FAILURE(status)) { michael@0: delete factories; michael@0: return NULL; michael@0: } michael@0: } michael@0: factories->insertElementAt(factoryToAdopt, 0, status); michael@0: if (U_SUCCESS(status)) { michael@0: clearCaches(); michael@0: } else { michael@0: delete factoryToAdopt; michael@0: factoryToAdopt = NULL; michael@0: } michael@0: } michael@0: michael@0: if (factoryToAdopt != NULL) { michael@0: notifyChanged(); michael@0: } michael@0: michael@0: return (URegistryKey)factoryToAdopt; michael@0: } michael@0: michael@0: UBool michael@0: ICUService::unregister(URegistryKey rkey, UErrorCode& status) michael@0: { michael@0: ICUServiceFactory *factory = (ICUServiceFactory*)rkey; michael@0: UBool result = FALSE; michael@0: if (factory != NULL && factories != NULL) { michael@0: Mutex mutex(&lock); michael@0: michael@0: if (factories->removeElement(factory)) { michael@0: clearCaches(); michael@0: result = TRUE; michael@0: } else { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: delete factory; michael@0: } michael@0: } michael@0: if (result) { michael@0: notifyChanged(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: ICUService::reset() michael@0: { michael@0: { michael@0: Mutex mutex(&lock); michael@0: reInitializeFactories(); michael@0: clearCaches(); michael@0: } michael@0: notifyChanged(); michael@0: } michael@0: michael@0: void michael@0: ICUService::reInitializeFactories() michael@0: { michael@0: if (factories != NULL) { michael@0: factories->removeAllElements(); michael@0: } michael@0: } michael@0: michael@0: UBool michael@0: ICUService::isDefault() const michael@0: { michael@0: return countFactories() == 0; michael@0: } michael@0: michael@0: ICUServiceKey* michael@0: ICUService::createKey(const UnicodeString* id, UErrorCode& status) const michael@0: { michael@0: return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); michael@0: } michael@0: michael@0: void michael@0: ICUService::clearCaches() michael@0: { michael@0: // callers synchronize before use michael@0: ++timestamp; michael@0: delete dnCache; michael@0: dnCache = NULL; michael@0: delete idCache; michael@0: idCache = NULL; michael@0: delete serviceCache; serviceCache = NULL; michael@0: } michael@0: michael@0: void michael@0: ICUService::clearServiceCache() michael@0: { michael@0: // callers synchronize before use michael@0: delete serviceCache; serviceCache = NULL; michael@0: } michael@0: michael@0: UBool michael@0: ICUService::acceptsListener(const EventListener& l) const michael@0: { michael@0: return dynamic_cast(&l) != NULL; michael@0: } michael@0: michael@0: void michael@0: ICUService::notifyListener(EventListener& l) const michael@0: { michael@0: ((ServiceListener&)l).serviceChanged(*this); michael@0: } michael@0: michael@0: UnicodeString& michael@0: ICUService::getName(UnicodeString& result) const michael@0: { michael@0: return result.append(name); michael@0: } michael@0: michael@0: int32_t michael@0: ICUService::countFactories() const michael@0: { michael@0: return factories == NULL ? 0 : factories->size(); michael@0: } michael@0: michael@0: int32_t michael@0: ICUService::getTimestamp() const michael@0: { michael@0: return timestamp; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: /* UCONFIG_NO_SERVICE */ michael@0: #endif