summaryrefslogtreecommitdiff
path: root/Build/source/libs/icu/icu-50.1/i18n/ucurr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/icu/icu-50.1/i18n/ucurr.cpp')
-rw-r--r--Build/source/libs/icu/icu-50.1/i18n/ucurr.cpp2436
1 files changed, 2436 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-50.1/i18n/ucurr.cpp b/Build/source/libs/icu/icu-50.1/i18n/ucurr.cpp
new file mode 100644
index 00000000000..ed6c53f99fc
--- /dev/null
+++ b/Build/source/libs/icu/icu-50.1/i18n/ucurr.cpp
@@ -0,0 +1,2436 @@
+/*
+**********************************************************************
+* Copyright (c) 2002-2012, International Business Machines
+* Corporation and others. All Rights Reserved.
+**********************************************************************
+*/
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/ucurr.h"
+#include "unicode/locid.h"
+#include "unicode/ures.h"
+#include "unicode/ustring.h"
+#include "unicode/choicfmt.h"
+#include "unicode/parsepos.h"
+#include "ustr_imp.h"
+#include "cmemory.h"
+#include "cstring.h"
+#include "uassert.h"
+#include "umutex.h"
+#include "ucln_in.h"
+#include "uenumimp.h"
+#include "uhash.h"
+#include "uresimp.h"
+#include "ulist.h"
+#include "ureslocs.h"
+
+// #define UCURR_DEBUG 1
+#ifdef UCURR_DEBUG
+#include "stdio.h"
+#endif
+
+typedef struct IsoCodeEntry {
+ const UChar *isoCode; /* const because it's a reference to a resource bundle string. */
+ UDate from;
+ UDate to;
+} IsoCodeEntry;
+
+//------------------------------------------------------------
+// Constants
+
+// Default currency meta data of last resort. We try to use the
+// defaults encoded in the meta data resource bundle. If there is a
+// configuration/build error and these are not available, we use these
+// hard-coded defaults (which should be identical).
+static const int32_t LAST_RESORT_DATA[] = { 2, 0 };
+
+// POW10[i] = 10^i, i=0..MAX_POW10
+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_CURRENCY_CODE_LENGTH 3
+
+//------------------------------------------------------------
+// Resource tags
+//
+
+static const char CURRENCY_DATA[] = "supplementalData";
+// Tag for meta-data, in root.
+static const char CURRENCY_META[] = "CurrencyMeta";
+
+// Tag for map from countries to currencies, in root.
+static const char CURRENCY_MAP[] = "CurrencyMap";
+
+// Tag for default meta-data, in CURRENCY_META
+static const char DEFAULT_META[] = "DEFAULT";
+
+// Variant for legacy pre-euro mapping in CurrencyMap
+static const char VAR_PRE_EURO[] = "PREEURO";
+
+// Variant for legacy euro mapping in CurrencyMap
+static const char VAR_EURO[] = "EURO";
+
+// Variant delimiter
+static const char VAR_DELIM = '_';
+static const char VAR_DELIM_STR[] = "_";
+
+// Variant for legacy euro mapping in CurrencyMap
+//static const char VAR_DELIM_EURO[] = "_EURO";
+
+#define VARIANT_IS_EMPTY 0
+#define VARIANT_IS_EURO 0x1
+#define VARIANT_IS_PREEURO 0x2
+
+// Tag for localized display names (symbols) of currencies
+static const char CURRENCIES[] = "Currencies";
+static const char CURRENCYPLURALS[] = "CurrencyPlurals";
+
+// Marker character indicating that a display name is a ChoiceFormat
+// pattern. Strings that start with one mark are ChoiceFormat
+// patterns. Strings that start with 2 marks are static strings, and
+// the first mark is deleted.
+static const UChar CHOICE_FORMAT_MARK = 0x003D; // Equals sign
+
+static const UChar EUR_STR[] = {0x0045,0x0055,0x0052,0};
+
+// ISO codes mapping table
+static UHashtable* gIsoCodes = NULL;
+static UBool gIsoCodesInitialized = FALSE;
+
+static UMutex gIsoCodesLock = U_MUTEX_INITIALIZER;
+
+//------------------------------------------------------------
+// Code
+
+/**
+ * Cleanup callback func
+ */
+static UBool U_CALLCONV
+isoCodes_cleanup(void)
+{
+ if (gIsoCodes != NULL) {
+ uhash_close(gIsoCodes);
+ gIsoCodes = NULL;
+ }
+ gIsoCodesInitialized = FALSE;
+
+ return TRUE;
+}
+
+/**
+ * Deleter for OlsonToMetaMappingEntry
+ */
+static void U_CALLCONV
+deleteIsoCodeEntry(void *obj) {
+ IsoCodeEntry *entry = (IsoCodeEntry*)obj;
+ uprv_free(entry);
+}
+
+/**
+ * Unfortunately, we have to convert the UChar* currency code to char*
+ * to use it as a resource key.
+ */
+static inline char*
+myUCharsToChars(char* resultOfLen4, const UChar* currency) {
+ u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH);
+ resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0;
+ return resultOfLen4;
+}
+
+/**
+ * Internal function to look up currency data. Result is an array of
+ * two integers. The first is the fraction digits. The second is the
+ * rounding increment, or 0 if none. The rounding increment is in
+ * units of 10^(-fraction_digits).
+ */
+static const int32_t*
+_findMetaData(const UChar* currency, UErrorCode& ec) {
+
+ if (currency == 0 || *currency == 0) {
+ if (U_SUCCESS(ec)) {
+ ec = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+ return LAST_RESORT_DATA;
+ }
+
+ // Get CurrencyMeta resource out of root locale file. [This may
+ // move out of the root locale file later; if it does, update this
+ // code.]
+ UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec);
+ UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec);
+
+ if (U_FAILURE(ec)) {
+ ures_close(currencyMeta);
+ // Config/build error; return hard-coded defaults
+ return LAST_RESORT_DATA;
+ }
+
+ // Look up our currency, or if that's not available, then DEFAULT
+ char buf[ISO_CURRENCY_CODE_LENGTH+1];
+ UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure
+ UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), NULL, &ec2);
+ if (U_FAILURE(ec2)) {
+ ures_close(rb);
+ rb = ures_getByKey(currencyMeta,DEFAULT_META, NULL, &ec);
+ if (U_FAILURE(ec)) {
+ ures_close(currencyMeta);
+ ures_close(rb);
+ // Config/build error; return hard-coded defaults
+ return LAST_RESORT_DATA;
+ }
+ }
+
+ int32_t len;
+ const int32_t *data = ures_getIntVector(rb, &len, &ec);
+ if (U_FAILURE(ec) || len != 2) {
+ // Config/build error; return hard-coded defaults
+ if (U_SUCCESS(ec)) {
+ ec = U_INVALID_FORMAT_ERROR;
+ }
+ ures_close(currencyMeta);
+ ures_close(rb);
+ return LAST_RESORT_DATA;
+ }
+
+ ures_close(currencyMeta);
+ ures_close(rb);
+ return data;
+}
+
+// -------------------------------------
+
+/**
+ * @see VARIANT_IS_EURO
+ * @see VARIANT_IS_PREEURO
+ */
+static uint32_t
+idForLocale(const char* locale, char* countryAndVariant, int capacity, UErrorCode* ec)
+{
+ uint32_t variantType = 0;
+ // !!! this is internal only, assumes buffer is not null and capacity is sufficient
+ // Extract the country name and variant name. We only
+ // recognize two variant names, EURO and PREEURO.
+ char variant[ULOC_FULLNAME_CAPACITY];
+ uloc_getCountry(locale, countryAndVariant, capacity, ec);
+ uloc_getVariant(locale, variant, sizeof(variant), ec);
+ if (variant[0] != 0) {
+ variantType = (uint32_t)(0 == uprv_strcmp(variant, VAR_EURO))
+ | ((uint32_t)(0 == uprv_strcmp(variant, VAR_PRE_EURO)) << 1);
+ if (variantType)
+ {
+ uprv_strcat(countryAndVariant, VAR_DELIM_STR);
+ uprv_strcat(countryAndVariant, variant);
+ }
+ }
+ return variantType;
+}
+
+// ------------------------------------------
+//
+// Registration
+//
+//-------------------------------------------
+
+// don't use ICUService since we don't need fallback
+
+U_CDECL_BEGIN
+static UBool U_CALLCONV currency_cleanup(void);
+U_CDECL_END
+
+#if !UCONFIG_NO_SERVICE
+struct CReg;
+
+static UMutex gCRegLock = U_MUTEX_INITIALIZER;
+static CReg* gCRegHead = 0;
+
+struct CReg : public icu::UMemory {
+ CReg *next;
+ UChar iso[ISO_CURRENCY_CODE_LENGTH+1];
+ char id[ULOC_FULLNAME_CAPACITY];
+
+ CReg(const UChar* _iso, const char* _id)
+ : next(0)
+ {
+ int32_t len = (int32_t)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_CURRENCY_CODE_LENGTH * sizeof(const UChar));
+ iso[ISO_CURRENCY_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_lock(&gCRegLock);
+ if (!gCRegHead) {
+ /* register for the first time */
+ ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
+ }
+ n->next = gCRegHead;
+ gCRegHead = n;
+ umtx_unlock(&gCRegLock);
+ return n;
+ }
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ }
+ return 0;
+ }
+
+ static UBool unreg(UCurrRegistryKey key) {
+ UBool found = FALSE;
+ umtx_lock(&gCRegLock);
+
+ CReg** p = &gCRegHead;
+ while (*p) {
+ if (*p == key) {
+ *p = ((CReg*)key)->next;
+ delete (CReg*)key;
+ found = TRUE;
+ break;
+ }
+ p = &((*p)->next);
+ }
+
+ umtx_unlock(&gCRegLock);
+ return found;
+ }
+
+ static const UChar* get(const char* id) {
+ const UChar* result = NULL;
+ umtx_lock(&gCRegLock);
+ CReg* p = gCRegHead;
+
+ /* register cleanup of the mutex */
+ ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
+ while (p) {
+ if (uprv_strcmp(id, p->id) == 0) {
+ result = p->iso;
+ break;
+ }
+ p = p->next;
+ }
+ umtx_unlock(&gCRegLock);
+ return result;
+ }
+
+ /* This doesn't need to be thread safe. It's for u_cleanup only. */
+ static void cleanup(void) {
+ while (gCRegHead) {
+ CReg* n = gCRegHead;
+ gCRegHead = gCRegHead->next;
+ delete n;
+ }
+ }
+};
+
+// -------------------------------------
+
+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;
+}
+#endif /* UCONFIG_NO_SERVICE */
+
+// -------------------------------------
+
+/**
+ * Release all static memory held by currency.
+ */
+/*The declaration here is needed so currency_cleanup(void)
+ * can call this function.
+ */
+static UBool U_CALLCONV
+currency_cache_cleanup(void);
+
+U_CDECL_BEGIN
+static UBool U_CALLCONV currency_cleanup(void) {
+#if !UCONFIG_NO_SERVICE
+ CReg::cleanup();
+#endif
+ /*
+ * There might be some cached currency data or isoCodes data.
+ */
+ currency_cache_cleanup();
+ isoCodes_cleanup();
+
+ return TRUE;
+}
+U_CDECL_END
+
+// -------------------------------------
+
+U_CAPI int32_t U_EXPORT2
+ucurr_forLocale(const char* locale,
+ UChar* buff,
+ int32_t buffCapacity,
+ UErrorCode* ec)
+{
+ int32_t resLen = 0;
+ const UChar* s = NULL;
+ if (ec != NULL && U_SUCCESS(*ec)) {
+ if ((buff && buffCapacity) || !buffCapacity) {
+ UErrorCode localStatus = U_ZERO_ERROR;
+ char id[ULOC_FULLNAME_CAPACITY];
+ if ((resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus))) {
+ // there is a currency keyword. Try to see if it's valid
+ if(buffCapacity > resLen) {
+ /* Normalize the currency keyword value to upper case. */
+ T_CString_toUpperCase(id);
+ u_charsToUChars(id, buff, resLen);
+ }
+ } else {
+ // get country or country_variant in `id'
+ uint32_t variantType = idForLocale(locale, id, sizeof(id), ec);
+
+ if (U_FAILURE(*ec)) {
+ return 0;
+ }
+
+#if !UCONFIG_NO_SERVICE
+ const UChar* result = CReg::get(id);
+ if (result) {
+ if(buffCapacity > u_strlen(result)) {
+ u_strcpy(buff, result);
+ }
+ return u_strlen(result);
+ }
+#endif
+ // Remove variants, which is only needed for registration.
+ char *idDelim = strchr(id, VAR_DELIM);
+ if (idDelim) {
+ idDelim[0] = 0;
+ }
+
+ // Look up the CurrencyMap element in the root bundle.
+ UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
+ UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
+ UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
+ UResourceBundle *currencyReq = ures_getByIndex(countryArray, 0, NULL, &localStatus);
+ s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
+
+ /*
+ Get the second item when PREEURO is requested, and this is a known Euro country.
+ If the requested variant is PREEURO, and this isn't a Euro country, assume
+ that the country changed over to the Euro in the future. This is probably
+ an old version of ICU that hasn't been updated yet. The latest currency is
+ probably correct.
+ */
+ if (U_SUCCESS(localStatus)) {
+ if ((variantType & VARIANT_IS_PREEURO) && u_strcmp(s, EUR_STR) == 0) {
+ currencyReq = ures_getByIndex(countryArray, 1, currencyReq, &localStatus);
+ s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus);
+ }
+ else if ((variantType & VARIANT_IS_EURO)) {
+ s = EUR_STR;
+ }
+ }
+ ures_close(countryArray);
+ ures_close(currencyReq);
+
+ if ((U_FAILURE(localStatus)) && strchr(id, '_') != 0)
+ {
+ // We don't know about it. Check to see if we support the variant.
+ uloc_getParent(locale, id, sizeof(id), ec);
+ *ec = U_USING_FALLBACK_WARNING;
+ return ucurr_forLocale(id, buff, buffCapacity, ec);
+ }
+ else if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
+ // There is nothing to fallback to. Report the failure/warning if possible.
+ *ec = localStatus;
+ }
+ if (U_SUCCESS(*ec)) {
+ if(buffCapacity > resLen) {
+ u_strcpy(buff, s);
+ }
+ }
+ }
+ return u_terminateUChars(buff, buffCapacity, resLen, ec);
+ } else {
+ *ec = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+ }
+ return resLen;
+}
+
+// end registration
+
+/**
+ * Modify the given locale name by removing the rightmost _-delimited
+ * element. If there is none, empty the string ("" == root).
+ * NOTE: The string "root" is not recognized; do not use it.
+ * @return TRUE if the fallback happened; FALSE if locale is already
+ * root ("").
+ */
+static UBool fallback(char *loc) {
+ if (!*loc) {
+ return FALSE;
+ }
+ UErrorCode status = U_ZERO_ERROR;
+ uloc_getParent(loc, loc, (int32_t)uprv_strlen(loc), &status);
+ /*
+ char *i = uprv_strrchr(loc, '_');
+ if (i == NULL) {
+ i = loc;
+ }
+ *i = 0;
+ */
+ return TRUE;
+}
+
+
+U_CAPI const UChar* U_EXPORT2
+ucurr_getName(const UChar* currency,
+ const char* locale,
+ UCurrNameStyle nameStyle,
+ UBool* isChoiceFormat, // fillin
+ int32_t* len, // fillin
+ UErrorCode* ec) {
+
+ // Look up the Currencies resource for the given locale. The
+ // Currencies locale data looks like this:
+ //|en {
+ //| Currencies {
+ //| USD { "US$", "US Dollar" }
+ //| CHF { "Sw F", "Swiss Franc" }
+ //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
+ //| //...
+ //| }
+ //|}
+
+ if (U_FAILURE(*ec)) {
+ return 0;
+ }
+
+ int32_t choice = (int32_t) nameStyle;
+ if (choice < 0 || choice > 1) {
+ *ec = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+
+ // In the future, resource bundles may implement multi-level
+ // fallback. That is, if a currency is not found in the en_US
+ // Currencies data, then the en Currencies data will be searched.
+ // Currently, if a Currencies datum exists in en_US and en, the
+ // en_US entry hides that in en.
+
+ // We want multi-level fallback for this resource, so we implement
+ // it manually.
+
+ // Use a separate UErrorCode here that does not propagate out of
+ // this function.
+ 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_CURRENCY_CODE_LENGTH+1];
+ myUCharsToChars(buf, currency);
+
+ /* Normalize the keyword value to uppercase */
+ T_CString_toUpperCase(buf);
+
+ const UChar* s = NULL;
+ ec2 = U_ZERO_ERROR;
+ UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
+
+ rb = ures_getByKey(rb, CURRENCIES, rb, &ec2);
+
+ // Fetch resource with multi-level resource inheritance fallback
+ rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2);
+
+ s = ures_getStringByIndex(rb, choice, len, &ec2);
+ ures_close(rb);
+
+ // If we've succeeded we're done. Otherwise, try to fallback.
+ // If that fails (because we are already at root) then exit.
+ if (U_SUCCESS(ec2)) {
+ if (ec2 == U_USING_DEFAULT_WARNING
+ || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
+ *ec = ec2;
+ }
+ }
+
+ // Determine if this is a ChoiceFormat pattern. One leading mark
+ // indicates a ChoiceFormat. Two indicates a static string that
+ // starts with a mark. In either case, the first mark is ignored,
+ // if present. Marks in the rest of the string have no special
+ // meaning.
+ *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; // Skip over first mark
+ return s;
+ }
+
+ // If we fail to find a match, use the ISO 4217 code
+ *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
+ *ec = U_USING_DEFAULT_WARNING;
+ return currency;
+}
+
+U_CAPI const UChar* U_EXPORT2
+ucurr_getPluralName(const UChar* currency,
+ const char* locale,
+ UBool* isChoiceFormat,
+ const char* pluralCount,
+ int32_t* len, // fillin
+ UErrorCode* ec) {
+ // Look up the Currencies resource for the given locale. The
+ // Currencies locale data looks like this:
+ //|en {
+ //| CurrencyPlurals {
+ //| USD{
+ //| one{"US dollar"}
+ //| other{"US dollars"}
+ //| }
+ //| }
+ //|}
+
+ if (U_FAILURE(*ec)) {
+ return 0;
+ }
+
+ // Use a separate UErrorCode here that does not propagate out of
+ // this function.
+ 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_CURRENCY_CODE_LENGTH+1];
+ myUCharsToChars(buf, currency);
+
+ const UChar* s = NULL;
+ ec2 = U_ZERO_ERROR;
+ UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
+
+ rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2);
+
+ // Fetch resource with multi-level resource inheritance fallback
+ rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2);
+
+ s = ures_getStringByKeyWithFallback(rb, pluralCount, len, &ec2);
+ if (U_FAILURE(ec2)) {
+ // fall back to "other"
+ ec2 = U_ZERO_ERROR;
+ s = ures_getStringByKeyWithFallback(rb, "other", len, &ec2);
+ if (U_FAILURE(ec2)) {
+ ures_close(rb);
+ // fall back to long name in Currencies
+ return ucurr_getName(currency, locale, UCURR_LONG_NAME,
+ isChoiceFormat, len, ec);
+ }
+ }
+ ures_close(rb);
+
+ // If we've succeeded we're done. Otherwise, try to fallback.
+ // If that fails (because we are already at root) then exit.
+ if (U_SUCCESS(ec2)) {
+ if (ec2 == U_USING_DEFAULT_WARNING
+ || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
+ *ec = ec2;
+ }
+ U_ASSERT(s != NULL);
+ return s;
+ }
+
+ // If we fail to find a match, use the ISO 4217 code
+ *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
+ *ec = U_USING_DEFAULT_WARNING;
+ return currency;
+}
+
+
+//========================================================================
+// Following are structure and function for parsing currency names
+
+#define NEED_TO_BE_DELETED 0x1
+
+// TODO: a better way to define this?
+#define MAX_CURRENCY_NAME_LEN 100
+
+typedef struct {
+ const char* IsoCode; // key
+ UChar* currencyName; // value
+ int32_t currencyNameLen; // value length
+ int32_t flag; // flags
+} CurrencyNameStruct;
+
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)<(b)) ? (b) : (a))
+#endif
+
+
+// Comparason function used in quick sort.
+static int U_CALLCONV currencyNameComparator(const void* a, const void* b) {
+ const CurrencyNameStruct* currName_1 = (const CurrencyNameStruct*)a;
+ const CurrencyNameStruct* currName_2 = (const CurrencyNameStruct*)b;
+ for (int32_t i = 0;
+ i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen);
+ ++i) {
+ if (currName_1->currencyName[i] < currName_2->currencyName[i]) {
+ return -1;
+ }
+ if (currName_1->currencyName[i] > currName_2->currencyName[i]) {
+ return 1;
+ }
+ }
+ if (currName_1->currencyNameLen < currName_2->currencyNameLen) {
+ return -1;
+ } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) {
+ return 1;
+ }
+ return 0;
+}
+
+
+// Give a locale, return the maximum number of currency names associated with
+// this locale.
+// It gets currency names from resource bundles using fallback.
+// It is the maximum number because in the fallback chain, some of the
+// currency names are duplicated.
+// For example, given locale as "en_US", the currency names get from resource
+// bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
+// all currency names in "en_US" and "en".
+static void
+getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) {
+ U_NAMESPACE_USE
+ *total_currency_name_count = 0;
+ *total_currency_symbol_count = 0;
+ const UChar* s = NULL;
+ char locale[ULOC_FULLNAME_CAPACITY];
+ uprv_strcpy(locale, loc);
+ for (;;) {
+ UErrorCode ec2 = U_ZERO_ERROR;
+ // TODO: ures_openDirect?
+ UResourceBundle* rb = ures_open(U_ICUDATA_CURR, locale, &ec2);
+ UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
+ int32_t n = ures_getSize(curr);
+ for (int32_t i=0; i<n; ++i) {
+ UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2);
+ int32_t len;
+ s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
+ UBool isChoice = FALSE;
+ if (len > 0 && s[0] == CHOICE_FORMAT_MARK) {
+ ++s;
+ --len;
+ if (len > 0 && s[0] != CHOICE_FORMAT_MARK) {
+ isChoice = TRUE;
+ }
+ }
+ if (isChoice) {
+ ChoiceFormat fmt(UnicodeString(TRUE, s, len), ec2);
+ int32_t fmt_count;
+ fmt.getFormats(fmt_count);
+ *total_currency_symbol_count += fmt_count;
+ } else {
+ ++(*total_currency_symbol_count); // currency symbol
+ }
+
+ ++(*total_currency_symbol_count); // iso code
+ ++(*total_currency_name_count); // long name
+ ures_close(names);
+ }
+
+ // currency plurals
+ UErrorCode ec3 = U_ZERO_ERROR;
+ UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3);
+ n = ures_getSize(curr_p);
+ for (int32_t i=0; i<n; ++i) {
+ UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3);
+ *total_currency_name_count += ures_getSize(names);
+ ures_close(names);
+ }
+ ures_close(curr_p);
+ ures_close(curr);
+ ures_close(rb);
+
+ if (!fallback(locale)) {
+ break;
+ }
+ }
+}
+
+static UChar*
+toUpperCase(const UChar* source, int32_t len, const char* locale) {
+ UChar* dest = NULL;
+ UErrorCode ec = U_ZERO_ERROR;
+ int32_t destLen = u_strToUpper(dest, 0, source, len, locale, &ec);
+
+ ec = U_ZERO_ERROR;
+ dest = (UChar*)uprv_malloc(sizeof(UChar) * MAX(destLen, len));
+ u_strToUpper(dest, destLen, source, len, locale, &ec);
+ if (U_FAILURE(ec)) {
+ uprv_memcpy(dest, source, sizeof(UChar) * len);
+ }
+ return dest;
+}
+
+
+// Collect all available currency names associated with the given locale
+// (enable fallback chain).
+// Read currenc names defined in resource bundle "Currencies" and
+// "CurrencyPlural", enable fallback chain.
+// return the malloc-ed currency name arrays and the total number of currency
+// names in the array.
+static void
+collectCurrencyNames(const char* locale,
+ CurrencyNameStruct** currencyNames,
+ int32_t* total_currency_name_count,
+ CurrencyNameStruct** currencySymbols,
+ int32_t* total_currency_symbol_count,
+ UErrorCode& ec) {
+ U_NAMESPACE_USE
+ // Look up the Currencies resource for the given locale.
+ 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;
+ }
+
+ // Get maximum currency name count first.
+ getCurrencyNameCount(loc, total_currency_name_count, total_currency_symbol_count);
+
+ *currencyNames = (CurrencyNameStruct*)uprv_malloc
+ (sizeof(CurrencyNameStruct) * (*total_currency_name_count));
+ *currencySymbols = (CurrencyNameStruct*)uprv_malloc
+ (sizeof(CurrencyNameStruct) * (*total_currency_symbol_count));
+
+ const UChar* s = NULL; // currency name
+ char* iso = NULL; // currency ISO code
+
+ *total_currency_name_count = 0;
+ *total_currency_symbol_count = 0;
+
+ UErrorCode ec3 = U_ZERO_ERROR;
+ UErrorCode ec4 = U_ZERO_ERROR;
+
+ // Using hash to remove duplicates caused by locale fallback
+ UHashtable* currencyIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec3);
+ UHashtable* currencyPluralIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &ec4);
+ for (int32_t localeLevel = 0; ; ++localeLevel) {
+ ec2 = U_ZERO_ERROR;
+ // TODO: ures_openDirect
+ UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc, &ec2);
+ UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, NULL, &ec2);
+ int32_t n = ures_getSize(curr);
+ for (int32_t i=0; i<n; ++i) {
+ UResourceBundle* names = ures_getByIndex(curr, i, NULL, &ec2);
+ int32_t len;
+ s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
+ // TODO: uhash_put wont change key/value?
+ iso = (char*)ures_getKey(names);
+ if (localeLevel == 0) {
+ uhash_put(currencyIsoCodes, iso, iso, &ec3);
+ } else {
+ if (uhash_get(currencyIsoCodes, iso) != NULL) {
+ ures_close(names);
+ continue;
+ } else {
+ uhash_put(currencyIsoCodes, iso, iso, &ec3);
+ }
+ }
+ UBool isChoice = FALSE;
+ if (len > 0 && s[0] == CHOICE_FORMAT_MARK) {
+ ++s;
+ --len;
+ if (len > 0 && s[0] != CHOICE_FORMAT_MARK) {
+ isChoice = TRUE;
+ }
+ }
+ if (isChoice) {
+ ChoiceFormat fmt(UnicodeString(TRUE, s, len), ec2);
+ int32_t fmt_count;
+ const UnicodeString* formats = fmt.getFormats(fmt_count);
+ for (int i = 0; i < fmt_count; ++i) {
+ // put iso, formats[i]; into array
+ int32_t length = formats[i].length();
+ UChar* name = (UChar*)uprv_malloc(sizeof(UChar)*length);
+ formats[i].extract(0, length, name);
+ (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
+ (*currencySymbols)[*total_currency_symbol_count].currencyName = name;
+ (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
+ (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = length;
+ }
+ } else {
+ // Add currency symbol.
+ (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
+ (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)s;
+ (*currencySymbols)[*total_currency_symbol_count].flag = 0;
+ (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len;
+ }
+
+ // Add currency long name.
+ s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2);
+ (*currencyNames)[*total_currency_name_count].IsoCode = iso;
+ UChar* upperName = toUpperCase(s, len, locale);
+ (*currencyNames)[*total_currency_name_count].currencyName = upperName;
+ (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
+ (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
+
+ // put (iso, 3, and iso) in to array
+ // Add currency ISO code.
+ (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
+ (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)uprv_malloc(sizeof(UChar)*3);
+ // Must convert iso[] into Unicode
+ u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3);
+ (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
+ (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3;
+
+ ures_close(names);
+ }
+
+ // currency plurals
+ UErrorCode ec3 = U_ZERO_ERROR;
+ UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, NULL, &ec3);
+ n = ures_getSize(curr_p);
+ for (int32_t i=0; i<n; ++i) {
+ UResourceBundle* names = ures_getByIndex(curr_p, i, NULL, &ec3);
+ iso = (char*)ures_getKey(names);
+ // Using hash to remove duplicated ISO codes in fallback chain.
+ if (localeLevel == 0) {
+ uhash_put(currencyPluralIsoCodes, iso, iso, &ec4);
+ } else {
+ if (uhash_get(currencyPluralIsoCodes, iso) != NULL) {
+ ures_close(names);
+ continue;
+ } else {
+ uhash_put(currencyPluralIsoCodes, iso, iso, &ec4);
+ }
+ }
+ int32_t num = ures_getSize(names);
+ int32_t len;
+ for (int32_t j = 0; j < num; ++j) {
+ // TODO: remove duplicates between singular name and
+ // currency long name?
+ s = ures_getStringByIndex(names, j, &len, &ec3);
+ (*currencyNames)[*total_currency_name_count].IsoCode = iso;
+ UChar* upperName = toUpperCase(s, len, locale);
+ (*currencyNames)[*total_currency_name_count].currencyName = upperName;
+ (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
+ (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
+ }
+ ures_close(names);
+ }
+ ures_close(curr_p);
+ ures_close(curr);
+ ures_close(rb);
+
+ if (!fallback(loc)) {
+ break;
+ }
+ }
+
+ uhash_close(currencyIsoCodes);
+ uhash_close(currencyPluralIsoCodes);
+
+ // quick sort the struct
+ qsort(*currencyNames, *total_currency_name_count,
+ sizeof(CurrencyNameStruct), currencyNameComparator);
+ qsort(*currencySymbols, *total_currency_symbol_count,
+ sizeof(CurrencyNameStruct), currencyNameComparator);
+
+#ifdef UCURR_DEBUG
+ printf("currency name count: %d\n", *total_currency_name_count);
+ for (int32_t index = 0; index < *total_currency_name_count; ++index) {
+ printf("index: %d\n", index);
+ printf("iso: %s\n", (*currencyNames)[index].IsoCode);
+ printf("currencyName:");
+ for (int32_t i = 0; i < (*currencyNames)[index].currencyNameLen; ++i) {
+ printf("%c", (unsigned char)(*currencyNames)[index].currencyName[i]);
+ }
+ printf("\n");
+ printf("len: %d\n", (*currencyNames)[index].currencyNameLen);
+ }
+ printf("currency symbol count: %d\n", *total_currency_symbol_count);
+ for (int32_t index = 0; index < *total_currency_symbol_count; ++index) {
+ printf("index: %d\n", index);
+ printf("iso: %s\n", (*currencySymbols)[index].IsoCode);
+ printf("currencySymbol:");
+ for (int32_t i = 0; i < (*currencySymbols)[index].currencyNameLen; ++i) {
+ printf("%c", (unsigned char)(*currencySymbols)[index].currencyName[i]);
+ }
+ printf("\n");
+ printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
+ }
+#endif
+}
+
+// @param currencyNames: currency names array
+// @param indexInCurrencyNames: the index of the character in currency names
+// array against which the comparison is done
+// @param key: input text char to compare against
+// @param begin(IN/OUT): the begin index of matching range in currency names array
+// @param end(IN/OUT): the end index of matching range in currency names array.
+static int32_t
+binarySearch(const CurrencyNameStruct* currencyNames,
+ int32_t indexInCurrencyNames,
+ const UChar key,
+ int32_t* begin, int32_t* end) {
+#ifdef UCURR_DEBUG
+ printf("key = %x\n", key);
+#endif
+ int32_t first = *begin;
+ int32_t last = *end;
+ while (first <= last) {
+ int32_t mid = (first + last) / 2; // compute mid point.
+ if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) {
+ first = mid + 1;
+ } else {
+ if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) {
+ first = mid + 1;
+ }
+ else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) {
+ last = mid - 1;
+ }
+ else {
+ // Find a match, and looking for ranges
+ // Now do two more binary searches. First, on the left side for
+ // the greatest L such that CurrencyNameStruct[L] < key.
+ int32_t L = *begin;
+ int32_t R = mid;
+
+#ifdef UCURR_DEBUG
+ printf("mid = %d\n", mid);
+#endif
+ while (L < R) {
+ int32_t M = (L + R) / 2;
+#ifdef UCURR_DEBUG
+ printf("L = %d, R = %d, M = %d\n", L, R, M);
+#endif
+ if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) {
+ L = M + 1;
+ } else {
+ if (currencyNames[M].currencyName[indexInCurrencyNames] < key) {
+ L = M + 1;
+ } else {
+#ifdef UCURR_DEBUG
+ U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
+#endif
+ R = M;
+ }
+ }
+ }
+#ifdef UCURR_DEBUG
+ U_ASSERT(L == R);
+#endif
+ *begin = L;
+#ifdef UCURR_DEBUG
+ printf("begin = %d\n", *begin);
+ U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key);
+#endif
+
+ // Now for the second search, finding the least R such that
+ // key < CurrencyNameStruct[R].
+ L = mid;
+ R = *end;
+ while (L < R) {
+ int32_t M = (L + R) / 2;
+#ifdef UCURR_DEBUG
+ printf("L = %d, R = %d, M = %d\n", L, R, M);
+#endif
+ if (currencyNames[M].currencyNameLen < indexInCurrencyNames) {
+ L = M + 1;
+ } else {
+ if (currencyNames[M].currencyName[indexInCurrencyNames] > key) {
+ R = M;
+ } else {
+#ifdef UCURR_DEBUG
+ U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
+#endif
+ L = M + 1;
+ }
+ }
+ }
+#ifdef UCURR_DEBUG
+ U_ASSERT(L == R);
+#endif
+ if (currencyNames[R].currencyName[indexInCurrencyNames] > key) {
+ *end = R - 1;
+ } else {
+ *end = R;
+ }
+#ifdef UCURR_DEBUG
+ printf("end = %d\n", *end);
+#endif
+
+ // now, found the range. check whether there is exact match
+ if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) {
+ return *begin; // find range and exact match.
+ }
+ return -1; // find range, but no exact match.
+ }
+ }
+ }
+ *begin = -1;
+ *end = -1;
+ return -1; // failed to find range.
+}
+
+
+// Linear search "text" in "currencyNames".
+// @param begin, end: the begin and end index in currencyNames, within which
+// range should the search be performed.
+// @param textLen: the length of the text to be compared
+// @param maxMatchLen(IN/OUT): passing in the computed max matching length
+// pass out the new max matching length
+// @param maxMatchIndex: the index in currencyName which has the longest
+// match with input text.
+static void
+linearSearch(const CurrencyNameStruct* currencyNames,
+ int32_t begin, int32_t end,
+ const UChar* text, int32_t textLen,
+ int32_t *maxMatchLen, int32_t* maxMatchIndex) {
+ for (int32_t index = begin; index <= end; ++index) {
+ int32_t len = currencyNames[index].currencyNameLen;
+ if (len > *maxMatchLen && len <= textLen &&
+ uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(UChar)) == 0) {
+ *maxMatchIndex = index;
+ *maxMatchLen = len;
+#ifdef UCURR_DEBUG
+ printf("maxMatchIndex = %d, maxMatchLen = %d\n",
+ *maxMatchIndex, *maxMatchLen);
+#endif
+ }
+ }
+}
+
+#define LINEAR_SEARCH_THRESHOLD 10
+
+// Find longest match between "text" and currency names in "currencyNames".
+// @param total_currency_count: total number of currency names in CurrencyNames.
+// @param textLen: the length of the text to be compared
+// @param maxMatchLen: passing in the computed max matching length
+// pass out the new max matching length
+// @param maxMatchIndex: the index in currencyName which has the longest
+// match with input text.
+static void
+searchCurrencyName(const CurrencyNameStruct* currencyNames,
+ int32_t total_currency_count,
+ const UChar* text, int32_t textLen,
+ int32_t* maxMatchLen, int32_t* maxMatchIndex) {
+ *maxMatchIndex = -1;
+ *maxMatchLen = 0;
+ int32_t matchIndex = -1;
+ int32_t binarySearchBegin = 0;
+ int32_t binarySearchEnd = total_currency_count - 1;
+ // It is a variant of binary search.
+ // For example, given the currency names in currencyNames array are:
+ // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
+ // and the input text is BBEXST
+ // The first round binary search search "B" in the text against
+ // the first char in currency names, and find the first char matching range
+ // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
+ // The 2nd round binary search search the second "B" in the text against
+ // the 2nd char in currency names, and narrow the matching range to
+ // "BB BBEX BBEXYZ" (and the maximum matching "BB").
+ // The 3rd round returnes the range as "BBEX BBEXYZ" (without changing
+ // maximum matching).
+ // The 4th round returns the same range (the maximum matching is "BBEX").
+ // The 5th round returns no matching range.
+ for (int32_t index = 0; index < textLen; ++index) {
+ // matchIndex saves the one with exact match till the current point.
+ // [binarySearchBegin, binarySearchEnd] saves the matching range.
+ matchIndex = binarySearch(currencyNames, index,
+ text[index],
+ &binarySearchBegin, &binarySearchEnd);
+ if (binarySearchBegin == -1) { // did not find the range
+ break;
+ }
+ if (matchIndex != -1) {
+ // find an exact match for text from text[0] to text[index]
+ // in currencyNames array.
+ *maxMatchLen = index + 1;
+ *maxMatchIndex = matchIndex;
+ }
+ if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) {
+ // linear search if within threshold.
+ linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
+ text, textLen,
+ maxMatchLen, maxMatchIndex);
+ break;
+ }
+ }
+ return;
+}
+
+//========================= currency name cache =====================
+typedef struct {
+ char locale[ULOC_FULLNAME_CAPACITY]; //key
+ // currency names, case insensitive
+ CurrencyNameStruct* currencyNames; // value
+ int32_t totalCurrencyNameCount; // currency name count
+ // currency symbols and ISO code, case sensitive
+ CurrencyNameStruct* currencySymbols; // value
+ int32_t totalCurrencySymbolCount; // count
+ // reference count.
+ // reference count is set to 1 when an entry is put to cache.
+ // it increases by 1 before accessing, and decreased by 1 after accessing.
+ // The entry is deleted when ref count is zero, which means
+ // the entry is replaced out of cache and no process is accessing it.
+ int32_t refCount;
+} CurrencyNameCacheEntry;
+
+
+#define CURRENCY_NAME_CACHE_NUM 10
+
+// Reserve 10 cache entries.
+static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {NULL};
+// Using an index to indicate which entry to be replaced when cache is full.
+// It is a simple round-robin replacement strategy.
+static int8_t currentCacheEntryIndex = 0;
+
+// Cache deletion
+static void
+deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) {
+ for (int32_t index = 0; index < count; ++index) {
+ if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) {
+ uprv_free(currencyNames[index].currencyName);
+ }
+ }
+ uprv_free(currencyNames);
+}
+
+
+static void
+deleteCacheEntry(CurrencyNameCacheEntry* entry) {
+ deleteCurrencyNames(entry->currencyNames, entry->totalCurrencyNameCount);
+ deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount);
+ uprv_free(entry);
+}
+
+
+// Cache clean up
+static UBool U_CALLCONV
+currency_cache_cleanup(void) {
+ for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
+ if (currCache[i]) {
+ deleteCacheEntry(currCache[i]);
+ currCache[i] = 0;
+ }
+ }
+ return TRUE;
+}
+
+
+U_CFUNC void
+uprv_parseCurrency(const char* locale,
+ const icu::UnicodeString& text,
+ icu::ParsePosition& pos,
+ int8_t type,
+ UChar* result,
+ UErrorCode& ec)
+{
+ U_NAMESPACE_USE
+
+ if (U_FAILURE(ec)) {
+ return;
+ }
+
+ int32_t total_currency_name_count = 0;
+ CurrencyNameStruct* currencyNames = NULL;
+ int32_t total_currency_symbol_count = 0;
+ CurrencyNameStruct* currencySymbols = NULL;
+ CurrencyNameCacheEntry* cacheEntry = NULL;
+
+ umtx_lock(NULL);
+ // in order to handle racing correctly,
+ // not putting 'search' in a separate function and using UMTX.
+ int8_t found = -1;
+ for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
+ if (currCache[i]!= NULL &&
+ uprv_strcmp(locale, currCache[i]->locale) == 0) {
+ found = i;
+ break;
+ }
+ }
+ if (found != -1) {
+ cacheEntry = currCache[found];
+ currencyNames = cacheEntry->currencyNames;
+ total_currency_name_count = cacheEntry->totalCurrencyNameCount;
+ currencySymbols = cacheEntry->currencySymbols;
+ total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
+ ++(cacheEntry->refCount);
+ }
+ umtx_unlock(NULL);
+ if (found == -1) {
+ collectCurrencyNames(locale, &currencyNames, &total_currency_name_count, &currencySymbols, &total_currency_symbol_count, ec);
+ if (U_FAILURE(ec)) {
+ return;
+ }
+ umtx_lock(NULL);
+ // check again.
+ int8_t found = -1;
+ for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
+ if (currCache[i]!= NULL &&
+ uprv_strcmp(locale, currCache[i]->locale) == 0) {
+ found = i;
+ break;
+ }
+ }
+ if (found == -1) {
+ // insert new entry to
+ // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
+ // and remove the existing entry
+ // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
+ // from cache.
+ cacheEntry = currCache[currentCacheEntryIndex];
+ if (cacheEntry) {
+ --(cacheEntry->refCount);
+ // delete if the ref count is zero
+ if (cacheEntry->refCount == 0) {
+ deleteCacheEntry(cacheEntry);
+ }
+ }
+ cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry));
+ currCache[currentCacheEntryIndex] = cacheEntry;
+ uprv_strcpy(cacheEntry->locale, locale);
+ cacheEntry->currencyNames = currencyNames;
+ cacheEntry->totalCurrencyNameCount = total_currency_name_count;
+ cacheEntry->currencySymbols = currencySymbols;
+ cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count;
+ cacheEntry->refCount = 2; // one for cache, one for reference
+ currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM;
+ ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cache_cleanup);
+
+ } else {
+ deleteCurrencyNames(currencyNames, total_currency_name_count);
+ deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
+ cacheEntry = currCache[found];
+ currencyNames = cacheEntry->currencyNames;
+ total_currency_name_count = cacheEntry->totalCurrencyNameCount;
+ currencySymbols = cacheEntry->currencySymbols;
+ total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
+ ++(cacheEntry->refCount);
+ }
+ umtx_unlock(NULL);
+ }
+
+ int32_t start = pos.getIndex();
+
+ UChar inputText[MAX_CURRENCY_NAME_LEN];
+ UChar upperText[MAX_CURRENCY_NAME_LEN];
+ int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start);
+ text.extract(start, textLen, inputText);
+ UErrorCode ec1 = U_ZERO_ERROR;
+ textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1);
+
+ int32_t max = 0;
+ int32_t matchIndex = -1;
+ // case in-sensitive comparision against currency names
+ searchCurrencyName(currencyNames, total_currency_name_count,
+ upperText, textLen, &max, &matchIndex);
+
+#ifdef UCURR_DEBUG
+ printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
+#endif
+
+ int32_t maxInSymbol = 0;
+ int32_t matchIndexInSymbol = -1;
+ if (type != UCURR_LONG_NAME) { // not name only
+ // case sensitive comparison against currency symbols and ISO code.
+ searchCurrencyName(currencySymbols, total_currency_symbol_count,
+ inputText, textLen,
+ &maxInSymbol, &matchIndexInSymbol);
+ }
+
+#ifdef UCURR_DEBUG
+ printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol);
+#endif
+
+ if (max >= maxInSymbol && matchIndex != -1) {
+ u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4);
+ pos.setIndex(start + max);
+ } else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
+ u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
+ pos.setIndex(start + maxInSymbol);
+ }
+
+ // decrease reference count
+ umtx_lock(NULL);
+ --(cacheEntry->refCount);
+ if (cacheEntry->refCount == 0) { // remove
+ deleteCacheEntry(cacheEntry);
+ }
+ umtx_unlock(NULL);
+}
+
+
+/**
+ * Internal method. Given a currency ISO code and a locale, return
+ * the "static" currency name. This is usually the same as the
+ * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
+ * format is applied to the number 2.0 (to yield the more common
+ * plural) to return a static name.
+ *
+ * This is used for backward compatibility with old currency logic in
+ * DecimalFormat and DecimalFormatSymbols.
+ */
+U_CFUNC void
+uprv_getStaticCurrencyName(const UChar* iso, const char* loc,
+ icu::UnicodeString& result, UErrorCode& ec)
+{
+ U_NAMESPACE_USE
+
+ UBool isChoiceFormat;
+ int32_t len;
+ const UChar* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME,
+ &isChoiceFormat, &len, &ec);
+ if (U_SUCCESS(ec)) {
+ // If this is a ChoiceFormat currency, then format an
+ // arbitrary value; pick something != 1; more common.
+ result.truncate(0);
+ if (isChoiceFormat) {
+ ChoiceFormat f(UnicodeString(TRUE, currname, len), ec);
+ if (U_SUCCESS(ec)) {
+ f.format(2.0, result);
+ } else {
+ result.setTo(iso, -1);
+ }
+ } else {
+ result.setTo(currname, -1);
+ }
+ }
+}
+
+U_CAPI int32_t U_EXPORT2
+ucurr_getDefaultFractionDigits(const UChar* currency, UErrorCode* ec) {
+ return (_findMetaData(currency, *ec))[0];
+}
+
+U_CAPI double U_EXPORT2
+ucurr_getRoundingIncrement(const UChar* currency, UErrorCode* ec) {
+ const int32_t *data = _findMetaData(currency, *ec);
+
+ // If the meta data is invalid, return 0.0.
+ if (data[0] < 0 || data[0] > MAX_POW10) {
+ if (U_SUCCESS(*ec)) {
+ *ec = U_INVALID_FORMAT_ERROR;
+ }
+ return 0.0;
+ }
+
+ // If there is no rounding, return 0.0 to indicate no rounding. A
+ // rounding value (data[1]) of 0 or 1 indicates no rounding.
+ if (data[1] < 2) {
+ return 0.0;
+ }
+
+ // Return data[1] / 10^(data[0]). The only actual rounding data,
+ // as of this writing, is CHF { 2, 5 }.
+ return double(data[1]) / POW10[data[0]];
+}
+
+U_CDECL_BEGIN
+
+typedef struct UCurrencyContext {
+ uint32_t currType; /* UCurrCurrencyType */
+ uint32_t listIdx;
+} UCurrencyContext;
+
+/*
+Please keep this list in alphabetical order.
+You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
+of these items.
+ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
+*/
+static const struct CurrencyList {
+ const char *currency;
+ uint32_t currType;
+} gCurrencyList[] = {
+ {"ADP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"AFA", UCURR_COMMON|UCURR_DEPRECATED},
+ {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ALK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"AOK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"AON", UCURR_COMMON|UCURR_DEPRECATED},
+ {"AOR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ARA", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ARL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ARM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ARP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ATS", UCURR_COMMON|UCURR_DEPRECATED},
+ {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"AZM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BAD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BAN", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"BEF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"BGL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BGM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BGO", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BOL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BOP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"BRB", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BRC", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BRE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BRN", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BRR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BRZ", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BUK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BYB", UCURR_COMMON|UCURR_DEPRECATED},
+ {"BYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"CLE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CSD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"CSK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"CYP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"DDM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"DEM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ECS", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"EEK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"EQE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"ESP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"FIM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"FRF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GEK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GHC", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GNS", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GQE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GRD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GWE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"GWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"HRD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"IEP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ILP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ILR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ISJ", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ITL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KRH", UCURR_COMMON|UCURR_DEPRECATED},
+ {"KRO", UCURR_COMMON|UCURR_DEPRECATED},
+ {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LSM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"LTL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LTT", UCURR_COMMON|UCURR_DEPRECATED},
+ {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"LUF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"LVL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"LVR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MAF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MCF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MDC", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MGF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MKN", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MLF", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MRO", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MTL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MTP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MVP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MXP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MZE", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"MZM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"NIC", UCURR_COMMON|UCURR_DEPRECATED},
+ {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"NLG", UCURR_COMMON|UCURR_DEPRECATED},
+ {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PEI", UCURR_COMMON|UCURR_DEPRECATED},
+ {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PES", UCURR_COMMON|UCURR_DEPRECATED},
+ {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"PLZ", UCURR_COMMON|UCURR_DEPRECATED},
+ {"PTE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"RHD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ROL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"RUR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SDD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SDP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SIT", UCURR_COMMON|UCURR_DEPRECATED},
+ {"SKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SRG", UCURR_COMMON|UCURR_DEPRECATED},
+ {"STD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SUR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"SVC", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TJR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TMM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TPE", UCURR_COMMON|UCURR_DEPRECATED},
+ {"TRL", UCURR_COMMON|UCURR_DEPRECATED},
+ {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"UAK", UCURR_COMMON|UCURR_DEPRECATED},
+ {"UGS", UCURR_COMMON|UCURR_DEPRECATED},
+ {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"UYP", UCURR_COMMON|UCURR_DEPRECATED},
+ {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"VEB", UCURR_COMMON|UCURR_DEPRECATED},
+ {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"VNN", UCURR_COMMON|UCURR_DEPRECATED},
+ {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED},
+ {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XRE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"YDD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"YUD", UCURR_COMMON|UCURR_DEPRECATED},
+ {"YUM", UCURR_COMMON|UCURR_DEPRECATED},
+ {"YUN", UCURR_COMMON|UCURR_DEPRECATED},
+ {"YUR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ZAL", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
+ {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ZMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ZRN", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ZWL", UCURR_COMMON|UCURR_NON_DEPRECATED},
+ {"ZWR", UCURR_COMMON|UCURR_DEPRECATED},
+ {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
+ { NULL, 0 } // Leave here to denote the end of the list.
+};
+
+#define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
+ ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
+
+static int32_t U_CALLCONV
+ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
+ UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
+ uint32_t currType = myContext->currType;
+ int32_t count = 0;
+
+ /* Count the number of items matching the type we are looking for. */
+ for (int32_t idx = 0; gCurrencyList[idx].currency != NULL; idx++) {
+ if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) {
+ count++;
+ }
+ }
+ return count;
+}
+
+static const char* U_CALLCONV
+ucurr_nextCurrencyList(UEnumeration *enumerator,
+ int32_t* resultLength,
+ UErrorCode * /*pErrorCode*/)
+{
+ UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
+
+ /* Find the next in the list that matches the type we are looking for. */
+ while (myContext->listIdx < (sizeof(gCurrencyList)/sizeof(gCurrencyList[0]))-1) {
+ const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++];
+ if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType))
+ {
+ if (resultLength) {
+ *resultLength = 3; /* Currency codes are only 3 chars long */
+ }
+ return currItem->currency;
+ }
+ }
+ /* We enumerated too far. */
+ if (resultLength) {
+ *resultLength = 0;
+ }
+ return NULL;
+}
+
+static void U_CALLCONV
+ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
+ ((UCurrencyContext *)(enumerator->context))->listIdx = 0;
+}
+
+static void U_CALLCONV
+ucurr_closeCurrencyList(UEnumeration *enumerator) {
+ uprv_free(enumerator->context);
+ uprv_free(enumerator);
+}
+
+static void U_CALLCONV
+ucurr_createCurrencyList(UErrorCode* status){
+ UErrorCode localStatus = U_ZERO_ERROR;
+
+ // Look up the CurrencyMap element in the root bundle.
+ UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
+ UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
+
+ if (U_SUCCESS(localStatus)) {
+ // process each entry in currency map
+ for (int32_t i=0; i<ures_getSize(currencyMapArray); i++) {
+ // get the currency resource
+ UResourceBundle *currencyArray = ures_getByIndex(currencyMapArray, i, NULL, &localStatus);
+ // process each currency
+ if (U_SUCCESS(localStatus)) {
+ for (int32_t j=0; j<ures_getSize(currencyArray); j++) {
+ // get the currency resource
+ UResourceBundle *currencyRes = ures_getByIndex(currencyArray, j, NULL, &localStatus);
+ IsoCodeEntry *entry = (IsoCodeEntry*)uprv_malloc(sizeof(IsoCodeEntry));
+ if (entry == NULL) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+
+ // get the ISO code
+ int32_t isoLength = 0;
+ UResourceBundle *idRes = ures_getByKey(currencyRes, "id", NULL, &localStatus);
+ if (idRes == NULL) {
+ continue;
+ }
+ const UChar *isoCode = ures_getString(idRes, &isoLength, &localStatus);
+
+ // get the from date
+ int32_t fromLength = 0;
+ UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
+ const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
+ int64_t currDate64 = (int64_t)fromArray[0] << 32;
+ currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
+ UDate fromDate = (UDate)currDate64;
+ UDate toDate = U_DATE_MAX;
+
+ if (ures_getSize(currencyRes)> 2) {
+ int32_t toLength = 0;
+ UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
+ const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
+
+ currDate64 = (int64_t)toArray[0] << 32;
+ currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
+ toDate = (UDate)currDate64;
+
+ ures_close(toRes);
+ }
+
+ ures_close(fromRes);
+ ures_close(idRes);
+ ures_close(currencyRes);
+
+ entry->isoCode = isoCode;
+ entry->from = fromDate;
+ entry->to = toDate;
+
+ uhash_put(gIsoCodes, (UChar *)isoCode, entry, &localStatus);
+ }
+ } else {
+ *status = localStatus;
+ }
+ ures_close(currencyArray);
+ }
+ } else {
+ *status = localStatus;
+ }
+
+ ures_close(currencyMapArray);
+}
+
+static const UEnumeration gEnumCurrencyList = {
+ NULL,
+ NULL,
+ ucurr_closeCurrencyList,
+ ucurr_countCurrencyList,
+ uenum_unextDefault,
+ ucurr_nextCurrencyList,
+ ucurr_resetCurrencyList
+};
+U_CDECL_END
+
+U_CAPI UBool U_EXPORT2
+ucurr_isAvailable(const UChar* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) {
+ UErrorCode status = U_ZERO_ERROR;
+ UBool initialized;
+ UMTX_CHECK(&gIsoCodesLock, gIsoCodesInitialized, initialized);
+
+ if (!initialized) {
+ umtx_lock(&gIsoCodesLock);
+ gIsoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+ if (U_FAILURE(status)) {
+ umtx_unlock(&gIsoCodesLock);
+ return FALSE;
+ }
+ uhash_setValueDeleter(gIsoCodes, deleteIsoCodeEntry);
+
+ ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
+
+ ucurr_createCurrencyList(&status);
+ if (U_FAILURE(status)) {
+ umtx_unlock(&gIsoCodesLock);
+ return FALSE;
+ }
+
+ gIsoCodesInitialized = TRUE;
+ umtx_unlock(&gIsoCodesLock);
+ }
+
+ umtx_lock(&gIsoCodesLock);
+ IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode);
+ umtx_unlock(&gIsoCodesLock);
+
+ if (result == NULL) {
+ return FALSE;
+ } else if (from > to) {
+ *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
+ return FALSE;
+ } else if ((from > result->to) || (to < result->from)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+U_CAPI UEnumeration * U_EXPORT2
+ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) {
+ UEnumeration *myEnum = NULL;
+ UCurrencyContext *myContext;
+
+ myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration));
+ if (myEnum == NULL) {
+ *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
+ return NULL;
+ }
+ uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration));
+ myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext));
+ if (myContext == NULL) {
+ *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
+ uprv_free(myEnum);
+ return NULL;
+ }
+ myContext->currType = currType;
+ myContext->listIdx = 0;
+ myEnum->context = myContext;
+ return myEnum;
+}
+
+U_CAPI int32_t U_EXPORT2
+ucurr_countCurrencies(const char* locale,
+ UDate date,
+ UErrorCode* ec)
+{
+ int32_t currCount = 0;
+
+ if (ec != NULL && U_SUCCESS(*ec))
+ {
+ // local variables
+ UErrorCode localStatus = U_ZERO_ERROR;
+ char id[ULOC_FULLNAME_CAPACITY];
+ uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
+ // get country or country_variant in `id'
+ /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
+
+ if (U_FAILURE(*ec))
+ {
+ return 0;
+ }
+
+ // Remove variants, which is only needed for registration.
+ char *idDelim = strchr(id, VAR_DELIM);
+ if (idDelim)
+ {
+ idDelim[0] = 0;
+ }
+
+ // Look up the CurrencyMap element in the root bundle.
+ UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
+ UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
+
+ // Using the id derived from the local, get the currency data
+ UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
+
+ // process each currency to see which one is valid for the given date
+ if (U_SUCCESS(localStatus))
+ {
+ for (int32_t i=0; i<ures_getSize(countryArray); i++)
+ {
+ // get the currency resource
+ UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus);
+
+ // get the from date
+ int32_t fromLength = 0;
+ UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
+ const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
+
+ int64_t currDate64 = (int64_t)fromArray[0] << 32;
+ currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
+ UDate fromDate = (UDate)currDate64;
+
+ if (ures_getSize(currencyRes)> 2)
+ {
+ int32_t toLength = 0;
+ UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
+ const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
+
+ currDate64 = (int64_t)toArray[0] << 32;
+ currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
+ UDate toDate = (UDate)currDate64;
+
+ if ((fromDate <= date) && (date < toDate))
+ {
+ currCount++;
+ }
+
+ ures_close(toRes);
+ }
+ else
+ {
+ if (fromDate <= date)
+ {
+ currCount++;
+ }
+ }
+
+ // close open resources
+ ures_close(currencyRes);
+ ures_close(fromRes);
+
+ } // end For loop
+ } // end if (U_SUCCESS(localStatus))
+
+ ures_close(countryArray);
+
+ // Check for errors
+ if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
+ {
+ // There is nothing to fallback to.
+ // Report the failure/warning if possible.
+ *ec = localStatus;
+ }
+
+ if (U_SUCCESS(*ec))
+ {
+ // no errors
+ return currCount;
+ }
+
+ }
+
+ // If we got here, either error code is invalid or
+ // some argument passed is no good.
+ return 0;
+}
+
+U_CAPI int32_t U_EXPORT2
+ucurr_forLocaleAndDate(const char* locale,
+ UDate date,
+ int32_t index,
+ UChar* buff,
+ int32_t buffCapacity,
+ UErrorCode* ec)
+{
+ int32_t resLen = 0;
+ int32_t currIndex = 0;
+ const UChar* s = NULL;
+
+ if (ec != NULL && U_SUCCESS(*ec))
+ {
+ // check the arguments passed
+ if ((buff && buffCapacity) || !buffCapacity )
+ {
+ // local variables
+ UErrorCode localStatus = U_ZERO_ERROR;
+ char id[ULOC_FULLNAME_CAPACITY];
+ resLen = uloc_getKeywordValue(locale, "currency", id, ULOC_FULLNAME_CAPACITY, &localStatus);
+
+ // get country or country_variant in `id'
+ /*uint32_t variantType =*/ idForLocale(locale, id, sizeof(id), ec);
+ if (U_FAILURE(*ec))
+ {
+ return 0;
+ }
+
+ // Remove variants, which is only needed for registration.
+ char *idDelim = strchr(id, VAR_DELIM);
+ if (idDelim)
+ {
+ idDelim[0] = 0;
+ }
+
+ // Look up the CurrencyMap element in the root bundle.
+ UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
+ UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
+
+ // Using the id derived from the local, get the currency data
+ UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus);
+
+ // process each currency to see which one is valid for the given date
+ bool matchFound = false;
+ if (U_SUCCESS(localStatus))
+ {
+ if ((index <= 0) || (index> ures_getSize(countryArray)))
+ {
+ // requested index is out of bounds
+ ures_close(countryArray);
+ return 0;
+ }
+
+ for (int32_t i=0; i<ures_getSize(countryArray); i++)
+ {
+ // get the currency resource
+ UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, NULL, &localStatus);
+ s = ures_getStringByKey(currencyRes, "id", &resLen, &localStatus);
+
+ // get the from date
+ int32_t fromLength = 0;
+ UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", NULL, &localStatus);
+ const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
+
+ int64_t currDate64 = (int64_t)fromArray[0] << 32;
+ currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
+ UDate fromDate = (UDate)currDate64;
+
+ if (ures_getSize(currencyRes)> 2)
+ {
+ int32_t toLength = 0;
+ UResourceBundle *toRes = ures_getByKey(currencyRes, "to", NULL, &localStatus);
+ const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
+
+ currDate64 = (int64_t)toArray[0] << 32;
+ currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
+ UDate toDate = (UDate)currDate64;
+
+ if ((fromDate <= date) && (date < toDate))
+ {
+ currIndex++;
+ if (currIndex == index)
+ {
+ matchFound = true;
+ }
+ }
+
+ ures_close(toRes);
+ }
+ else
+ {
+ if (fromDate <= date)
+ {
+ currIndex++;
+ if (currIndex == index)
+ {
+ matchFound = true;
+ }
+ }
+ }
+
+ // close open resources
+ ures_close(currencyRes);
+ ures_close(fromRes);
+
+ // check for loop exit
+ if (matchFound)
+ {
+ break;
+ }
+
+ } // end For loop
+ }
+
+ ures_close(countryArray);
+
+ // Check for errors
+ if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
+ {
+ // There is nothing to fallback to.
+ // Report the failure/warning if possible.
+ *ec = localStatus;
+ }
+
+ if (U_SUCCESS(*ec))
+ {
+ // no errors
+ if((buffCapacity> resLen) && matchFound)
+ {
+ // write out the currency value
+ u_strcpy(buff, s);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ // return null terminated currency string
+ return u_terminateUChars(buff, buffCapacity, resLen, ec);
+ }
+ else
+ {
+ // illegal argument encountered
+ *ec = U_ILLEGAL_ARGUMENT_ERROR;
+ }
+
+ }
+
+ // If we got here, either error code is invalid or
+ // some argument passed is no good.
+ return resLen;
+}
+
+static const UEnumeration defaultKeywordValues = {
+ NULL,
+ NULL,
+ ulist_close_keyword_values_iterator,
+ ulist_count_keyword_values,
+ uenum_unextDefault,
+ ulist_next_keyword_value,
+ ulist_reset_keyword_values_iterator
+};
+
+U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) {
+ // Resolve region
+ char prefRegion[ULOC_FULLNAME_CAPACITY] = "";
+ int32_t prefRegionLength = 0;
+ prefRegionLength = uloc_getCountry(locale, prefRegion, sizeof(prefRegion), status);
+ if (prefRegionLength == 0) {
+ char loc[ULOC_FULLNAME_CAPACITY] = "";
+ uloc_addLikelySubtags(locale, loc, sizeof(loc), status);
+
+ prefRegionLength = uloc_getCountry(loc, prefRegion, sizeof(prefRegion), status);
+ }
+
+ // Read value from supplementalData
+ UList *values = ulist_createEmptyList(status);
+ UList *otherValues = ulist_createEmptyList(status);
+ UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
+ if (U_FAILURE(*status) || en == NULL) {
+ if (en == NULL) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ uprv_free(en);
+ }
+ ulist_deleteList(values);
+ ulist_deleteList(otherValues);
+ return NULL;
+ }
+ memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
+ en->context = values;
+
+ UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status);
+ ures_getByKey(bundle, "CurrencyMap", bundle, status);
+ UResourceBundle bundlekey, regbndl, curbndl, to;
+ ures_initStackObject(&bundlekey);
+ ures_initStackObject(&regbndl);
+ ures_initStackObject(&curbndl);
+ ures_initStackObject(&to);
+
+ while (U_SUCCESS(*status) && ures_hasNext(bundle)) {
+ ures_getNextResource(bundle, &bundlekey, status);
+ if (U_FAILURE(*status)) {
+ break;
+ }
+ const char *region = ures_getKey(&bundlekey);
+ UBool isPrefRegion = uprv_strcmp(region, prefRegion) == 0 ? TRUE : FALSE;
+ if (!isPrefRegion && commonlyUsed) {
+ // With commonlyUsed=true, we do not put
+ // currencies for other regions in the
+ // result list.
+ continue;
+ }
+ ures_getByKey(bundle, region, &regbndl, status);
+ if (U_FAILURE(*status)) {
+ break;
+ }
+ while (U_SUCCESS(*status) && ures_hasNext(&regbndl)) {
+ ures_getNextResource(&regbndl, &curbndl, status);
+ if (ures_getType(&curbndl) != URES_TABLE) {
+ // Currently, an empty ARRAY is mixed in.
+ continue;
+ }
+ char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
+ int32_t curIDLength = ULOC_KEYWORDS_CAPACITY;
+ if (curID == NULL) {
+ *status = U_MEMORY_ALLOCATION_ERROR;
+ break;
+ }
+
+#if U_CHARSET_FAMILY==U_ASCII_FAMILY
+ ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, TRUE, status);
+ /* optimize - use the utf-8 string */
+#else
+ {
+ const UChar* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status);
+ if(U_SUCCESS(*status)) {
+ if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) {
+ *status = U_BUFFER_OVERFLOW_ERROR;
+ } else {
+ u_UCharsToChars(defString, curID, curIDLength+1);
+ }
+ }
+ }
+#endif
+
+ if (U_FAILURE(*status)) {
+ break;
+ }
+ UBool hasTo = FALSE;
+ ures_getByKey(&curbndl, "to", &to, status);
+ if (U_FAILURE(*status)) {
+ // Do nothing here...
+ *status = U_ZERO_ERROR;
+ } else {
+ hasTo = TRUE;
+ }
+ if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) {
+ // Currently active currency for the target country
+ ulist_addItemEndList(values, curID, TRUE, status);
+ } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) {
+ ulist_addItemEndList(otherValues, curID, TRUE, status);
+ } else {
+ uprv_free(curID);
+ }
+ }
+
+ }
+ if (U_SUCCESS(*status)) {
+ if (commonlyUsed) {
+ if (ulist_getListSize(values) == 0) {
+ // This could happen if no valid region is supplied in the input
+ // locale. In this case, we use the CLDR's default.
+ uenum_close(en);
+ en = ucurr_getKeywordValuesForLocale(key, "und", TRUE, status);
+ }
+ } else {
+ // Consolidate the list
+ char *value = NULL;
+ ulist_resetList(otherValues);
+ while ((value = (char *)ulist_getNext(otherValues)) != NULL) {
+ if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) {
+ char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
+ uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1);
+ ulist_addItemEndList(values, tmpValue, TRUE, status);
+ if (U_FAILURE(*status)) {
+ break;
+ }
+ }
+ }
+ }
+
+ ulist_resetList((UList *)(en->context));
+ } else {
+ ulist_deleteList(values);
+ uprv_free(en);
+ values = NULL;
+ en = NULL;
+ }
+ ures_close(&to);
+ ures_close(&curbndl);
+ ures_close(&regbndl);
+ ures_close(&bundlekey);
+ ures_close(bundle);
+
+ ulist_deleteList(otherValues);
+
+ return en;
+}
+
+
+U_CAPI int32_t U_EXPORT2
+ucurr_getNumericCode(const UChar* currency) {
+ int32_t code = 0;
+ if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) {
+ UErrorCode status = U_ZERO_ERROR;
+
+ UResourceBundle *bundle = ures_openDirect(0, "currencyNumericCodes", &status);
+ ures_getByKey(bundle, "codeMap", bundle, &status);
+ if (U_SUCCESS(status)) {
+ char alphaCode[ISO_CURRENCY_CODE_LENGTH+1];
+ myUCharsToChars(alphaCode, currency);
+ T_CString_toUpperCase(alphaCode);
+ ures_getByKey(bundle, alphaCode, bundle, &status);
+ int tmpCode = ures_getInt(bundle, &status);
+ if (U_SUCCESS(status)) {
+ code = tmpCode;
+ }
+ }
+ ures_close(bundle);
+ }
+ return code;
+}
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+//eof