1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/intl/icu/source/common/listformatter.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,266 @@ 1.4 +/* 1.5 +******************************************************************************* 1.6 +* 1.7 +* Copyright (C) 2013, International Business Machines 1.8 +* Corporation and others. All Rights Reserved. 1.9 +* 1.10 +******************************************************************************* 1.11 +* file name: listformatter.cpp 1.12 +* encoding: US-ASCII 1.13 +* tab size: 8 (not used) 1.14 +* indentation:4 1.15 +* 1.16 +* created on: 2012aug27 1.17 +* created by: Umesh P. Nair 1.18 +*/ 1.19 + 1.20 +#include "unicode/listformatter.h" 1.21 +#include "mutex.h" 1.22 +#include "hash.h" 1.23 +#include "cstring.h" 1.24 +#include "ulocimp.h" 1.25 +#include "charstr.h" 1.26 +#include "ucln_cmn.h" 1.27 +#include "uresimp.h" 1.28 + 1.29 +U_NAMESPACE_BEGIN 1.30 + 1.31 +static Hashtable* listPatternHash = NULL; 1.32 +static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; 1.33 +static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}" 1.34 +static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}" 1.35 +static const char *STANDARD_STYLE = "standard"; 1.36 + 1.37 +U_CDECL_BEGIN 1.38 +static UBool U_CALLCONV uprv_listformatter_cleanup() { 1.39 + delete listPatternHash; 1.40 + listPatternHash = NULL; 1.41 + return TRUE; 1.42 +} 1.43 + 1.44 +static void U_CALLCONV 1.45 +uprv_deleteListFormatData(void *obj) { 1.46 + delete static_cast<ListFormatData *>(obj); 1.47 +} 1.48 + 1.49 +U_CDECL_END 1.50 + 1.51 +static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode); 1.52 +static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode); 1.53 + 1.54 +ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) { 1.55 +} 1.56 + 1.57 +ListFormatter& ListFormatter::operator=(const ListFormatter& other) { 1.58 + data = other.data; 1.59 + return *this; 1.60 +} 1.61 + 1.62 +void ListFormatter::initializeHash(UErrorCode& errorCode) { 1.63 + if (U_FAILURE(errorCode)) { 1.64 + return; 1.65 + } 1.66 + 1.67 + listPatternHash = new Hashtable(); 1.68 + if (listPatternHash == NULL) { 1.69 + errorCode = U_MEMORY_ALLOCATION_ERROR; 1.70 + return; 1.71 + } 1.72 + 1.73 + listPatternHash->setValueDeleter(uprv_deleteListFormatData); 1.74 + ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup); 1.75 + 1.76 +} 1.77 + 1.78 +const ListFormatData* ListFormatter::getListFormatData( 1.79 + const Locale& locale, const char *style, UErrorCode& errorCode) { 1.80 + if (U_FAILURE(errorCode)) { 1.81 + return NULL; 1.82 + } 1.83 + CharString keyBuffer(locale.getName(), errorCode); 1.84 + keyBuffer.append(':', errorCode).append(style, errorCode); 1.85 + UnicodeString key(keyBuffer.data(), -1, US_INV); 1.86 + ListFormatData* result = NULL; 1.87 + { 1.88 + Mutex m(&listFormatterMutex); 1.89 + if (listPatternHash == NULL) { 1.90 + initializeHash(errorCode); 1.91 + if (U_FAILURE(errorCode)) { 1.92 + return NULL; 1.93 + } 1.94 + } 1.95 + result = static_cast<ListFormatData*>(listPatternHash->get(key)); 1.96 + } 1.97 + if (result != NULL) { 1.98 + return result; 1.99 + } 1.100 + result = loadListFormatData(locale, style, errorCode); 1.101 + if (U_FAILURE(errorCode)) { 1.102 + return NULL; 1.103 + } 1.104 + 1.105 + { 1.106 + Mutex m(&listFormatterMutex); 1.107 + ListFormatData* temp = static_cast<ListFormatData*>(listPatternHash->get(key)); 1.108 + if (temp != NULL) { 1.109 + delete result; 1.110 + result = temp; 1.111 + } else { 1.112 + listPatternHash->put(key, result, errorCode); 1.113 + if (U_FAILURE(errorCode)) { 1.114 + return NULL; 1.115 + } 1.116 + } 1.117 + } 1.118 + return result; 1.119 +} 1.120 + 1.121 +static ListFormatData* loadListFormatData( 1.122 + const Locale& locale, const char * style, UErrorCode& errorCode) { 1.123 + UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); 1.124 + if (U_FAILURE(errorCode)) { 1.125 + ures_close(rb); 1.126 + return NULL; 1.127 + } 1.128 + rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); 1.129 + rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode); 1.130 + 1.131 + // TODO(Travis Keep): This is a hack until fallbacks can be added for 1.132 + // listPattern/duration and listPattern/duration-narrow in CLDR. 1.133 + if (errorCode == U_MISSING_RESOURCE_ERROR) { 1.134 + errorCode = U_ZERO_ERROR; 1.135 + rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode); 1.136 + } 1.137 + if (U_FAILURE(errorCode)) { 1.138 + ures_close(rb); 1.139 + return NULL; 1.140 + } 1.141 + UnicodeString two, start, middle, end; 1.142 + getStringByKey(rb, "2", two, errorCode); 1.143 + getStringByKey(rb, "start", start, errorCode); 1.144 + getStringByKey(rb, "middle", middle, errorCode); 1.145 + getStringByKey(rb, "end", end, errorCode); 1.146 + ures_close(rb); 1.147 + if (U_FAILURE(errorCode)) { 1.148 + return NULL; 1.149 + } 1.150 + ListFormatData* result = new ListFormatData(two, start, middle, end); 1.151 + if (result == NULL) { 1.152 + errorCode = U_MEMORY_ALLOCATION_ERROR; 1.153 + return NULL; 1.154 + } 1.155 + return result; 1.156 +} 1.157 + 1.158 +static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) { 1.159 + int32_t len; 1.160 + const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode); 1.161 + if (U_FAILURE(errorCode)) { 1.162 + return; 1.163 + } 1.164 + result.setTo(ustr, len); 1.165 +} 1.166 + 1.167 +ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { 1.168 + Locale locale; // The default locale. 1.169 + return createInstance(locale, errorCode); 1.170 +} 1.171 + 1.172 +ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { 1.173 + return createInstance(locale, STANDARD_STYLE, errorCode); 1.174 +} 1.175 + 1.176 +ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { 1.177 + Locale tempLocale = locale; 1.178 + const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode); 1.179 + if (U_FAILURE(errorCode)) { 1.180 + return NULL; 1.181 + } 1.182 + ListFormatter* p = new ListFormatter(listFormatData); 1.183 + if (p == NULL) { 1.184 + errorCode = U_MEMORY_ALLOCATION_ERROR; 1.185 + return NULL; 1.186 + } 1.187 + return p; 1.188 +} 1.189 + 1.190 + 1.191 +ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) { 1.192 +} 1.193 + 1.194 +ListFormatter::~ListFormatter() {} 1.195 + 1.196 +UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems, 1.197 + UnicodeString& appendTo, UErrorCode& errorCode) const { 1.198 + if (U_FAILURE(errorCode)) { 1.199 + return appendTo; 1.200 + } 1.201 + if (data == NULL) { 1.202 + errorCode = U_INVALID_STATE_ERROR; 1.203 + return appendTo; 1.204 + } 1.205 + 1.206 + if (nItems > 0) { 1.207 + UnicodeString newString = items[0]; 1.208 + if (nItems == 2) { 1.209 + addNewString(data->twoPattern, newString, items[1], errorCode); 1.210 + } else if (nItems > 2) { 1.211 + addNewString(data->startPattern, newString, items[1], errorCode); 1.212 + int32_t i; 1.213 + for (i = 2; i < nItems - 1; ++i) { 1.214 + addNewString(data->middlePattern, newString, items[i], errorCode); 1.215 + } 1.216 + addNewString(data->endPattern, newString, items[nItems - 1], errorCode); 1.217 + } 1.218 + if (U_SUCCESS(errorCode)) { 1.219 + appendTo += newString; 1.220 + } 1.221 + } 1.222 + return appendTo; 1.223 +} 1.224 + 1.225 +/** 1.226 + * Joins originalString and nextString using the pattern pat and puts the result in 1.227 + * originalString. 1.228 + */ 1.229 +void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString, 1.230 + const UnicodeString& nextString, UErrorCode& errorCode) const { 1.231 + if (U_FAILURE(errorCode)) { 1.232 + return; 1.233 + } 1.234 + 1.235 + int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0); 1.236 + if (p0Offset < 0) { 1.237 + errorCode = U_ILLEGAL_ARGUMENT_ERROR; 1.238 + return; 1.239 + } 1.240 + int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0); 1.241 + if (p1Offset < 0) { 1.242 + errorCode = U_ILLEGAL_ARGUMENT_ERROR; 1.243 + return; 1.244 + } 1.245 + 1.246 + int32_t i, j; 1.247 + 1.248 + const UnicodeString* firstString; 1.249 + const UnicodeString* secondString; 1.250 + if (p0Offset < p1Offset) { 1.251 + i = p0Offset; 1.252 + j = p1Offset; 1.253 + firstString = &originalString; 1.254 + secondString = &nextString; 1.255 + } else { 1.256 + i = p1Offset; 1.257 + j = p0Offset; 1.258 + firstString = &nextString; 1.259 + secondString = &originalString; 1.260 + } 1.261 + 1.262 + UnicodeString result = UnicodeString(pat, 0, i) + *firstString; 1.263 + result += UnicodeString(pat, i+3, j-i-3); 1.264 + result += *secondString; 1.265 + result += UnicodeString(pat, j+3); 1.266 + originalString = result; 1.267 +} 1.268 + 1.269 +U_NAMESPACE_END