2 *******************************************************************************
3 * Copyright (C) 2001-2012, International Business Machines Corporation.
5 *******************************************************************************
8 #include "unicode/utypes.h"
10 #if !UCONFIG_NO_SERVICE
15 #undef SERVICE_REFCOUNT
17 // in case we use the refcount stuff
22 ******************************************************************
25 const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */
27 ICUServiceKey::ICUServiceKey(const UnicodeString& id)
31 ICUServiceKey::~ICUServiceKey()
36 ICUServiceKey::getID() const
42 ICUServiceKey::canonicalID(UnicodeString& result) const
44 return result.append(_id);
48 ICUServiceKey::currentID(UnicodeString& result) const
50 return canonicalID(result);
54 ICUServiceKey::currentDescriptor(UnicodeString& result) const
57 result.append(PREFIX_DELIMITER);
58 return currentID(result);
62 ICUServiceKey::fallback()
68 ICUServiceKey::isFallbackOf(const UnicodeString& id) const
74 ICUServiceKey::prefix(UnicodeString& result) const
80 ICUServiceKey::parsePrefix(UnicodeString& result)
82 int32_t n = result.indexOf(PREFIX_DELIMITER);
91 ICUServiceKey::parseSuffix(UnicodeString& result)
93 int32_t n = result.indexOf(PREFIX_DELIMITER);
95 result.remove(0, n+1);
102 ICUServiceKey::debug(UnicodeString& result) const
105 result.append(" id: ");
111 ICUServiceKey::debugClass(UnicodeString& result) const
113 return result.append("ICUServiceKey");
117 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
120 ******************************************************************
123 ICUServiceFactory::~ICUServiceFactory() {}
125 SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
126 : _instance(instanceToAdopt), _id(id), _visible(visible)
130 SimpleFactory::~SimpleFactory()
136 SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
138 if (U_SUCCESS(status)) {
140 if (_id == key.currentID(temp)) {
141 return service->cloneInstance(_instance);
148 SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const
151 result.put(_id, (void*)this, status); // cast away const
158 SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const
160 if (_visible && _id == id) {
170 SimpleFactory::debug(UnicodeString& toAppendTo) const
172 debugClass(toAppendTo);
173 toAppendTo.append(" id: ");
174 toAppendTo.append(_id);
175 toAppendTo.append(", visible: ");
176 toAppendTo.append(_visible ? "T" : "F");
181 SimpleFactory::debugClass(UnicodeString& toAppendTo) const
183 return toAppendTo.append("SimpleFactory");
187 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
190 ******************************************************************
193 ServiceListener::~ServiceListener() {}
195 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener)
198 ******************************************************************
201 // Record the actual id for this service in the cache, so we can return it
202 // even if we succeed later with a different id.
203 class CacheEntry : public UMemory {
208 UnicodeString actualDescriptor;
212 * Releases a reference to the shared resource.
218 CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service)
219 : refcount(1), actualDescriptor(_actualDescriptor), service(_service) {
223 * Instantiation creates an initial reference, so don't call this
224 * unless you're creating a new pointer to this. Management of
225 * that pointer will have to know how to deal with refcounts.
226 * Return true if the resource has not already been released.
234 * Destructions removes a reference, so don't call this unless
235 * you're removing pointer to this somewhere. Management of that
236 * pointer will have to know how to deal with refcounts. Once
237 * the refcount drops to zero, the resource is released. Return
238 * false if the resouce has been released.
240 CacheEntry* unref() {
241 if ((--refcount) == 0) {
249 * Return TRUE if there is at least one reference to this and the
250 * resource has not been released.
252 UBool isShared() const {
257 // UObjectDeleter for serviceCache
259 static void U_CALLCONV
260 cacheDeleter(void* obj) {
261 U_NAMESPACE_USE ((CacheEntry*)obj)->unref();
265 * Deleter for UObjects
267 static void U_CALLCONV
268 deleteUObject(void *obj) {
269 U_NAMESPACE_USE delete (UObject*) obj;
274 ******************************************************************
277 class DNCache : public UMemory {
282 DNCache(const Locale& _locale)
283 : cache(), locale(_locale)
285 // cache.setKeyDeleter(uprv_deleteUObject);
291 ******************************************************************
295 StringPair::create(const UnicodeString& displayName,
296 const UnicodeString& id,
299 if (U_SUCCESS(status)) {
300 StringPair* sp = new StringPair(displayName, id);
301 if (sp == NULL || sp->isBogus()) {
302 status = U_MEMORY_ALLOCATION_ERROR;
312 StringPair::isBogus() const {
313 return displayName.isBogus() || id.isBogus();
316 StringPair::StringPair(const UnicodeString& _displayName,
317 const UnicodeString& _id)
318 : displayName(_displayName)
324 static void U_CALLCONV
325 userv_deleteStringPair(void *obj) {
326 U_NAMESPACE_USE delete (StringPair*) obj;
331 ******************************************************************
334 static UMutex lock = U_MUTEX_INITIALIZER;
336 ICUService::ICUService()
346 ICUService::ICUService(const UnicodeString& newName)
356 ICUService::~ICUService()
367 ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
369 return get(descriptor, NULL, status);
373 ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
375 UObject* result = NULL;
376 ICUServiceKey* key = createKey(&descriptor, status);
378 result = getKey(*key, actualReturn, status);
385 ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const
387 return getKey(key, NULL, status);
390 // this is a vector that subclasses of ICUService can override to further customize the result object
391 // before returning it. All other public get functions should call this one.
394 ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const
396 return getKey(key, actualReturn, NULL, status);
399 // make it possible to call reentrantly on systems that don't have reentrant mutexes.
400 // we can use this simple approach since we know the situation where we're calling
401 // reentrantly even without knowing the thread.
402 class XMutex : public UMemory {
404 inline XMutex(UMutex *mutex, UBool reentering)
406 , fActive(!reentering)
408 if (fActive) umtx_lock(fMutex);
411 if (fActive) umtx_unlock(fMutex);
419 struct UVectorDeleter {
421 UVectorDeleter() : _obj(NULL) {}
422 ~UVectorDeleter() { delete _obj; }
425 // called only by factories, treat as private
427 ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
429 if (U_FAILURE(status)) {
434 return handleDefault(key, actualReturn, status);
437 ICUService* ncthis = (ICUService*)this; // cast away semantic const
439 CacheEntry* result = NULL;
441 // The factory list can't be modified until we're done,
442 // otherwise we might update the cache with an invalid result.
443 // The cache has to stay in synch with the factory list.
444 // ICU doesn't have monitors so we can't use rw locks, so
445 // we single-thread everything using this service, for now.
447 // if factory is not null, we're calling from within the mutex,
448 // and since some unix machines don't have reentrant mutexes we
449 // need to make sure not to try to lock it again.
450 XMutex mutex(&lock, factory != NULL);
452 if (serviceCache == NULL) {
453 ncthis->serviceCache = new Hashtable(status);
454 if (ncthis->serviceCache == NULL) {
457 if (U_FAILURE(status)) {
461 serviceCache->setValueDeleter(cacheDeleter);
464 UnicodeString currentDescriptor;
465 UVectorDeleter cacheDescriptorList;
466 UBool putInCache = FALSE;
468 int32_t startIndex = 0;
469 int32_t limit = factories->size();
470 UBool cacheResult = TRUE;
472 if (factory != NULL) {
473 for (int32_t i = 0; i < limit; ++i) {
474 if (factory == (const ICUServiceFactory*)factories->elementAt(i)) {
479 if (startIndex == 0) {
480 // throw new InternalError("Factory " + factory + "not registered with service: " + this);
481 status = U_ILLEGAL_ARGUMENT_ERROR;
488 currentDescriptor.remove();
489 key.currentDescriptor(currentDescriptor);
490 result = (CacheEntry*)serviceCache->get(currentDescriptor);
491 if (result != NULL) {
495 // first test of cache failed, so we'll have to update
496 // the cache if we eventually succeed-- that is, if we're
497 // going to update the cache at all.
500 int32_t index = startIndex;
501 while (index < limit) {
502 ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++);
503 UObject* service = f->create(key, this, status);
504 if (U_FAILURE(status)) {
508 if (service != NULL) {
509 result = new CacheEntry(currentDescriptor, service);
510 if (result == NULL) {
512 status = U_MEMORY_ALLOCATION_ERROR;
520 // prepare to load the cache with all additional ids that
521 // will resolve to result, assuming we'll succeed. We
522 // don't want to keep querying on an id that's going to
523 // fallback to the one that succeeded, we want to hit the
524 // cache the first time next goaround.
525 if (cacheDescriptorList._obj == NULL) {
526 cacheDescriptorList._obj = new UVector(uprv_deleteUObject, NULL, 5, status);
527 if (U_FAILURE(status)) {
531 UnicodeString* idToCache = new UnicodeString(currentDescriptor);
532 if (idToCache == NULL || idToCache->isBogus()) {
533 status = U_MEMORY_ALLOCATION_ERROR;
537 cacheDescriptorList._obj->addElement(idToCache, status);
538 if (U_FAILURE(status)) {
541 } while (key.fallback());
544 if (result != NULL) {
545 if (putInCache && cacheResult) {
546 serviceCache->put(result->actualDescriptor, result, status);
547 if (U_FAILURE(status)) {
552 if (cacheDescriptorList._obj != NULL) {
553 for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) {
554 UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i);
555 serviceCache->put(*desc, result, status);
556 if (U_FAILURE(status)) {
562 cacheDescriptorList._obj->removeElementAt(i);
567 if (actualReturn != NULL) {
569 if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/)
570 actualReturn->remove();
571 actualReturn->append(result->actualDescriptor,
573 result->actualDescriptor.length() - 1);
575 *actualReturn = result->actualDescriptor;
578 if (actualReturn->isBogus()) {
579 status = U_MEMORY_ALLOCATION_ERROR;
585 UObject* service = cloneInstance(result->service);
586 if (putInCache && !cacheResult) {
593 return handleDefault(key, actualReturn, status);
597 ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
603 ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
604 return getVisibleIDs(result, NULL, status);
608 ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
610 result.removeAllElements();
612 if (U_FAILURE(status)) {
618 const Hashtable* map = getVisibleIDMap(status);
620 ICUServiceKey* fallbackKey = createKey(matchID, status);
622 for (int32_t pos = -1;;) {
623 const UHashElement* e = map->nextElement(pos);
628 const UnicodeString* id = (const UnicodeString*)e->key.pointer;
629 if (fallbackKey != NULL) {
630 if (!fallbackKey->isFallbackOf(*id)) {
635 UnicodeString* idClone = new UnicodeString(*id);
636 if (idClone == NULL || idClone->isBogus()) {
638 status = U_MEMORY_ALLOCATION_ERROR;
641 result.addElement(idClone, status);
642 if (U_FAILURE(status)) {
650 if (U_FAILURE(status)) {
651 result.removeAllElements();
657 ICUService::getVisibleIDMap(UErrorCode& status) const {
658 if (U_FAILURE(status)) return NULL;
660 // must only be called when lock is already held
662 ICUService* ncthis = (ICUService*)this; // cast away semantic const
663 if (idCache == NULL) {
664 ncthis->idCache = new Hashtable(status);
665 if (idCache == NULL) {
666 status = U_MEMORY_ALLOCATION_ERROR;
667 } else if (factories != NULL) {
668 for (int32_t pos = factories->size(); --pos >= 0;) {
669 ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos);
670 f->updateVisibleIDs(*idCache, status);
672 if (U_FAILURE(status)) {
674 ncthis->idCache = NULL;
684 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
686 return getDisplayName(id, result, Locale::getDefault());
690 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const
693 UErrorCode status = U_ZERO_ERROR;
695 const Hashtable* map = getVisibleIDMap(status);
697 ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
699 f->getDisplayName(id, locale, result);
704 UErrorCode status = U_ZERO_ERROR;
705 ICUServiceKey* fallbackKey = createKey(&id, status);
706 while (fallbackKey->fallback()) {
708 fallbackKey->currentID(us);
709 f = (ICUServiceFactory*)map->get(us);
711 f->getDisplayName(id, locale, result);
724 ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
726 return getDisplayNames(result, Locale::getDefault(), NULL, status);
731 ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
733 return getDisplayNames(result, locale, NULL, status);
737 ICUService::getDisplayNames(UVector& result,
738 const Locale& locale,
739 const UnicodeString* matchID,
740 UErrorCode& status) const
742 result.removeAllElements();
743 result.setDeleter(userv_deleteStringPair);
744 if (U_SUCCESS(status)) {
745 ICUService* ncthis = (ICUService*)this; // cast away semantic const
748 if (dnCache != NULL && dnCache->locale != locale) {
750 ncthis->dnCache = NULL;
753 if (dnCache == NULL) {
754 const Hashtable* m = getVisibleIDMap(status);
755 if (U_FAILURE(status)) {
758 ncthis->dnCache = new DNCache(locale);
759 if (dnCache == NULL) {
760 status = U_MEMORY_ALLOCATION_ERROR;
765 const UHashElement* entry = NULL;
766 while ((entry = m->nextElement(pos)) != NULL) {
767 const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
768 ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer;
770 f->getDisplayName(*id, locale, dname);
771 if (dname.isBogus()) {
772 status = U_MEMORY_ALLOCATION_ERROR;
774 dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
775 if (U_SUCCESS(status)) {
780 ncthis->dnCache = NULL;
786 ICUServiceKey* matchKey = createKey(matchID, status);
787 /* To ensure that all elements in the hashtable are iterated, set pos to -1.
788 * nextElement(pos) will skip the position at pos and begin the iteration
789 * at the next position, which in this case will be 0.
792 const UHashElement *entry = NULL;
793 while ((entry = dnCache->cache.nextElement(pos)) != NULL) {
794 const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
795 if (matchKey != NULL && !matchKey->isFallbackOf(*id)) {
798 const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
799 StringPair* sp = StringPair::create(*id, *dn, status);
800 result.addElement(sp, status);
801 if (U_FAILURE(status)) {
802 result.removeAllElements();
812 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
814 return registerInstance(objToAdopt, id, TRUE, status);
818 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
820 ICUServiceKey* key = createKey(&id, status);
822 UnicodeString canonicalID;
823 key->canonicalID(canonicalID);
826 ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
828 return registerFactory(f, status);
836 ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
838 if (U_SUCCESS(status)) {
839 if ((objToAdopt != NULL) && (!id.isBogus())) {
840 return new SimpleFactory(objToAdopt, id, visible);
842 status = U_ILLEGAL_ARGUMENT_ERROR;
848 ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status)
850 if (U_SUCCESS(status) && factoryToAdopt != NULL) {
853 if (factories == NULL) {
854 factories = new UVector(deleteUObject, NULL, status);
855 if (U_FAILURE(status)) {
860 factories->insertElementAt(factoryToAdopt, 0, status);
861 if (U_SUCCESS(status)) {
864 delete factoryToAdopt;
865 factoryToAdopt = NULL;
869 if (factoryToAdopt != NULL) {
873 return (URegistryKey)factoryToAdopt;
877 ICUService::unregister(URegistryKey rkey, UErrorCode& status)
879 ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
880 UBool result = FALSE;
881 if (factory != NULL && factories != NULL) {
884 if (factories->removeElement(factory)) {
888 status = U_ILLEGAL_ARGUMENT_ERROR;
903 reInitializeFactories();
910 ICUService::reInitializeFactories()
912 if (factories != NULL) {
913 factories->removeAllElements();
918 ICUService::isDefault() const
920 return countFactories() == 0;
924 ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
926 return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id);
930 ICUService::clearCaches()
932 // callers synchronize before use
938 delete serviceCache; serviceCache = NULL;
942 ICUService::clearServiceCache()
944 // callers synchronize before use
945 delete serviceCache; serviceCache = NULL;
949 ICUService::acceptsListener(const EventListener& l) const
951 return dynamic_cast<const ServiceListener*>(&l) != NULL;
955 ICUService::notifyListener(EventListener& l) const
957 ((ServiceListener&)l).serviceChanged(*this);
961 ICUService::getName(UnicodeString& result) const
963 return result.append(name);
967 ICUService::countFactories() const
969 return factories == NULL ? 0 : factories->size();
973 ICUService::getTimestamp() const
980 /* UCONFIG_NO_SERVICE */