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 | |
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 | |
29 | ICUServiceKey::ICUServiceKey(const UnicodeString& id) |
30 | : _id(id) { |
31 | } |
32 | |
33 | ICUServiceKey::~ICUServiceKey() |
34 | { |
35 | } |
36 | |
37 | const UnicodeString& |
38 | ICUServiceKey::getID() const |
39 | { |
40 | return _id; |
41 | } |
42 | |
43 | UnicodeString& |
44 | ICUServiceKey::canonicalID(UnicodeString& result) const |
45 | { |
46 | return result.append(_id); |
47 | } |
48 | |
49 | UnicodeString& |
50 | ICUServiceKey::currentID(UnicodeString& result) const |
51 | { |
52 | return canonicalID(result); |
53 | } |
54 | |
55 | UnicodeString& |
56 | ICUServiceKey::currentDescriptor(UnicodeString& result) const |
57 | { |
58 | prefix(result); |
59 | result.append(PREFIX_DELIMITER); |
60 | return currentID(result); |
61 | } |
62 | |
63 | UBool |
64 | ICUServiceKey::fallback() |
65 | { |
66 | return FALSE; |
67 | } |
68 | |
69 | UBool |
70 | ICUServiceKey::isFallbackOf(const UnicodeString& id) const |
71 | { |
72 | return id == _id; |
73 | } |
74 | |
75 | UnicodeString& |
76 | ICUServiceKey::prefix(UnicodeString& result) const |
77 | { |
78 | return result; |
79 | } |
80 | |
81 | UnicodeString& |
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& |
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& |
104 | 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& |
113 | ICUServiceKey::debugClass(UnicodeString& result) const |
114 | { |
115 | return result.append((UnicodeString)"ICUServiceKey" ); |
116 | } |
117 | #endif |
118 | |
119 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey) |
120 | |
121 | /* |
122 | ****************************************************************** |
123 | */ |
124 | |
125 | ICUServiceFactory::~ICUServiceFactory() {} |
126 | |
127 | SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) |
128 | : _instance(instanceToAdopt), _id(id), _visible(visible) |
129 | { |
130 | } |
131 | |
132 | SimpleFactory::~SimpleFactory() |
133 | { |
134 | delete _instance; |
135 | } |
136 | |
137 | UObject* |
138 | 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 |
150 | 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& |
160 | 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& |
172 | 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& |
183 | SimpleFactory::debugClass(UnicodeString& toAppendTo) const |
184 | { |
185 | return toAppendTo.append((UnicodeString)"SimpleFactory" ); |
186 | } |
187 | #endif |
188 | |
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 | */ |
216 | ~CacheEntry() { |
217 | delete service; |
218 | } |
219 | |
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 | */ |
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 | */ |
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 | */ |
254 | UBool isShared() const { |
255 | return refcount > 1; |
256 | } |
257 | }; |
258 | |
259 | // UObjectDeleter for serviceCache |
260 | U_CDECL_BEGIN |
261 | static void U_CALLCONV |
262 | cacheDeleter(void* obj) { |
263 | U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); |
264 | } |
265 | |
266 | /** |
267 | * Deleter for UObjects |
268 | */ |
269 | static void U_CALLCONV |
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 | |
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* |
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 |
314 | StringPair::isBogus() const { |
315 | return displayName.isBogus() || id.isBogus(); |
316 | } |
317 | |
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 |
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; |
337 | |
338 | ICUService::ICUService() |
339 | : name() |
340 | , timestamp(0) |
341 | , factories(NULL) |
342 | , serviceCache(NULL) |
343 | , idCache(NULL) |
344 | , dnCache(NULL) |
345 | { |
346 | } |
347 | |
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 | |
358 | ICUService::~ICUService() |
359 | { |
360 | { |
361 | Mutex mutex(&lock); |
362 | clearCaches(); |
363 | delete factories; |
364 | factories = NULL; |
365 | } |
366 | } |
367 | |
368 | UObject* |
369 | ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const |
370 | { |
371 | return get(descriptor, NULL, status); |
372 | } |
373 | |
374 | UObject* |
375 | 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* |
387 | 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* |
396 | 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: |
406 | inline XMutex(UMutex *mutex, UBool reentering) |
407 | : fMutex(mutex) |
408 | , fActive(!reentering) |
409 | { |
410 | if (fActive) umtx_lock(fMutex); |
411 | } |
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; |
423 | UVectorDeleter() : _obj(NULL) {} |
424 | ~UVectorDeleter() { delete _obj; } |
425 | }; |
426 | |
427 | // called only by factories, treat as private |
428 | UObject* |
429 | 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 | return NULL; |
551 | } |
552 | |
553 | if (cacheDescriptorList._obj != NULL) { |
554 | for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) { |
555 | UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i); |
556 | |
557 | serviceCache->put(*desc, result, status); |
558 | if (U_FAILURE(status)) { |
559 | return NULL; |
560 | } |
561 | |
562 | result->ref(); |
563 | cacheDescriptorList._obj->removeElementAt(i); |
564 | } |
565 | } |
566 | } |
567 | |
568 | if (actualReturn != NULL) { |
569 | // strip null prefix |
570 | if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/) |
571 | actualReturn->remove(); |
572 | actualReturn->append(result->actualDescriptor, |
573 | 1, |
574 | result->actualDescriptor.length() - 1); |
575 | } else { |
576 | *actualReturn = result->actualDescriptor; |
577 | } |
578 | |
579 | if (actualReturn->isBogus()) { |
580 | status = U_MEMORY_ALLOCATION_ERROR; |
581 | delete result; |
582 | return NULL; |
583 | } |
584 | } |
585 | |
586 | UObject* service = cloneInstance(result->service); |
587 | if (putInCache && !cacheResult) { |
588 | delete result; |
589 | } |
590 | return service; |
591 | } |
592 | } |
593 | |
594 | return handleDefault(key, actualReturn, status); |
595 | } |
596 | |
597 | UObject* |
598 | ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const |
599 | { |
600 | return NULL; |
601 | } |
602 | |
603 | UVector& |
604 | ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { |
605 | return getVisibleIDs(result, NULL, status); |
606 | } |
607 | |
608 | UVector& |
609 | ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const |
610 | { |
611 | result.removeAllElements(); |
612 | |
613 | if (U_FAILURE(status)) { |
614 | return result; |
615 | } |
616 | |
617 | { |
618 | Mutex mutex(&lock); |
619 | const Hashtable* map = getVisibleIDMap(status); |
620 | if (map != NULL) { |
621 | ICUServiceKey* fallbackKey = createKey(matchID, status); |
622 | |
623 | for (int32_t pos = UHASH_FIRST;;) { |
624 | const UHashElement* e = map->nextElement(pos); |
625 | if (e == NULL) { |
626 | break; |
627 | } |
628 | |
629 | const UnicodeString* id = (const UnicodeString*)e->key.pointer; |
630 | if (fallbackKey != NULL) { |
631 | if (!fallbackKey->isFallbackOf(*id)) { |
632 | continue; |
633 | } |
634 | } |
635 | |
636 | UnicodeString* idClone = new UnicodeString(*id); |
637 | if (idClone == NULL || idClone->isBogus()) { |
638 | delete idClone; |
639 | status = U_MEMORY_ALLOCATION_ERROR; |
640 | break; |
641 | } |
642 | result.addElement(idClone, status); |
643 | if (U_FAILURE(status)) { |
644 | delete idClone; |
645 | break; |
646 | } |
647 | } |
648 | delete fallbackKey; |
649 | } |
650 | } |
651 | if (U_FAILURE(status)) { |
652 | result.removeAllElements(); |
653 | } |
654 | return result; |
655 | } |
656 | |
657 | const Hashtable* |
658 | ICUService::getVisibleIDMap(UErrorCode& status) const { |
659 | if (U_FAILURE(status)) return NULL; |
660 | |
661 | // must only be called when lock is already held |
662 | |
663 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
664 | if (idCache == NULL) { |
665 | ncthis->idCache = new Hashtable(status); |
666 | if (idCache == NULL) { |
667 | status = U_MEMORY_ALLOCATION_ERROR; |
668 | } else if (factories != NULL) { |
669 | for (int32_t pos = factories->size(); --pos >= 0;) { |
670 | ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); |
671 | f->updateVisibleIDs(*idCache, status); |
672 | } |
673 | if (U_FAILURE(status)) { |
674 | delete idCache; |
675 | ncthis->idCache = NULL; |
676 | } |
677 | } |
678 | } |
679 | |
680 | return idCache; |
681 | } |
682 | |
683 | |
684 | UnicodeString& |
685 | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const |
686 | { |
687 | return getDisplayName(id, result, Locale::getDefault()); |
688 | } |
689 | |
690 | UnicodeString& |
691 | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const |
692 | { |
693 | { |
694 | UErrorCode status = U_ZERO_ERROR; |
695 | Mutex mutex(&lock); |
696 | const Hashtable* map = getVisibleIDMap(status); |
697 | if (map != NULL) { |
698 | ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); |
699 | if (f != NULL) { |
700 | f->getDisplayName(id, locale, result); |
701 | return result; |
702 | } |
703 | |
704 | // fallback |
705 | status = U_ZERO_ERROR; |
706 | ICUServiceKey* fallbackKey = createKey(&id, status); |
707 | while (fallbackKey != NULL && fallbackKey->fallback()) { |
708 | UnicodeString us; |
709 | fallbackKey->currentID(us); |
710 | f = (ICUServiceFactory*)map->get(us); |
711 | if (f != NULL) { |
712 | f->getDisplayName(id, locale, result); |
713 | delete fallbackKey; |
714 | return result; |
715 | } |
716 | } |
717 | delete fallbackKey; |
718 | } |
719 | } |
720 | result.setToBogus(); |
721 | return result; |
722 | } |
723 | |
724 | UVector& |
725 | ICUService::getDisplayNames(UVector& result, UErrorCode& status) const |
726 | { |
727 | return getDisplayNames(result, Locale::getDefault(), NULL, status); |
728 | } |
729 | |
730 | |
731 | UVector& |
732 | ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const |
733 | { |
734 | return getDisplayNames(result, locale, NULL, status); |
735 | } |
736 | |
737 | UVector& |
738 | ICUService::getDisplayNames(UVector& result, |
739 | const Locale& locale, |
740 | const UnicodeString* matchID, |
741 | UErrorCode& status) const |
742 | { |
743 | result.removeAllElements(); |
744 | result.setDeleter(userv_deleteStringPair); |
745 | if (U_SUCCESS(status)) { |
746 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
747 | Mutex mutex(&lock); |
748 | |
749 | if (dnCache != NULL && dnCache->locale != locale) { |
750 | delete dnCache; |
751 | ncthis->dnCache = NULL; |
752 | } |
753 | |
754 | if (dnCache == NULL) { |
755 | const Hashtable* m = getVisibleIDMap(status); |
756 | if (U_FAILURE(status)) { |
757 | return result; |
758 | } |
759 | ncthis->dnCache = new DNCache(locale); |
760 | if (dnCache == NULL) { |
761 | status = U_MEMORY_ALLOCATION_ERROR; |
762 | return result; |
763 | } |
764 | |
765 | int32_t pos = UHASH_FIRST; |
766 | const UHashElement* entry = NULL; |
767 | while ((entry = m->nextElement(pos)) != NULL) { |
768 | const UnicodeString* id = (const UnicodeString*)entry->key.pointer; |
769 | ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; |
770 | UnicodeString dname; |
771 | f->getDisplayName(*id, locale, dname); |
772 | if (dname.isBogus()) { |
773 | status = U_MEMORY_ALLOCATION_ERROR; |
774 | } else { |
775 | dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap |
776 | if (U_SUCCESS(status)) { |
777 | continue; |
778 | } |
779 | } |
780 | delete dnCache; |
781 | ncthis->dnCache = NULL; |
782 | return result; |
783 | } |
784 | } |
785 | } |
786 | |
787 | ICUServiceKey* matchKey = createKey(matchID, status); |
788 | /* To ensure that all elements in the hashtable are iterated, set pos to -1. |
789 | * nextElement(pos) will skip the position at pos and begin the iteration |
790 | * at the next position, which in this case will be 0. |
791 | */ |
792 | int32_t pos = UHASH_FIRST; |
793 | const UHashElement *entry = NULL; |
794 | while ((entry = dnCache->cache.nextElement(pos)) != NULL) { |
795 | const UnicodeString* id = (const UnicodeString*)entry->value.pointer; |
796 | if (matchKey != NULL && !matchKey->isFallbackOf(*id)) { |
797 | continue; |
798 | } |
799 | const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; |
800 | StringPair* sp = StringPair::create(*id, *dn, status); |
801 | result.addElement(sp, status); |
802 | if (U_FAILURE(status)) { |
803 | result.removeAllElements(); |
804 | break; |
805 | } |
806 | } |
807 | delete matchKey; |
808 | |
809 | return result; |
810 | } |
811 | |
812 | URegistryKey |
813 | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) |
814 | { |
815 | return registerInstance(objToAdopt, id, TRUE, status); |
816 | } |
817 | |
818 | URegistryKey |
819 | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) |
820 | { |
821 | ICUServiceKey* key = createKey(&id, status); |
822 | if (key != NULL) { |
823 | UnicodeString canonicalID; |
824 | key->canonicalID(canonicalID); |
825 | delete key; |
826 | |
827 | ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); |
828 | if (f != NULL) { |
829 | return registerFactory(f, status); |
830 | } |
831 | } |
832 | delete objToAdopt; |
833 | return NULL; |
834 | } |
835 | |
836 | ICUServiceFactory* |
837 | ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) |
838 | { |
839 | if (U_SUCCESS(status)) { |
840 | if ((objToAdopt != NULL) && (!id.isBogus())) { |
841 | return new SimpleFactory(objToAdopt, id, visible); |
842 | } |
843 | status = U_ILLEGAL_ARGUMENT_ERROR; |
844 | } |
845 | return NULL; |
846 | } |
847 | |
848 | URegistryKey |
849 | ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) |
850 | { |
851 | if (U_SUCCESS(status) && factoryToAdopt != NULL) { |
852 | Mutex mutex(&lock); |
853 | |
854 | if (factories == NULL) { |
855 | factories = new UVector(deleteUObject, NULL, status); |
856 | if (U_FAILURE(status)) { |
857 | delete factories; |
858 | return NULL; |
859 | } |
860 | } |
861 | factories->insertElementAt(factoryToAdopt, 0, status); |
862 | if (U_SUCCESS(status)) { |
863 | clearCaches(); |
864 | } else { |
865 | delete factoryToAdopt; |
866 | factoryToAdopt = NULL; |
867 | } |
868 | } |
869 | |
870 | if (factoryToAdopt != NULL) { |
871 | notifyChanged(); |
872 | } |
873 | |
874 | return (URegistryKey)factoryToAdopt; |
875 | } |
876 | |
877 | UBool |
878 | ICUService::unregister(URegistryKey rkey, UErrorCode& status) |
879 | { |
880 | ICUServiceFactory *factory = (ICUServiceFactory*)rkey; |
881 | UBool result = FALSE; |
882 | if (factory != NULL && factories != NULL) { |
883 | Mutex mutex(&lock); |
884 | |
885 | if (factories->removeElement(factory)) { |
886 | clearCaches(); |
887 | result = TRUE; |
888 | } else { |
889 | status = U_ILLEGAL_ARGUMENT_ERROR; |
890 | delete factory; |
891 | } |
892 | } |
893 | if (result) { |
894 | notifyChanged(); |
895 | } |
896 | return result; |
897 | } |
898 | |
899 | void |
900 | ICUService::reset() |
901 | { |
902 | { |
903 | Mutex mutex(&lock); |
904 | reInitializeFactories(); |
905 | clearCaches(); |
906 | } |
907 | notifyChanged(); |
908 | } |
909 | |
910 | void |
911 | ICUService::reInitializeFactories() |
912 | { |
913 | if (factories != NULL) { |
914 | factories->removeAllElements(); |
915 | } |
916 | } |
917 | |
918 | UBool |
919 | ICUService::isDefault() const |
920 | { |
921 | return countFactories() == 0; |
922 | } |
923 | |
924 | ICUServiceKey* |
925 | ICUService::createKey(const UnicodeString* id, UErrorCode& status) const |
926 | { |
927 | return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); |
928 | } |
929 | |
930 | void |
931 | ICUService::clearCaches() |
932 | { |
933 | // callers synchronize before use |
934 | ++timestamp; |
935 | delete dnCache; |
936 | dnCache = NULL; |
937 | delete idCache; |
938 | idCache = NULL; |
939 | delete serviceCache; serviceCache = NULL; |
940 | } |
941 | |
942 | void |
943 | ICUService::clearServiceCache() |
944 | { |
945 | // callers synchronize before use |
946 | delete serviceCache; serviceCache = NULL; |
947 | } |
948 | |
949 | UBool |
950 | ICUService::acceptsListener(const EventListener& l) const |
951 | { |
952 | return dynamic_cast<const ServiceListener*>(&l) != NULL; |
953 | } |
954 | |
955 | void |
956 | ICUService::notifyListener(EventListener& l) const |
957 | { |
958 | ((ServiceListener&)l).serviceChanged(*this); |
959 | } |
960 | |
961 | UnicodeString& |
962 | ICUService::getName(UnicodeString& result) const |
963 | { |
964 | return result.append(name); |
965 | } |
966 | |
967 | int32_t |
968 | ICUService::countFactories() const |
969 | { |
970 | return factories == NULL ? 0 : factories->size(); |
971 | } |
972 | |
973 | int32_t |
974 | ICUService::getTimestamp() const |
975 | { |
976 | return timestamp; |
977 | } |
978 | |
979 | U_NAMESPACE_END |
980 | |
981 | /* UCONFIG_NO_SERVICE */ |
982 | #endif |
983 | |