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 */