intl/icu/source/common/serv.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial