]>
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 | |
3d1f044b A |
336 | static UMutex *lock() { |
337 | static UMutex *m = STATIC_NEW(UMutex); | |
338 | return m; | |
339 | } | |
4388f060 | 340 | |
b75a7d8f | 341 | ICUService::ICUService() |
46f4442e | 342 | : name() |
46f4442e A |
343 | , timestamp(0) |
344 | , factories(NULL) | |
345 | , serviceCache(NULL) | |
346 | , idCache(NULL) | |
347 | , dnCache(NULL) | |
b75a7d8f | 348 | { |
b75a7d8f A |
349 | } |
350 | ||
351 | ICUService::ICUService(const UnicodeString& newName) | |
46f4442e | 352 | : name(newName) |
46f4442e A |
353 | , timestamp(0) |
354 | , factories(NULL) | |
355 | , serviceCache(NULL) | |
356 | , idCache(NULL) | |
357 | , dnCache(NULL) | |
358 | { | |
b75a7d8f A |
359 | } |
360 | ||
361 | ICUService::~ICUService() | |
46f4442e A |
362 | { |
363 | { | |
3d1f044b | 364 | Mutex mutex(lock()); |
46f4442e A |
365 | clearCaches(); |
366 | delete factories; | |
367 | factories = NULL; | |
368 | } | |
b75a7d8f A |
369 | } |
370 | ||
371 | UObject* | |
372 | ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const | |
373 | { | |
46f4442e | 374 | return get(descriptor, NULL, status); |
b75a7d8f A |
375 | } |
376 | ||
377 | UObject* | |
378 | ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const | |
379 | { | |
46f4442e | 380 | UObject* result = NULL; |
b75a7d8f A |
381 | ICUServiceKey* key = createKey(&descriptor, status); |
382 | if (key) { | |
46f4442e A |
383 | result = getKey(*key, actualReturn, status); |
384 | delete key; | |
b75a7d8f | 385 | } |
46f4442e | 386 | return result; |
b75a7d8f A |
387 | } |
388 | ||
389 | UObject* | |
390 | ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const | |
391 | { | |
46f4442e | 392 | return getKey(key, NULL, status); |
b75a7d8f A |
393 | } |
394 | ||
395 | // this is a vector that subclasses of ICUService can override to further customize the result object | |
396 | // before returning it. All other public get functions should call this one. | |
397 | ||
398 | UObject* | |
399 | ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const | |
400 | { | |
46f4442e | 401 | return getKey(key, actualReturn, NULL, status); |
b75a7d8f A |
402 | } |
403 | ||
404 | // make it possible to call reentrantly on systems that don't have reentrant mutexes. | |
405 | // we can use this simple approach since we know the situation where we're calling | |
406 | // reentrantly even without knowing the thread. | |
407 | class XMutex : public UMemory { | |
408 | public: | |
51004dcb | 409 | inline XMutex(UMutex *mutex, UBool reentering) |
46f4442e A |
410 | : fMutex(mutex) |
411 | , fActive(!reentering) | |
412 | { | |
413 | if (fActive) umtx_lock(fMutex); | |
414 | } | |
415 | inline ~XMutex() { | |
416 | if (fActive) umtx_unlock(fMutex); | |
417 | } | |
b75a7d8f A |
418 | |
419 | private: | |
51004dcb | 420 | UMutex *fMutex; |
46f4442e | 421 | UBool fActive; |
b75a7d8f A |
422 | }; |
423 | ||
424 | struct UVectorDeleter { | |
46f4442e A |
425 | UVector* _obj; |
426 | UVectorDeleter() : _obj(NULL) {} | |
427 | ~UVectorDeleter() { delete _obj; } | |
b75a7d8f A |
428 | }; |
429 | ||
430 | // called only by factories, treat as private | |
431 | UObject* | |
432 | ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const | |
433 | { | |
46f4442e | 434 | if (U_FAILURE(status)) { |
b75a7d8f | 435 | return NULL; |
b75a7d8f A |
436 | } |
437 | ||
46f4442e A |
438 | if (isDefault()) { |
439 | return handleDefault(key, actualReturn, status); | |
440 | } | |
b75a7d8f | 441 | |
46f4442e | 442 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
b75a7d8f | 443 | |
46f4442e A |
444 | CacheEntry* result = NULL; |
445 | { | |
446 | // The factory list can't be modified until we're done, | |
447 | // otherwise we might update the cache with an invalid result. | |
448 | // The cache has to stay in synch with the factory list. | |
449 | // ICU doesn't have monitors so we can't use rw locks, so | |
450 | // we single-thread everything using this service, for now. | |
451 | ||
452 | // if factory is not null, we're calling from within the mutex, | |
453 | // and since some unix machines don't have reentrant mutexes we | |
454 | // need to make sure not to try to lock it again. | |
3d1f044b | 455 | XMutex mutex(lock(), factory != NULL); |
46f4442e A |
456 | |
457 | if (serviceCache == NULL) { | |
458 | ncthis->serviceCache = new Hashtable(status); | |
459 | if (ncthis->serviceCache == NULL) { | |
460 | return NULL; | |
461 | } | |
462 | if (U_FAILURE(status)) { | |
463 | delete serviceCache; | |
464 | return NULL; | |
465 | } | |
466 | serviceCache->setValueDeleter(cacheDeleter); | |
b75a7d8f | 467 | } |
b75a7d8f | 468 | |
46f4442e A |
469 | UnicodeString currentDescriptor; |
470 | UVectorDeleter cacheDescriptorList; | |
471 | UBool putInCache = FALSE; | |
b75a7d8f | 472 | |
46f4442e A |
473 | int32_t startIndex = 0; |
474 | int32_t limit = factories->size(); | |
475 | UBool cacheResult = TRUE; | |
b75a7d8f | 476 | |
46f4442e A |
477 | if (factory != NULL) { |
478 | for (int32_t i = 0; i < limit; ++i) { | |
479 | if (factory == (const ICUServiceFactory*)factories->elementAt(i)) { | |
480 | startIndex = i + 1; | |
481 | break; | |
482 | } | |
483 | } | |
484 | if (startIndex == 0) { | |
485 | // throw new InternalError("Factory " + factory + "not registered with service: " + this); | |
486 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
487 | return NULL; | |
488 | } | |
489 | cacheResult = FALSE; | |
b75a7d8f A |
490 | } |
491 | ||
46f4442e A |
492 | do { |
493 | currentDescriptor.remove(); | |
494 | key.currentDescriptor(currentDescriptor); | |
495 | result = (CacheEntry*)serviceCache->get(currentDescriptor); | |
496 | if (result != NULL) { | |
497 | break; | |
498 | } | |
499 | ||
500 | // first test of cache failed, so we'll have to update | |
501 | // the cache if we eventually succeed-- that is, if we're | |
502 | // going to update the cache at all. | |
503 | putInCache = TRUE; | |
504 | ||
505 | int32_t index = startIndex; | |
506 | while (index < limit) { | |
507 | ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++); | |
508 | UObject* service = f->create(key, this, status); | |
509 | if (U_FAILURE(status)) { | |
510 | delete service; | |
511 | return NULL; | |
512 | } | |
513 | if (service != NULL) { | |
514 | result = new CacheEntry(currentDescriptor, service); | |
515 | if (result == NULL) { | |
516 | delete service; | |
517 | status = U_MEMORY_ALLOCATION_ERROR; | |
518 | return NULL; | |
519 | } | |
520 | ||
521 | goto outerEnd; | |
522 | } | |
523 | } | |
524 | ||
525 | // prepare to load the cache with all additional ids that | |
526 | // will resolve to result, assuming we'll succeed. We | |
527 | // don't want to keep querying on an id that's going to | |
528 | // fallback to the one that succeeded, we want to hit the | |
529 | // cache the first time next goaround. | |
530 | if (cacheDescriptorList._obj == NULL) { | |
4388f060 | 531 | cacheDescriptorList._obj = new UVector(uprv_deleteUObject, NULL, 5, status); |
46f4442e A |
532 | if (U_FAILURE(status)) { |
533 | return NULL; | |
534 | } | |
535 | } | |
536 | UnicodeString* idToCache = new UnicodeString(currentDescriptor); | |
537 | if (idToCache == NULL || idToCache->isBogus()) { | |
538 | status = U_MEMORY_ALLOCATION_ERROR; | |
539 | return NULL; | |
540 | } | |
541 | ||
542 | cacheDescriptorList._obj->addElement(idToCache, status); | |
b75a7d8f | 543 | if (U_FAILURE(status)) { |
46f4442e A |
544 | return NULL; |
545 | } | |
546 | } while (key.fallback()); | |
547 | outerEnd: | |
548 | ||
549 | if (result != NULL) { | |
550 | if (putInCache && cacheResult) { | |
551 | serviceCache->put(result->actualDescriptor, result, status); | |
552 | if (U_FAILURE(status)) { | |
46f4442e A |
553 | return NULL; |
554 | } | |
555 | ||
556 | if (cacheDescriptorList._obj != NULL) { | |
557 | for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) { | |
558 | UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i); | |
0f5d89e8 | 559 | |
46f4442e A |
560 | serviceCache->put(*desc, result, status); |
561 | if (U_FAILURE(status)) { | |
46f4442e A |
562 | return NULL; |
563 | } | |
564 | ||
565 | result->ref(); | |
566 | cacheDescriptorList._obj->removeElementAt(i); | |
567 | } | |
568 | } | |
b75a7d8f A |
569 | } |
570 | ||
46f4442e A |
571 | if (actualReturn != NULL) { |
572 | // strip null prefix | |
573 | if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/) | |
574 | actualReturn->remove(); | |
575 | actualReturn->append(result->actualDescriptor, | |
576 | 1, | |
577 | result->actualDescriptor.length() - 1); | |
578 | } else { | |
579 | *actualReturn = result->actualDescriptor; | |
580 | } | |
581 | ||
582 | if (actualReturn->isBogus()) { | |
583 | status = U_MEMORY_ALLOCATION_ERROR; | |
584 | delete result; | |
585 | return NULL; | |
586 | } | |
587 | } | |
b75a7d8f | 588 | |
46f4442e A |
589 | UObject* service = cloneInstance(result->service); |
590 | if (putInCache && !cacheResult) { | |
591 | delete result; | |
592 | } | |
593 | return service; | |
b75a7d8f | 594 | } |
b75a7d8f | 595 | } |
b75a7d8f | 596 | |
46f4442e | 597 | return handleDefault(key, actualReturn, status); |
b75a7d8f A |
598 | } |
599 | ||
600 | UObject* | |
374ca955 | 601 | ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const |
b75a7d8f | 602 | { |
46f4442e | 603 | return NULL; |
b75a7d8f | 604 | } |
46f4442e | 605 | |
b75a7d8f A |
606 | UVector& |
607 | ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { | |
46f4442e | 608 | return getVisibleIDs(result, NULL, status); |
b75a7d8f A |
609 | } |
610 | ||
611 | UVector& | |
612 | ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const | |
613 | { | |
46f4442e | 614 | result.removeAllElements(); |
b75a7d8f | 615 | |
46f4442e A |
616 | if (U_FAILURE(status)) { |
617 | return result; | |
618 | } | |
b75a7d8f | 619 | |
46f4442e | 620 | { |
3d1f044b | 621 | Mutex mutex(lock()); |
46f4442e A |
622 | const Hashtable* map = getVisibleIDMap(status); |
623 | if (map != NULL) { | |
624 | ICUServiceKey* fallbackKey = createKey(matchID, status); | |
625 | ||
b331163b | 626 | for (int32_t pos = UHASH_FIRST;;) { |
46f4442e A |
627 | const UHashElement* e = map->nextElement(pos); |
628 | if (e == NULL) { | |
629 | break; | |
630 | } | |
631 | ||
632 | const UnicodeString* id = (const UnicodeString*)e->key.pointer; | |
633 | if (fallbackKey != NULL) { | |
634 | if (!fallbackKey->isFallbackOf(*id)) { | |
635 | continue; | |
636 | } | |
637 | } | |
638 | ||
639 | UnicodeString* idClone = new UnicodeString(*id); | |
640 | if (idClone == NULL || idClone->isBogus()) { | |
641 | delete idClone; | |
642 | status = U_MEMORY_ALLOCATION_ERROR; | |
643 | break; | |
644 | } | |
645 | result.addElement(idClone, status); | |
646 | if (U_FAILURE(status)) { | |
647 | delete idClone; | |
648 | break; | |
649 | } | |
650 | } | |
651 | delete fallbackKey; | |
b75a7d8f | 652 | } |
b75a7d8f | 653 | } |
46f4442e A |
654 | if (U_FAILURE(status)) { |
655 | result.removeAllElements(); | |
656 | } | |
657 | return result; | |
b75a7d8f A |
658 | } |
659 | ||
660 | const Hashtable* | |
661 | ICUService::getVisibleIDMap(UErrorCode& status) const { | |
46f4442e | 662 | if (U_FAILURE(status)) return NULL; |
b75a7d8f | 663 | |
46f4442e | 664 | // must only be called when lock is already held |
b75a7d8f | 665 | |
46f4442e | 666 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
b75a7d8f | 667 | if (idCache == NULL) { |
46f4442e A |
668 | ncthis->idCache = new Hashtable(status); |
669 | if (idCache == NULL) { | |
670 | status = U_MEMORY_ALLOCATION_ERROR; | |
671 | } else if (factories != NULL) { | |
672 | for (int32_t pos = factories->size(); --pos >= 0;) { | |
673 | ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); | |
674 | f->updateVisibleIDs(*idCache, status); | |
675 | } | |
676 | if (U_FAILURE(status)) { | |
677 | delete idCache; | |
678 | ncthis->idCache = NULL; | |
679 | } | |
680 | } | |
b75a7d8f | 681 | } |
b75a7d8f | 682 | |
46f4442e | 683 | return idCache; |
b75a7d8f A |
684 | } |
685 | ||
46f4442e | 686 | |
b75a7d8f A |
687 | UnicodeString& |
688 | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const | |
689 | { | |
46f4442e | 690 | return getDisplayName(id, result, Locale::getDefault()); |
b75a7d8f A |
691 | } |
692 | ||
693 | UnicodeString& | |
694 | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const | |
695 | { | |
46f4442e | 696 | { |
46f4442e | 697 | UErrorCode status = U_ZERO_ERROR; |
3d1f044b | 698 | Mutex mutex(lock()); |
46f4442e A |
699 | const Hashtable* map = getVisibleIDMap(status); |
700 | if (map != NULL) { | |
701 | ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); | |
702 | if (f != NULL) { | |
703 | f->getDisplayName(id, locale, result); | |
704 | return result; | |
705 | } | |
706 | ||
707 | // fallback | |
3d1f044b | 708 | status = U_ZERO_ERROR; |
46f4442e | 709 | ICUServiceKey* fallbackKey = createKey(&id, status); |
3d1f044b | 710 | while (fallbackKey != NULL && fallbackKey->fallback()) { |
46f4442e A |
711 | UnicodeString us; |
712 | fallbackKey->currentID(us); | |
713 | f = (ICUServiceFactory*)map->get(us); | |
714 | if (f != NULL) { | |
715 | f->getDisplayName(id, locale, result); | |
716 | delete fallbackKey; | |
717 | return result; | |
718 | } | |
719 | } | |
720 | delete fallbackKey; | |
721 | } | |
722 | } | |
723 | result.setToBogus(); | |
724 | return result; | |
b75a7d8f A |
725 | } |
726 | ||
727 | UVector& | |
728 | ICUService::getDisplayNames(UVector& result, UErrorCode& status) const | |
729 | { | |
46f4442e | 730 | return getDisplayNames(result, Locale::getDefault(), NULL, status); |
b75a7d8f A |
731 | } |
732 | ||
733 | ||
734 | UVector& | |
735 | ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const | |
736 | { | |
46f4442e | 737 | return getDisplayNames(result, locale, NULL, status); |
b75a7d8f A |
738 | } |
739 | ||
740 | UVector& | |
741 | ICUService::getDisplayNames(UVector& result, | |
742 | const Locale& locale, | |
743 | const UnicodeString* matchID, | |
744 | UErrorCode& status) const | |
745 | { | |
46f4442e A |
746 | result.removeAllElements(); |
747 | result.setDeleter(userv_deleteStringPair); | |
748 | if (U_SUCCESS(status)) { | |
749 | ICUService* ncthis = (ICUService*)this; // cast away semantic const | |
3d1f044b | 750 | Mutex mutex(lock()); |
b75a7d8f | 751 | |
46f4442e A |
752 | if (dnCache != NULL && dnCache->locale != locale) { |
753 | delete dnCache; | |
754 | ncthis->dnCache = NULL; | |
b75a7d8f A |
755 | } |
756 | ||
46f4442e A |
757 | if (dnCache == NULL) { |
758 | const Hashtable* m = getVisibleIDMap(status); | |
4388f060 A |
759 | if (U_FAILURE(status)) { |
760 | return result; | |
761 | } | |
762 | ncthis->dnCache = new DNCache(locale); | |
763 | if (dnCache == NULL) { | |
764 | status = U_MEMORY_ALLOCATION_ERROR; | |
765 | return result; | |
766 | } | |
46f4442e | 767 | |
b331163b | 768 | int32_t pos = UHASH_FIRST; |
4388f060 A |
769 | const UHashElement* entry = NULL; |
770 | while ((entry = m->nextElement(pos)) != NULL) { | |
771 | const UnicodeString* id = (const UnicodeString*)entry->key.pointer; | |
772 | ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; | |
773 | UnicodeString dname; | |
774 | f->getDisplayName(*id, locale, dname); | |
775 | if (dname.isBogus()) { | |
776 | status = U_MEMORY_ALLOCATION_ERROR; | |
777 | } else { | |
778 | dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap | |
779 | if (U_SUCCESS(status)) { | |
780 | continue; | |
46f4442e | 781 | } |
46f4442e | 782 | } |
4388f060 A |
783 | delete dnCache; |
784 | ncthis->dnCache = NULL; | |
785 | return result; | |
b75a7d8f | 786 | } |
b75a7d8f | 787 | } |
b75a7d8f | 788 | } |
b75a7d8f | 789 | |
46f4442e A |
790 | ICUServiceKey* matchKey = createKey(matchID, status); |
791 | /* To ensure that all elements in the hashtable are iterated, set pos to -1. | |
792 | * nextElement(pos) will skip the position at pos and begin the iteration | |
793 | * at the next position, which in this case will be 0. | |
794 | */ | |
b331163b | 795 | int32_t pos = UHASH_FIRST; |
46f4442e A |
796 | const UHashElement *entry = NULL; |
797 | while ((entry = dnCache->cache.nextElement(pos)) != NULL) { | |
798 | const UnicodeString* id = (const UnicodeString*)entry->value.pointer; | |
799 | if (matchKey != NULL && !matchKey->isFallbackOf(*id)) { | |
800 | continue; | |
801 | } | |
802 | const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; | |
803 | StringPair* sp = StringPair::create(*id, *dn, status); | |
804 | result.addElement(sp, status); | |
805 | if (U_FAILURE(status)) { | |
806 | result.removeAllElements(); | |
807 | break; | |
808 | } | |
b75a7d8f | 809 | } |
46f4442e | 810 | delete matchKey; |
b75a7d8f | 811 | |
46f4442e | 812 | return result; |
b75a7d8f A |
813 | } |
814 | ||
815 | URegistryKey | |
816 | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) | |
817 | { | |
46f4442e | 818 | return registerInstance(objToAdopt, id, TRUE, status); |
b75a7d8f A |
819 | } |
820 | ||
821 | URegistryKey | |
822 | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) | |
823 | { | |
46f4442e A |
824 | ICUServiceKey* key = createKey(&id, status); |
825 | if (key != NULL) { | |
826 | UnicodeString canonicalID; | |
827 | key->canonicalID(canonicalID); | |
828 | delete key; | |
b75a7d8f | 829 | |
46f4442e A |
830 | ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); |
831 | if (f != NULL) { | |
832 | return registerFactory(f, status); | |
833 | } | |
b75a7d8f | 834 | } |
46f4442e A |
835 | delete objToAdopt; |
836 | return NULL; | |
b75a7d8f A |
837 | } |
838 | ||
839 | ICUServiceFactory* | |
840 | ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) | |
841 | { | |
46f4442e A |
842 | if (U_SUCCESS(status)) { |
843 | if ((objToAdopt != NULL) && (!id.isBogus())) { | |
844 | return new SimpleFactory(objToAdopt, id, visible); | |
845 | } | |
846 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
b75a7d8f | 847 | } |
46f4442e | 848 | return NULL; |
b75a7d8f A |
849 | } |
850 | ||
851 | URegistryKey | |
852 | ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) | |
853 | { | |
46f4442e | 854 | if (U_SUCCESS(status) && factoryToAdopt != NULL) { |
3d1f044b | 855 | Mutex mutex(lock()); |
b75a7d8f | 856 | |
46f4442e A |
857 | if (factories == NULL) { |
858 | factories = new UVector(deleteUObject, NULL, status); | |
859 | if (U_FAILURE(status)) { | |
860 | delete factories; | |
861 | return NULL; | |
862 | } | |
863 | } | |
864 | factories->insertElementAt(factoryToAdopt, 0, status); | |
865 | if (U_SUCCESS(status)) { | |
866 | clearCaches(); | |
867 | } else { | |
868 | delete factoryToAdopt; | |
869 | factoryToAdopt = NULL; | |
870 | } | |
b75a7d8f | 871 | } |
b75a7d8f | 872 | |
46f4442e A |
873 | if (factoryToAdopt != NULL) { |
874 | notifyChanged(); | |
875 | } | |
b75a7d8f | 876 | |
46f4442e | 877 | return (URegistryKey)factoryToAdopt; |
b75a7d8f A |
878 | } |
879 | ||
880 | UBool | |
881 | ICUService::unregister(URegistryKey rkey, UErrorCode& status) | |
882 | { | |
46f4442e A |
883 | ICUServiceFactory *factory = (ICUServiceFactory*)rkey; |
884 | UBool result = FALSE; | |
885 | if (factory != NULL && factories != NULL) { | |
3d1f044b | 886 | Mutex mutex(lock()); |
b75a7d8f | 887 | |
46f4442e A |
888 | if (factories->removeElement(factory)) { |
889 | clearCaches(); | |
890 | result = TRUE; | |
891 | } else { | |
892 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
893 | delete factory; | |
894 | } | |
b75a7d8f | 895 | } |
46f4442e A |
896 | if (result) { |
897 | notifyChanged(); | |
898 | } | |
899 | return result; | |
b75a7d8f A |
900 | } |
901 | ||
902 | void | |
903 | ICUService::reset() | |
904 | { | |
46f4442e | 905 | { |
3d1f044b | 906 | Mutex mutex(lock()); |
46f4442e A |
907 | reInitializeFactories(); |
908 | clearCaches(); | |
909 | } | |
910 | notifyChanged(); | |
b75a7d8f A |
911 | } |
912 | ||
913 | void | |
914 | ICUService::reInitializeFactories() | |
915 | { | |
46f4442e A |
916 | if (factories != NULL) { |
917 | factories->removeAllElements(); | |
918 | } | |
b75a7d8f A |
919 | } |
920 | ||
921 | UBool | |
922 | ICUService::isDefault() const | |
923 | { | |
46f4442e | 924 | return countFactories() == 0; |
b75a7d8f A |
925 | } |
926 | ||
927 | ICUServiceKey* | |
928 | ICUService::createKey(const UnicodeString* id, UErrorCode& status) const | |
929 | { | |
46f4442e | 930 | return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); |
b75a7d8f A |
931 | } |
932 | ||
933 | void | |
934 | ICUService::clearCaches() | |
935 | { | |
46f4442e A |
936 | // callers synchronize before use |
937 | ++timestamp; | |
938 | delete dnCache; | |
939 | dnCache = NULL; | |
940 | delete idCache; | |
941 | idCache = NULL; | |
942 | delete serviceCache; serviceCache = NULL; | |
b75a7d8f A |
943 | } |
944 | ||
945 | void | |
946 | ICUService::clearServiceCache() | |
947 | { | |
46f4442e A |
948 | // callers synchronize before use |
949 | delete serviceCache; serviceCache = NULL; | |
b75a7d8f A |
950 | } |
951 | ||
952 | UBool | |
953 | ICUService::acceptsListener(const EventListener& l) const | |
954 | { | |
729e4ab9 | 955 | return dynamic_cast<const ServiceListener*>(&l) != NULL; |
b75a7d8f A |
956 | } |
957 | ||
958 | void | |
959 | ICUService::notifyListener(EventListener& l) const | |
960 | { | |
46f4442e | 961 | ((ServiceListener&)l).serviceChanged(*this); |
b75a7d8f A |
962 | } |
963 | ||
964 | UnicodeString& | |
965 | ICUService::getName(UnicodeString& result) const | |
966 | { | |
46f4442e | 967 | return result.append(name); |
b75a7d8f A |
968 | } |
969 | ||
970 | int32_t | |
971 | ICUService::countFactories() const | |
972 | { | |
46f4442e | 973 | return factories == NULL ? 0 : factories->size(); |
b75a7d8f A |
974 | } |
975 | ||
976 | int32_t | |
977 | ICUService::getTimestamp() const | |
978 | { | |
46f4442e | 979 | return timestamp; |
b75a7d8f A |
980 | } |
981 | ||
982 | U_NAMESPACE_END | |
983 | ||
984 | /* UCONFIG_NO_SERVICE */ | |
985 | #endif |