1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /**
4 *******************************************************************************
5 * Copyright (C) 2001-2014, International Business Machines Corporation.
6 * All Rights Reserved.
7 *******************************************************************************
8 */
9 
10 #include "unicode/utypes.h"
11 #include "unicode/localpointer.h"
12 
13 #if !UCONFIG_NO_SERVICE
14 
15 #include "serv.h"
16 #include "umutex.h"
17 
18 #undef SERVICE_REFCOUNT
19 
20 // in case we use the refcount stuff
21 
22 U_NAMESPACE_BEGIN
23 
24 /*
25 ******************************************************************
26 */
27 
28 const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F;   /* '/' */
29 
ICUServiceKey(const UnicodeString & id)30 ICUServiceKey::ICUServiceKey(const UnicodeString& id)
31 : _id(id) {
32 }
33 
~ICUServiceKey()34 ICUServiceKey::~ICUServiceKey()
35 {
36 }
37 
38 const UnicodeString&
getID() const39 ICUServiceKey::getID() const
40 {
41     return _id;
42 }
43 
44 UnicodeString&
canonicalID(UnicodeString & result) const45 ICUServiceKey::canonicalID(UnicodeString& result) const
46 {
47     return result.append(_id);
48 }
49 
50 UnicodeString&
currentID(UnicodeString & result) const51 ICUServiceKey::currentID(UnicodeString& result) const
52 {
53     return canonicalID(result);
54 }
55 
56 UnicodeString&
currentDescriptor(UnicodeString & result) const57 ICUServiceKey::currentDescriptor(UnicodeString& result) const
58 {
59     prefix(result);
60     result.append(PREFIX_DELIMITER);
61     return currentID(result);
62 }
63 
64 UBool
fallback()65 ICUServiceKey::fallback()
66 {
67     return FALSE;
68 }
69 
70 UBool
isFallbackOf(const UnicodeString & id) const71 ICUServiceKey::isFallbackOf(const UnicodeString& id) const
72 {
73     return id == _id;
74 }
75 
76 UnicodeString&
prefix(UnicodeString & result) const77 ICUServiceKey::prefix(UnicodeString& result) const
78 {
79     return result;
80 }
81 
82 UnicodeString&
parsePrefix(UnicodeString & result)83 ICUServiceKey::parsePrefix(UnicodeString& result)
84 {
85     int32_t n = result.indexOf(PREFIX_DELIMITER);
86     if (n < 0) {
87         n = 0;
88     }
89     result.remove(n);
90     return result;
91 }
92 
93 UnicodeString&
parseSuffix(UnicodeString & result)94 ICUServiceKey::parseSuffix(UnicodeString& result)
95 {
96     int32_t n = result.indexOf(PREFIX_DELIMITER);
97     if (n >= 0) {
98         result.remove(0, n+1);
99     }
100     return result;
101 }
102 
103 #ifdef SERVICE_DEBUG
104 UnicodeString&
debug(UnicodeString & result) const105 ICUServiceKey::debug(UnicodeString& result) const
106 {
107     debugClass(result);
108     result.append((UnicodeString)" id: ");
109     result.append(_id);
110     return result;
111 }
112 
113 UnicodeString&
debugClass(UnicodeString & result) const114 ICUServiceKey::debugClass(UnicodeString& result) const
115 {
116     return result.append((UnicodeString)"ICUServiceKey");
117 }
118 #endif
119 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)120 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey)
121 
122 /*
123 ******************************************************************
124 */
125 
126 ICUServiceFactory::~ICUServiceFactory() {}
127 
SimpleFactory(UObject * instanceToAdopt,const UnicodeString & id,UBool visible)128 SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible)
129 : _instance(instanceToAdopt), _id(id), _visible(visible)
130 {
131 }
132 
~SimpleFactory()133 SimpleFactory::~SimpleFactory()
134 {
135     delete _instance;
136 }
137 
138 UObject*
create(const ICUServiceKey & key,const ICUService * service,UErrorCode & status) const139 SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const
140 {
141     if (U_SUCCESS(status)) {
142         UnicodeString temp;
143         if (_id == key.currentID(temp)) {
144             return service->cloneInstance(_instance);
145         }
146     }
147     return NULL;
148 }
149 
150 void
updateVisibleIDs(Hashtable & result,UErrorCode & status) const151 SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const
152 {
153     if (_visible) {
154         result.put(_id, (void*)this, status); // cast away const
155     } else {
156         result.remove(_id);
157     }
158 }
159 
160 UnicodeString&
getDisplayName(const UnicodeString & id,const Locale &,UnicodeString & result) const161 SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const
162 {
163     if (_visible && _id == id) {
164         result = _id;
165     } else {
166         result.setToBogus();
167     }
168     return result;
169 }
170 
171 #ifdef SERVICE_DEBUG
172 UnicodeString&
debug(UnicodeString & toAppendTo) const173 SimpleFactory::debug(UnicodeString& toAppendTo) const
174 {
175     debugClass(toAppendTo);
176     toAppendTo.append((UnicodeString)" id: ");
177     toAppendTo.append(_id);
178     toAppendTo.append((UnicodeString)", visible: ");
179     toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F");
180     return toAppendTo;
181 }
182 
183 UnicodeString&
debugClass(UnicodeString & toAppendTo) const184 SimpleFactory::debugClass(UnicodeString& toAppendTo) const
185 {
186     return toAppendTo.append((UnicodeString)"SimpleFactory");
187 }
188 #endif
189 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)190 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory)
191 
192 /*
193 ******************************************************************
194 */
195 
196 ServiceListener::~ServiceListener() {}
197 
198 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener)
199 
200 /*
201 ******************************************************************
202 */
203 
204 // Record the actual id for this service in the cache, so we can return it
205 // even if we succeed later with a different id.
206 class CacheEntry : public UMemory {
207 private:
208     int32_t refcount;
209 
210 public:
211     UnicodeString actualDescriptor;
212     UObject* service;
213 
214     /**
215     * Releases a reference to the shared resource.
216     */
~CacheEntry()217     ~CacheEntry() {
218         delete service;
219     }
220 
CacheEntry(const UnicodeString & _actualDescriptor,UObject * _service)221     CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service)
222         : refcount(1), actualDescriptor(_actualDescriptor), service(_service) {
223     }
224 
225     /**
226     * Instantiation creates an initial reference, so don't call this
227     * unless you're creating a new pointer to this.  Management of
228     * that pointer will have to know how to deal with refcounts.
229     * Return true if the resource has not already been released.
230     */
ref()231     CacheEntry* ref() {
232         ++refcount;
233         return this;
234     }
235 
236     /**
237     * Destructions removes a reference, so don't call this unless
238     * you're removing pointer to this somewhere.  Management of that
239     * pointer will have to know how to deal with refcounts.  Once
240     * the refcount drops to zero, the resource is released.  Return
241     * false if the resource has been released.
242     */
unref()243     CacheEntry* unref() {
244         if ((--refcount) == 0) {
245             delete this;
246             return NULL;
247         }
248         return this;
249     }
250 
251     /**
252     * Return TRUE if there is at least one reference to this and the
253     * resource has not been released.
254     */
isShared() const255     UBool isShared() const {
256         return refcount > 1;
257     }
258 };
259 
260 // Deleter for serviceCache
261 U_CDECL_BEGIN
262 static void U_CALLCONV
cacheDeleter(void * obj)263 cacheDeleter(void* obj) {
264     U_NAMESPACE_USE ((CacheEntry*)obj)->unref();
265 }
266 
267 U_CDECL_END
268 
269 /*
270 ******************************************************************
271 */
272 
273 class DNCache : public UMemory {
274 public:
275     Hashtable cache;
276     const Locale locale;
277 
DNCache(const Locale & _locale)278     DNCache(const Locale& _locale)
279         : cache(), locale(_locale)
280     {
281         // cache.setKeyDeleter(uprv_deleteUObject);
282     }
283 };
284 
285 
286 /*
287 ******************************************************************
288 */
289 
290 StringPair*
create(const UnicodeString & displayName,const UnicodeString & id,UErrorCode & status)291 StringPair::create(const UnicodeString& displayName,
292                    const UnicodeString& id,
293                    UErrorCode& status)
294 {
295     if (U_SUCCESS(status)) {
296         StringPair* sp = new StringPair(displayName, id);
297         if (sp == NULL || sp->isBogus()) {
298             status = U_MEMORY_ALLOCATION_ERROR;
299             delete sp;
300             return NULL;
301         }
302         return sp;
303     }
304     return NULL;
305 }
306 
307 UBool
isBogus() const308 StringPair::isBogus() const {
309     return displayName.isBogus() || id.isBogus();
310 }
311 
StringPair(const UnicodeString & _displayName,const UnicodeString & _id)312 StringPair::StringPair(const UnicodeString& _displayName,
313                        const UnicodeString& _id)
314 : displayName(_displayName)
315 , id(_id)
316 {
317 }
318 
319 U_CDECL_BEGIN
320 static void U_CALLCONV
userv_deleteStringPair(void * obj)321 userv_deleteStringPair(void *obj) {
322     U_NAMESPACE_USE delete (StringPair*) obj;
323 }
324 U_CDECL_END
325 
326 /*
327 ******************************************************************
328 */
329 
330 static UMutex lock;
331 
ICUService()332 ICUService::ICUService()
333 : name()
334 , timestamp(0)
335 , factories(NULL)
336 , serviceCache(NULL)
337 , idCache(NULL)
338 , dnCache(NULL)
339 {
340 }
341 
ICUService(const UnicodeString & newName)342 ICUService::ICUService(const UnicodeString& newName)
343 : name(newName)
344 , timestamp(0)
345 , factories(NULL)
346 , serviceCache(NULL)
347 , idCache(NULL)
348 , dnCache(NULL)
349 {
350 }
351 
~ICUService()352 ICUService::~ICUService()
353 {
354     {
355         Mutex mutex(&lock);
356         clearCaches();
357         delete factories;
358         factories = NULL;
359     }
360 }
361 
362 UObject*
get(const UnicodeString & descriptor,UErrorCode & status) const363 ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const
364 {
365     return get(descriptor, NULL, status);
366 }
367 
368 UObject*
get(const UnicodeString & descriptor,UnicodeString * actualReturn,UErrorCode & status) const369 ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const
370 {
371     UObject* result = NULL;
372     ICUServiceKey* key = createKey(&descriptor, status);
373     if (key) {
374         result = getKey(*key, actualReturn, status);
375         delete key;
376     }
377     return result;
378 }
379 
380 UObject*
getKey(ICUServiceKey & key,UErrorCode & status) const381 ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const
382 {
383     return getKey(key, NULL, status);
384 }
385 
386 // this is a vector that subclasses of ICUService can override to further customize the result object
387 // before returning it.  All other public get functions should call this one.
388 
389 UObject*
getKey(ICUServiceKey & key,UnicodeString * actualReturn,UErrorCode & status) const390 ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const
391 {
392     return getKey(key, actualReturn, NULL, status);
393 }
394 
395 // make it possible to call reentrantly on systems that don't have reentrant mutexes.
396 // we can use this simple approach since we know the situation where we're calling
397 // reentrantly even without knowing the thread.
398 class XMutex : public UMemory {
399 public:
XMutex(UMutex * mutex,UBool reentering)400     inline XMutex(UMutex *mutex, UBool reentering)
401         : fMutex(mutex)
402         , fActive(!reentering)
403     {
404         if (fActive) umtx_lock(fMutex);
405     }
~XMutex()406     inline ~XMutex() {
407         if (fActive) umtx_unlock(fMutex);
408     }
409 
410 private:
411     UMutex  *fMutex;
412     UBool fActive;
413 };
414 
415 // called only by factories, treat as private
416 UObject*
getKey(ICUServiceKey & key,UnicodeString * actualReturn,const ICUServiceFactory * factory,UErrorCode & status) const417 ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const
418 {
419     if (U_FAILURE(status)) {
420         return NULL;
421     }
422 
423     if (isDefault()) {
424         return handleDefault(key, actualReturn, status);
425     }
426 
427     ICUService* ncthis = (ICUService*)this; // cast away semantic const
428 
429     CacheEntry* result = NULL;
430     {
431         // The factory list can't be modified until we're done,
432         // otherwise we might update the cache with an invalid result.
433         // The cache has to stay in synch with the factory list.
434         // ICU doesn't have monitors so we can't use rw locks, so
435         // we single-thread everything using this service, for now.
436 
437         // if factory is not null, we're calling from within the mutex,
438         // and since some unix machines don't have reentrant mutexes we
439         // need to make sure not to try to lock it again.
440         XMutex mutex(&lock, factory != NULL);
441 
442         if (serviceCache == NULL) {
443             ncthis->serviceCache = new Hashtable(status);
444             if (ncthis->serviceCache == NULL) {
445                 status = U_MEMORY_ALLOCATION_ERROR;
446                 return NULL;
447             }
448             if (U_FAILURE(status)) {
449                 delete serviceCache;
450                 return NULL;
451             }
452             serviceCache->setValueDeleter(cacheDeleter);
453         }
454 
455         UnicodeString currentDescriptor;
456         LocalPointer<UVector> cacheDescriptorList;
457         UBool putInCache = FALSE;
458 
459         int32_t startIndex = 0;
460         int32_t limit = factories->size();
461         UBool cacheResult = TRUE;
462 
463         if (factory != NULL) {
464             for (int32_t i = 0; i < limit; ++i) {
465                 if (factory == (const ICUServiceFactory*)factories->elementAt(i)) {
466                     startIndex = i + 1;
467                     break;
468                 }
469             }
470             if (startIndex == 0) {
471                 // throw new InternalError("Factory " + factory + "not registered with service: " + this);
472                 status = U_ILLEGAL_ARGUMENT_ERROR;
473                 return NULL;
474             }
475             cacheResult = FALSE;
476         }
477 
478         do {
479             currentDescriptor.remove();
480             key.currentDescriptor(currentDescriptor);
481             result = (CacheEntry*)serviceCache->get(currentDescriptor);
482             if (result != NULL) {
483                 break;
484             }
485 
486             // first test of cache failed, so we'll have to update
487             // the cache if we eventually succeed-- that is, if we're
488             // going to update the cache at all.
489             putInCache = TRUE;
490 
491             int32_t index = startIndex;
492             while (index < limit) {
493                 ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++);
494                 LocalPointer<UObject> service(f->create(key, this, status));
495                 if (U_FAILURE(status)) {
496                     return NULL;
497                 }
498                 if (service.isValid()) {
499                     result = new CacheEntry(currentDescriptor, service.getAlias());
500                     if (result == NULL) {
501                         status = U_MEMORY_ALLOCATION_ERROR;
502                         return NULL;
503                     }
504                     service.orphan(); // result now owns service.
505 
506                     goto outerEnd;
507                 }
508             }
509 
510             // prepare to load the cache with all additional ids that
511             // will resolve to result, assuming we'll succeed.  We
512             // don't want to keep querying on an id that's going to
513             // fallback to the one that succeeded, we want to hit the
514             // cache the first time next goaround.
515             if (cacheDescriptorList.isNull()) {
516                 cacheDescriptorList.adoptInsteadAndCheckErrorCode(new UVector(uprv_deleteUObject, NULL, 5, status), status);
517                 if (U_FAILURE(status)) {
518                     return NULL;
519                 }
520             }
521 
522             LocalPointer<UnicodeString> idToCache(new UnicodeString(currentDescriptor), status);
523             if (U_FAILURE(status)) {
524                 return NULL;
525             }
526             if (idToCache->isBogus()) {
527                 status = U_MEMORY_ALLOCATION_ERROR;
528                 return NULL;
529             }
530             cacheDescriptorList->adoptElement(idToCache.orphan(), status);
531             if (U_FAILURE(status)) {
532                 return NULL;
533             }
534         } while (key.fallback());
535 outerEnd:
536 
537         if (result != NULL) {
538             if (putInCache && cacheResult) {
539                 serviceCache->put(result->actualDescriptor, result, status);
540                 if (U_FAILURE(status)) {
541                     return NULL;
542                 }
543 
544                 if (cacheDescriptorList.isValid()) {
545                     for (int32_t i = cacheDescriptorList->size(); --i >= 0;) {
546                         UnicodeString* desc = (UnicodeString*)cacheDescriptorList->elementAt(i);
547 
548                         serviceCache->put(*desc, result, status);
549                         if (U_FAILURE(status)) {
550                             return NULL;
551                         }
552 
553                         result->ref();
554                         cacheDescriptorList->removeElementAt(i);
555                     }
556                 }
557             }
558 
559             if (actualReturn != NULL) {
560                 // strip null prefix
561                 if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/)
562                     actualReturn->remove();
563                     actualReturn->append(result->actualDescriptor,
564                         1,
565                         result->actualDescriptor.length() - 1);
566                 } else {
567                     *actualReturn = result->actualDescriptor;
568                 }
569 
570                 if (actualReturn->isBogus()) {
571                     status = U_MEMORY_ALLOCATION_ERROR;
572                     delete result;
573                     return NULL;
574                 }
575             }
576 
577             UObject* service = cloneInstance(result->service);
578             if (putInCache && !cacheResult) {
579                 delete result;
580             }
581             return service;
582         }
583     }
584 
585     return handleDefault(key, actualReturn, status);
586 }
587 
588 UObject*
handleDefault(const ICUServiceKey &,UnicodeString *,UErrorCode &) const589 ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const
590 {
591     return NULL;
592 }
593 
594 UVector&
getVisibleIDs(UVector & result,UErrorCode & status) const595 ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const {
596     return getVisibleIDs(result, NULL, status);
597 }
598 
599 UVector&
getVisibleIDs(UVector & result,const UnicodeString * matchID,UErrorCode & status) const600 ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const
601 {
602     result.removeAllElements();
603 
604     if (U_FAILURE(status)) {
605         return result;
606     }
607     UObjectDeleter *savedDeleter = result.setDeleter(uprv_deleteUObject);
608 
609     {
610         Mutex mutex(&lock);
611         const Hashtable* map = getVisibleIDMap(status);
612         if (map != NULL) {
613             ICUServiceKey* fallbackKey = createKey(matchID, status);
614 
615             for (int32_t pos = UHASH_FIRST; U_SUCCESS(status); ) {
616                 const UHashElement* e = map->nextElement(pos);
617                 if (e == NULL) {
618                     break;
619                 }
620 
621                 const UnicodeString* id = (const UnicodeString*)e->key.pointer;
622                 if (fallbackKey != NULL) {
623                     if (!fallbackKey->isFallbackOf(*id)) {
624                         continue;
625                     }
626                 }
627 
628                 LocalPointer<UnicodeString> idClone(new UnicodeString(*id), status);
629                 if (U_SUCCESS(status) && idClone->isBogus()) {
630                     status = U_MEMORY_ALLOCATION_ERROR;
631                 }
632                 result.adoptElement(idClone.orphan(), status);
633             }
634             delete fallbackKey;
635         }
636     }
637     if (U_FAILURE(status)) {
638         result.removeAllElements();
639     }
640     result.setDeleter(savedDeleter);
641     return result;
642 }
643 
644 const Hashtable*
getVisibleIDMap(UErrorCode & status) const645 ICUService::getVisibleIDMap(UErrorCode& status) const {
646     if (U_FAILURE(status)) return NULL;
647 
648     // must only be called when lock is already held
649 
650     ICUService* ncthis = (ICUService*)this; // cast away semantic const
651     if (idCache == NULL) {
652         ncthis->idCache = new Hashtable(status);
653         if (idCache == NULL) {
654             status = U_MEMORY_ALLOCATION_ERROR;
655         } else if (factories != NULL) {
656             for (int32_t pos = factories->size(); --pos >= 0;) {
657                 ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos);
658                 f->updateVisibleIDs(*idCache, status);
659             }
660             if (U_FAILURE(status)) {
661                 delete idCache;
662                 ncthis->idCache = NULL;
663             }
664         }
665     }
666 
667     return idCache;
668 }
669 
670 
671 UnicodeString&
getDisplayName(const UnicodeString & id,UnicodeString & result) const672 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const
673 {
674     return getDisplayName(id, result, Locale::getDefault());
675 }
676 
677 UnicodeString&
getDisplayName(const UnicodeString & id,UnicodeString & result,const Locale & locale) const678 ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const
679 {
680     {
681         UErrorCode status = U_ZERO_ERROR;
682         Mutex mutex(&lock);
683         const Hashtable* map = getVisibleIDMap(status);
684         if (map != NULL) {
685             ICUServiceFactory* f = (ICUServiceFactory*)map->get(id);
686             if (f != NULL) {
687                 f->getDisplayName(id, locale, result);
688                 return result;
689             }
690 
691             // fallback
692             status = U_ZERO_ERROR;
693             ICUServiceKey* fallbackKey = createKey(&id, status);
694             while (fallbackKey != NULL && fallbackKey->fallback()) {
695                 UnicodeString us;
696                 fallbackKey->currentID(us);
697                 f = (ICUServiceFactory*)map->get(us);
698                 if (f != NULL) {
699                     f->getDisplayName(id, locale, result);
700                     delete fallbackKey;
701                     return result;
702                 }
703             }
704             delete fallbackKey;
705         }
706     }
707     result.setToBogus();
708     return result;
709 }
710 
711 UVector&
getDisplayNames(UVector & result,UErrorCode & status) const712 ICUService::getDisplayNames(UVector& result, UErrorCode& status) const
713 {
714     return getDisplayNames(result, Locale::getDefault(), NULL, status);
715 }
716 
717 
718 UVector&
getDisplayNames(UVector & result,const Locale & locale,UErrorCode & status) const719 ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const
720 {
721     return getDisplayNames(result, locale, NULL, status);
722 }
723 
724 UVector&
getDisplayNames(UVector & result,const Locale & locale,const UnicodeString * matchID,UErrorCode & status) const725 ICUService::getDisplayNames(UVector& result,
726                             const Locale& locale,
727                             const UnicodeString* matchID,
728                             UErrorCode& status) const
729 {
730     result.removeAllElements();
731     result.setDeleter(userv_deleteStringPair);
732     if (U_SUCCESS(status)) {
733         ICUService* ncthis = (ICUService*)this; // cast away semantic const
734         Mutex mutex(&lock);
735 
736         if (dnCache != NULL && dnCache->locale != locale) {
737             delete dnCache;
738             ncthis->dnCache = NULL;
739         }
740 
741         if (dnCache == NULL) {
742             const Hashtable* m = getVisibleIDMap(status);
743             if (U_FAILURE(status)) {
744                 return result;
745             }
746             ncthis->dnCache = new DNCache(locale);
747             if (dnCache == NULL) {
748                 status = U_MEMORY_ALLOCATION_ERROR;
749                 return result;
750             }
751 
752             int32_t pos = UHASH_FIRST;
753             const UHashElement* entry = NULL;
754             while ((entry = m->nextElement(pos)) != NULL) {
755                 const UnicodeString* id = (const UnicodeString*)entry->key.pointer;
756                 ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer;
757                 UnicodeString dname;
758                 f->getDisplayName(*id, locale, dname);
759                 if (dname.isBogus()) {
760                     status = U_MEMORY_ALLOCATION_ERROR;
761                 } else {
762                     dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap
763                     if (U_SUCCESS(status)) {
764                         continue;
765                     }
766                 }
767                 delete dnCache;
768                 ncthis->dnCache = NULL;
769                 return result;
770             }
771         }
772     }
773 
774     ICUServiceKey* matchKey = createKey(matchID, status);
775     /* To ensure that all elements in the hashtable are iterated, set pos to -1.
776      * nextElement(pos) will skip the position at pos and begin the iteration
777      * at the next position, which in this case will be 0.
778      */
779     int32_t pos = UHASH_FIRST;
780     const UHashElement *entry = NULL;
781     while ((entry = dnCache->cache.nextElement(pos)) != NULL) {
782         const UnicodeString* id = (const UnicodeString*)entry->value.pointer;
783         if (matchKey != NULL && !matchKey->isFallbackOf(*id)) {
784             continue;
785         }
786         const UnicodeString* dn = (const UnicodeString*)entry->key.pointer;
787         StringPair* sp = StringPair::create(*id, *dn, status);
788         result.adoptElement(sp, status);
789         if (U_FAILURE(status)) {
790             result.removeAllElements();
791             break;
792         }
793     }
794     delete matchKey;
795 
796     return result;
797 }
798 
799 URegistryKey
registerInstance(UObject * objToAdopt,const UnicodeString & id,UErrorCode & status)800 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status)
801 {
802     return registerInstance(objToAdopt, id, TRUE, status);
803 }
804 
805 URegistryKey
registerInstance(UObject * objToAdopt,const UnicodeString & id,UBool visible,UErrorCode & status)806 ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
807 {
808     ICUServiceKey* key = createKey(&id, status);
809     if (key != NULL) {
810         UnicodeString canonicalID;
811         key->canonicalID(canonicalID);
812         delete key;
813 
814         ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status);
815         if (f != NULL) {
816             return registerFactory(f, status);
817         }
818     }
819     delete objToAdopt;
820     return NULL;
821 }
822 
823 ICUServiceFactory*
createSimpleFactory(UObject * objToAdopt,const UnicodeString & id,UBool visible,UErrorCode & status)824 ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status)
825 {
826     if (U_SUCCESS(status)) {
827         if ((objToAdopt != NULL) && (!id.isBogus())) {
828             return new SimpleFactory(objToAdopt, id, visible);
829         }
830         status = U_ILLEGAL_ARGUMENT_ERROR;
831     }
832     return NULL;
833 }
834 
835 URegistryKey
registerFactory(ICUServiceFactory * factoryToAdopt,UErrorCode & status)836 ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status)
837 {
838     LocalPointer<ICUServiceFactory>lpFactoryToAdopt(factoryToAdopt);
839     if (U_FAILURE(status) || factoryToAdopt == nullptr) {
840         return nullptr;
841     }
842     {
843         Mutex mutex(&lock);
844 
845         if (factories == nullptr) {
846             LocalPointer<UVector> lpFactories(new UVector(uprv_deleteUObject, nullptr, status), status);
847             if (U_FAILURE(status)) {
848                 return nullptr;
849             }
850             factories = lpFactories.orphan();
851         }
852         factories->insertElementAt(lpFactoryToAdopt.orphan(), 0, status);
853         if (U_SUCCESS(status)) {
854             clearCaches();
855         }
856     }   // Close of mutex lock block.
857 
858     if (U_SUCCESS(status)) {
859         notifyChanged();
860         return (URegistryKey)factoryToAdopt;
861     } else {
862         return nullptr;
863     }
864 }
865 
866 UBool
unregister(URegistryKey rkey,UErrorCode & status)867 ICUService::unregister(URegistryKey rkey, UErrorCode& status)
868 {
869     ICUServiceFactory *factory = (ICUServiceFactory*)rkey;
870     UBool result = FALSE;
871     if (factory != NULL && factories != NULL) {
872         Mutex mutex(&lock);
873 
874         if (factories->removeElement(factory)) {
875             clearCaches();
876             result = TRUE;
877         } else {
878             status = U_ILLEGAL_ARGUMENT_ERROR;
879             delete factory;
880         }
881     }
882     if (result) {
883         notifyChanged();
884     }
885     return result;
886 }
887 
888 void
reset()889 ICUService::reset()
890 {
891     {
892         Mutex mutex(&lock);
893         reInitializeFactories();
894         clearCaches();
895     }
896     notifyChanged();
897 }
898 
899 void
reInitializeFactories()900 ICUService::reInitializeFactories()
901 {
902     if (factories != NULL) {
903         factories->removeAllElements();
904     }
905 }
906 
907 UBool
isDefault() const908 ICUService::isDefault() const
909 {
910     return countFactories() == 0;
911 }
912 
913 ICUServiceKey*
createKey(const UnicodeString * id,UErrorCode & status) const914 ICUService::createKey(const UnicodeString* id, UErrorCode& status) const
915 {
916     return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id);
917 }
918 
919 void
clearCaches()920 ICUService::clearCaches()
921 {
922     // callers synchronize before use
923     ++timestamp;
924     delete dnCache;
925     dnCache = NULL;
926     delete idCache;
927     idCache = NULL;
928     delete serviceCache; serviceCache = NULL;
929 }
930 
931 void
clearServiceCache()932 ICUService::clearServiceCache()
933 {
934     // callers synchronize before use
935     delete serviceCache; serviceCache = NULL;
936 }
937 
938 UBool
acceptsListener(const EventListener & l) const939 ICUService::acceptsListener(const EventListener& l) const
940 {
941     return dynamic_cast<const ServiceListener*>(&l) != NULL;
942 }
943 
944 void
notifyListener(EventListener & l) const945 ICUService::notifyListener(EventListener& l) const
946 {
947     ((ServiceListener&)l).serviceChanged(*this);
948 }
949 
950 UnicodeString&
getName(UnicodeString & result) const951 ICUService::getName(UnicodeString& result) const
952 {
953     return result.append(name);
954 }
955 
956 int32_t
countFactories() const957 ICUService::countFactories() const
958 {
959     return factories == NULL ? 0 : factories->size();
960 }
961 
962 int32_t
getTimestamp() const963 ICUService::getTimestamp() const
964 {
965     return timestamp;
966 }
967 
968 U_NAMESPACE_END
969 
970 /* UCONFIG_NO_SERVICE */
971 #endif
972