#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
#include "unicode/ucurr.h"
#include "unicode/locid.h"
#include "unicode/resbund.h"
#include "unicode/ustring.h"
#include "cmemory.h"
#include "cstring.h"
#include "uassert.h"
#include "iculserv.h"
#include "ucln_in.h"
static const int32_t LAST_RESORT_DATA[] = { 2, 0 };
static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
1000000, 10000000, 100000000, 1000000000 };
static const int32_t MAX_POW10 = (sizeof(POW10)/sizeof(POW10[0])) - 1;
#define ISO_COUNTRY_CODE_LENGTH 3
static const char CURRENCY_META[] = "CurrencyMeta";
static const char CURRENCY_MAP[] = "CurrencyMap";
static const char DEFAULT_META[] = "DEFAULT";
static const char VAR_PRE_EURO[] = "PREEURO";
static const char VAR_EURO[] = "EURO";
static const char VAR_DELIM[] = "_";
static const char CURRENCIES[] = "Currencies";
static const UChar CHOICE_FORMAT_MARK = 0x003D;
static inline char*
myUCharsToChars(char* resultOfLen4, const UChar* currency) {
u_UCharsToChars(currency, resultOfLen4, ISO_COUNTRY_CODE_LENGTH);
resultOfLen4[ISO_COUNTRY_CODE_LENGTH] = 0;
return resultOfLen4;
}
static const int32_t*
_findMetaData(const UChar* currency) {
UErrorCode ec = U_ZERO_ERROR;
ResourceBundle currencyMeta =
ResourceBundle((char*)0, Locale(""), ec).get(CURRENCY_META, ec);
if (U_FAILURE(ec)) {
return LAST_RESORT_DATA;
}
char buf[ISO_COUNTRY_CODE_LENGTH+1];
ResourceBundle rb = currencyMeta.get(myUCharsToChars(buf, currency), ec);
if (U_FAILURE(ec)) {
rb = currencyMeta.get(DEFAULT_META, ec);
if (U_FAILURE(ec)) {
return LAST_RESORT_DATA;
}
}
int32_t len;
const int32_t *data = rb.getIntVector(len, ec);
if (U_FAILURE(ec) || len < 2) {
return LAST_RESORT_DATA;
}
return data;
}
struct CReg;
static UMTX gCRegLock = 0;
static CReg* gCRegHead = 0;
struct CReg : public UMemory {
CReg *next;
UChar iso[ISO_COUNTRY_CODE_LENGTH+1];
char id[ULOC_FULLNAME_CAPACITY];
CReg(const UChar* _iso, const char* _id)
: next(0)
{
int32_t len = uprv_strlen(_id);
if (len > (int32_t)(sizeof(id)-1)) {
len = (sizeof(id)-1);
}
uprv_strncpy(id, _id, len);
id[len] = 0;
uprv_memcpy(iso, _iso, ISO_COUNTRY_CODE_LENGTH * sizeof(const UChar));
iso[ISO_COUNTRY_CODE_LENGTH] = 0;
}
static UCurrRegistryKey reg(const UChar* _iso, const char* _id, UErrorCode* status)
{
if (status && U_SUCCESS(*status) && _iso && _id) {
CReg* n = new CReg(_iso, _id);
if (n) {
umtx_init(&gCRegLock);
Mutex mutex(&gCRegLock);
if (!gCRegHead) {
ucln_i18n_registerCleanup();
}
n->next = gCRegHead;
gCRegHead = n;
return n;
}
*status = U_MEMORY_ALLOCATION_ERROR;
}
return 0;
}
static UBool unreg(UCurrRegistryKey key) {
umtx_init(&gCRegLock);
Mutex mutex(&gCRegLock);
if (gCRegHead == key) {
gCRegHead = gCRegHead->next;
delete (CReg*)key;
return TRUE;
}
CReg* p = gCRegHead;
while (p) {
if (p->next == key) {
p->next = ((CReg*)key)->next;
delete (CReg*)key;
return TRUE;
}
p = p->next;
}
return FALSE;
}
static const UChar* get(const char* id) {
umtx_init(&gCRegLock);
Mutex mutex(&gCRegLock);
CReg* p = gCRegHead;
while (p) {
if (uprv_strcmp(id, p->id) == 0) {
return p->iso;
}
p = p->next;
}
return NULL;
}
static void cleanup(void) {
while (gCRegHead) {
CReg* n = gCRegHead;
gCRegHead = gCRegHead->next;
delete n;
}
umtx_destroy(&gCRegLock);
}
};
static void
idForLocale(const char* locale, char* buffer, int capacity, UErrorCode* ec)
{
char variant[ULOC_FULLNAME_CAPACITY];
uloc_getCountry(locale, buffer, capacity, ec);
uloc_getVariant(locale, variant, sizeof(variant), ec);
if (0 == uprv_strcmp(variant, VAR_PRE_EURO) ||
0 == uprv_strcmp(variant, VAR_EURO))
{
uprv_strcat(buffer, VAR_DELIM);
uprv_strcat(buffer, variant);
}
}
U_CAPI UCurrRegistryKey U_EXPORT2
ucurr_register(const UChar* isoCode, const char* locale, UErrorCode *status)
{
if (status && U_SUCCESS(*status)) {
char id[ULOC_FULLNAME_CAPACITY];
idForLocale(locale, id, sizeof(id), status);
return CReg::reg(isoCode, id, status);
}
return NULL;
}
U_CAPI UBool U_EXPORT2
ucurr_unregister(UCurrRegistryKey key, UErrorCode* status)
{
if (status && U_SUCCESS(*status)) {
return CReg::unreg(key);
}
return FALSE;
}
U_CAPI const UChar* U_EXPORT2
ucurr_forLocale(const char* locale, UErrorCode* ec) {
if (ec != NULL && U_SUCCESS(*ec)) {
char id[ULOC_FULLNAME_CAPACITY];
idForLocale(locale, id, sizeof(id), ec);
if (U_FAILURE(*ec)) {
return NULL;
}
const UChar* result = CReg::get(id);
if (result) {
return result;
}
UResourceBundle* rb = ures_open(NULL, "", ec);
UResourceBundle* cm = ures_getByKey(rb, CURRENCY_MAP, NULL, ec);
int32_t len;
const UChar* s = ures_getStringByKey(cm, id, &len, ec);
ures_close(cm);
ures_close(rb);
if (U_SUCCESS(*ec)) {
return s;
}
}
return NULL;
}
static UBool fallback(char *loc) {
if (!*loc) {
return FALSE;
}
UErrorCode status = U_ZERO_ERROR;
uloc_getParent(loc, loc, uprv_strlen(loc), &status);
return TRUE;
}
U_CAPI const UChar* U_EXPORT2
ucurr_getName(const UChar* currency,
const char* locale,
UCurrNameStyle nameStyle,
UBool* isChoiceFormat, int32_t* len, UErrorCode* ec) {
if (ec == NULL || U_FAILURE(*ec)) {
return 0;
}
int32_t choice = (int32_t) nameStyle;
if (choice < 0 || choice > 1) {
*ec = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
UErrorCode ec2 = U_ZERO_ERROR;
char loc[ULOC_FULLNAME_CAPACITY];
uloc_getName(locale, loc, sizeof(loc), &ec2);
if (U_FAILURE(ec2) || ec2 == U_STRING_NOT_TERMINATED_WARNING) {
*ec = U_ILLEGAL_ARGUMENT_ERROR;
return 0;
}
char buf[ISO_COUNTRY_CODE_LENGTH+1];
myUCharsToChars(buf, currency);
const UChar* s = NULL;
for (;;) {
ec2 = U_ZERO_ERROR;
UResourceBundle* rb = ures_open(NULL, loc, &ec2);
UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
UResourceBundle* names = ures_getByKey(curr, buf, NULL, &ec2);
s = ures_getStringByIndex(names, choice, len, &ec2);
ures_close(names);
ures_close(curr);
ures_close(rb);
if (U_SUCCESS(ec2) || !fallback(loc)) {
break;
}
}
*isChoiceFormat = FALSE;
if (U_SUCCESS(ec2)) {
U_ASSERT(s != NULL);
int32_t i=0;
while (i < *len && s[i] == CHOICE_FORMAT_MARK && i < 2) {
++i;
}
*isChoiceFormat = (i == 1);
if (i != 0) ++s; return s;
}
*len = u_strlen(currency); return currency;
}
U_CAPI int32_t U_EXPORT2
ucurr_getDefaultFractionDigits(const UChar* currency) {
return (_findMetaData(currency))[0];
}
U_CAPI double U_EXPORT2
ucurr_getRoundingIncrement(const UChar* currency) {
const int32_t *data = _findMetaData(currency);
if (data[1] < 2 || data[0] < 0 || data[0] > MAX_POW10) {
return 0.0;
}
return double(data[1]) / POW10[data[0]];
}
U_CFUNC UBool currency_cleanup(void) {
CReg::cleanup();
return TRUE;
}
#endif