1 /**
2 *******************************************************************************
3 * Copyright (C) 2001-2012, 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(" 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("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(" id: ");
174 toAppendTo.append(_id);
175 toAppendTo.append(", visible: ");
176 toAppendTo.append(_visible ? "T" : "F");
177 return toAppendTo;
178 }
179
180 UnicodeString&
debugClass(UnicodeString & toAppendTo) const181 SimpleFactory::debugClass(UnicodeString& toAppendTo) const
182 {
183 return toAppendTo.append("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 = -1;;) {
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 = -1;
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 = -1;
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