]>
Commit | Line | Data |
---|---|---|
f3c0d7a5 A |
1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
46f4442e A |
3 | /** |
4 | ******************************************************************************* | |
b331163b | 5 | * Copyright (C) 2001-2014, International Business Machines Corporation. |
729e4ab9 | 6 | * All Rights Reserved. |
46f4442e A |
7 | ******************************************************************************* |
8 | */ | |
b75a7d8f A |
9 | |
10 | #include "unicode/utypes.h" | |
11 | ||
12 | #if !UCONFIG_NO_SERVICE | |
13 | ||
73c04bcf | 14 | #include "serv.h" |
b75a7d8f A |
15 | #include "umutex.h" |
16 | ||
17 | #undef SERVICE_REFCOUNT | |
18 | ||
19 | // in case we use the refcount stuff | |
20 | ||
21 | U_NAMESPACE_BEGIN | |
22 | ||
b75a7d8f | 23 | /* |
46f4442e A |
24 | ****************************************************************** |
25 | */ | |
b75a7d8f A |
26 | |
27 | const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */ | |
28 | ||
29 | ICUServiceKey::ICUServiceKey(const UnicodeString& id) | |
46f4442e | 30 | : _id(id) { |
b75a7d8f A |
31 | } |
32 | ||
33 | ICUServiceKey::~ICUServiceKey() | |
34 | { | |
35 | } | |
36 | ||
37 | const UnicodeString& | |
38 | ICUServiceKey::getID() const | |
39 | { | |
46f4442e | 40 | return _id; |
b75a7d8f A |
41 | } |
42 | ||
43 | UnicodeString& | |
44 | ICUServiceKey::canonicalID(UnicodeString& result) const | |
45 | { | |
46f4442e | 46 | return result.append(_id); |
b75a7d8f A |
47 | } |
48 | ||
49 | UnicodeString& | |
50 | ICUServiceKey::currentID(UnicodeString& result) const | |
51 | { | |
46f4442e | 52 | return canonicalID(result); |
b75a7d8f A |
53 | } |
54 | ||
55 | UnicodeString& | |
56 | ICUServiceKey::currentDescriptor(UnicodeString& result) const | |
57 | { | |
46f4442e A |
58 | prefix(result); |
59 | result.append(PREFIX_DELIMITER); | |
60 | return currentID(result); | |
b75a7d8f A |
61 | } |
62 | ||
63 | UBool | |
64 | ICUServiceKey::fallback() | |
65 | { | |
46f4442e | 66 | return FALSE; |
b75a7d8f A |
67 | } |
68 | ||
69 | UBool | |
70 | ICUServiceKey::isFallbackOf(const UnicodeString& id) const | |
71 | { | |
46f4442e | 72 | return id == _id; |
b75a7d8f A |
73 | } |
74 | ||
75 | UnicodeString& | |
76 | ICUServiceKey::prefix(UnicodeString& result) const | |
77 | { | |
46f4442e | 78 | return result; |
b75a7d8f A |
79 | } |
80 | ||
81 | UnicodeString& | |
82 | ICUServiceKey::parsePrefix(UnicodeString& result) | |
83 | { | |
46f4442e A |
84 | int32_t n = result.indexOf(PREFIX_DELIMITER); |
85 | if (n < 0) { | |
86 | n = 0; | |
87 | } | |
88 | result.remove(n); | |
89 | return result; | |
b75a7d8f A |
90 | } |
91 | ||
92 | UnicodeString& | |
93 | ICUServiceKey::parseSuffix(UnicodeString& result) | |
94 | { | |
46f4442e A |
95 | int32_t n = result.indexOf(PREFIX_DELIMITER); |
96 | if (n >= 0) { | |
97 | result.remove(0, n+1); | |
98 | } | |
99 | return result; | |
b75a7d8f A |
100 | } |
101 | ||
102 | #ifdef SERVICE_DEBUG | |
103 | UnicodeString& | |
104 | ICUServiceKey::debug(UnicodeString& result) const | |
105 | { | |
46f4442e | 106 | debugClass(result); |
b331163b | 107 | result.append((UnicodeString)" id: "); |
46f4442e A |
108 | result.append(_id); |
109 | return result; | |
b75a7d8f A |
110 | } |
111 | ||
112 | UnicodeString& | |
113 | ICUServiceKey::debugClass(UnicodeString& result) const | |
114 | { | |
b331163b | 115 | return result.append((UnicodeString)"ICUServiceKey"); |
b75a7d8f A |
116 | } |
117 | #endif | |
118 | ||
374ca955 | 119 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey) |
b75a7d8f A |
120 | |
121 | /* | |
46f4442e A |
122 | ****************************************************************** |
123 | */ | |
b75a7d8f | 124 | |
4388f060 A |
125 | ICUServiceFactory::~ICUServiceFactory() {} |
126 | ||
b75a7d8f | 127 | SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) |
46f4442e | 128 | : _instance(instanceToAdopt), _id(id), _visible(visible) |
b75a7d8f A |
129 | { |
130 | } | |
131 | ||
132 | SimpleFactory::~SimpleFactory() | |
133 | { | |
46f4442e | 134 | delete _instance; |
b75a7d8f A |
135 | } |
136 | ||
137 | UObject* | |
138 | SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const | |
139 | { | |
46f4442e A |
140 | if (U_SUCCESS(status)) { |
141 | UnicodeString temp; | |
142 | if (_id == key.currentID(temp)) { | |
143 | return service->cloneInstance(_instance); | |
144 | } | |
b75a7d8f | 145 | } |
46f4442e | 146 | return NULL; |
b75a7d8f A |
147 | } |
148 | ||
149 | void | |
150 | SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const | |
151 | { | |
46f4442e A |
152 | if (_visible) { |
153 | result.put(_id, (void*)this, status); // cast away const | |
154 | } else { | |
155 | result.remove(_id); | |
156 | } | |
b75a7d8f A |
157 | } |
158 | ||
159 | UnicodeString& | |
374ca955 | 160 | SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const |
b75a7d8f | 161 | { |
46f4442e A |
162 | if (_visible && _id == id) { |
163 | result = _id; | |
164 | } else { | |
165 | result.setToBogus(); | |
166 | } | |
167 | return result; | |
b75a7d8f A |
168 | } |
169 | ||
170 | #ifdef SERVICE_DEBUG | |
171 | UnicodeString& | |
172 | SimpleFactory::debug(UnicodeString& toAppendTo) const | |
173 | { | |
46f4442e | 174 | debugClass(toAppendTo); |
b331163b | 175 | toAppendTo.append((UnicodeString)" id: "); |
46f4442e | 176 | toAppendTo.append(_id); |
b331163b A |
177 | toAppendTo.append((UnicodeString)", visible: "); |
178 | toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F"); | |
46f4442e | 179 | return toAppendTo; |
b75a7d8f A |
180 | } |
181 | ||
182 | UnicodeString& | |
183 | SimpleFactory::debugClass(UnicodeString& toAppendTo) const | |
184 | { | |
b331163b | 185 | return toAppendTo.append((UnicodeString)"SimpleFactory"); |
b75a7d8f A |
186 | } |
187 | #endif | |
188 | ||
374ca955 | 189 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory) |
b75a7d8f A |
190 | |
191 | /* | |
46f4442e A |
192 | ****************************************************************** |
193 | */ | |
b75a7d8f | 194 | |
4388f060 A |
195 | ServiceListener::~ServiceListener() {} |
196 | ||
374ca955 | 197 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener) |
b75a7d8f A |
198 | |
199 | /* | |
46f4442e A |
200 | ****************************************************************** |
201 | */ | |
b75a7d8f | 202 | |
46f4442e A |
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. | |
b75a7d8f A |
205 | class CacheEntry : public UMemory { |
206 | private: | |
46f4442e | 207 | int32_t refcount; |
b75a7d8f A |
208 | |
209 | public: | |
46f4442e A |
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 | } | |
b75a7d8f A |
257 | }; |
258 | ||
259 | // UObjectDeleter for serviceCache | |
374ca955 A |
260 | U_CDECL_BEGIN |
261 | static void U_CALLCONV | |
b75a7d8f | 262 | cacheDeleter(void* obj) { |
46f4442e | 263 | U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); |
b75a7d8f A |
264 | } |
265 | ||
266 | /** | |
46f4442e A |
267 | * Deleter for UObjects |
268 | */ | |
374ca955 | 269 | static void U_CALLCONV |
b75a7d8f | 270 | deleteUObject(void *obj) { |
46f4442e | 271 | U_NAMESPACE_USE delete (UObject*) obj; |
b75a7d8f | 272 | } |
374ca955 | 273 | U_CDECL_END |
b75a7d8f A |
274 | |
275 | /* | |
46f4442e A |
276 | ****************************************************************** |
277 | */ | |
b75a7d8f A |
278 | |
279 | class DNCache : public UMemory { | |
280 | public: | |
46f4442e A |
281 | Hashtable cache; |
282 | const Locale locale; | |
283 | ||
284 | DNCache(const Locale& _locale) | |
285 | : cache(), locale(_locale) | |
286 | { | |
4388f060 | 287 | // cache.setKeyDeleter(uprv_deleteUObject); |
46f4442e | 288 | } |
b75a7d8f A |
289 | }; |
290 | ||
291 | ||
292 | /* | |
46f4442e A |
293 | ****************************************************************** |
294 | */ | |
b75a7d8f A |
295 | |
296 | StringPair* | |
297 | StringPair::create(const UnicodeString& displayName, | |
298 | const UnicodeString& id, | |
299 | UErrorCode& status) | |
300 | { | |
46f4442e A |
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; | |
b75a7d8f | 309 | } |
46f4442e | 310 | return NULL; |
b75a7d8f A |
311 | } |
312 | ||
313 | UBool | |
314 | StringPair::isBogus() const { | |
46f4442e | 315 | return displayName.isBogus() || id.isBogus(); |
b75a7d8f A |
316 | } |
317 | ||
318 | StringPair::StringPair(const UnicodeString& _displayName, | |
319 | const UnicodeString& _id) | |
46f4442e A |
320 | : displayName(_displayName) |
321 | , id(_id) | |
b75a7d8f A |
322 | { |
323 | } | |
324 | ||
46f4442e A |
325 | U_CDECL_BEGIN |
326 | static void U_CALLCONV | |
374ca955 | 327 | userv_deleteStringPair(void *obj) { |
46f4442e | 328 | U_NAMESPACE_USE delete (StringPair*) obj; |
b75a7d8f | 329 | } |
46f4442e | 330 | U_CDECL_END |
b75a7d8f A |
331 | |
332 | /* | |
46f4442e A |
333 | ****************************************************************** |
334 | */ | |
b75a7d8f | 335 | |
51004dcb | 336 | static UMutex lock = U_MUTEX_INITIALIZER; |
4388f060 | 337 | |
b75a7d8f | 338 | ICUService::ICUService() |
46f4442e | 339 | : name() |
46f4442e A |
340 | , timestamp(0) |
341 | , factories(NULL) | |
342 | , serviceCache(NULL) | |
343 | , idCache(NULL) | |
344 | , dnCache(NULL) | |
b75a7d8f | 345 | { |
b75a7d8f A |
346 | } |
347 | ||
348 | ICUService::ICUService(const UnicodeString& newName) | |
46f4442e | 349 | : name(newName) |
46f4442e A |
350 | , timestamp(0) |
351 | , factories(NULL) | |
352 | , serviceCache(NULL) | |
353 | , idCache(NULL) | |
354 | , dnCache(NULL) | |
355 | { | |
b75a7d8f A |
356 | } |
357 | ||
358 | ICUService::~ICUService() | |
46f4442e A |
359 | { |
360 | { | |
361 | Mutex mutex(&lock); | |
362 | clearCaches(); | |
363 | delete factories; | |
364 | factories = NULL; | |
365 | } | |
b75a7d8f A |
366 | } |
367 | ||
368 | UObject* | |
369 | ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const | |
370 | { | |
46f4442e | 371 | return get(descriptor, NULL, status); |
b75a7d8f A |
372 | } |
373 | ||
374 | UObject* | |
375 | ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const | |
376 | { | |
46f4442e | 377 | UObject* result = NULL; |
b75a7d8f A |
378 | ICUServiceKey* key = createKey(&descriptor, status); |
379 | if (key) { | |
46f4442e A |
380 | result = getKey(*key, actualReturn, status); |
381 | delete key; | |
b75a7d8f | 382 | } |
46f4442e | 383 | return result; |
b75a7d8f A |
384 | } |
385 | ||
386 | UObject* | |
387 | ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const | |
388 | { | |
46f4442e | 389 | return getKey(key, NULL, status); |
b75a7d8f A |
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 | { | |
46f4442e | 398 | return getKey(key, actualReturn, NULL, status); |
b75a7d8f A |
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: | |
51004dcb | 406 | inline XMutex(UMutex *mutex, UBool reentering) |
46f4442e A |
407 | : fMutex(mutex) |
408 | , fActive(!reentering) | |
409 | { | |
410 | if (fActive) umtx_lock(fMutex); | |
411 | } | |
412 | inline ~XMutex() { | |
413 | if (fActive) umtx_unlock(fMutex); | |
414 | } | |
b75a7d8f A |
415 | |
416 | private: | |
51004dcb | 417 | UMutex *fMutex; |
46f4442e | 418 | UBool fActive; |
b75a7d8f A |
419 | }; |
420 | ||
421 | struct UVectorDeleter { | |
46f4442e A |
422 | UVector* _obj; |
423 | UVectorDeleter() : _obj(NULL) {} | |
424 | ~UVectorDeleter() { delete _obj; } | |
b75a7d8f A |
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 | { | |
46f4442e | 431 | if (U_FAILURE(status)) { |
b75a7d8f | 432 | return NULL; |
b75a7d8f A |
433 | } |
434 | ||
46f4442e A |
435 | if (isDefault()) { |
436 | return handleDefault(key, actualReturn, status); | |
437 | } | |
b75a7d8f | 438 | |
46f4442e | 439 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
b75a7d8f | 440 | |
46f4442e A |
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. | |
4388f060 | 452 | XMutex mutex(&lock, factory != NULL); |
46f4442e A |
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); | |
b75a7d8f | 464 | } |
b75a7d8f | 465 | |
46f4442e A |
466 | UnicodeString currentDescriptor; |
467 | UVectorDeleter cacheDescriptorList; | |
468 | UBool putInCache = FALSE; | |
b75a7d8f | 469 | |
46f4442e A |
470 | int32_t startIndex = 0; |
471 | int32_t limit = factories->size(); | |
472 | UBool cacheResult = TRUE; | |
b75a7d8f | 473 | |
46f4442e A |
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; | |
b75a7d8f A |
487 | } |
488 | ||
46f4442e A |
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) { | |
4388f060 | 528 | cacheDescriptorList._obj = new UVector(uprv_deleteUObject, NULL, 5, status); |
46f4442e A |
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); | |
b75a7d8f | 540 | if (U_FAILURE(status)) { |
46f4442e A |
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)) { | |
46f4442e A |
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); | |
0f5d89e8 | 556 | |
46f4442e A |
557 | serviceCache->put(*desc, result, status); |
558 | if (U_FAILURE(status)) { | |
46f4442e A |
559 | return NULL; |
560 | } | |
561 | ||
562 | result->ref(); | |
563 | cacheDescriptorList._obj->removeElementAt(i); | |
564 | } | |
565 | } | |
b75a7d8f A |
566 | } |
567 | ||
46f4442e A |
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 | } | |
b75a7d8f | 585 | |
46f4442e A |
586 | UObject* service = cloneInstance(result->service); |
587 | if (putInCache && !cacheResult) { | |
588 | delete result; | |
589 | } | |
590 | return service; | |
b75a7d8f | 591 | } |
b75a7d8f | 592 | } |
b75a7d8f | 593 | |
46f4442e | 594 | return handleDefault(key, actualReturn, status); |
b75a7d8f A |
595 | } |
596 | ||
597 | UObject* | |
374ca955 | 598 | ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const |
b75a7d8f | 599 | { |
46f4442e | 600 | return NULL; |
b75a7d8f | 601 | } |
46f4442e | 602 | |
b75a7d8f A |
603 | UVector& |
604 | ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { | |
46f4442e | 605 | return getVisibleIDs(result, NULL, status); |
b75a7d8f A |
606 | } |
607 | ||
608 | UVector& | |
609 | ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const | |
610 | { | |
46f4442e | 611 | result.removeAllElements(); |
b75a7d8f | 612 | |
46f4442e A |
613 | if (U_FAILURE(status)) { |
614 | return result; | |
615 | } | |
b75a7d8f | 616 | |
46f4442e | 617 | { |
4388f060 | 618 | Mutex mutex(&lock); |
46f4442e A |
619 | const Hashtable* map = getVisibleIDMap(status); |
620 | if (map != NULL) { | |
621 | ICUServiceKey* fallbackKey = createKey(matchID, status); | |
622 | ||
b331163b | 623 | for (int32_t pos = UHASH_FIRST;;) { |
46f4442e A |
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; | |
b75a7d8f | 649 | } |
b75a7d8f | 650 | } |
46f4442e A |
651 | if (U_FAILURE(status)) { |
652 | result.removeAllElements(); | |
653 | } | |
654 | return result; | |
b75a7d8f A |
655 | } |
656 | ||
657 | const Hashtable* | |
658 | ICUService::getVisibleIDMap(UErrorCode& status) const { | |
46f4442e | 659 | if (U_FAILURE(status)) return NULL; |
b75a7d8f | 660 | |
46f4442e | 661 | // must only be called when lock is already held |
b75a7d8f | 662 | |
46f4442e | 663 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
b75a7d8f | 664 | if (idCache == NULL) { |
46f4442e A |
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 | } | |
b75a7d8f | 678 | } |
b75a7d8f | 679 | |
46f4442e | 680 | return idCache; |
b75a7d8f A |
681 | } |
682 | ||
46f4442e | 683 | |
b75a7d8f A |
684 | UnicodeString& |
685 | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const | |
686 | { | |
46f4442e | 687 | return getDisplayName(id, result, Locale::getDefault()); |
b75a7d8f A |
688 | } |
689 | ||
690 | UnicodeString& | |
691 | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const | |
692 | { | |
46f4442e | 693 | { |
46f4442e | 694 | UErrorCode status = U_ZERO_ERROR; |
4388f060 | 695 | Mutex mutex(&lock); |
46f4442e A |
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 | UErrorCode status = U_ZERO_ERROR; | |
706 | ICUServiceKey* fallbackKey = createKey(&id, status); | |
707 | while (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; | |
b75a7d8f A |
722 | } |
723 | ||
724 | UVector& | |
725 | ICUService::getDisplayNames(UVector& result, UErrorCode& status) const | |
726 | { | |
46f4442e | 727 | return getDisplayNames(result, Locale::getDefault(), NULL, status); |
b75a7d8f A |
728 | } |
729 | ||
730 | ||
731 | UVector& | |
732 | ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const | |
733 | { | |
46f4442e | 734 | return getDisplayNames(result, locale, NULL, status); |
b75a7d8f A |
735 | } |
736 | ||
737 | UVector& | |
738 | ICUService::getDisplayNames(UVector& result, | |
739 | const Locale& locale, | |
740 | const UnicodeString* matchID, | |
741 | UErrorCode& status) const | |
742 | { | |
46f4442e A |
743 | result.removeAllElements(); |
744 | result.setDeleter(userv_deleteStringPair); | |
745 | if (U_SUCCESS(status)) { | |
746 | ICUService* ncthis = (ICUService*)this; // cast away semantic const | |
4388f060 | 747 | Mutex mutex(&lock); |
b75a7d8f | 748 | |
46f4442e A |
749 | if (dnCache != NULL && dnCache->locale != locale) { |
750 | delete dnCache; | |
751 | ncthis->dnCache = NULL; | |
b75a7d8f A |
752 | } |
753 | ||
46f4442e A |
754 | if (dnCache == NULL) { |
755 | const Hashtable* m = getVisibleIDMap(status); | |
4388f060 A |
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 | } | |
46f4442e | 764 | |
b331163b | 765 | int32_t pos = UHASH_FIRST; |
4388f060 A |
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; | |
46f4442e | 778 | } |
46f4442e | 779 | } |
4388f060 A |
780 | delete dnCache; |
781 | ncthis->dnCache = NULL; | |
782 | return result; | |
b75a7d8f | 783 | } |
b75a7d8f | 784 | } |
b75a7d8f | 785 | } |
b75a7d8f | 786 | |
46f4442e A |
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 | */ | |
b331163b | 792 | int32_t pos = UHASH_FIRST; |
46f4442e A |
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 | } | |
b75a7d8f | 806 | } |
46f4442e | 807 | delete matchKey; |
b75a7d8f | 808 | |
46f4442e | 809 | return result; |
b75a7d8f A |
810 | } |
811 | ||
812 | URegistryKey | |
813 | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) | |
814 | { | |
46f4442e | 815 | return registerInstance(objToAdopt, id, TRUE, status); |
b75a7d8f A |
816 | } |
817 | ||
818 | URegistryKey | |
819 | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) | |
820 | { | |
46f4442e A |
821 | ICUServiceKey* key = createKey(&id, status); |
822 | if (key != NULL) { | |
823 | UnicodeString canonicalID; | |
824 | key->canonicalID(canonicalID); | |
825 | delete key; | |
b75a7d8f | 826 | |
46f4442e A |
827 | ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); |
828 | if (f != NULL) { | |
829 | return registerFactory(f, status); | |
830 | } | |
b75a7d8f | 831 | } |
46f4442e A |
832 | delete objToAdopt; |
833 | return NULL; | |
b75a7d8f A |
834 | } |
835 | ||
836 | ICUServiceFactory* | |
837 | ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) | |
838 | { | |
46f4442e A |
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; | |
b75a7d8f | 844 | } |
46f4442e | 845 | return NULL; |
b75a7d8f A |
846 | } |
847 | ||
848 | URegistryKey | |
849 | ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) | |
850 | { | |
46f4442e A |
851 | if (U_SUCCESS(status) && factoryToAdopt != NULL) { |
852 | Mutex mutex(&lock); | |
b75a7d8f | 853 | |
46f4442e A |
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 | } | |
b75a7d8f | 868 | } |
b75a7d8f | 869 | |
46f4442e A |
870 | if (factoryToAdopt != NULL) { |
871 | notifyChanged(); | |
872 | } | |
b75a7d8f | 873 | |
46f4442e | 874 | return (URegistryKey)factoryToAdopt; |
b75a7d8f A |
875 | } |
876 | ||
877 | UBool | |
878 | ICUService::unregister(URegistryKey rkey, UErrorCode& status) | |
879 | { | |
46f4442e A |
880 | ICUServiceFactory *factory = (ICUServiceFactory*)rkey; |
881 | UBool result = FALSE; | |
882 | if (factory != NULL && factories != NULL) { | |
883 | Mutex mutex(&lock); | |
b75a7d8f | 884 | |
46f4442e A |
885 | if (factories->removeElement(factory)) { |
886 | clearCaches(); | |
887 | result = TRUE; | |
888 | } else { | |
889 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
890 | delete factory; | |
891 | } | |
b75a7d8f | 892 | } |
46f4442e A |
893 | if (result) { |
894 | notifyChanged(); | |
895 | } | |
896 | return result; | |
b75a7d8f A |
897 | } |
898 | ||
899 | void | |
900 | ICUService::reset() | |
901 | { | |
46f4442e A |
902 | { |
903 | Mutex mutex(&lock); | |
904 | reInitializeFactories(); | |
905 | clearCaches(); | |
906 | } | |
907 | notifyChanged(); | |
b75a7d8f A |
908 | } |
909 | ||
910 | void | |
911 | ICUService::reInitializeFactories() | |
912 | { | |
46f4442e A |
913 | if (factories != NULL) { |
914 | factories->removeAllElements(); | |
915 | } | |
b75a7d8f A |
916 | } |
917 | ||
918 | UBool | |
919 | ICUService::isDefault() const | |
920 | { | |
46f4442e | 921 | return countFactories() == 0; |
b75a7d8f A |
922 | } |
923 | ||
924 | ICUServiceKey* | |
925 | ICUService::createKey(const UnicodeString* id, UErrorCode& status) const | |
926 | { | |
46f4442e | 927 | return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); |
b75a7d8f A |
928 | } |
929 | ||
930 | void | |
931 | ICUService::clearCaches() | |
932 | { | |
46f4442e A |
933 | // callers synchronize before use |
934 | ++timestamp; | |
935 | delete dnCache; | |
936 | dnCache = NULL; | |
937 | delete idCache; | |
938 | idCache = NULL; | |
939 | delete serviceCache; serviceCache = NULL; | |
b75a7d8f A |
940 | } |
941 | ||
942 | void | |
943 | ICUService::clearServiceCache() | |
944 | { | |
46f4442e A |
945 | // callers synchronize before use |
946 | delete serviceCache; serviceCache = NULL; | |
b75a7d8f A |
947 | } |
948 | ||
949 | UBool | |
950 | ICUService::acceptsListener(const EventListener& l) const | |
951 | { | |
729e4ab9 | 952 | return dynamic_cast<const ServiceListener*>(&l) != NULL; |
b75a7d8f A |
953 | } |
954 | ||
955 | void | |
956 | ICUService::notifyListener(EventListener& l) const | |
957 | { | |
46f4442e | 958 | ((ServiceListener&)l).serviceChanged(*this); |
b75a7d8f A |
959 | } |
960 | ||
961 | UnicodeString& | |
962 | ICUService::getName(UnicodeString& result) const | |
963 | { | |
46f4442e | 964 | return result.append(name); |
b75a7d8f A |
965 | } |
966 | ||
967 | int32_t | |
968 | ICUService::countFactories() const | |
969 | { | |
46f4442e | 970 | return factories == NULL ? 0 : factories->size(); |
b75a7d8f A |
971 | } |
972 | ||
973 | int32_t | |
974 | ICUService::getTimestamp() const | |
975 | { | |
46f4442e | 976 | return timestamp; |
b75a7d8f A |
977 | } |
978 | ||
979 | U_NAMESPACE_END | |
980 | ||
981 | /* UCONFIG_NO_SERVICE */ | |
982 | #endif |