#include "ucln_in.h"
#include "uenumimp.h"
#include "uhash.h"
+#include "hash.h"
#include "uresimp.h"
#include "ulist.h"
#include "ureslocs.h"
-// #define UCURR_DEBUG 1
+//#define UCURR_DEBUG_EQUIV 1
+#ifdef UCURR_DEBUG_EQUIV
+#include "stdio.h"
+#endif
+//#define UCURR_DEBUG 1
#ifdef UCURR_DEBUG
#include "stdio.h"
#endif
// 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 };
+static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 };
// POW10[i] = 10^i, i=0..MAX_POW10
static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
static const int32_t MAX_POW10 = (sizeof(POW10)/sizeof(POW10[0])) - 1;
+// Defines equivalent currency symbols.
+static const char *EQUIV_CURRENCY_SYMBOLS[][2] = {
+ {"\\u00a5", "\\uffe5"},
+ {"$", "\\ufe69"},
+ {"$", "\\uff04"},
+ {"\\u20a8", "\\u20b9"},
+ {"\\u00a3", "\\u20a4"}};
+
#define ISO_CURRENCY_CODE_LENGTH 3
//------------------------------------------------------------
static const UChar EUR_STR[] = {0x0045,0x0055,0x0052,0};
// ISO codes mapping table
-static UHashtable* gIsoCodes = NULL;
-static UBool gIsoCodesInitialized = FALSE;
+static const UHashtable* gIsoCodes = NULL;
+static icu::UInitOnce gIsoCodesInitOnce = U_INITONCE_INITIALIZER;
+
+// Currency symbol equivalances
+static const icu::Hashtable* gCurrSymbolsEquiv = NULL;
+static icu::UInitOnce gCurrSymbolsEquivInitOnce = U_INITONCE_INITIALIZER;
+
+// EquivIterator iterates over all strings that are equivalent to a given
+// string, s. Note that EquivIterator will never yield s itself.
+class EquivIterator : icu::UMemory {
+public:
+ // Constructor. hash stores the equivalence relationships; s is the string
+ // for which we find equivalent strings.
+ inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s)
+ : _hash(hash) {
+ _start = _current = &s;
+ }
+ inline ~EquivIterator() { }
+
+ // next returns the next equivalent string or NULL if there are no more.
+ // If s has no equivalent strings, next returns NULL on the first call.
+ const icu::UnicodeString *next();
+private:
+ const icu::Hashtable& _hash;
+ const icu::UnicodeString* _start;
+ const icu::UnicodeString* _current;
+};
-static UMutex gIsoCodesLock = U_MUTEX_INITIALIZER;
+const icu::UnicodeString *
+EquivIterator::next() {
+ const icu::UnicodeString* _next = (const icu::UnicodeString*) _hash.get(*_current);
+ if (_next == NULL) {
+ U_ASSERT(_current == _start);
+ return NULL;
+ }
+ if (*_next == *_start) {
+ return NULL;
+ }
+ _current = _next;
+ return _next;
+}
+
+// makeEquivalent makes lhs and rhs equivalent by updating the equivalence
+// relations in hash accordingly.
+static void makeEquivalent(
+ const icu::UnicodeString &lhs,
+ const icu::UnicodeString &rhs,
+ icu::Hashtable* hash, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (lhs == rhs) {
+ // already equivalent
+ return;
+ }
+ EquivIterator leftIter(*hash, lhs);
+ EquivIterator rightIter(*hash, rhs);
+ const icu::UnicodeString *firstLeft = leftIter.next();
+ const icu::UnicodeString *firstRight = rightIter.next();
+ const icu::UnicodeString *nextLeft = firstLeft;
+ const icu::UnicodeString *nextRight = firstRight;
+ while (nextLeft != NULL && nextRight != NULL) {
+ if (*nextLeft == rhs || *nextRight == lhs) {
+ // Already equivalent
+ return;
+ }
+ nextLeft = leftIter.next();
+ nextRight = rightIter.next();
+ }
+ // Not equivalent. Must join.
+ icu::UnicodeString *newFirstLeft;
+ icu::UnicodeString *newFirstRight;
+ if (firstRight == NULL && firstLeft == NULL) {
+ // Neither lhs or rhs belong to an equivalence circle, so we form
+ // a new equivalnce circle of just lhs and rhs.
+ newFirstLeft = new icu::UnicodeString(rhs);
+ newFirstRight = new icu::UnicodeString(lhs);
+ } else if (firstRight == NULL) {
+ // lhs belongs to an equivalence circle, but rhs does not, so we link
+ // rhs into lhs' circle.
+ newFirstLeft = new icu::UnicodeString(rhs);
+ newFirstRight = new icu::UnicodeString(*firstLeft);
+ } else if (firstLeft == NULL) {
+ // rhs belongs to an equivlance circle, but lhs does not, so we link
+ // lhs into rhs' circle.
+ newFirstLeft = new icu::UnicodeString(*firstRight);
+ newFirstRight = new icu::UnicodeString(lhs);
+ } else {
+ // Both lhs and rhs belong to different equivalnce circles. We link
+ // them together to form one single, larger equivalnce circle.
+ newFirstLeft = new icu::UnicodeString(*firstRight);
+ newFirstRight = new icu::UnicodeString(*firstLeft);
+ }
+ if (newFirstLeft == NULL || newFirstRight == NULL) {
+ delete newFirstLeft;
+ delete newFirstRight;
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ hash->put(lhs, (void *) newFirstLeft, status);
+ hash->put(rhs, (void *) newFirstRight, status);
+}
+
+// countEquivalent counts how many strings are equivalent to s.
+// hash stores all the equivalnce relations.
+// countEquivalent does not include s itself in the count.
+static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) {
+ int32_t result = 0;
+ EquivIterator iter(hash, s);
+ while (iter.next() != NULL) {
+ ++result;
+ }
+#ifdef UCURR_DEBUG_EQUIV
+ {
+ char tmp[200];
+ s.extract(0,s.length(),tmp, "UTF-8");
+ printf("CountEquivalent('%s') = %d\n", tmp, result);
+ }
+#endif
+ return result;
+}
+
+static const icu::Hashtable* getCurrSymbolsEquiv();
//------------------------------------------------------------
// Code
isoCodes_cleanup(void)
{
if (gIsoCodes != NULL) {
- uhash_close(gIsoCodes);
+ uhash_close(const_cast<UHashtable *>(gIsoCodes));
gIsoCodes = NULL;
}
- gIsoCodesInitialized = FALSE;
+ gIsoCodesInitOnce.reset();
+ return TRUE;
+}
+/**
+ * Cleanup callback func
+ */
+static UBool U_CALLCONV
+currSymbolsEquiv_cleanup(void)
+{
+ delete const_cast<icu::Hashtable *>(gCurrSymbolsEquiv);
+ gCurrSymbolsEquiv = NULL;
+ gCurrSymbolsEquivInitOnce.reset();
return TRUE;
}
uprv_free(entry);
}
+/**
+ * Deleter for gCurrSymbolsEquiv.
+ */
+static void U_CALLCONV
+deleteUnicode(void *obj) {
+ icu::UnicodeString *entry = (icu::UnicodeString*)obj;
+ delete entry;
+}
+
/**
* Unfortunately, we have to convert the UChar* currency code to char*
* to use it as a resource key.
/**
* Internal function to look up currency data. Result is an array of
- * two integers. The first is the fraction digits. The second is the
+ * four 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).
+ * units of 10^(-fraction_digits). The third and fourth are the same
+ * except that they are those used in cash transations ( cashDigits
+ * and cashRounding ).
*/
static const int32_t*
_findMetaData(const UChar* currency, UErrorCode& ec) {
int32_t len;
const int32_t *data = ures_getIntVector(rb, &len, &ec);
- if (U_FAILURE(ec) || len != 2) {
+ if (U_FAILURE(ec) || len != 4) {
// Config/build error; return hard-coded defaults
if (U_SUCCESS(ec)) {
ec = U_INVALID_FORMAT_ERROR;
*/
currency_cache_cleanup();
isoCodes_cleanup();
+ currSymbolsEquiv_cleanup();
return TRUE;
}
const UChar* s = NULL;
char locale[ULOC_FULLNAME_CAPACITY];
uprv_strcpy(locale, loc);
+ const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
for (;;) {
UErrorCode ec2 = U_ZERO_ERROR;
// TODO: ures_openDirect?
*total_currency_symbol_count += fmt_count;
} else {
++(*total_currency_symbol_count); // currency symbol
+ if (currencySymbolsEquiv != NULL) {
+ *total_currency_symbol_count += countEquivalent(*currencySymbolsEquiv, UnicodeString(TRUE, s, len));
+ }
}
++(*total_currency_symbol_count); // iso code
int32_t* total_currency_symbol_count,
UErrorCode& ec) {
U_NAMESPACE_USE
+ const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
// Look up the Currencies resource for the given locale.
UErrorCode ec2 = U_ZERO_ERROR;
(*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*)s;
(*currencySymbols)[*total_currency_symbol_count].flag = 0;
(*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len;
+ // Add equivalent symbols
+ if (currencySymbolsEquiv != NULL) {
+ EquivIterator iter(*currencySymbolsEquiv, UnicodeString(TRUE, s, len));
+ const UnicodeString *symbol;
+ while ((symbol = iter.next()) != NULL) {
+ (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
+ (*currencySymbols)[*total_currency_symbol_count].currencyName = (UChar*) symbol->getBuffer();
+ (*currencySymbols)[*total_currency_symbol_count].flag = 0;
+ (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length();
+ }
+ }
}
// Add currency long name.
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");
+ char curNameBuf[1024];
+ memset(curNameBuf, 0, 1024);
+ u_austrncpy(curNameBuf, (*currencyNames)[index].currencyName, (*currencyNames)[index].currencyNameLen);
+ printf("currencyName: %s\n", curNameBuf);
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");
+ char curNameBuf[1024];
+ memset(curNameBuf, 0, 1024);
+ u_austrncpy(curNameBuf, (*currencySymbols)[index].currencyName, (*currencySymbols)[index].currencyNameLen);
+ printf("currencySymbol: %s\n", curNameBuf);
printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
}
#endif
// It is a simple round-robin replacement strategy.
static int8_t currentCacheEntryIndex = 0;
+static UMutex gCurrencyCacheMutex = U_MUTEX_INITIALIZER;
+
// Cache deletion
static void
deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) {
CurrencyNameStruct* currencySymbols = NULL;
CurrencyNameCacheEntry* cacheEntry = NULL;
- umtx_lock(NULL);
+ umtx_lock(&gCurrencyCacheMutex);
// in order to handle racing correctly,
- // not putting 'search' in a separate function and using UMTX.
+ // not putting 'search' in a separate function.
int8_t found = -1;
for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
if (currCache[i]!= NULL &&
total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
++(cacheEntry->refCount);
}
- umtx_unlock(NULL);
+ umtx_unlock(&gCurrencyCacheMutex);
if (found == -1) {
collectCurrencyNames(locale, ¤cyNames, &total_currency_name_count, ¤cySymbols, &total_currency_symbol_count, ec);
if (U_FAILURE(ec)) {
return;
}
- umtx_lock(NULL);
+ umtx_lock(&gCurrencyCacheMutex);
// check again.
int8_t found = -1;
for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
++(cacheEntry->refCount);
}
- umtx_unlock(NULL);
+ umtx_unlock(&gCurrencyCacheMutex);
}
int32_t start = pos.getIndex();
#ifdef UCURR_DEBUG
printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol);
+ if(matchIndexInSymbol != -1) {
+ printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode);
+ }
#endif
if (max >= maxInSymbol && matchIndex != -1) {
}
// decrease reference count
- umtx_lock(NULL);
+ umtx_lock(&gCurrencyCacheMutex);
--(cacheEntry->refCount);
if (cacheEntry->refCount == 0) { // remove
deleteCacheEntry(cacheEntry);
}
- umtx_unlock(NULL);
+ umtx_unlock(&gCurrencyCacheMutex);
}
}
static void U_CALLCONV
-ucurr_createCurrencyList(UErrorCode* status){
+ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){
UErrorCode localStatus = U_ZERO_ERROR;
// Look up the CurrencyMap element in the root bundle.
entry->to = toDate;
localStatus = U_ZERO_ERROR;
- uhash_put(gIsoCodes, (UChar *)isoCode, entry, &localStatus);
+ uhash_put(isoCodes, (UChar *)isoCode, entry, &localStatus);
}
} else {
*status = localStatus;
};
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);
+static void U_CALLCONV initIsoCodes(UErrorCode &status) {
+ U_ASSERT(gIsoCodes == NULL);
+ ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
- ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
- ucurr_createCurrencyList(&status);
+ UHashtable *isoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, NULL, &status);
+ if (U_FAILURE(status)) {
+ return;
+ }
+ uhash_setValueDeleter(isoCodes, deleteIsoCodeEntry);
+
+ ucurr_createCurrencyList(isoCodes, &status);
+ if (U_FAILURE(status)) {
+ uhash_close(isoCodes);
+ return;
+ }
+ gIsoCodes = isoCodes; // Note: gIsoCodes is const. Once set up here it is never altered,
+ // and read only access is safe without synchronization.
+}
+
+static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ int32_t length = sizeof(EQUIV_CURRENCY_SYMBOLS) / sizeof(EQUIV_CURRENCY_SYMBOLS[0]);
+ for (int32_t i = 0; i < length; ++i) {
+ icu::UnicodeString lhs(EQUIV_CURRENCY_SYMBOLS[i][0], -1, US_INV);
+ icu::UnicodeString rhs(EQUIV_CURRENCY_SYMBOLS[i][1], -1, US_INV);
+ makeEquivalent(lhs.unescape(), rhs.unescape(), hash, status);
if (U_FAILURE(status)) {
- umtx_unlock(&gIsoCodesLock);
- return FALSE;
+ return;
}
+ }
+}
+
+static void U_CALLCONV initCurrSymbolsEquiv() {
+ U_ASSERT(gCurrSymbolsEquiv == NULL);
+ UErrorCode status = U_ZERO_ERROR;
+ ucln_i18n_registerCleanup(UCLN_I18N_CURRENCY, currency_cleanup);
+ icu::Hashtable *temp = new icu::Hashtable(status);
+ if (temp == NULL) {
+ return;
+ }
+ if (U_FAILURE(status)) {
+ delete temp;
+ return;
+ }
+ temp->setValueDeleter(deleteUnicode);
+ populateCurrSymbolsEquiv(temp, status);
+ if (U_FAILURE(status)) {
+ delete temp;
+ return;
+ }
+ gCurrSymbolsEquiv = temp;
+}
- gIsoCodesInitialized = TRUE;
- umtx_unlock(&gIsoCodesLock);
+U_CAPI UBool U_EXPORT2
+ucurr_isAvailable(const UChar* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) {
+ umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode);
+ if (U_FAILURE(*eErrorCode)) {
+ return FALSE;
}
- umtx_lock(&gIsoCodesLock);
IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode);
- umtx_unlock(&gIsoCodesLock);
-
if (result == NULL) {
return FALSE;
} else if (from > to) {
} else if ((from > result->to) || (to < result->from)) {
return FALSE;
}
-
return TRUE;
}
+static const icu::Hashtable* getCurrSymbolsEquiv() {
+ umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv);
+ return gCurrSymbolsEquiv;
+}
+
U_CAPI UEnumeration * U_EXPORT2
ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) {
UEnumeration *myEnum = NULL;