/* ******************************************************************************* * * 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" 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 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); addDataToHash("af", "{0} en {1}", "{0}, {1}", "{0}, {1}", "{0} en {1}", errorCode); addDataToHash("am", "{0} \\u12a5\\u1293 {1}", "{0}, {1}", "{0}, {1}", "{0}, \\u12a5\\u1293 {1}", errorCode); addDataToHash("ar", "{0} \\u0648 {1}", "{0}\\u060c {1}", "{0}\\u060c {1}", "{0}\\u060c \\u0648 {1}", errorCode); addDataToHash("bg", "{0} \\u0438 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0438 {1}", errorCode); addDataToHash("bn", "{0} \\u098f\\u09ac\\u0982 {1}", "{0}, {1}", "{0}, {1}", "{0}, \\u098f\\u09ac\\u0982 {1}", errorCode); addDataToHash("bs", "{0} i {1}", "{0}, {1}", "{0}, {1}", "{0} i {1}", errorCode); addDataToHash("ca", "{0} i {1}", "{0}, {1}", "{0}, {1}", "{0} i {1}", errorCode); addDataToHash("cs", "{0} a {1}", "{0}, {1}", "{0}, {1}", "{0} a {1}", errorCode); addDataToHash("da", "{0} og {1}", "{0}, {1}", "{0}, {1}", "{0} og {1}", errorCode); addDataToHash("de", "{0} und {1}", "{0}, {1}", "{0}, {1}", "{0} und {1}", errorCode); addDataToHash("ee", "{0} kple {1}", "{0}, {1}", "{0}, {1}", "{0}, kple {1}", errorCode); addDataToHash("el", "{0} \\u03ba\\u03b1\\u03b9 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u03ba\\u03b1\\u03b9 {1}", errorCode); addDataToHash("en", "{0} and {1}", "{0}, {1}", "{0}, {1}", "{0}, and {1}", errorCode); addDataToHash("es", "{0} y {1}", "{0}, {1}", "{0}, {1}", "{0} y {1}", errorCode); addDataToHash("et", "{0} ja {1}", "{0}, {1}", "{0}, {1}", "{0} ja {1}", errorCode); addDataToHash("eu", "{0} eta {1}", "{0}, {1}", "{0}, {1}", "{0} eta {1}", errorCode); addDataToHash("fa", "{0} \\u0648 {1}", "{0}\\u060c\\u200f {1}", "{0}\\u060c\\u200f {1}", "{0}\\u060c \\u0648 {1}", errorCode); addDataToHash("fi", "{0} ja {1}", "{0}, {1}", "{0}, {1}", "{0} ja {1}", errorCode); addDataToHash("fil", "{0} at {1}", "{0}, {1}", "{0}, {1}", "{0} at {1}", errorCode); addDataToHash("fo", "{0} og {1}", "{0}, {1}", "{0}, {1}", "{0} og {1}", errorCode); addDataToHash("fr", "{0} et {1}", "{0}, {1}", "{0}, {1}", "{0} et {1}", errorCode); addDataToHash("fur", "{0} e {1}", "{0}, {1}", "{0}, {1}", "{0} e {1}", errorCode); addDataToHash("gd", "{0} agus {1}", "{0}, {1}", "{0}, {1}", "{0}, agus {1}", errorCode); addDataToHash("gl", "{0} e {1}", "{0}, {1}", "{0}, {1}", "{0} e {1}", errorCode); addDataToHash("gsw", "{0} und {1}", "{0}, {1}", "{0}, {1}", "{0} und {1}", errorCode); addDataToHash("gu", "{0} \\u0a85\\u0aa8\\u0ac7 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0a85\\u0aa8\\u0ac7 {1}", errorCode); addDataToHash("he", "{0} \\u05d5-{1}", "{0}, {1}", "{0}, {1}", "{0} \\u05d5-{1}", errorCode); addDataToHash("hi", "{0} \\u0914\\u0930 {1}", "{0}, {1}", "{0}, {1}", "{0}, \\u0914\\u0930 {1}", errorCode); addDataToHash("hr", "{0} i {1}", "{0}, {1}", "{0}, {1}", "{0} i {1}", errorCode); addDataToHash("hu", "{0} \\u00e9s {1}", "{0}, {1}", "{0}, {1}", "{0} \\u00e9s {1}", errorCode); addDataToHash("id", "{0} dan {1}", "{0}, {1}", "{0}, {1}", "{0}, dan {1}", errorCode); addDataToHash("is", "{0} og {1}", "{0}, {1}", "{0}, {1}", "{0} og {1}", errorCode); addDataToHash("it", "{0} e {1}", "{0}, {1}", "{0}, {1}", "{0}, e {1}", errorCode); addDataToHash("ja", "{0}\\u3001{1}", "{0}\\u3001{1}", "{0}\\u3001{1}", "{0}\\u3001{1}", errorCode); addDataToHash("ka", "{0} \\u10d3\\u10d0 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u10d3\\u10d0 {1}", errorCode); addDataToHash("kea", "{0} y {1}", "{0}, {1}", "{0}, {1}", "{0} y {1}", errorCode); addDataToHash("kl", "{0} aamma {1}", "{0} aamma {1}", "{0}, {1}", "{0}, {1}", errorCode); addDataToHash("kn", "{0} \\u0cae\\u0ca4\\u0ccd\\u0ca4\\u0cc1 {1}", "{0}, {1}", "{0}, {1}", "{0}, \\u0cae\\u0ca4\\u0ccd\\u0ca4\\u0cc1 {1}", errorCode); addDataToHash("ko", "{0} \\ubc0f {1}", "{0}, {1}", "{0}, {1}", "{0} \\ubc0f {1}", errorCode); addDataToHash("ksh", "{0} un {1}", "{0}, {1}", "{0}, {1}", "{0} un {1}", errorCode); addDataToHash("lt", "{0} ir {1}", "{0}, {1}", "{0}, {1}", "{0} ir {1}", errorCode); addDataToHash("lv", "{0} un {1}", "{0}, {1}", "{0}, {1}", "{0} un {1}", errorCode); addDataToHash("ml", "{0} \\u0d15\\u0d42\\u0d1f\\u0d3e\\u0d24\\u0d46 {1}", "{0}, {1}", "{0}, {1}", "{0}, {1} \\u0d0e\\u0d28\\u0d4d\\u0d28\\u0d3f\\u0d35", errorCode); addDataToHash("mr", "{0} \\u0906\\u0923\\u093f {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0906\\u0923\\u093f {1}", errorCode); addDataToHash("ms", "{0} dan {1}", "{0}, {1}", "{0}, {1}", "{0}, dan {1}", errorCode); addDataToHash("nb", "{0} og {1}", "{0}, {1}", "{0}, {1}", "{0} og {1}", errorCode); addDataToHash("nl", "{0} en {1}", "{0}, {1}", "{0}, {1}", "{0} en {1}", errorCode); addDataToHash("nn", "{0} og {1}", "{0}, {1}", "{0}, {1}", "{0} og {1}", errorCode); addDataToHash("pl", "{0} i {1}", "{0}; {1}", "{0}; {1}", "{0} i {1}", errorCode); addDataToHash("pt", "{0} e {1}", "{0}, {1}", "{0}, {1}", "{0} e {1}", errorCode); addDataToHash("ro", "{0} \\u015fi {1}", "{0}, {1}", "{0}, {1}", "{0} \\u015fi {1}", errorCode); addDataToHash("", "{0}, {1}", "{0}, {1}", "{0}, {1}", "{0}, {1}", errorCode); // root addDataToHash("ru", "{0} \\u0438 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0438 {1}", errorCode); addDataToHash("se", "{0} ja {1}", "{0}, {1}", "{0}, {1}", "{0} ja {1}", errorCode); addDataToHash("sk", "{0} a {1}", "{0}, {1}", "{0}, {1}", "{0} a {1}", errorCode); addDataToHash("sl", "{0} in {1}", "{0}, {1}", "{0}, {1}", "{0} in {1}", errorCode); addDataToHash("sr", "{0} \\u0438 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0438 {1}", errorCode); addDataToHash("sr_Cyrl", "{0} \\u0438 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0438 {1}", errorCode); addDataToHash("sr_Latn", "{0} i {1}", "{0}, {1}", "{0}, {1}", "{0} i {1}", errorCode); addDataToHash("sv", "{0} och {1}", "{0}, {1}", "{0}, {1}", "{0} och {1}", errorCode); addDataToHash("sw", "{0} na {1}", "{0}, {1}", "{0}, {1}", "{0}, na {1}", errorCode); addDataToHash("ta", "{0} \\u0bae\\u0bb1\\u0bcd\\u0bb1\\u0bc1\\u0bae\\u0bcd {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0bae\\u0bb1\\u0bcd\\u0bb1\\u0bc1\\u0bae\\u0bcd {1}", errorCode); addDataToHash("te", "{0} \\u0c2e\\u0c30\\u0c3f\\u0c2f\\u0c41 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0c2e\\u0c30\\u0c3f\\u0c2f\\u0c41 {1}", errorCode); addDataToHash("th", "{0}\\u0e41\\u0e25\\u0e30{1}", "{0} {1}", "{0} {1}", "{0} \\u0e41\\u0e25\\u0e30{1}", errorCode); addDataToHash("tr", "{0} ve {1}", "{0}, {1}", "{0}, {1}", "{0} ve {1}", errorCode); addDataToHash("uk", "{0} \\u0442\\u0430 {1}", "{0}, {1}", "{0}, {1}", "{0} \\u0442\\u0430 {1}", errorCode); addDataToHash("ur", "{0} \\u0627\\u0648\\u0631 {1}", "{0}\\u060c {1}", "{0}\\u060c {1}", "{0}\\u060c \\u0627\\u0648\\u0631 {1}", errorCode); addDataToHash("vi", "{0} v\\u00e0 {1}", "{0}, {1}", "{0}, {1}", "{0} v\\u00e0 {1}", errorCode); addDataToHash("wae", "{0} und {1}", "{0}, {1}", "{0}, {1}", "{0} und {1}", errorCode); addDataToHash("zh", "{0}\\u548c{1}", "{0}\\u3001{1}", "{0}\\u3001{1}", "{0}\\u548c{1}", errorCode); addDataToHash("zu", "I-{0} ne-{1}", "{0}, {1}", "{0}, {1}", "{0}, no-{1}", errorCode); } void ListFormatter::addDataToHash( const char* locale, const char* two, const char* start, const char* middle, const char* end, UErrorCode& errorCode) { if (U_FAILURE(errorCode)) { return; } UnicodeString key(locale, -1, US_INV); ListFormatData* value = new ListFormatData( UnicodeString(two, -1, US_INV).unescape(), UnicodeString(start, -1, US_INV).unescape(), UnicodeString(middle, -1, US_INV).unescape(), UnicodeString(end, -1, US_INV).unescape()); if (value == NULL) { errorCode = U_MEMORY_ALLOCATION_ERROR; return; } listPatternHash->put(key, value, errorCode); } const ListFormatData* ListFormatter::getListFormatData( const Locale& locale, UErrorCode& errorCode) { if (U_FAILURE(errorCode)) { return NULL; } { Mutex m(&listFormatterMutex); if (listPatternHash == NULL) { initializeHash(errorCode); if (U_FAILURE(errorCode)) { return NULL; } } } UnicodeString key(locale.getName(), -1, US_INV); return static_cast(listPatternHash->get(key)); } 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; for (;;) { const ListFormatData* listFormatData = getListFormatData(tempLocale, errorCode); if (U_FAILURE(errorCode)) { return NULL; } if (listFormatData != NULL) { ListFormatter* p = new ListFormatter(*listFormatData); if (p == NULL) { errorCode = U_MEMORY_ALLOCATION_ERROR; return NULL; } return p; } errorCode = U_ZERO_ERROR; Locale correctLocale; getFallbackLocale(tempLocale, correctLocale, errorCode); if (U_FAILURE(errorCode)) { return NULL; } if (correctLocale.isBogus()) { return createInstance(Locale::getRoot(), errorCode); } tempLocale = correctLocale; } } ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) { } ListFormatter::~ListFormatter() {} void ListFormatter::getFallbackLocale(const Locale& in, Locale& out, UErrorCode& errorCode) { if (uprv_strcmp(in.getName(), "zh_TW") == 0) { out = Locale::getTraditionalChinese(); } else { const char* localeString = in.getName(); const char* extStart = locale_getKeywordsStart(localeString); if (extStart == NULL) { extStart = uprv_strchr(localeString, 0); } const char* last = extStart; // TODO: Check whether uloc_getParent() will work here. while (last > localeString && *(last - 1) != '_') { --last; } // Truncate empty segment. while (last > localeString) { if (*(last-1) != '_') { break; } --last; } size_t localePortionLen = last - localeString; CharString fullLocale; fullLocale.append(localeString, localePortionLen, errorCode).append(extStart, errorCode); if (U_FAILURE(errorCode)) { return; } out = Locale(fullLocale.data()); } } 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); int 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; } UOBJECT_DEFINE_NO_RTTI_IMPLEMENTATION(ListFormatter) U_NAMESPACE_END