diff options
author | vvvv <vvvv@yandex-team.ru> | 2022-03-03 23:09:11 +0300 |
---|---|---|
committer | vvvv <vvvv@yandex-team.ru> | 2022-03-03 23:09:11 +0300 |
commit | 8dbdbab054b6eb352cfffdad09991052008062fc (patch) | |
tree | 71befed92d327f2cc409bff4986ec510d0530d92 /contrib/libs/icu/common/serv.cpp | |
parent | ed3f4e21f3a3837059607ec2606e1dc910269ac0 (diff) | |
download | ydb-8dbdbab054b6eb352cfffdad09991052008062fc.tar.gz |
YQL-13710 try to switch to new shiny PG
ref:2bed0445c8e8bd6c575883632adf19445f0a72a7
Diffstat (limited to 'contrib/libs/icu/common/serv.cpp')
-rw-r--r-- | contrib/libs/icu/common/serv.cpp | 982 |
1 files changed, 982 insertions, 0 deletions
diff --git a/contrib/libs/icu/common/serv.cpp b/contrib/libs/icu/common/serv.cpp new file mode 100644 index 0000000000..ce545b9dbd --- /dev/null +++ b/contrib/libs/icu/common/serv.cpp @@ -0,0 +1,982 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/** +******************************************************************************* +* Copyright (C) 2001-2014, International Business Machines Corporation. +* All Rights Reserved. +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_SERVICE + +#include "serv.h" +#include "umutex.h" + +#undef SERVICE_REFCOUNT + +// in case we use the refcount stuff + +U_NAMESPACE_BEGIN + +/* +****************************************************************** +*/ + +const UChar ICUServiceKey::PREFIX_DELIMITER = 0x002F; /* '/' */ + +ICUServiceKey::ICUServiceKey(const UnicodeString& id) +: _id(id) { +} + +ICUServiceKey::~ICUServiceKey() +{ +} + +const UnicodeString& +ICUServiceKey::getID() const +{ + return _id; +} + +UnicodeString& +ICUServiceKey::canonicalID(UnicodeString& result) const +{ + return result.append(_id); +} + +UnicodeString& +ICUServiceKey::currentID(UnicodeString& result) const +{ + return canonicalID(result); +} + +UnicodeString& +ICUServiceKey::currentDescriptor(UnicodeString& result) const +{ + prefix(result); + result.append(PREFIX_DELIMITER); + return currentID(result); +} + +UBool +ICUServiceKey::fallback() +{ + return FALSE; +} + +UBool +ICUServiceKey::isFallbackOf(const UnicodeString& id) const +{ + return id == _id; +} + +UnicodeString& +ICUServiceKey::prefix(UnicodeString& result) const +{ + return result; +} + +UnicodeString& +ICUServiceKey::parsePrefix(UnicodeString& result) +{ + int32_t n = result.indexOf(PREFIX_DELIMITER); + if (n < 0) { + n = 0; + } + result.remove(n); + return result; +} + +UnicodeString& +ICUServiceKey::parseSuffix(UnicodeString& result) +{ + int32_t n = result.indexOf(PREFIX_DELIMITER); + if (n >= 0) { + result.remove(0, n+1); + } + return result; +} + +#ifdef SERVICE_DEBUG +UnicodeString& +ICUServiceKey::debug(UnicodeString& result) const +{ + debugClass(result); + result.append((UnicodeString)" id: "); + result.append(_id); + return result; +} + +UnicodeString& +ICUServiceKey::debugClass(UnicodeString& result) const +{ + return result.append((UnicodeString)"ICUServiceKey"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ICUServiceKey) + +/* +****************************************************************** +*/ + +ICUServiceFactory::~ICUServiceFactory() {} + +SimpleFactory::SimpleFactory(UObject* instanceToAdopt, const UnicodeString& id, UBool visible) +: _instance(instanceToAdopt), _id(id), _visible(visible) +{ +} + +SimpleFactory::~SimpleFactory() +{ + delete _instance; +} + +UObject* +SimpleFactory::create(const ICUServiceKey& key, const ICUService* service, UErrorCode& status) const +{ + if (U_SUCCESS(status)) { + UnicodeString temp; + if (_id == key.currentID(temp)) { + return service->cloneInstance(_instance); + } + } + return NULL; +} + +void +SimpleFactory::updateVisibleIDs(Hashtable& result, UErrorCode& status) const +{ + if (_visible) { + result.put(_id, (void*)this, status); // cast away const + } else { + result.remove(_id); + } +} + +UnicodeString& +SimpleFactory::getDisplayName(const UnicodeString& id, const Locale& /* locale */, UnicodeString& result) const +{ + if (_visible && _id == id) { + result = _id; + } else { + result.setToBogus(); + } + return result; +} + +#ifdef SERVICE_DEBUG +UnicodeString& +SimpleFactory::debug(UnicodeString& toAppendTo) const +{ + debugClass(toAppendTo); + toAppendTo.append((UnicodeString)" id: "); + toAppendTo.append(_id); + toAppendTo.append((UnicodeString)", visible: "); + toAppendTo.append(_visible ? (UnicodeString)"T" : (UnicodeString)"F"); + return toAppendTo; +} + +UnicodeString& +SimpleFactory::debugClass(UnicodeString& toAppendTo) const +{ + return toAppendTo.append((UnicodeString)"SimpleFactory"); +} +#endif + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleFactory) + +/* +****************************************************************** +*/ + +ServiceListener::~ServiceListener() {} + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ServiceListener) + +/* +****************************************************************** +*/ + +// Record the actual id for this service in the cache, so we can return it +// even if we succeed later with a different id. +class CacheEntry : public UMemory { +private: + int32_t refcount; + +public: + UnicodeString actualDescriptor; + UObject* service; + + /** + * Releases a reference to the shared resource. + */ + ~CacheEntry() { + delete service; + } + + CacheEntry(const UnicodeString& _actualDescriptor, UObject* _service) + : refcount(1), actualDescriptor(_actualDescriptor), service(_service) { + } + + /** + * Instantiation creates an initial reference, so don't call this + * unless you're creating a new pointer to this. Management of + * that pointer will have to know how to deal with refcounts. + * Return true if the resource has not already been released. + */ + CacheEntry* ref() { + ++refcount; + return this; + } + + /** + * Destructions removes a reference, so don't call this unless + * you're removing pointer to this somewhere. Management of that + * pointer will have to know how to deal with refcounts. Once + * the refcount drops to zero, the resource is released. Return + * false if the resouce has been released. + */ + CacheEntry* unref() { + if ((--refcount) == 0) { + delete this; + return NULL; + } + return this; + } + + /** + * Return TRUE if there is at least one reference to this and the + * resource has not been released. + */ + UBool isShared() const { + return refcount > 1; + } +}; + +// UObjectDeleter for serviceCache +U_CDECL_BEGIN +static void U_CALLCONV +cacheDeleter(void* obj) { + U_NAMESPACE_USE ((CacheEntry*)obj)->unref(); +} + +/** +* Deleter for UObjects +*/ +static void U_CALLCONV +deleteUObject(void *obj) { + U_NAMESPACE_USE delete (UObject*) obj; +} +U_CDECL_END + +/* +****************************************************************** +*/ + +class DNCache : public UMemory { +public: + Hashtable cache; + const Locale locale; + + DNCache(const Locale& _locale) + : cache(), locale(_locale) + { + // cache.setKeyDeleter(uprv_deleteUObject); + } +}; + + +/* +****************************************************************** +*/ + +StringPair* +StringPair::create(const UnicodeString& displayName, + const UnicodeString& id, + UErrorCode& status) +{ + if (U_SUCCESS(status)) { + StringPair* sp = new StringPair(displayName, id); + if (sp == NULL || sp->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + delete sp; + return NULL; + } + return sp; + } + return NULL; +} + +UBool +StringPair::isBogus() const { + return displayName.isBogus() || id.isBogus(); +} + +StringPair::StringPair(const UnicodeString& _displayName, + const UnicodeString& _id) +: displayName(_displayName) +, id(_id) +{ +} + +U_CDECL_BEGIN +static void U_CALLCONV +userv_deleteStringPair(void *obj) { + U_NAMESPACE_USE delete (StringPair*) obj; +} +U_CDECL_END + +/* +****************************************************************** +*/ + +static UMutex lock; + +ICUService::ICUService() +: name() +, timestamp(0) +, factories(NULL) +, serviceCache(NULL) +, idCache(NULL) +, dnCache(NULL) +{ +} + +ICUService::ICUService(const UnicodeString& newName) +: name(newName) +, timestamp(0) +, factories(NULL) +, serviceCache(NULL) +, idCache(NULL) +, dnCache(NULL) +{ +} + +ICUService::~ICUService() +{ + { + Mutex mutex(&lock); + clearCaches(); + delete factories; + factories = NULL; + } +} + +UObject* +ICUService::get(const UnicodeString& descriptor, UErrorCode& status) const +{ + return get(descriptor, NULL, status); +} + +UObject* +ICUService::get(const UnicodeString& descriptor, UnicodeString* actualReturn, UErrorCode& status) const +{ + UObject* result = NULL; + ICUServiceKey* key = createKey(&descriptor, status); + if (key) { + result = getKey(*key, actualReturn, status); + delete key; + } + return result; +} + +UObject* +ICUService::getKey(ICUServiceKey& key, UErrorCode& status) const +{ + return getKey(key, NULL, status); +} + +// this is a vector that subclasses of ICUService can override to further customize the result object +// before returning it. All other public get functions should call this one. + +UObject* +ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, UErrorCode& status) const +{ + return getKey(key, actualReturn, NULL, status); +} + +// make it possible to call reentrantly on systems that don't have reentrant mutexes. +// we can use this simple approach since we know the situation where we're calling +// reentrantly even without knowing the thread. +class XMutex : public UMemory { +public: + inline XMutex(UMutex *mutex, UBool reentering) + : fMutex(mutex) + , fActive(!reentering) + { + if (fActive) umtx_lock(fMutex); + } + inline ~XMutex() { + if (fActive) umtx_unlock(fMutex); + } + +private: + UMutex *fMutex; + UBool fActive; +}; + +struct UVectorDeleter { + UVector* _obj; + UVectorDeleter() : _obj(NULL) {} + ~UVectorDeleter() { delete _obj; } +}; + +// called only by factories, treat as private +UObject* +ICUService::getKey(ICUServiceKey& key, UnicodeString* actualReturn, const ICUServiceFactory* factory, UErrorCode& status) const +{ + if (U_FAILURE(status)) { + return NULL; + } + + if (isDefault()) { + return handleDefault(key, actualReturn, status); + } + + ICUService* ncthis = (ICUService*)this; // cast away semantic const + + CacheEntry* result = NULL; + { + // The factory list can't be modified until we're done, + // otherwise we might update the cache with an invalid result. + // The cache has to stay in synch with the factory list. + // ICU doesn't have monitors so we can't use rw locks, so + // we single-thread everything using this service, for now. + + // if factory is not null, we're calling from within the mutex, + // and since some unix machines don't have reentrant mutexes we + // need to make sure not to try to lock it again. + XMutex mutex(&lock, factory != NULL); + + if (serviceCache == NULL) { + ncthis->serviceCache = new Hashtable(status); + if (ncthis->serviceCache == NULL) { + return NULL; + } + if (U_FAILURE(status)) { + delete serviceCache; + return NULL; + } + serviceCache->setValueDeleter(cacheDeleter); + } + + UnicodeString currentDescriptor; + UVectorDeleter cacheDescriptorList; + UBool putInCache = FALSE; + + int32_t startIndex = 0; + int32_t limit = factories->size(); + UBool cacheResult = TRUE; + + if (factory != NULL) { + for (int32_t i = 0; i < limit; ++i) { + if (factory == (const ICUServiceFactory*)factories->elementAt(i)) { + startIndex = i + 1; + break; + } + } + if (startIndex == 0) { + // throw new InternalError("Factory " + factory + "not registered with service: " + this); + status = U_ILLEGAL_ARGUMENT_ERROR; + return NULL; + } + cacheResult = FALSE; + } + + do { + currentDescriptor.remove(); + key.currentDescriptor(currentDescriptor); + result = (CacheEntry*)serviceCache->get(currentDescriptor); + if (result != NULL) { + break; + } + + // first test of cache failed, so we'll have to update + // the cache if we eventually succeed-- that is, if we're + // going to update the cache at all. + putInCache = TRUE; + + int32_t index = startIndex; + while (index < limit) { + ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(index++); + UObject* service = f->create(key, this, status); + if (U_FAILURE(status)) { + delete service; + return NULL; + } + if (service != NULL) { + result = new CacheEntry(currentDescriptor, service); + if (result == NULL) { + delete service; + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + + goto outerEnd; + } + } + + // prepare to load the cache with all additional ids that + // will resolve to result, assuming we'll succeed. We + // don't want to keep querying on an id that's going to + // fallback to the one that succeeded, we want to hit the + // cache the first time next goaround. + if (cacheDescriptorList._obj == NULL) { + cacheDescriptorList._obj = new UVector(uprv_deleteUObject, NULL, 5, status); + if (U_FAILURE(status)) { + return NULL; + } + } + UnicodeString* idToCache = new UnicodeString(currentDescriptor); + if (idToCache == NULL || idToCache->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + return NULL; + } + + cacheDescriptorList._obj->addElement(idToCache, status); + if (U_FAILURE(status)) { + return NULL; + } + } while (key.fallback()); +outerEnd: + + if (result != NULL) { + if (putInCache && cacheResult) { + serviceCache->put(result->actualDescriptor, result, status); + if (U_FAILURE(status)) { + return NULL; + } + + if (cacheDescriptorList._obj != NULL) { + for (int32_t i = cacheDescriptorList._obj->size(); --i >= 0;) { + UnicodeString* desc = (UnicodeString*)cacheDescriptorList._obj->elementAt(i); + + serviceCache->put(*desc, result, status); + if (U_FAILURE(status)) { + return NULL; + } + + result->ref(); + cacheDescriptorList._obj->removeElementAt(i); + } + } + } + + if (actualReturn != NULL) { + // strip null prefix + if (result->actualDescriptor.indexOf((UChar)0x2f) == 0) { // U+002f=slash (/) + actualReturn->remove(); + actualReturn->append(result->actualDescriptor, + 1, + result->actualDescriptor.length() - 1); + } else { + *actualReturn = result->actualDescriptor; + } + + if (actualReturn->isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + delete result; + return NULL; + } + } + + UObject* service = cloneInstance(result->service); + if (putInCache && !cacheResult) { + delete result; + } + return service; + } + } + + return handleDefault(key, actualReturn, status); +} + +UObject* +ICUService::handleDefault(const ICUServiceKey& /* key */, UnicodeString* /* actualIDReturn */, UErrorCode& /* status */) const +{ + return NULL; +} + +UVector& +ICUService::getVisibleIDs(UVector& result, UErrorCode& status) const { + return getVisibleIDs(result, NULL, status); +} + +UVector& +ICUService::getVisibleIDs(UVector& result, const UnicodeString* matchID, UErrorCode& status) const +{ + result.removeAllElements(); + + if (U_FAILURE(status)) { + return result; + } + + { + Mutex mutex(&lock); + const Hashtable* map = getVisibleIDMap(status); + if (map != NULL) { + ICUServiceKey* fallbackKey = createKey(matchID, status); + + for (int32_t pos = UHASH_FIRST;;) { + const UHashElement* e = map->nextElement(pos); + if (e == NULL) { + break; + } + + const UnicodeString* id = (const UnicodeString*)e->key.pointer; + if (fallbackKey != NULL) { + if (!fallbackKey->isFallbackOf(*id)) { + continue; + } + } + + UnicodeString* idClone = new UnicodeString(*id); + if (idClone == NULL || idClone->isBogus()) { + delete idClone; + status = U_MEMORY_ALLOCATION_ERROR; + break; + } + result.addElement(idClone, status); + if (U_FAILURE(status)) { + delete idClone; + break; + } + } + delete fallbackKey; + } + } + if (U_FAILURE(status)) { + result.removeAllElements(); + } + return result; +} + +const Hashtable* +ICUService::getVisibleIDMap(UErrorCode& status) const { + if (U_FAILURE(status)) return NULL; + + // must only be called when lock is already held + + ICUService* ncthis = (ICUService*)this; // cast away semantic const + if (idCache == NULL) { + ncthis->idCache = new Hashtable(status); + if (idCache == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } else if (factories != NULL) { + for (int32_t pos = factories->size(); --pos >= 0;) { + ICUServiceFactory* f = (ICUServiceFactory*)factories->elementAt(pos); + f->updateVisibleIDs(*idCache, status); + } + if (U_FAILURE(status)) { + delete idCache; + ncthis->idCache = NULL; + } + } + } + + return idCache; +} + + +UnicodeString& +ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result) const +{ + return getDisplayName(id, result, Locale::getDefault()); +} + +UnicodeString& +ICUService::getDisplayName(const UnicodeString& id, UnicodeString& result, const Locale& locale) const +{ + { + UErrorCode status = U_ZERO_ERROR; + Mutex mutex(&lock); + const Hashtable* map = getVisibleIDMap(status); + if (map != NULL) { + ICUServiceFactory* f = (ICUServiceFactory*)map->get(id); + if (f != NULL) { + f->getDisplayName(id, locale, result); + return result; + } + + // fallback + status = U_ZERO_ERROR; + ICUServiceKey* fallbackKey = createKey(&id, status); + while (fallbackKey != NULL && fallbackKey->fallback()) { + UnicodeString us; + fallbackKey->currentID(us); + f = (ICUServiceFactory*)map->get(us); + if (f != NULL) { + f->getDisplayName(id, locale, result); + delete fallbackKey; + return result; + } + } + delete fallbackKey; + } + } + result.setToBogus(); + return result; +} + +UVector& +ICUService::getDisplayNames(UVector& result, UErrorCode& status) const +{ + return getDisplayNames(result, Locale::getDefault(), NULL, status); +} + + +UVector& +ICUService::getDisplayNames(UVector& result, const Locale& locale, UErrorCode& status) const +{ + return getDisplayNames(result, locale, NULL, status); +} + +UVector& +ICUService::getDisplayNames(UVector& result, + const Locale& locale, + const UnicodeString* matchID, + UErrorCode& status) const +{ + result.removeAllElements(); + result.setDeleter(userv_deleteStringPair); + if (U_SUCCESS(status)) { + ICUService* ncthis = (ICUService*)this; // cast away semantic const + Mutex mutex(&lock); + + if (dnCache != NULL && dnCache->locale != locale) { + delete dnCache; + ncthis->dnCache = NULL; + } + + if (dnCache == NULL) { + const Hashtable* m = getVisibleIDMap(status); + if (U_FAILURE(status)) { + return result; + } + ncthis->dnCache = new DNCache(locale); + if (dnCache == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + return result; + } + + int32_t pos = UHASH_FIRST; + const UHashElement* entry = NULL; + while ((entry = m->nextElement(pos)) != NULL) { + const UnicodeString* id = (const UnicodeString*)entry->key.pointer; + ICUServiceFactory* f = (ICUServiceFactory*)entry->value.pointer; + UnicodeString dname; + f->getDisplayName(*id, locale, dname); + if (dname.isBogus()) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + dnCache->cache.put(dname, (void*)id, status); // share pointer with visibleIDMap + if (U_SUCCESS(status)) { + continue; + } + } + delete dnCache; + ncthis->dnCache = NULL; + return result; + } + } + } + + ICUServiceKey* matchKey = createKey(matchID, status); + /* To ensure that all elements in the hashtable are iterated, set pos to -1. + * nextElement(pos) will skip the position at pos and begin the iteration + * at the next position, which in this case will be 0. + */ + int32_t pos = UHASH_FIRST; + const UHashElement *entry = NULL; + while ((entry = dnCache->cache.nextElement(pos)) != NULL) { + const UnicodeString* id = (const UnicodeString*)entry->value.pointer; + if (matchKey != NULL && !matchKey->isFallbackOf(*id)) { + continue; + } + const UnicodeString* dn = (const UnicodeString*)entry->key.pointer; + StringPair* sp = StringPair::create(*id, *dn, status); + result.addElement(sp, status); + if (U_FAILURE(status)) { + result.removeAllElements(); + break; + } + } + delete matchKey; + + return result; +} + +URegistryKey +ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UErrorCode& status) +{ + return registerInstance(objToAdopt, id, TRUE, status); +} + +URegistryKey +ICUService::registerInstance(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) +{ + ICUServiceKey* key = createKey(&id, status); + if (key != NULL) { + UnicodeString canonicalID; + key->canonicalID(canonicalID); + delete key; + + ICUServiceFactory* f = createSimpleFactory(objToAdopt, canonicalID, visible, status); + if (f != NULL) { + return registerFactory(f, status); + } + } + delete objToAdopt; + return NULL; +} + +ICUServiceFactory* +ICUService::createSimpleFactory(UObject* objToAdopt, const UnicodeString& id, UBool visible, UErrorCode& status) +{ + if (U_SUCCESS(status)) { + if ((objToAdopt != NULL) && (!id.isBogus())) { + return new SimpleFactory(objToAdopt, id, visible); + } + status = U_ILLEGAL_ARGUMENT_ERROR; + } + return NULL; +} + +URegistryKey +ICUService::registerFactory(ICUServiceFactory* factoryToAdopt, UErrorCode& status) +{ + if (U_SUCCESS(status) && factoryToAdopt != NULL) { + Mutex mutex(&lock); + + if (factories == NULL) { + factories = new UVector(deleteUObject, NULL, status); + if (U_FAILURE(status)) { + delete factories; + return NULL; + } + } + factories->insertElementAt(factoryToAdopt, 0, status); + if (U_SUCCESS(status)) { + clearCaches(); + } else { + delete factoryToAdopt; + factoryToAdopt = NULL; + } + } + + if (factoryToAdopt != NULL) { + notifyChanged(); + } + + return (URegistryKey)factoryToAdopt; +} + +UBool +ICUService::unregister(URegistryKey rkey, UErrorCode& status) +{ + ICUServiceFactory *factory = (ICUServiceFactory*)rkey; + UBool result = FALSE; + if (factory != NULL && factories != NULL) { + Mutex mutex(&lock); + + if (factories->removeElement(factory)) { + clearCaches(); + result = TRUE; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + delete factory; + } + } + if (result) { + notifyChanged(); + } + return result; +} + +void +ICUService::reset() +{ + { + Mutex mutex(&lock); + reInitializeFactories(); + clearCaches(); + } + notifyChanged(); +} + +void +ICUService::reInitializeFactories() +{ + if (factories != NULL) { + factories->removeAllElements(); + } +} + +UBool +ICUService::isDefault() const +{ + return countFactories() == 0; +} + +ICUServiceKey* +ICUService::createKey(const UnicodeString* id, UErrorCode& status) const +{ + return (U_FAILURE(status) || id == NULL) ? NULL : new ICUServiceKey(*id); +} + +void +ICUService::clearCaches() +{ + // callers synchronize before use + ++timestamp; + delete dnCache; + dnCache = NULL; + delete idCache; + idCache = NULL; + delete serviceCache; serviceCache = NULL; +} + +void +ICUService::clearServiceCache() +{ + // callers synchronize before use + delete serviceCache; serviceCache = NULL; +} + +UBool +ICUService::acceptsListener(const EventListener& l) const +{ + return dynamic_cast<const ServiceListener*>(&l) != NULL; +} + +void +ICUService::notifyListener(EventListener& l) const +{ + ((ServiceListener&)l).serviceChanged(*this); +} + +UnicodeString& +ICUService::getName(UnicodeString& result) const +{ + return result.append(name); +} + +int32_t +ICUService::countFactories() const +{ + return factories == NULL ? 0 : factories->size(); +} + +int32_t +ICUService::getTimestamp() const +{ + return timestamp; +} + +U_NAMESPACE_END + +/* UCONFIG_NO_SERVICE */ +#endif |