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