6b90326fe2
X-SVN-Rev: 12368
1186 lines
28 KiB
C++
1186 lines
28 KiB
C++
/**
|
|
*******************************************************************************
|
|
* Copyright (C) 2001-2003, International Business Machines Corporation. *
|
|
* All Rights Reserved. *
|
|
*******************************************************************************
|
|
*/
|
|
|
|
#include "unicode/utypes.h"
|
|
|
|
#if !UCONFIG_NO_SERVICE
|
|
|
|
#include "icuserv.h"
|
|
#include "umutex.h"
|
|
|
|
#undef SERVICE_REFCOUNT
|
|
|
|
// in case we use the refcount stuff
|
|
|
|
U_NAMESPACE_BEGIN
|
|
|
|
// A reference counted wrapper for an object. Creation and access is
|
|
// through RefHandle.
|
|
|
|
#ifdef SERVICE_REFCOUNT
|
|
|
|
#include "unicode/strenum.h"
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
class RefCounted {
|
|
private:
|
|
int32_t _count;
|
|
UObject* _obj;
|
|
|
|
friend class RefHandle;
|
|
|
|
RefCounted(UObject* objectToAdopt) : _count(1), _obj(objectToAdopt) {}
|
|
~RefCounted() { delete _obj; }
|
|
void ref() { umtx_atomic_inc(&_count); }
|
|
void unref() { if (umtx_atomic_dec(&_count) == 0) { delete this; }}
|
|
};
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
// Reference counted handle for an object
|
|
class RefHandle {
|
|
RefCounted* _ref;
|
|
|
|
public:
|
|
RefHandle() : _ref(NULL) {}
|
|
RefHandle(UObject* obj) : _ref(new RefCounted(obj)) {}
|
|
RefHandle(const RefHandle& rhs) : _ref(NULL) { operator=(rhs); }
|
|
~RefHandle() { if (_ref) _ref->unref(); }
|
|
RefHandle& operator=(const RefHandle& rhs) {
|
|
if (rhs._ref) rhs._ref->ref();
|
|
if (_ref) _ref->unref();
|
|
_ref = rhs._ref;
|
|
}
|
|
const UObject* get() const { return _ref ? _ref->_obj : NULL; }
|
|
};
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
// Generic enumeration class with fail-fast behavior.
|
|
|
|
class MapEnumeration : public UObject, public StringEnumeration
|
|
{
|
|
private:
|
|
UChar* _buffer;
|
|
int _buflen;
|
|
|
|
protected:
|
|
const ICUService* _service;
|
|
uint32_t _timestamp;
|
|
RefHandle _table;
|
|
ICUServiceKey* _filter;
|
|
int32_t _position;
|
|
int32_t _count;
|
|
|
|
protected:
|
|
MapEnumeration(ICUService* service, int32_t timestamp, RefHandle& table, ICUServiceKey* filter = NULL)
|
|
: _buffer(NULL)
|
|
, _buflen(0)
|
|
, _service(service)
|
|
, _timestamp(timestamp)
|
|
, _table(table)
|
|
, _filter(filter)
|
|
, _position(0)
|
|
, _count(((const Hashtable*)table.get())->count())
|
|
{
|
|
}
|
|
|
|
virtual ~MapEnumeration()
|
|
{
|
|
delete _filter;
|
|
}
|
|
|
|
int32_t count(UErrorCode& status) const
|
|
{
|
|
return U_SUCCESS(status) ? _count : 0;
|
|
}
|
|
|
|
const char* next(UErrorCode& status) {
|
|
const UnicodeString* us = snext(status);
|
|
if (us) {
|
|
int newlen;
|
|
for (newlen = us->extract((char*)_buffer, _buflen / sizeof(char), NULL, status);
|
|
status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR;)
|
|
{
|
|
resizeBuffer((newlen + 1) * sizeof(char));
|
|
status = U_ZERO_ERROR;
|
|
}
|
|
|
|
if (U_SUCCESS(status)) {
|
|
((char*)_buffer)[newlen] = 0;
|
|
return (const char*)_buffer;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const UChar* unext(UErrorCode& status) {
|
|
const UnicodeString* us = snext(status);
|
|
if (us) {
|
|
int newlen;
|
|
for (newlen = us->extract((UChar*)_buffer, _buflen / sizeof(UChar), NULL, status);
|
|
status == U_STRING_NOT_TERMINATED_WARNING || status == U_BUFFER_OVERFLOW_ERROR;)
|
|
{
|
|
resizeBuffer((newlen + 1) * sizeof(UChar));
|
|
status = U_ZERO_ERROR;
|
|
}
|
|
|
|
if (U_SUCCESS(status)) {
|
|
((UChar*)_buffer)[newlen] = 0;
|
|
return (const UChar*)_buffer;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const UnicodeString* snext(UErrorCode& status)
|
|
{
|
|
if (U_SUCCESS(status)) {
|
|
if (_timestamp != _service->_timestamp) {
|
|
status = U_ENUM_OUT_OF_SYNCH_ERROR;
|
|
} else {
|
|
return internalNext((Hashtable*)_table.get());
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void reset(UErrorCode& status)
|
|
{
|
|
if (U_SUCCESS(status)) {
|
|
service->reset(this);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
virtual const UnicodeString* internalNext(Hashtable* table) = 0;
|
|
|
|
private:
|
|
void reset(RefHandle& table, int32_t timestamp)
|
|
{
|
|
_table = table;
|
|
_timestamp = timestamp;
|
|
_position = 0;
|
|
_count = ((const Hashtable*)table.get())->count();
|
|
}
|
|
|
|
friend class ICUService;
|
|
};
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
// An enumeration over the visible ids in a service. The ids
|
|
// are in the hashtable, which is refcounted, so it will not
|
|
// disappear as long as the enumeration exists even if the
|
|
// service itself unrefs it. For "fail-fast" behavior the
|
|
// enumeration checks the timestamp of the service, but this
|
|
// is not a guarantee that the result the client receives will
|
|
// still be valid once the function returns.
|
|
|
|
class IDEnumeration : public MapEnumeration {
|
|
public:
|
|
IDEnumeration(ICUService* service, int32_t timestamp, RefHandle& table, ICUServiceKey* filter = NULL)
|
|
: MapEnumeration(service, timestamp, table, filter)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
const UnicodeString* internalNext(Hashtable* table) {
|
|
while (TRUE) {
|
|
const UnicodeString* elem = (const UnicodeString*)(table->nextElement(_position).key.pointer);
|
|
if (elem == NULL ||
|
|
_filter == NULL ||
|
|
_filter->isFallbackOf(*elem)) {
|
|
return elem;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
};
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
class DisplayEnumeration : public MapEnumeration {
|
|
private:
|
|
Locale _locale;
|
|
UnicodeString _cache;
|
|
|
|
public:
|
|
DisplayEnumeration(ICUService* service, int32_t timestamp, RefHandle& table, Locale& locale, ICUServiceKey* filter = NULL)
|
|
: MapEnumeration(service, timestamp, table, filter), _locale(locale)
|
|
{
|
|
}
|
|
|
|
protected:
|
|
const UnicodeString* internalNext(Hashtable* table) {
|
|
while (TRUE) {
|
|
UHashElement* elem = table->nextElement(_position);
|
|
if (elem == NULL) {
|
|
return NULL;
|
|
}
|
|
const UnicodeString* id = (const UnicodeString*)elem->key.pointer;
|
|
const ICUServiceFactory* factory = (const ICUServiceFactory*)elem->value.pointer;
|
|
if (_filter == NULL || _filter->isFallbackOf(*id)) {
|
|
factory->getDisplayName(*id, cache, locale);
|
|
return &cache;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
};
|
|
|
|
/* SERVICE_REFCOUNT */
|
|
#endif
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
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(" id: ");
|
|
result.append(_id);
|
|
return result;
|
|
}
|
|
|
|
UnicodeString&
|
|
ICUServiceKey::debugClass(UnicodeString& result) const
|
|
{
|
|
return result.append("ICUServiceKey");
|
|
}
|
|
#endif
|
|
|
|
const char ICUServiceKey::fgClassID = '\0';
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
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(" id: ");
|
|
toAppendTo.append(_id);
|
|
toAppendTo.append(", visible: ");
|
|
toAppendTo.append(_visible ? "T" : "F");
|
|
return toAppendTo;
|
|
}
|
|
|
|
UnicodeString&
|
|
SimpleFactory::debugClass(UnicodeString& toAppendTo) const
|
|
{
|
|
return toAppendTo.append("SimpleFactory");
|
|
}
|
|
#endif
|
|
|
|
const char SimpleFactory::fgClassID = '\0';
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
const char ServiceListener::fgClassID = '\0';
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
// 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_CAPI void U_EXPORT2
|
|
cacheDeleter(void* obj) {
|
|
U_NAMESPACE_USE
|
|
((CacheEntry*)obj)->unref();
|
|
}
|
|
|
|
/**
|
|
* Deleter for UObjects
|
|
*/
|
|
U_CAPI void U_EXPORT2
|
|
deleteUObject(void *obj) {
|
|
U_NAMESPACE_USE
|
|
delete (UObject*) obj;
|
|
}
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
class DNCache : public UMemory {
|
|
public:
|
|
Hashtable cache;
|
|
const Locale locale;
|
|
|
|
DNCache(const Locale& _locale)
|
|
: cache(FALSE), locale(_locale)
|
|
{
|
|
// cache.setKeyDeleter(uhash_deleteUnicodeString);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
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_CAPI void U_EXPORT2
|
|
deleteStringPair(void *obj) {
|
|
U_NAMESPACE_USE
|
|
delete (StringPair*) obj;
|
|
}
|
|
|
|
/*
|
|
******************************************************************
|
|
*/
|
|
|
|
ICUService::ICUService()
|
|
: name()
|
|
, lock(0)
|
|
, timestamp(0)
|
|
, factories(NULL)
|
|
, serviceCache(NULL)
|
|
, idCache(NULL)
|
|
, dnCache(NULL)
|
|
{
|
|
umtx_init(&lock);
|
|
}
|
|
|
|
ICUService::ICUService(const UnicodeString& newName)
|
|
: name(newName)
|
|
, lock(0)
|
|
, timestamp(0)
|
|
, factories(NULL)
|
|
, serviceCache(NULL)
|
|
, idCache(NULL)
|
|
, dnCache(NULL) {
|
|
umtx_init(&lock);
|
|
}
|
|
|
|
ICUService::~ICUService()
|
|
{
|
|
{
|
|
Mutex mutex(&lock);
|
|
clearCaches();
|
|
delete factories;
|
|
factories = NULL;
|
|
}
|
|
umtx_destroy(&lock);
|
|
}
|
|
|
|
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(UMTX *mutex, UBool reentering)
|
|
: fMutex(mutex)
|
|
, fActive(!reentering)
|
|
{
|
|
if (fActive) umtx_lock(fMutex);
|
|
}
|
|
inline ~XMutex() {
|
|
if (fActive) umtx_unlock(fMutex);
|
|
}
|
|
|
|
private:
|
|
UMTX *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(&ncthis->lock, factory != NULL);
|
|
|
|
if (serviceCache == NULL) {
|
|
ncthis->serviceCache = new Hashtable(FALSE, status);
|
|
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 n = 0;
|
|
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(uhash_deleteUnicodeString, 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)) {
|
|
delete result;
|
|
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)) {
|
|
delete result;
|
|
return NULL;
|
|
}
|
|
|
|
result->ref();
|
|
cacheDescriptorList._obj->removeElementAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (actualReturn != NULL) {
|
|
// strip null prefix
|
|
if (result->actualDescriptor.indexOf("/") == 0) {
|
|
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;
|
|
}
|
|
|
|
ICUService * ncthis = (ICUService*)this; // cast away semantic const
|
|
{
|
|
Mutex mutex(&ncthis->lock);
|
|
const Hashtable* map = getVisibleIDMap(status);
|
|
if (map != NULL) {
|
|
ICUServiceKey* fallbackKey = createKey(matchID, status);
|
|
|
|
for (int32_t pos = 0;;) {
|
|
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();
|
|
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
|
|
{
|
|
{
|
|
ICUService* ncthis = (ICUService*)this; // cast away semantic const
|
|
UErrorCode status = U_ZERO_ERROR;
|
|
Mutex mutex(&ncthis->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;
|
|
}
|
|
}
|
|
}
|
|
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();
|
|
if (U_SUCCESS(status)) {
|
|
ICUService* ncthis = (ICUService*)this; // cast away semantic const
|
|
Mutex mutex(&ncthis->lock);
|
|
|
|
if (dnCache != NULL && dnCache->locale != locale) {
|
|
delete dnCache;
|
|
ncthis->dnCache = NULL;
|
|
}
|
|
|
|
if (dnCache == NULL) {
|
|
const Hashtable* m = getVisibleIDMap(status);
|
|
if (m != NULL) {
|
|
ncthis->dnCache = new DNCache(locale);
|
|
if (dnCache == NULL) {
|
|
status = U_MEMORY_ALLOCATION_ERROR;
|
|
return result;
|
|
}
|
|
|
|
int32_t pos = 0;
|
|
const UHashElement* entry = NULL;
|
|
while (entry = m->nextElement(pos)) {
|
|
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);
|
|
int32_t pos = 0;
|
|
const UHashElement *entry = NULL;
|
|
while (entry = dnCache->cache.nextElement(pos)) {
|
|
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 l.getDynamicClassID() == ServiceListener::getStaticClassID();
|
|
}
|
|
|
|
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
|