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