1// Copyright (C) 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html 3/* 4******************************************************************************* 5* 6* Copyright (C) 2013-2016, International Business Machines 7* Corporation and others. All Rights Reserved. 8* 9******************************************************************************* 10* file name: listformatter.cpp 11* encoding: US-ASCII 12* tab size: 8 (not used) 13* indentation:4 14* 15* created on: 2012aug27 16* created by: Umesh P. Nair 17*/ 18 19#include "unicode/listformatter.h" 20#include "unicode/simpleformatter.h" 21#include "mutex.h" 22#include "hash.h" 23#include "cstring.h" 24#include "ulocimp.h" 25#include "charstr.h" 26#include "ucln_cmn.h" 27#include "uresimp.h" 28 29U_NAMESPACE_BEGIN 30 31struct ListFormatInternal : public UMemory { 32 SimpleFormatter twoPattern; 33 SimpleFormatter startPattern; 34 SimpleFormatter middlePattern; 35 SimpleFormatter endPattern; 36 37ListFormatInternal( 38 const UnicodeString& two, 39 const UnicodeString& start, 40 const UnicodeString& middle, 41 const UnicodeString& end, 42 UErrorCode &errorCode) : 43 twoPattern(two, 2, 2, errorCode), 44 startPattern(start, 2, 2, errorCode), 45 middlePattern(middle, 2, 2, errorCode), 46 endPattern(end, 2, 2, errorCode) {} 47 48ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) : 49 twoPattern(data.twoPattern, errorCode), 50 startPattern(data.startPattern, errorCode), 51 middlePattern(data.middlePattern, errorCode), 52 endPattern(data.endPattern, errorCode) { } 53 54ListFormatInternal(const ListFormatInternal &other) : 55 twoPattern(other.twoPattern), 56 startPattern(other.startPattern), 57 middlePattern(other.middlePattern), 58 endPattern(other.endPattern) { } 59}; 60 61 62 63static Hashtable* listPatternHash = NULL; 64static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; 65static const char *STANDARD_STYLE = "standard"; 66 67U_CDECL_BEGIN 68static UBool U_CALLCONV uprv_listformatter_cleanup() { 69 delete listPatternHash; 70 listPatternHash = NULL; 71 return TRUE; 72} 73 74static void U_CALLCONV 75uprv_deleteListFormatInternal(void *obj) { 76 delete static_cast<ListFormatInternal *>(obj); 77} 78 79U_CDECL_END 80 81static ListFormatInternal* loadListFormatInternal( 82 const Locale& locale, 83 const char* style, 84 UErrorCode& errorCode); 85 86static void getStringByKey( 87 const UResourceBundle* rb, 88 const char* key, 89 UnicodeString& result, 90 UErrorCode& errorCode); 91 92ListFormatter::ListFormatter(const ListFormatter& other) : 93 owned(other.owned), data(other.data) { 94 if (other.owned != NULL) { 95 owned = new ListFormatInternal(*other.owned); 96 data = owned; 97 } 98} 99 100ListFormatter& ListFormatter::operator=(const ListFormatter& other) { 101 if (this == &other) { 102 return *this; 103 } 104 delete owned; 105 if (other.owned) { 106 owned = new ListFormatInternal(*other.owned); 107 data = owned; 108 } else { 109 owned = NULL; 110 data = other.data; 111 } 112 return *this; 113} 114 115void ListFormatter::initializeHash(UErrorCode& errorCode) { 116 if (U_FAILURE(errorCode)) { 117 return; 118 } 119 120 listPatternHash = new Hashtable(); 121 if (listPatternHash == NULL) { 122 errorCode = U_MEMORY_ALLOCATION_ERROR; 123 return; 124 } 125 126 listPatternHash->setValueDeleter(uprv_deleteListFormatInternal); 127 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup); 128 129} 130 131const ListFormatInternal* ListFormatter::getListFormatInternal( 132 const Locale& locale, const char *style, UErrorCode& errorCode) { 133 if (U_FAILURE(errorCode)) { 134 return NULL; 135 } 136 CharString keyBuffer(locale.getName(), errorCode); 137 keyBuffer.append(':', errorCode).append(style, errorCode); 138 UnicodeString key(keyBuffer.data(), -1, US_INV); 139 ListFormatInternal* result = NULL; 140 { 141 Mutex m(&listFormatterMutex); 142 if (listPatternHash == NULL) { 143 initializeHash(errorCode); 144 if (U_FAILURE(errorCode)) { 145 return NULL; 146 } 147 } 148 result = static_cast<ListFormatInternal*>(listPatternHash->get(key)); 149 } 150 if (result != NULL) { 151 return result; 152 } 153 result = loadListFormatInternal(locale, style, errorCode); 154 if (U_FAILURE(errorCode)) { 155 return NULL; 156 } 157 158 { 159 Mutex m(&listFormatterMutex); 160 ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key)); 161 if (temp != NULL) { 162 delete result; 163 result = temp; 164 } else { 165 listPatternHash->put(key, result, errorCode); 166 if (U_FAILURE(errorCode)) { 167 return NULL; 168 } 169 } 170 } 171 return result; 172} 173 174static ListFormatInternal* loadListFormatInternal( 175 const Locale& locale, const char * style, UErrorCode& errorCode) { 176 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); 177 if (U_FAILURE(errorCode)) { 178 ures_close(rb); 179 return NULL; 180 } 181 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); 182 rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode); 183 184 if (U_FAILURE(errorCode)) { 185 ures_close(rb); 186 return NULL; 187 } 188 UnicodeString two, start, middle, end; 189 getStringByKey(rb, "2", two, errorCode); 190 getStringByKey(rb, "start", start, errorCode); 191 getStringByKey(rb, "middle", middle, errorCode); 192 getStringByKey(rb, "end", end, errorCode); 193 ures_close(rb); 194 if (U_FAILURE(errorCode)) { 195 return NULL; 196 } 197 ListFormatInternal* result = new ListFormatInternal(two, start, middle, end, errorCode); 198 if (result == NULL) { 199 errorCode = U_MEMORY_ALLOCATION_ERROR; 200 return NULL; 201 } 202 if (U_FAILURE(errorCode)) { 203 delete result; 204 return NULL; 205 } 206 return result; 207} 208 209static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) { 210 int32_t len; 211 const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode); 212 if (U_FAILURE(errorCode)) { 213 return; 214 } 215 result.setTo(ustr, len); 216} 217 218ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { 219 Locale locale; // The default locale. 220 return createInstance(locale, errorCode); 221} 222 223ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { 224 return createInstance(locale, STANDARD_STYLE, errorCode); 225} 226 227ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { 228 Locale tempLocale = locale; 229 const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode); 230 if (U_FAILURE(errorCode)) { 231 return NULL; 232 } 233 ListFormatter* p = new ListFormatter(listFormatInternal); 234 if (p == NULL) { 235 errorCode = U_MEMORY_ALLOCATION_ERROR; 236 return NULL; 237 } 238 return p; 239} 240 241ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) { 242 owned = new ListFormatInternal(listFormatData, errorCode); 243 data = owned; 244} 245 246ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) { 247} 248 249ListFormatter::~ListFormatter() { 250 delete owned; 251} 252 253/** 254 * Joins first and second using the pattern pat. 255 * On entry offset is an offset into first or -1 if offset unspecified. 256 * On exit offset is offset of second in result if recordOffset was set 257 * Otherwise if it was >=0 it is set to point into result where it used 258 * to point into first. On exit, result is the join of first and second 259 * according to pat. Any previous value of result gets replaced. 260 */ 261static void joinStringsAndReplace( 262 const SimpleFormatter& pat, 263 const UnicodeString& first, 264 const UnicodeString& second, 265 UnicodeString &result, 266 UBool recordOffset, 267 int32_t &offset, 268 UErrorCode& errorCode) { 269 if (U_FAILURE(errorCode)) { 270 return; 271 } 272 const UnicodeString *params[2] = {&first, &second}; 273 int32_t offsets[2]; 274 pat.formatAndReplace( 275 params, 276 UPRV_LENGTHOF(params), 277 result, 278 offsets, 279 UPRV_LENGTHOF(offsets), 280 errorCode); 281 if (U_FAILURE(errorCode)) { 282 return; 283 } 284 if (offsets[0] == -1 || offsets[1] == -1) { 285 errorCode = U_INVALID_FORMAT_ERROR; 286 return; 287 } 288 if (recordOffset) { 289 offset = offsets[1]; 290 } else if (offset >= 0) { 291 offset += offsets[0]; 292 } 293} 294 295UnicodeString& ListFormatter::format( 296 const UnicodeString items[], 297 int32_t nItems, 298 UnicodeString& appendTo, 299 UErrorCode& errorCode) const { 300 int32_t offset; 301 return format(items, nItems, appendTo, -1, offset, errorCode); 302} 303 304UnicodeString& ListFormatter::format( 305 const UnicodeString items[], 306 int32_t nItems, 307 UnicodeString& appendTo, 308 int32_t index, 309 int32_t &offset, 310 UErrorCode& errorCode) const { 311 offset = -1; 312 if (U_FAILURE(errorCode)) { 313 return appendTo; 314 } 315 if (data == NULL) { 316 errorCode = U_INVALID_STATE_ERROR; 317 return appendTo; 318 } 319 320 if (nItems <= 0) { 321 return appendTo; 322 } 323 if (nItems == 1) { 324 if (index == 0) { 325 offset = appendTo.length(); 326 } 327 appendTo.append(items[0]); 328 return appendTo; 329 } 330 UnicodeString result(items[0]); 331 if (index == 0) { 332 offset = 0; 333 } 334 joinStringsAndReplace( 335 nItems == 2 ? data->twoPattern : data->startPattern, 336 result, 337 items[1], 338 result, 339 index == 1, 340 offset, 341 errorCode); 342 if (nItems > 2) { 343 for (int32_t i = 2; i < nItems - 1; ++i) { 344 joinStringsAndReplace( 345 data->middlePattern, 346 result, 347 items[i], 348 result, 349 index == i, 350 offset, 351 errorCode); 352 } 353 joinStringsAndReplace( 354 data->endPattern, 355 result, 356 items[nItems - 1], 357 result, 358 index == nItems - 1, 359 offset, 360 errorCode); 361 } 362 if (U_SUCCESS(errorCode)) { 363 if (offset >= 0) { 364 offset += appendTo.length(); 365 } 366 appendTo += result; 367 } 368 return appendTo; 369} 370 371U_NAMESPACE_END 372