/* ******************************************************************************* * * Copyright (C) 2012, International Business Machines * Corporation and others. All Rights Reserved. * ******************************************************************************* * file name: listformatter.cpp * encoding: US-ASCII * tab size: 8 (not used) * indentation:4 * * created on: 2012aug27 * created by: Umesh P. Nair */ #include "unicode/listformatter.h" #include "mutex.h" #include "hash.h" #include "cstring.h" #include "ulocimp.h" #include "charstr.h" #include "ucln_cmn.h" #include "uresimp.h" U_NAMESPACE_BEGIN static Hashtable* listPatternHash = NULL; static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}" static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}" U_CDECL_BEGIN static UBool U_CALLCONV uprv_listformatter_cleanup() { delete listPatternHash; listPatternHash = NULL; return TRUE; } static void U_CALLCONV uprv_deleteListFormatData(void *obj) { delete static_cast(obj); } U_CDECL_END static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode); static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode); void ListFormatter::initializeHash(UErrorCode& errorCode) { if (U_FAILURE(errorCode)) { return; } listPatternHash = new Hashtable(); if (listPatternHash == NULL) { errorCode = U_MEMORY_ALLOCATION_ERROR; return; } listPatternHash->setValueDeleter(uprv_deleteListFormatData); ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup); } const ListFormatData* ListFormatter::getListFormatData( const Locale& locale, UErrorCode& errorCode) { if (U_FAILURE(errorCode)) { return NULL; } UnicodeString key(locale.getName(), -1, US_INV); ListFormatData* result = NULL; { Mutex m(&listFormatterMutex); if (listPatternHash == NULL) { initializeHash(errorCode); if (U_FAILURE(errorCode)) { return NULL; } } result = static_cast(listPatternHash->get(key)); } if (result != NULL) { return result; } result = loadListFormatData(locale, errorCode); if (U_FAILURE(errorCode)) { return NULL; } { Mutex m(&listFormatterMutex); ListFormatData* temp = static_cast(listPatternHash->get(key)); if (temp != NULL) { delete result; result = temp; } else { listPatternHash->put(key, result, errorCode); if (U_FAILURE(errorCode)) { return NULL; } } } return result; } static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode) { UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); if (U_FAILURE(errorCode)) { ures_close(rb); return NULL; } rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode); if (U_FAILURE(errorCode)) { ures_close(rb); return NULL; } UnicodeString two, start, middle, end; getStringByKey(rb, "2", two, errorCode); getStringByKey(rb, "start", start, errorCode); getStringByKey(rb, "middle", middle, errorCode); getStringByKey(rb, "end", end, errorCode); ures_close(rb); if (U_FAILURE(errorCode)) { return NULL; } ListFormatData* result = new ListFormatData(two, start, middle, end); if (result == NULL) { errorCode = U_MEMORY_ALLOCATION_ERROR; return NULL; } return result; } static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) { int32_t len; const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode); if (U_FAILURE(errorCode)) { return; } result.setTo(ustr, len); } ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { Locale locale; // The default locale. return createInstance(locale, errorCode); } ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { Locale tempLocale = locale; const ListFormatData* listFormatData = getListFormatData(tempLocale, errorCode); if (U_FAILURE(errorCode)) { return NULL; } ListFormatter* p = new ListFormatter(*listFormatData); if (p == NULL) { errorCode = U_MEMORY_ALLOCATION_ERROR; return NULL; } return p; } ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) { } ListFormatter::~ListFormatter() {} UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems, UnicodeString& appendTo, UErrorCode& errorCode) const { if (U_FAILURE(errorCode)) { return appendTo; } if (nItems > 0) { UnicodeString newString = items[0]; if (nItems == 2) { addNewString(data.twoPattern, newString, items[1], errorCode); } else if (nItems > 2) { addNewString(data.startPattern, newString, items[1], errorCode); int32_t i; for (i = 2; i < nItems - 1; ++i) { addNewString(data.middlePattern, newString, items[i], errorCode); } addNewString(data.endPattern, newString, items[nItems - 1], errorCode); } if (U_SUCCESS(errorCode)) { appendTo += newString; } } return appendTo; } /** * Joins originalString and nextString using the pattern pat and puts the result in * originalString. */ void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString, const UnicodeString& nextString, UErrorCode& errorCode) const { if (U_FAILURE(errorCode)) { return; } int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0); if (p0Offset < 0) { errorCode = U_ILLEGAL_ARGUMENT_ERROR; return; } int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0); if (p1Offset < 0) { errorCode = U_ILLEGAL_ARGUMENT_ERROR; return; } int32_t i, j; const UnicodeString* firstString; const UnicodeString* secondString; if (p0Offset < p1Offset) { i = p0Offset; j = p1Offset; firstString = &originalString; secondString = &nextString; } else { i = p1Offset; j = p0Offset; firstString = &nextString; secondString = &originalString; } UnicodeString result = UnicodeString(pat, 0, i) + *firstString; result += UnicodeString(pat, i+3, j-i-3); result += *secondString; result += UnicodeString(pat, j+3); originalString = result; } U_NAMESPACE_END