/src/mozilla-central/intl/icu/source/common/serv.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | : _id(id) { |
31 | 0 | } |
32 | | |
33 | | ICUServiceKey::~ICUServiceKey() |
34 | 0 | { |
35 | 0 | } |
36 | | |
37 | | const UnicodeString& |
38 | | ICUServiceKey::getID() const |
39 | 0 | { |
40 | 0 | return _id; |
41 | 0 | } |
42 | | |
43 | | UnicodeString& |
44 | | ICUServiceKey::canonicalID(UnicodeString& result) const |
45 | 0 | { |
46 | 0 | return result.append(_id); |
47 | 0 | } |
48 | | |
49 | | UnicodeString& |
50 | | ICUServiceKey::currentID(UnicodeString& result) const |
51 | 0 | { |
52 | 0 | return canonicalID(result); |
53 | 0 | } |
54 | | |
55 | | UnicodeString& |
56 | | ICUServiceKey::currentDescriptor(UnicodeString& result) const |
57 | 0 | { |
58 | 0 | prefix(result); |
59 | 0 | result.append(PREFIX_DELIMITER); |
60 | 0 | return currentID(result); |
61 | 0 | } |
62 | | |
63 | | UBool |
64 | | ICUServiceKey::fallback() |
65 | 0 | { |
66 | 0 | return FALSE; |
67 | 0 | } |
68 | | |
69 | | UBool |
70 | | ICUServiceKey::isFallbackOf(const UnicodeString& id) const |
71 | 0 | { |
72 | 0 | return id == _id; |
73 | 0 | } |
74 | | |
75 | | UnicodeString& |
76 | | ICUServiceKey::prefix(UnicodeString& result) const |
77 | 0 | { |
78 | 0 | return result; |
79 | 0 | } |
80 | | |
81 | | UnicodeString& |
82 | | ICUServiceKey::parsePrefix(UnicodeString& result) |
83 | 0 | { |
84 | 0 | int32_t n = result.indexOf(PREFIX_DELIMITER); |
85 | 0 | if (n < 0) { |
86 | 0 | n = 0; |
87 | 0 | } |
88 | 0 | result.remove(n); |
89 | 0 | return result; |
90 | 0 | } |
91 | | |
92 | | UnicodeString& |
93 | | ICUServiceKey::parseSuffix(UnicodeString& result) |
94 | 0 | { |
95 | 0 | int32_t n = result.indexOf(PREFIX_DELIMITER); |
96 | 0 | if (n >= 0) { |
97 | 0 | result.remove(0, n+1); |
98 | 0 | } |
99 | 0 | return result; |
100 | 0 | } |
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 | 0 | ICUServiceFactory::~ICUServiceFactory() {} |
126 | | |
127 | | SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) |
128 | | : _instance(instanceToAdopt), _id(id), _visible(visible) |
129 | 0 | { |
130 | 0 | } |
131 | | |
132 | | SimpleFactory::~SimpleFactory() |
133 | 0 | { |
134 | 0 | delete _instance; |
135 | 0 | } |
136 | | |
137 | | UObject* |
138 | | SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const |
139 | 0 | { |
140 | 0 | if (U_SUCCESS(status)) { |
141 | 0 | UnicodeString temp; |
142 | 0 | if (_id == key.currentID(temp)) { |
143 | 0 | return service->cloneInstance(_instance); |
144 | 0 | } |
145 | 0 | } |
146 | 0 | return NULL; |
147 | 0 | } |
148 | | |
149 | | void |
150 | | SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const |
151 | 0 | { |
152 | 0 | if (_visible) { |
153 | 0 | result.put(_id, (void*)this, status); // cast away const |
154 | 0 | } else { |
155 | 0 | result.remove(_id); |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | | UnicodeString& |
160 | | SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const |
161 | 0 | { |
162 | 0 | if (_visible && _id == id) { |
163 | 0 | result = _id; |
164 | 0 | } else { |
165 | 0 | result.setToBogus(); |
166 | 0 | } |
167 | 0 | return result; |
168 | 0 | } |
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 | 0 | 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 | 0 | ~CacheEntry() { |
217 | 0 | delete service; |
218 | 0 | } |
219 | | |
220 | | CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) |
221 | 0 | : refcount(1), actualDescriptor(_actualDescriptor), service(_service) { |
222 | 0 | } |
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 | 0 | CacheEntry* ref() { |
231 | 0 | ++refcount; |
232 | 0 | return this; |
233 | 0 | } |
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 | 0 | CacheEntry* unref() { |
243 | 0 | if ((--refcount) == 0) { |
244 | 0 | delete this; |
245 | 0 | return NULL; |
246 | 0 | } |
247 | 0 | return this; |
248 | 0 | } |
249 | | |
250 | | /** |
251 | | * Return TRUE if there is at least one reference to this and the |
252 | | * resource has not been released. |
253 | | */ |
254 | 0 | UBool isShared() const { |
255 | 0 | return refcount > 1; |
256 | 0 | } |
257 | | }; |
258 | | |
259 | | // UObjectDeleter for serviceCache |
260 | | U_CDECL_BEGIN |
261 | | static void U_CALLCONV |
262 | 0 | cacheDeleter(void* obj) { |
263 | 0 | U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); |
264 | 0 | } |
265 | | |
266 | | /** |
267 | | * Deleter for UObjects |
268 | | */ |
269 | | static void U_CALLCONV |
270 | 0 | deleteUObject(void *obj) { |
271 | 0 | U_NAMESPACE_USE delete (UObject*) obj; |
272 | 0 | } |
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 | 0 | { |
287 | 0 | // cache.setKeyDeleter(uprv_deleteUObject); |
288 | 0 | } |
289 | | }; |
290 | | |
291 | | |
292 | | /* |
293 | | ****************************************************************** |
294 | | */ |
295 | | |
296 | | StringPair* |
297 | | StringPair::create(const UnicodeString& displayName, |
298 | | const UnicodeString& id, |
299 | | UErrorCode& status) |
300 | 0 | { |
301 | 0 | if (U_SUCCESS(status)) { |
302 | 0 | StringPair* sp = new StringPair(displayName, id); |
303 | 0 | if (sp == NULL || sp->isBogus()) { |
304 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
305 | 0 | delete sp; |
306 | 0 | return NULL; |
307 | 0 | } |
308 | 0 | return sp; |
309 | 0 | } |
310 | 0 | return NULL; |
311 | 0 | } |
312 | | |
313 | | UBool |
314 | 0 | StringPair::isBogus() const { |
315 | 0 | return displayName.isBogus() || id.isBogus(); |
316 | 0 | } |
317 | | |
318 | | StringPair::StringPair(const UnicodeString& _displayName, |
319 | | const UnicodeString& _id) |
320 | | : displayName(_displayName) |
321 | | , id(_id) |
322 | 0 | { |
323 | 0 | } |
324 | | |
325 | | U_CDECL_BEGIN |
326 | | static void U_CALLCONV |
327 | 0 | userv_deleteStringPair(void *obj) { |
328 | 0 | U_NAMESPACE_USE delete (StringPair*) obj; |
329 | 0 | } |
330 | | U_CDECL_END |
331 | | |
332 | | /* |
333 | | ****************************************************************** |
334 | | */ |
335 | | |
336 | | static UMutex lock = U_MUTEX_INITIALIZER; |
337 | | |
338 | | ICUService::ICUService() |
339 | | : name() |
340 | | , timestamp(0) |
341 | | , factories(NULL) |
342 | | , serviceCache(NULL) |
343 | | , idCache(NULL) |
344 | | , dnCache(NULL) |
345 | 0 | { |
346 | 0 | } |
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 | 0 | { |
356 | 0 | } |
357 | | |
358 | | ICUService::~ICUService() |
359 | 0 | { |
360 | 0 | { |
361 | 0 | Mutex mutex(&lock); |
362 | 0 | clearCaches(); |
363 | 0 | delete factories; |
364 | 0 | factories = NULL; |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | | UObject* |
369 | | ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const |
370 | 0 | { |
371 | 0 | return get(descriptor, NULL, status); |
372 | 0 | } |
373 | | |
374 | | UObject* |
375 | | ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const |
376 | 0 | { |
377 | 0 | UObject* result = NULL; |
378 | 0 | ICUServiceKey* key = createKey(&descriptor, status); |
379 | 0 | if (key) { |
380 | 0 | result = getKey(*key, actualReturn, status); |
381 | 0 | delete key; |
382 | 0 | } |
383 | 0 | return result; |
384 | 0 | } |
385 | | |
386 | | UObject* |
387 | | ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const |
388 | 0 | { |
389 | 0 | return getKey(key, NULL, status); |
390 | 0 | } |
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 | 0 | { |
398 | 0 | return getKey(key, actualReturn, NULL, status); |
399 | 0 | } |
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 | 0 | { |
410 | 0 | if (fActive) umtx_lock(fMutex); |
411 | 0 | } |
412 | 0 | inline ~XMutex() { |
413 | 0 | if (fActive) umtx_unlock(fMutex); |
414 | 0 | } |
415 | | |
416 | | private: |
417 | | UMutex *fMutex; |
418 | | UBool fActive; |
419 | | }; |
420 | | |
421 | | struct UVectorDeleter { |
422 | | UVector* _obj; |
423 | 0 | UVectorDeleter() : _obj(NULL) {} |
424 | 0 | ~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 | 0 | { |
431 | 0 | if (U_FAILURE(status)) { |
432 | 0 | return NULL; |
433 | 0 | } |
434 | 0 | |
435 | 0 | if (isDefault()) { |
436 | 0 | return handleDefault(key, actualReturn, status); |
437 | 0 | } |
438 | 0 | |
439 | 0 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
440 | 0 |
|
441 | 0 | CacheEntry* result = NULL; |
442 | 0 | { |
443 | 0 | // The factory list can't be modified until we're done, |
444 | 0 | // otherwise we might update the cache with an invalid result. |
445 | 0 | // The cache has to stay in synch with the factory list. |
446 | 0 | // ICU doesn't have monitors so we can't use rw locks, so |
447 | 0 | // we single-thread everything using this service, for now. |
448 | 0 |
|
449 | 0 | // if factory is not null, we're calling from within the mutex, |
450 | 0 | // and since some unix machines don't have reentrant mutexes we |
451 | 0 | // need to make sure not to try to lock it again. |
452 | 0 | XMutex mutex(&lock, factory != NULL); |
453 | 0 |
|
454 | 0 | if (serviceCache == NULL) { |
455 | 0 | ncthis->serviceCache = new Hashtable(status); |
456 | 0 | if (ncthis->serviceCache == NULL) { |
457 | 0 | return NULL; |
458 | 0 | } |
459 | 0 | if (U_FAILURE(status)) { |
460 | 0 | delete serviceCache; |
461 | 0 | return NULL; |
462 | 0 | } |
463 | 0 | serviceCache->setValueDeleter(cacheDeleter); |
464 | 0 | } |
465 | 0 |
|
466 | 0 | UnicodeString currentDescriptor; |
467 | 0 | UVectorDeleter cacheDescriptorList; |
468 | 0 | UBool putInCache = FALSE; |
469 | 0 |
|
470 | 0 | int32_t startIndex = 0; |
471 | 0 | int32_t limit = factories->size(); |
472 | 0 | UBool cacheResult = TRUE; |
473 | 0 |
|
474 | 0 | if (factory != NULL) { |
475 | 0 | for (int32_t i = 0; i < limit; ++i) { |
476 | 0 | if (factory == (const ICUServiceFactory*)factories->elementAt(i)) { |
477 | 0 | startIndex = i + 1; |
478 | 0 | break; |
479 | 0 | } |
480 | 0 | } |
481 | 0 | if (startIndex == 0) { |
482 | 0 | // throw new InternalError("Factory " + factory + "not registered with service: " + this); |
483 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
484 | 0 | return NULL; |
485 | 0 | } |
486 | 0 | cacheResult = FALSE; |
487 | 0 | } |
488 | 0 |
|
489 | 0 | do { |
490 | 0 | currentDescriptor.remove(); |
491 | 0 | key.currentDescriptor(currentDescriptor); |
492 | 0 | result = (CacheEntry*)serviceCache->get(currentDescriptor); |
493 | 0 | if (result != NULL) { |
494 | 0 | break; |
495 | 0 | } |
496 | 0 | |
497 | 0 | // first test of cache failed, so we'll have to update |
498 | 0 | // the cache if we eventually succeed-- that is, if we're |
499 | 0 | // going to update the cache at all. |
500 | 0 | putInCache = TRUE; |
501 | 0 |
|
502 | 0 | int32_t index = startIndex; |
503 | 0 | while (index < limit) { |
504 | 0 | ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++); |
505 | 0 | UObject* service = f->create(key, this, status); |
506 | 0 | if (U_FAILURE(status)) { |
507 | 0 | delete service; |
508 | 0 | return NULL; |
509 | 0 | } |
510 | 0 | if (service != NULL) { |
511 | 0 | result = new CacheEntry(currentDescriptor, service); |
512 | 0 | if (result == NULL) { |
513 | 0 | delete service; |
514 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
515 | 0 | return NULL; |
516 | 0 | } |
517 | 0 | |
518 | 0 | goto outerEnd; |
519 | 0 | } |
520 | 0 | } |
521 | 0 |
|
522 | 0 | // prepare to load the cache with all additional ids that |
523 | 0 | // will resolve to result, assuming we'll succeed. We |
524 | 0 | // don't want to keep querying on an id that's going to |
525 | 0 | // fallback to the one that succeeded, we want to hit the |
526 | 0 | // cache the first time next goaround. |
527 | 0 | if (cacheDescriptorList._obj == NULL) { |
528 | 0 | cacheDescriptorList._obj = new UVector(uprv_deleteUObject, NULL, 5, status); |
529 | 0 | if (U_FAILURE(status)) { |
530 | 0 | return NULL; |
531 | 0 | } |
532 | 0 | } |
533 | 0 | UnicodeString* idToCache = new UnicodeString(currentDescriptor); |
534 | 0 | if (idToCache == NULL || idToCache->isBogus()) { |
535 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
536 | 0 | return NULL; |
537 | 0 | } |
538 | 0 | |
539 | 0 | cacheDescriptorList._obj->addElement(idToCache, status); |
540 | 0 | if (U_FAILURE(status)) { |
541 | 0 | return NULL; |
542 | 0 | } |
543 | 0 | } while (key.fallback()); |
544 | 0 | outerEnd: |
545 | 0 |
|
546 | 0 | if (result != NULL) { |
547 | 0 | if (putInCache && cacheResult) { |
548 | 0 | serviceCache->put(result->actualDescriptor, result, status); |
549 | 0 | if (U_FAILURE(status)) { |
550 | 0 | return NULL; |
551 | 0 | } |
552 | 0 | |
553 | 0 | if (cacheDescriptorList._obj != NULL) { |
554 | 0 | for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) { |
555 | 0 | UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i); |
556 | 0 |
|
557 | 0 | serviceCache->put(*desc, result, status); |
558 | 0 | if (U_FAILURE(status)) { |
559 | 0 | return NULL; |
560 | 0 | } |
561 | 0 | |
562 | 0 | result->ref(); |
563 | 0 | cacheDescriptorList._obj->removeElementAt(i); |
564 | 0 | } |
565 | 0 | } |
566 | 0 | } |
567 | 0 |
|
568 | 0 | if (actualReturn != NULL) { |
569 | 0 | // strip null prefix |
570 | 0 | if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/) |
571 | 0 | actualReturn->remove(); |
572 | 0 | actualReturn->append(result->actualDescriptor, |
573 | 0 | 1, |
574 | 0 | result->actualDescriptor.length() - 1); |
575 | 0 | } else { |
576 | 0 | *actualReturn = result->actualDescriptor; |
577 | 0 | } |
578 | 0 |
|
579 | 0 | if (actualReturn->isBogus()) { |
580 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
581 | 0 | delete result; |
582 | 0 | return NULL; |
583 | 0 | } |
584 | 0 | } |
585 | 0 | |
586 | 0 | UObject* service = cloneInstance(result->service); |
587 | 0 | if (putInCache && !cacheResult) { |
588 | 0 | delete result; |
589 | 0 | } |
590 | 0 | return service; |
591 | 0 | } |
592 | 0 | } |
593 | 0 |
|
594 | 0 | return handleDefault(key, actualReturn, status); |
595 | 0 | } |
596 | | |
597 | | UObject* |
598 | | ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const |
599 | 0 | { |
600 | 0 | return NULL; |
601 | 0 | } |
602 | | |
603 | | UVector& |
604 | 0 | ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { |
605 | 0 | return getVisibleIDs(result, NULL, status); |
606 | 0 | } |
607 | | |
608 | | UVector& |
609 | | ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const |
610 | 0 | { |
611 | 0 | result.removeAllElements(); |
612 | 0 |
|
613 | 0 | if (U_FAILURE(status)) { |
614 | 0 | return result; |
615 | 0 | } |
616 | 0 | |
617 | 0 | { |
618 | 0 | Mutex mutex(&lock); |
619 | 0 | const Hashtable* map = getVisibleIDMap(status); |
620 | 0 | if (map != NULL) { |
621 | 0 | ICUServiceKey* fallbackKey = createKey(matchID, status); |
622 | 0 |
|
623 | 0 | for (int32_t pos = UHASH_FIRST;;) { |
624 | 0 | const UHashElement* e = map->nextElement(pos); |
625 | 0 | if (e == NULL) { |
626 | 0 | break; |
627 | 0 | } |
628 | 0 | |
629 | 0 | const UnicodeString* id = (const UnicodeString*)e->key.pointer; |
630 | 0 | if (fallbackKey != NULL) { |
631 | 0 | if (!fallbackKey->isFallbackOf(*id)) { |
632 | 0 | continue; |
633 | 0 | } |
634 | 0 | } |
635 | 0 | |
636 | 0 | UnicodeString* idClone = new UnicodeString(*id); |
637 | 0 | if (idClone == NULL || idClone->isBogus()) { |
638 | 0 | delete idClone; |
639 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
640 | 0 | break; |
641 | 0 | } |
642 | 0 | result.addElement(idClone, status); |
643 | 0 | if (U_FAILURE(status)) { |
644 | 0 | delete idClone; |
645 | 0 | break; |
646 | 0 | } |
647 | 0 | } |
648 | 0 | delete fallbackKey; |
649 | 0 | } |
650 | 0 | } |
651 | 0 | if (U_FAILURE(status)) { |
652 | 0 | result.removeAllElements(); |
653 | 0 | } |
654 | 0 | return result; |
655 | 0 | } |
656 | | |
657 | | const Hashtable* |
658 | 0 | ICUService::getVisibleIDMap(UErrorCode& status) const { |
659 | 0 | if (U_FAILURE(status)) return NULL; |
660 | 0 | |
661 | 0 | // must only be called when lock is already held |
662 | 0 | |
663 | 0 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
664 | 0 | if (idCache == NULL) { |
665 | 0 | ncthis->idCache = new Hashtable(status); |
666 | 0 | if (idCache == NULL) { |
667 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
668 | 0 | } else if (factories != NULL) { |
669 | 0 | for (int32_t pos = factories->size(); --pos >= 0;) { |
670 | 0 | ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); |
671 | 0 | f->updateVisibleIDs(*idCache, status); |
672 | 0 | } |
673 | 0 | if (U_FAILURE(status)) { |
674 | 0 | delete idCache; |
675 | 0 | ncthis->idCache = NULL; |
676 | 0 | } |
677 | 0 | } |
678 | 0 | } |
679 | 0 |
|
680 | 0 | return idCache; |
681 | 0 | } |
682 | | |
683 | | |
684 | | UnicodeString& |
685 | | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const |
686 | 0 | { |
687 | 0 | return getDisplayName(id, result, Locale::getDefault()); |
688 | 0 | } |
689 | | |
690 | | UnicodeString& |
691 | | ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const |
692 | 0 | { |
693 | 0 | { |
694 | 0 | UErrorCode status = U_ZERO_ERROR; |
695 | 0 | Mutex mutex(&lock); |
696 | 0 | const Hashtable* map = getVisibleIDMap(status); |
697 | 0 | if (map != NULL) { |
698 | 0 | ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); |
699 | 0 | if (f != NULL) { |
700 | 0 | f->getDisplayName(id, locale, result); |
701 | 0 | return result; |
702 | 0 | } |
703 | 0 | |
704 | 0 | // fallback |
705 | 0 | UErrorCode status = U_ZERO_ERROR; |
706 | 0 | ICUServiceKey* fallbackKey = createKey(&id, status); |
707 | 0 | while (fallbackKey->fallback()) { |
708 | 0 | UnicodeString us; |
709 | 0 | fallbackKey->currentID(us); |
710 | 0 | f = (ICUServiceFactory*)map->get(us); |
711 | 0 | if (f != NULL) { |
712 | 0 | f->getDisplayName(id, locale, result); |
713 | 0 | delete fallbackKey; |
714 | 0 | return result; |
715 | 0 | } |
716 | 0 | } |
717 | 0 | delete fallbackKey; |
718 | 0 | } |
719 | 0 | } |
720 | 0 | result.setToBogus(); |
721 | 0 | return result; |
722 | 0 | } |
723 | | |
724 | | UVector& |
725 | | ICUService::getDisplayNames(UVector& result, UErrorCode& status) const |
726 | 0 | { |
727 | 0 | return getDisplayNames(result, Locale::getDefault(), NULL, status); |
728 | 0 | } |
729 | | |
730 | | |
731 | | UVector& |
732 | | ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const |
733 | 0 | { |
734 | 0 | return getDisplayNames(result, locale, NULL, status); |
735 | 0 | } |
736 | | |
737 | | UVector& |
738 | | ICUService::getDisplayNames(UVector& result, |
739 | | const Locale& locale, |
740 | | const UnicodeString* matchID, |
741 | | UErrorCode& status) const |
742 | 0 | { |
743 | 0 | result.removeAllElements(); |
744 | 0 | result.setDeleter(userv_deleteStringPair); |
745 | 0 | if (U_SUCCESS(status)) { |
746 | 0 | ICUService* ncthis = (ICUService*)this; // cast away semantic const |
747 | 0 | Mutex mutex(&lock); |
748 | 0 |
|
749 | 0 | if (dnCache != NULL && dnCache->locale != locale) { |
750 | 0 | delete dnCache; |
751 | 0 | ncthis->dnCache = NULL; |
752 | 0 | } |
753 | 0 |
|
754 | 0 | if (dnCache == NULL) { |
755 | 0 | const Hashtable* m = getVisibleIDMap(status); |
756 | 0 | if (U_FAILURE(status)) { |
757 | 0 | return result; |
758 | 0 | } |
759 | 0 | ncthis->dnCache = new DNCache(locale); |
760 | 0 | if (dnCache == NULL) { |
761 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
762 | 0 | return result; |
763 | 0 | } |
764 | 0 | |
765 | 0 | int32_t pos = UHASH_FIRST; |
766 | 0 | const UHashElement* entry = NULL; |
767 | 0 | while ((entry = m->nextElement(pos)) != NULL) { |
768 | 0 | const UnicodeString* id = (const UnicodeString*)entry->key.pointer; |
769 | 0 | ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; |
770 | 0 | UnicodeString dname; |
771 | 0 | f->getDisplayName(*id, locale, dname); |
772 | 0 | if (dname.isBogus()) { |
773 | 0 | status = U_MEMORY_ALLOCATION_ERROR; |
774 | 0 | } else { |
775 | 0 | dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap |
776 | 0 | if (U_SUCCESS(status)) { |
777 | 0 | continue; |
778 | 0 | } |
779 | 0 | } |
780 | 0 | delete dnCache; |
781 | 0 | ncthis->dnCache = NULL; |
782 | 0 | return result; |
783 | 0 | } |
784 | 0 | } |
785 | 0 | } |
786 | 0 |
|
787 | 0 | ICUServiceKey* matchKey = createKey(matchID, status); |
788 | 0 | /* To ensure that all elements in the hashtable are iterated, set pos to -1. |
789 | 0 | * nextElement(pos) will skip the position at pos and begin the iteration |
790 | 0 | * at the next position, which in this case will be 0. |
791 | 0 | */ |
792 | 0 | int32_t pos = UHASH_FIRST; |
793 | 0 | const UHashElement *entry = NULL; |
794 | 0 | while ((entry = dnCache->cache.nextElement(pos)) != NULL) { |
795 | 0 | const UnicodeString* id = (const UnicodeString*)entry->value.pointer; |
796 | 0 | if (matchKey != NULL && !matchKey->isFallbackOf(*id)) { |
797 | 0 | continue; |
798 | 0 | } |
799 | 0 | const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; |
800 | 0 | StringPair* sp = StringPair::create(*id, *dn, status); |
801 | 0 | result.addElement(sp, status); |
802 | 0 | if (U_FAILURE(status)) { |
803 | 0 | result.removeAllElements(); |
804 | 0 | break; |
805 | 0 | } |
806 | 0 | } |
807 | 0 | delete matchKey; |
808 | 0 |
|
809 | 0 | return result; |
810 | 0 | } |
811 | | |
812 | | URegistryKey |
813 | | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) |
814 | 0 | { |
815 | 0 | return registerInstance(objToAdopt, id, TRUE, status); |
816 | 0 | } |
817 | | |
818 | | URegistryKey |
819 | | ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) |
820 | 0 | { |
821 | 0 | ICUServiceKey* key = createKey(&id, status); |
822 | 0 | if (key != NULL) { |
823 | 0 | UnicodeString canonicalID; |
824 | 0 | key->canonicalID(canonicalID); |
825 | 0 | delete key; |
826 | 0 |
|
827 | 0 | ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); |
828 | 0 | if (f != NULL) { |
829 | 0 | return registerFactory(f, status); |
830 | 0 | } |
831 | 0 | } |
832 | 0 | delete objToAdopt; |
833 | 0 | return NULL; |
834 | 0 | } |
835 | | |
836 | | ICUServiceFactory* |
837 | | ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) |
838 | 0 | { |
839 | 0 | if (U_SUCCESS(status)) { |
840 | 0 | if ((objToAdopt != NULL) && (!id.isBogus())) { |
841 | 0 | return new SimpleFactory(objToAdopt, id, visible); |
842 | 0 | } |
843 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
844 | 0 | } |
845 | 0 | return NULL; |
846 | 0 | } |
847 | | |
848 | | URegistryKey |
849 | | ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) |
850 | 0 | { |
851 | 0 | if (U_SUCCESS(status) && factoryToAdopt != NULL) { |
852 | 0 | Mutex mutex(&lock); |
853 | 0 |
|
854 | 0 | if (factories == NULL) { |
855 | 0 | factories = new UVector(deleteUObject, NULL, status); |
856 | 0 | if (U_FAILURE(status)) { |
857 | 0 | delete factories; |
858 | 0 | return NULL; |
859 | 0 | } |
860 | 0 | } |
861 | 0 | factories->insertElementAt(factoryToAdopt, 0, status); |
862 | 0 | if (U_SUCCESS(status)) { |
863 | 0 | clearCaches(); |
864 | 0 | } else { |
865 | 0 | delete factoryToAdopt; |
866 | 0 | factoryToAdopt = NULL; |
867 | 0 | } |
868 | 0 | } |
869 | 0 |
|
870 | 0 | if (factoryToAdopt != NULL) { |
871 | 0 | notifyChanged(); |
872 | 0 | } |
873 | 0 |
|
874 | 0 | return (URegistryKey)factoryToAdopt; |
875 | 0 | } |
876 | | |
877 | | UBool |
878 | | ICUService::unregister(URegistryKey rkey, UErrorCode& status) |
879 | 0 | { |
880 | 0 | ICUServiceFactory *factory = (ICUServiceFactory*)rkey; |
881 | 0 | UBool result = FALSE; |
882 | 0 | if (factory != NULL && factories != NULL) { |
883 | 0 | Mutex mutex(&lock); |
884 | 0 |
|
885 | 0 | if (factories->removeElement(factory)) { |
886 | 0 | clearCaches(); |
887 | 0 | result = TRUE; |
888 | 0 | } else { |
889 | 0 | status = U_ILLEGAL_ARGUMENT_ERROR; |
890 | 0 | delete factory; |
891 | 0 | } |
892 | 0 | } |
893 | 0 | if (result) { |
894 | 0 | notifyChanged(); |
895 | 0 | } |
896 | 0 | return result; |
897 | 0 | } |
898 | | |
899 | | void |
900 | | ICUService::reset() |
901 | 0 | { |
902 | 0 | { |
903 | 0 | Mutex mutex(&lock); |
904 | 0 | reInitializeFactories(); |
905 | 0 | clearCaches(); |
906 | 0 | } |
907 | 0 | notifyChanged(); |
908 | 0 | } |
909 | | |
910 | | void |
911 | | ICUService::reInitializeFactories() |
912 | 0 | { |
913 | 0 | if (factories != NULL) { |
914 | 0 | factories->removeAllElements(); |
915 | 0 | } |
916 | 0 | } |
917 | | |
918 | | UBool |
919 | | ICUService::isDefault() const |
920 | 0 | { |
921 | 0 | return countFactories() == 0; |
922 | 0 | } |
923 | | |
924 | | ICUServiceKey* |
925 | | ICUService::createKey(const UnicodeString* id, UErrorCode& status) const |
926 | 0 | { |
927 | 0 | return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); |
928 | 0 | } |
929 | | |
930 | | void |
931 | | ICUService::clearCaches() |
932 | 0 | { |
933 | 0 | // callers synchronize before use |
934 | 0 | ++timestamp; |
935 | 0 | delete dnCache; |
936 | 0 | dnCache = NULL; |
937 | 0 | delete idCache; |
938 | 0 | idCache = NULL; |
939 | 0 | delete serviceCache; serviceCache = NULL; |
940 | 0 | } |
941 | | |
942 | | void |
943 | | ICUService::clearServiceCache() |
944 | 0 | { |
945 | 0 | // callers synchronize before use |
946 | 0 | delete serviceCache; serviceCache = NULL; |
947 | 0 | } |
948 | | |
949 | | UBool |
950 | | ICUService::acceptsListener(const EventListener& l) const |
951 | 0 | { |
952 | 0 | return dynamic_cast<const ServiceListener*>(&l) != NULL; |
953 | 0 | } |
954 | | |
955 | | void |
956 | | ICUService::notifyListener(EventListener& l) const |
957 | 0 | { |
958 | 0 | ((ServiceListener&)l).serviceChanged(*this); |
959 | 0 | } |
960 | | |
961 | | UnicodeString& |
962 | | ICUService::getName(UnicodeString& result) const |
963 | 0 | { |
964 | 0 | return result.append(name); |
965 | 0 | } |
966 | | |
967 | | int32_t |
968 | | ICUService::countFactories() const |
969 | 0 | { |
970 | 0 | return factories == NULL ? 0 : factories->size(); |
971 | 0 | } |
972 | | |
973 | | int32_t |
974 | | ICUService::getTimestamp() const |
975 | 0 | { |
976 | 0 | return timestamp; |
977 | 0 | } |
978 | | |
979 | | U_NAMESPACE_END |
980 | | |
981 | | /* UCONFIG_NO_SERVICE */ |
982 | | #endif |