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 27 #define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0])) 28 29 U_NAMESPACE_BEGIN 30 31 struct ListFormatInternal : public UMemory { 32 SimplePatternFormatter twoPattern; 33 SimplePatternFormatter startPattern; 34 SimplePatternFormatter middlePattern; 35 SimplePatternFormatter endPattern; 36 37 ListFormatInternal( 38 const UnicodeString& two, 39 const UnicodeString& start, 40 const UnicodeString& middle, 41 const UnicodeString& end) : 42 twoPattern(two), 43 startPattern(start), 44 middlePattern(middle), 45 endPattern(end) {} 46 47 ListFormatInternal(const ListFormatData &data) : 48 twoPattern(data.twoPattern), 49 startPattern(data.startPattern), 50 middlePattern(data.middlePattern), 51 endPattern(data.endPattern) { } 52 53 ListFormatInternal(const ListFormatInternal &other) : 54 twoPattern(other.twoPattern), 55 startPattern(other.startPattern), 56 middlePattern(other.middlePattern), 57 endPattern(other.endPattern) { } 58 }; 59 60 61 62 static Hashtable* listPatternHash = NULL; 63 static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; 64 static const char *STANDARD_STYLE = "standard"; 65 66 U_CDECL_BEGIN 67 static UBool U_CALLCONV uprv_listformatter_cleanup() { 68 delete listPatternHash; 69 listPatternHash = NULL; 70 return TRUE; 71 } 72 73 static void U_CALLCONV 74 uprv_deleteListFormatInternal(void *obj) { 75 delete static_cast<ListFormatInternal *>(obj); 76 } 77 78 U_CDECL_END 79 80 static ListFormatInternal* loadListFormatInternal( 81 const Locale& locale, 82 const char* style, 83 UErrorCode& errorCode); 84 85 static void getStringByKey( 86 const UResourceBundle* rb, 87 const char* key, 88 UnicodeString& result, 89 UErrorCode& errorCode); 90 91 ListFormatter::ListFormatter(const ListFormatter& other) : 92 owned(other.owned), data(other.data) { 93 if (other.owned != NULL) { 94 owned = new ListFormatInternal(*other.owned); 95 data = owned; 96 } 97 } 98 99 ListFormatter& ListFormatter::operator=(const ListFormatter& other) { 100 if (this == &other) { 101 return *this; 102 } 103 delete owned; 104 if (other.owned) { 105 owned = new ListFormatInternal(*other.owned); 106 data = owned; 107 } else { 108 owned = NULL; 109 data = other.data; 110 } 111 return *this; 112 } 113 114 void ListFormatter::initializeHash(UErrorCode& errorCode) { 115 if (U_FAILURE(errorCode)) { 116 return; 117 } 118 119 listPatternHash = new Hashtable(); 120 if (listPatternHash == NULL) { 121 errorCode = U_MEMORY_ALLOCATION_ERROR; 122 return; 123 } 124 125 listPatternHash->setValueDeleter(uprv_deleteListFormatInternal); 126 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup); 127 128 } 129 130 const ListFormatInternal* ListFormatter::getListFormatInternal( 131 const Locale& locale, const char *style, UErrorCode& errorCode) { 132 if (U_FAILURE(errorCode)) { 133 return NULL; 134 } 135 CharString keyBuffer(locale.getName(), errorCode); 136 keyBuffer.append(':', errorCode).append(style, errorCode); 137 UnicodeString key(keyBuffer.data(), -1, US_INV); 138 ListFormatInternal* result = NULL; 139 { 140 Mutex m(&listFormatterMutex); 141 if (listPatternHash == NULL) { 142 initializeHash(errorCode); 143 if (U_FAILURE(errorCode)) { 144 return NULL; 145 } 146 } 147 result = static_cast<ListFormatInternal*>(listPatternHash->get(key)); 148 } 149 if (result != NULL) { 150 return result; 151 } 152 result = loadListFormatInternal(locale, style, errorCode); 153 if (U_FAILURE(errorCode)) { 154 return NULL; 155 } 156 157 { 158 Mutex m(&listFormatterMutex); 159 ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key)); 160 if (temp != NULL) { 161 delete result; 162 result = temp; 163 } else { 164 listPatternHash->put(key, result, errorCode); 165 if (U_FAILURE(errorCode)) { 166 return NULL; 167 } 168 } 169 } 170 return result; 171 } 172 173 static ListFormatInternal* loadListFormatInternal( 174 const Locale& locale, const char * style, UErrorCode& errorCode) { 175 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); 176 if (U_FAILURE(errorCode)) { 177 ures_close(rb); 178 return NULL; 179 } 180 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); 181 rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode); 182 183 // TODO(Travis Keep): This is a hack until fallbacks can be added for 184 // listPattern/duration and listPattern/duration-narrow in CLDR. 185 if (errorCode == U_MISSING_RESOURCE_ERROR) { 186 errorCode = U_ZERO_ERROR; 187 rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode); 188 } 189 if (U_FAILURE(errorCode)) { 190 ures_close(rb); 191 return NULL; 192 } 193 UnicodeString two, start, middle, end; 194 getStringByKey(rb, "2", two, errorCode); 195 getStringByKey(rb, "start", start, errorCode); 196 getStringByKey(rb, "middle", middle, errorCode); 197 getStringByKey(rb, "end", end, errorCode); 198 ures_close(rb); 199 if (U_FAILURE(errorCode)) { 200 return NULL; 201 } 202 ListFormatInternal* result = new ListFormatInternal(two, start, middle, end); 203 if (result == NULL) { 204 errorCode = U_MEMORY_ALLOCATION_ERROR; 205 return NULL; 206 } 207 return result; 208 } 209 210 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) { 211 int32_t len; 212 const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode); 213 if (U_FAILURE(errorCode)) { 214 return; 215 } 216 result.setTo(ustr, len); 217 } 218 219 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { 220 Locale locale; // The default locale. 221 return createInstance(locale, errorCode); 222 } 223 224 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { 225 return createInstance(locale, STANDARD_STYLE, errorCode); 226 } 227 228 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { 229 Locale tempLocale = locale; 230 const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode); 231 if (U_FAILURE(errorCode)) { 232 return NULL; 233 } 234 ListFormatter* p = new ListFormatter(listFormatInternal); 235 if (p == NULL) { 236 errorCode = U_MEMORY_ALLOCATION_ERROR; 237 return NULL; 238 } 239 return p; 240 } 241 242 ListFormatter::ListFormatter(const ListFormatData& listFormatData) { 243 owned = new ListFormatInternal(listFormatData); 244 data = owned; 245 } 246 247 ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) { 248 } 249 250 ListFormatter::~ListFormatter() { 251 delete owned; 252 } 253 254 /** 255 * Joins first and second using the pattern pat. 256 * On entry offset is an offset into first or -1 if offset unspecified. 257 * On exit offset is offset of second in result if recordOffset was set 258 * Otherwise if it was >=0 it is set to point into result where it used 259 * to point into first. 260 */ 261 static void joinStrings( 262 const SimplePatternFormatter& 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.format( 275 params, 276 LENGTHOF(params), 277 result, 278 offsets, 279 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 295 UnicodeString& 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 304 UnicodeString& 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 if (nItems == 2) { 331 if (index == 0) { 332 offset = 0; 333 } 334 joinStrings( 335 data->twoPattern, 336 items[0], 337 items[1], 338 appendTo, 339 index == 1, 340 offset, 341 errorCode); 342 return appendTo; 343 } 344 UnicodeString temp[2]; 345 if (index == 0) { 346 offset = 0; 347 } 348 joinStrings( 349 data->startPattern, 350 items[0], 351 items[1], 352 temp[0], 353 index == 1, 354 offset, 355 errorCode); 356 int32_t i; 357 int32_t pos = 0; 358 int32_t npos = 1; 359 for (i = 2; i < nItems - 1; ++i) { 360 temp[npos].remove(); 361 joinStrings( 362 data->middlePattern, 363 temp[pos], 364 items[i], 365 temp[npos], 366 index == i, 367 offset, 368 errorCode); 369 pos = npos; 370 npos = (pos + 1) & 1; 371 } 372 temp[npos].remove(); 373 joinStrings( 374 data->endPattern, 375 temp[pos], 376 items[nItems - 1], 377 temp[npos], 378 index == nItems - 1, 379 offset, 380 errorCode); 381 if (U_SUCCESS(errorCode)) { 382 if (offset >= 0) { 383 offset += appendTo.length(); 384 } 385 appendTo += temp[npos]; 386 } 387 return appendTo; 388 } 389 390 U_NAMESPACE_END 391