1 /**
2 *******************************************************************************
3 * Copyright (C) 2001-2014, International Business Machines Corporation.
4 * All Rights Reserved.
5 *******************************************************************************
6 */
7 
8 #include "unicode/utypes.h"
9 
10 #if !UCONFIG_NO_SERVICE
11 
12 #include "serv.h"
13 #include "umutex.h"
14 
15 #undef SERVICE_REFCOUNT
16 
17 // in case we use the refcount stuff
18 
19 U_NAMESPACE_BEGIN
20 
21 /*
22 ******************************************************************
23 */
24 
25 const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F;   /* '/' */
26 
ICUServiceKey(const UnicodeString & id)27 ICUServiceKey::ICUServiceKey(const UnicodeString& id)
28 : _id(id) {
29 }
30 
~ICUServiceKey()31 ICUServiceKey::~ICUServiceKey()
32 {
33 }
34 
35 const UnicodeString&
getID() const36 ICUServiceKey::getID() const
37 {
38     return _id;
39 }
40 
41 UnicodeString&
canonicalID(UnicodeString & result) const42 ICUServiceKey::canonicalID(UnicodeString& result) const
43 {
44     return result.append(_id);
45 }
46 
47 UnicodeString&
currentID(UnicodeString & result) const48 ICUServiceKey::currentID(UnicodeString& result) const
49 {
50     return canonicalID(result);
51 }
52 
53 UnicodeString&
currentDescriptor(UnicodeString & result) const54 ICUServiceKey::currentDescriptor(UnicodeString& result) const
55 {
56     prefix(result);
57     result.append(PREFIX_DELIMITER);
58     return currentID(result);
59 }
60 
61 UBool
fallback()62 ICUServiceKey::fallback()
63 {
64     return FALSE;
65 }
66 
67 UBool
isFallbackOf(const UnicodeString & id) const68 ICUServiceKey::isFallbackOf(const UnicodeString& id) const
69 {
70     return id == _id;
71 }
72 
73 UnicodeString&
prefix(UnicodeString & result) const74 ICUServiceKey::prefix(UnicodeString& result) const
75 {
76     return result;
77 }
78 
79 UnicodeString&
parsePrefix(UnicodeString & result)80 ICUServiceKey::parsePrefix(UnicodeString& result)
81 {
82     int32_t n = result.indexOf(PREFIX_DELIMITER);
83     if (n < 0) {
84         n = 0;
85     }
86     result.remove(n);
87     return result;
88 }
89 
90 UnicodeString&
parseSuffix(UnicodeString & result)91 ICUServiceKey::parseSuffix(UnicodeString& result)
92 {
93     int32_t n = result.indexOf(PREFIX_DELIMITER);
94     if (n >= 0) {
95         result.remove(0, n+1);
96     }
97     return result;
98 }
99 
100 #ifdef SERVICE_DEBUG
101 UnicodeString&
debug(UnicodeString & result) const102 ICUServiceKey::debug(UnicodeString& result) const
103 {
104     debugClass(result);
105     result.append((UnicodeString)" id: ");
106     result.append(_id);
107     return result;
108 }
109 
110 UnicodeString&
debugClass(UnicodeString & result) const111 ICUServiceKey::debugClass(UnicodeString& result) const
112 {
113     return result.append((UnicodeString)"ICUServiceKey");
114 }
115 #endif
116 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)117 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
118 
119 /*
120 ******************************************************************
121 */
122 
123 ICUServiceFactory::~ICUServiceFactory() {}
124 
SimpleFactory(UObject * instanceToAdopt,const UnicodeString & id,UBool visible)125 SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
126 : _instance(instanceToAdopt), _id(id), _visible(visible)
127 {
128 }
129 
~SimpleFactory()130 SimpleFactory::~SimpleFactory()
131 {
132     delete _instance;
133 }
134 
135 UObject*
create(const ICUServiceKey & key,const ICUService * service,UErrorCode & status) const136 SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
137 {
138     if (U_SUCCESS(status)) {
139         UnicodeString temp;
140         if (_id == key.currentID(temp)) {
141             return service->cloneInstance(_instance);
142         }
143     }
144     return NULL;
145 }
146 
147 void
updateVisibleIDs(Hashtable & result,UErrorCode & status) const148 SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const
149 {
150     if (_visible) {
151         result.put(_id, (void*)this, status); // cast away const
152     } else {
153         result.remove(_id);
154     }
155 }
156 
157 UnicodeString&
getDisplayName(const UnicodeString & id,const Locale &,UnicodeString & result) const158 SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const
159 {
160     if (_visible && _id == id) {
161         result = _id;
162     } else {
163         result.setToBogus();
164     }
165     return result;
166 }
167 
168 #ifdef SERVICE_DEBUG
169 UnicodeString&
debug(UnicodeString & toAppendTo) const170 SimpleFactory::debug(UnicodeString& toAppendTo) const
171 {
172     debugClass(toAppendTo);
173     toAppendTo.append((UnicodeString)" id: ");
174     toAppendTo.append(_id);
175     toAppendTo.append((UnicodeString)", visible: ");
176     toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F");
177     return toAppendTo;
178 }
179 
180 UnicodeString&
debugClass(UnicodeString & toAppendTo) const181 SimpleFactory::debugClass(UnicodeString& toAppendTo) const
182 {
183     return toAppendTo.append((UnicodeString)"SimpleFactory");
184 }
185 #endif
186 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)187 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
188 
189 /*
190 ******************************************************************
191 */
192 
193 ServiceListener::~ServiceListener() {}
194 
195 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener)
196 
197 /*
198 ******************************************************************
199 */
200 
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 {
204 private:
205     int32_t refcount;
206 
207 public:
208     UnicodeString actualDescriptor;
209     UObject* service;
210 
211     /**
212     * Releases a reference to the shared resource.
213     */
~CacheEntry()214     ~CacheEntry() {
215         delete service;
216     }
217 
CacheEntry(const UnicodeString & _actualDescriptor,UObject * _service)218     CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service)
219         : refcount(1), actualDescriptor(_actualDescriptor), service(_service) {
220     }
221 
222     /**
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.
227     */
ref()228     CacheEntry* ref() {
229         ++refcount;
230         return this;
231     }
232 
233     /**
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.
239     */
unref()240     CacheEntry* unref() {
241         if ((--refcount) == 0) {
242             delete this;
243             return NULL;
244         }
245         return this;
246     }
247 
248     /**
249     * Return TRUE if there is at least one reference to this and the
250     * resource has not been released.
251     */
isShared() const252     UBool isShared() const {
253         return refcount > 1;
254     }
255 };
256 
257 // UObjectDeleter for serviceCache
258 U_CDECL_BEGIN
259 static void U_CALLCONV
cacheDeleter(void * obj)260 cacheDeleter(void* obj) {
261     U_NAMESPACE_USE ((CacheEntry*)obj)->unref();
262 }
263 
264 /**
265 * Deleter for UObjects
266 */
267 static void U_CALLCONV
deleteUObject(void * obj)268 deleteUObject(void *obj) {
269     U_NAMESPACE_USE delete (UObject*) obj;
270 }
271 U_CDECL_END
272 
273 /*
274 ******************************************************************
275 */
276 
277 class DNCache : public UMemory {
278 public:
279     Hashtable cache;
280     const Locale locale;
281 
DNCache(const Locale & _locale)282     DNCache(const Locale& _locale)
283         : cache(), locale(_locale)
284     {
285         // cache.setKeyDeleter(uprv_deleteUObject);
286     }
287 };
288 
289 
290 /*
291 ******************************************************************
292 */
293 
294 StringPair*
create(const UnicodeString & displayName,const UnicodeString & id,UErrorCode & status)295 StringPair::create(const UnicodeString& displayName,
296                    const UnicodeString& id,
297                    UErrorCode& status)
298 {
299     if (U_SUCCESS(status)) {
300         StringPair* sp = new StringPair(displayName, id);
301         if (sp == NULL || sp->isBogus()) {
302             status = U_MEMORY_ALLOCATION_ERROR;
303             delete sp;
304             return NULL;
305         }
306         return sp;
307     }
308     return NULL;
309 }
310 
311 UBool
isBogus() const312 StringPair::isBogus() const {
313     return displayName.isBogus() || id.isBogus();
314 }
315 
StringPair(const UnicodeString & _displayName,const UnicodeString & _id)316 StringPair::StringPair(const UnicodeString& _displayName,
317                        const UnicodeString& _id)
318 : displayName(_displayName)
319 , id(_id)
320 {
321 }
322 
323 U_CDECL_BEGIN
324 static void U_CALLCONV
userv_deleteStringPair(void * obj)325 userv_deleteStringPair(void *obj) {
326     U_NAMESPACE_USE delete (StringPair*) obj;
327 }
328 U_CDECL_END
329 
330 /*
331 ******************************************************************
332 */
333 
334 static UMutex lock = U_MUTEX_INITIALIZER;
335 
ICUService()336 ICUService::ICUService()
337 : name()
338 , timestamp(0)
339 , factories(NULL)
340 , serviceCache(NULL)
341 , idCache(NULL)
342 , dnCache(NULL)
343 {
344 }
345 
ICUService(const UnicodeString & newName)346 ICUService::ICUService(const UnicodeString& newName)
347 : name(newName)
348 , timestamp(0)
349 , factories(NULL)
350 , serviceCache(NULL)
351 , idCache(NULL)
352 , dnCache(NULL)
353 {
354 }
355 
~ICUService()356 ICUService::~ICUService()
357 {
358     {
359         Mutex mutex(&lock);
360         clearCaches();
361         delete factories;
362         factories = NULL;
363     }
364 }
365 
366 UObject*
get(const UnicodeString & descriptor,UErrorCode & status) const367 ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
368 {
369     return get(descriptor, NULL, status);
370 }
371 
372 UObject*
get(const UnicodeString & descriptor,UnicodeString * actualReturn,UErrorCode & status) const373 ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
374 {
375     UObject* result = NULL;
376     ICUServiceKey* key = createKey(&descriptor, status);
377     if (key) {
378         result = getKey(*key, actualReturn, status);
379         delete key;
380     }
381     return result;
382 }
383 
384 UObject*
getKey(ICUServiceKey & key,UErrorCode & status) const385 ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const
386 {
387     return getKey(key, NULL, status);
388 }
389 
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.
392 
393 UObject*
getKey(ICUServiceKey & key,UnicodeString * actualReturn,UErrorCode & status) const394 ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const
395 {
396     return getKey(key, actualReturn, NULL, status);
397 }
398 
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 {
403 public:
XMutex(UMutex * mutex,UBool reentering)404     inline XMutex(UMutex *mutex, UBool reentering)
405         : fMutex(mutex)
406         , fActive(!reentering)
407     {
408         if (fActive) umtx_lock(fMutex);
409     }
~XMutex()410     inline ~XMutex() {
411         if (fActive) umtx_unlock(fMutex);
412     }
413 
414 private:
415     UMutex  *fMutex;
416     UBool fActive;
417 };
418 
419 struct UVectorDeleter {
420     UVector* _obj;
UVectorDeleterUVectorDeleter421     UVectorDeleter() : _obj(NULL) {}
~UVectorDeleterUVectorDeleter422     ~UVectorDeleter() { delete _obj; }
423 };
424 
425 // called only by factories, treat as private
426 UObject*
getKey(ICUServiceKey & key,UnicodeString * actualReturn,const ICUServiceFactory * factory,UErrorCode & status) const427 ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
428 {
429     if (U_FAILURE(status)) {
430         return NULL;
431     }
432 
433     if (isDefault()) {
434         return handleDefault(key, actualReturn, status);
435     }
436 
437     ICUService* ncthis = (ICUService*)this; // cast away semantic const
438 
439     CacheEntry* result = NULL;
440     {
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.
446 
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);
451 
452         if (serviceCache == NULL) {
453             ncthis->serviceCache = new Hashtable(status);
454             if (ncthis->serviceCache == NULL) {
455                 return NULL;
456             }
457             if (U_FAILURE(status)) {
458                 delete serviceCache;
459                 return NULL;
460             }
461             serviceCache->setValueDeleter(cacheDeleter);
462         }
463 
464         UnicodeString currentDescriptor;
465         UVectorDeleter cacheDescriptorList;
466         UBool putInCache = FALSE;
467 
468         int32_t startIndex = 0;
469         int32_t limit = factories->size();
470         UBool cacheResult = TRUE;
471 
472         if (factory != NULL) {
473             for (int32_t i = 0; i < limit; ++i) {
474                 if (factory == (const ICUServiceFactory*)factories->elementAt(i)) {
475                     startIndex = i + 1;
476                     break;
477                 }
478             }
479             if (startIndex == 0) {
480                 // throw new InternalError("Factory " + factory + "not registered with service: " + this);
481                 status = U_ILLEGAL_ARGUMENT_ERROR;
482                 return NULL;
483             }
484             cacheResult = FALSE;
485         }
486 
487         do {
488             currentDescriptor.remove();
489             key.currentDescriptor(currentDescriptor);
490             result = (CacheEntry*)serviceCache->get(currentDescriptor);
491             if (result != NULL) {
492                 break;
493             }
494 
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.
498             putInCache = TRUE;
499 
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)) {
505                     delete service;
506                     return NULL;
507                 }
508                 if (service != NULL) {
509                     result = new CacheEntry(currentDescriptor, service);
510                     if (result == NULL) {
511                         delete service;
512                         status = U_MEMORY_ALLOCATION_ERROR;
513                         return NULL;
514                     }
515 
516                     goto outerEnd;
517                 }
518             }
519 
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)) {
528                     return NULL;
529                 }
530             }
531             UnicodeString* idToCache = new UnicodeString(currentDescriptor);
532             if (idToCache == NULL || idToCache->isBogus()) {
533                 status = U_MEMORY_ALLOCATION_ERROR;
534                 return NULL;
535             }
536 
537             cacheDescriptorList._obj->addElement(idToCache, status);
538             if (U_FAILURE(status)) {
539                 return NULL;
540             }
541         } while (key.fallback());
542 outerEnd:
543 
544         if (result != NULL) {
545             if (putInCache && cacheResult) {
546                 serviceCache->put(result->actualDescriptor, result, status);
547                 if (U_FAILURE(status)) {
548                     delete result;
549                     return NULL;
550                 }
551 
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)) {
557                             delete result;
558                             return NULL;
559                         }
560 
561                         result->ref();
562                         cacheDescriptorList._obj->removeElementAt(i);
563                     }
564                 }
565             }
566 
567             if (actualReturn != NULL) {
568                 // strip null prefix
569                 if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/)
570                     actualReturn->remove();
571                     actualReturn->append(result->actualDescriptor,
572                         1,
573                         result->actualDescriptor.length() - 1);
574                 } else {
575                     *actualReturn = result->actualDescriptor;
576                 }
577 
578                 if (actualReturn->isBogus()) {
579                     status = U_MEMORY_ALLOCATION_ERROR;
580                     delete result;
581                     return NULL;
582                 }
583             }
584 
585             UObject* service = cloneInstance(result->service);
586             if (putInCache && !cacheResult) {
587                 delete result;
588             }
589             return service;
590         }
591     }
592 
593     return handleDefault(key, actualReturn, status);
594 }
595 
596 UObject*
handleDefault(const ICUServiceKey &,UnicodeString *,UErrorCode &) const597 ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
598 {
599     return NULL;
600 }
601 
602 UVector&
getVisibleIDs(UVector & result,UErrorCode & status) const603 ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
604     return getVisibleIDs(result, NULL, status);
605 }
606 
607 UVector&
getVisibleIDs(UVector & result,const UnicodeString * matchID,UErrorCode & status) const608 ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
609 {
610     result.removeAllElements();
611 
612     if (U_FAILURE(status)) {
613         return result;
614     }
615 
616     {
617         Mutex mutex(&lock);
618         const Hashtable* map = getVisibleIDMap(status);
619         if (map != NULL) {
620             ICUServiceKey* fallbackKey = createKey(matchID, status);
621 
622             for (int32_t pos = UHASH_FIRST;;) {
623                 const UHashElement* e = map->nextElement(pos);
624                 if (e == NULL) {
625                     break;
626                 }
627 
628                 const UnicodeString* id = (const UnicodeString*)e->key.pointer;
629                 if (fallbackKey != NULL) {
630                     if (!fallbackKey->isFallbackOf(*id)) {
631                         continue;
632                     }
633                 }
634 
635                 UnicodeString* idClone = new UnicodeString(*id);
636                 if (idClone == NULL || idClone->isBogus()) {
637                     delete idClone;
638                     status = U_MEMORY_ALLOCATION_ERROR;
639                     break;
640                 }
641                 result.addElement(idClone, status);
642                 if (U_FAILURE(status)) {
643                     delete idClone;
644                     break;
645                 }
646             }
647             delete fallbackKey;
648         }
649     }
650     if (U_FAILURE(status)) {
651         result.removeAllElements();
652     }
653     return result;
654 }
655 
656 const Hashtable*
getVisibleIDMap(UErrorCode & status) const657 ICUService::getVisibleIDMap(UErrorCode& status) const {
658     if (U_FAILURE(status)) return NULL;
659 
660     // must only be called when lock is already held
661 
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);
671             }
672             if (U_FAILURE(status)) {
673                 delete idCache;
674                 ncthis->idCache = NULL;
675             }
676         }
677     }
678 
679     return idCache;
680 }
681 
682 
683 UnicodeString&
getDisplayName(const UnicodeString & id,UnicodeString & result) const684 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
685 {
686     return getDisplayName(id, result, Locale::getDefault());
687 }
688 
689 UnicodeString&
getDisplayName(const UnicodeString & id,UnicodeString & result,const Locale & locale) const690 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const
691 {
692     {
693         UErrorCode status = U_ZERO_ERROR;
694         Mutex mutex(&lock);
695         const Hashtable* map = getVisibleIDMap(status);
696         if (map != NULL) {
697             ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
698             if (f != NULL) {
699                 f->getDisplayName(id, locale, result);
700                 return result;
701             }
702 
703             // fallback
704             UErrorCode status = U_ZERO_ERROR;
705             ICUServiceKey* fallbackKey = createKey(&id, status);
706             while (fallbackKey->fallback()) {
707                 UnicodeString us;
708                 fallbackKey->currentID(us);
709                 f = (ICUServiceFactory*)map->get(us);
710                 if (f != NULL) {
711                     f->getDisplayName(id, locale, result);
712                     delete fallbackKey;
713                     return result;
714                 }
715             }
716             delete fallbackKey;
717         }
718     }
719     result.setToBogus();
720     return result;
721 }
722 
723 UVector&
getDisplayNames(UVector & result,UErrorCode & status) const724 ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
725 {
726     return getDisplayNames(result, Locale::getDefault(), NULL, status);
727 }
728 
729 
730 UVector&
getDisplayNames(UVector & result,const Locale & locale,UErrorCode & status) const731 ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
732 {
733     return getDisplayNames(result, locale, NULL, status);
734 }
735 
736 UVector&
getDisplayNames(UVector & result,const Locale & locale,const UnicodeString * matchID,UErrorCode & status) const737 ICUService::getDisplayNames(UVector& result,
738                             const Locale& locale,
739                             const UnicodeString* matchID,
740                             UErrorCode& status) const
741 {
742     result.removeAllElements();
743     result.setDeleter(userv_deleteStringPair);
744     if (U_SUCCESS(status)) {
745         ICUService* ncthis = (ICUService*)this; // cast away semantic const
746         Mutex mutex(&lock);
747 
748         if (dnCache != NULL && dnCache->locale != locale) {
749             delete dnCache;
750             ncthis->dnCache = NULL;
751         }
752 
753         if (dnCache == NULL) {
754             const Hashtable* m = getVisibleIDMap(status);
755             if (U_FAILURE(status)) {
756                 return result;
757             }
758             ncthis->dnCache = new DNCache(locale);
759             if (dnCache == NULL) {
760                 status = U_MEMORY_ALLOCATION_ERROR;
761                 return result;
762             }
763 
764             int32_t pos = UHASH_FIRST;
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;
769                 UnicodeString dname;
770                 f->getDisplayName(*id, locale, dname);
771                 if (dname.isBogus()) {
772                     status = U_MEMORY_ALLOCATION_ERROR;
773                 } else {
774                     dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
775                     if (U_SUCCESS(status)) {
776                         continue;
777                     }
778                 }
779                 delete dnCache;
780                 ncthis->dnCache = NULL;
781                 return result;
782             }
783         }
784     }
785 
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.
790      */
791     int32_t pos = UHASH_FIRST;
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)) {
796             continue;
797         }
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();
803             break;
804         }
805     }
806     delete matchKey;
807 
808     return result;
809 }
810 
811 URegistryKey
registerInstance(UObject * objToAdopt,const UnicodeString & id,UErrorCode & status)812 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
813 {
814     return registerInstance(objToAdopt, id, TRUE, status);
815 }
816 
817 URegistryKey
registerInstance(UObject * objToAdopt,const UnicodeString & id,UBool visible,UErrorCode & status)818 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
819 {
820     ICUServiceKey* key = createKey(&id, status);
821     if (key != NULL) {
822         UnicodeString canonicalID;
823         key->canonicalID(canonicalID);
824         delete key;
825 
826         ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
827         if (f != NULL) {
828             return registerFactory(f, status);
829         }
830     }
831     delete objToAdopt;
832     return NULL;
833 }
834 
835 ICUServiceFactory*
createSimpleFactory(UObject * objToAdopt,const UnicodeString & id,UBool visible,UErrorCode & status)836 ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
837 {
838     if (U_SUCCESS(status)) {
839         if ((objToAdopt != NULL) && (!id.isBogus())) {
840             return new SimpleFactory(objToAdopt, id, visible);
841         }
842         status = U_ILLEGAL_ARGUMENT_ERROR;
843     }
844     return NULL;
845 }
846 
847 URegistryKey
registerFactory(ICUServiceFactory * factoryToAdopt,UErrorCode & status)848 ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status)
849 {
850     if (U_SUCCESS(status) && factoryToAdopt != NULL) {
851         Mutex mutex(&lock);
852 
853         if (factories == NULL) {
854             factories = new UVector(deleteUObject, NULL, status);
855             if (U_FAILURE(status)) {
856                 delete factories;
857                 return NULL;
858             }
859         }
860         factories->insertElementAt(factoryToAdopt, 0, status);
861         if (U_SUCCESS(status)) {
862             clearCaches();
863         } else {
864             delete factoryToAdopt;
865             factoryToAdopt = NULL;
866         }
867     }
868 
869     if (factoryToAdopt != NULL) {
870         notifyChanged();
871     }
872 
873     return (URegistryKey)factoryToAdopt;
874 }
875 
876 UBool
unregister(URegistryKey rkey,UErrorCode & status)877 ICUService::unregister(URegistryKey rkey, UErrorCode& status)
878 {
879     ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
880     UBool result = FALSE;
881     if (factory != NULL && factories != NULL) {
882         Mutex mutex(&lock);
883 
884         if (factories->removeElement(factory)) {
885             clearCaches();
886             result = TRUE;
887         } else {
888             status = U_ILLEGAL_ARGUMENT_ERROR;
889             delete factory;
890         }
891     }
892     if (result) {
893         notifyChanged();
894     }
895     return result;
896 }
897 
898 void
reset()899 ICUService::reset()
900 {
901     {
902         Mutex mutex(&lock);
903         reInitializeFactories();
904         clearCaches();
905     }
906     notifyChanged();
907 }
908 
909 void
reInitializeFactories()910 ICUService::reInitializeFactories()
911 {
912     if (factories != NULL) {
913         factories->removeAllElements();
914     }
915 }
916 
917 UBool
isDefault() const918 ICUService::isDefault() const
919 {
920     return countFactories() == 0;
921 }
922 
923 ICUServiceKey*
createKey(const UnicodeString * id,UErrorCode & status) const924 ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
925 {
926     return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id);
927 }
928 
929 void
clearCaches()930 ICUService::clearCaches()
931 {
932     // callers synchronize before use
933     ++timestamp;
934     delete dnCache;
935     dnCache = NULL;
936     delete idCache;
937     idCache = NULL;
938     delete serviceCache; serviceCache = NULL;
939 }
940 
941 void
clearServiceCache()942 ICUService::clearServiceCache()
943 {
944     // callers synchronize before use
945     delete serviceCache; serviceCache = NULL;
946 }
947 
948 UBool
acceptsListener(const EventListener & l) const949 ICUService::acceptsListener(const EventListener& l) const
950 {
951     return dynamic_cast<const ServiceListener*>(&l) != NULL;
952 }
953 
954 void
notifyListener(EventListener & l) const955 ICUService::notifyListener(EventListener& l) const
956 {
957     ((ServiceListener&)l).serviceChanged(*this);
958 }
959 
960 UnicodeString&
getName(UnicodeString & result) const961 ICUService::getName(UnicodeString& result) const
962 {
963     return result.append(name);
964 }
965 
966 int32_t
countFactories() const967 ICUService::countFactories() const
968 {
969     return factories == NULL ? 0 : factories->size();
970 }
971 
972 int32_t
getTimestamp() const973 ICUService::getTimestamp() const
974 {
975     return timestamp;
976 }
977 
978 U_NAMESPACE_END
979 
980 /* UCONFIG_NO_SERVICE */
981 #endif
982