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