1 /* 2 ******************************************************************************* 3 * 4 * Copyright (C) 2013, 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 "mutex.h" 19 #include "hash.h" 20 #include "cstring.h" 21 #include "ulocimp.h" 22 #include "charstr.h" 23 #include "ucln_cmn.h" 24 #include "uresimp.h" 25 26 U_NAMESPACE_BEGIN 27 28 static Hashtable* listPatternHash = NULL; 29 static UMutex listFormatterMutex = U_MUTEX_INITIALIZER; 30 static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}" 31 static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}" 32 static const char *STANDARD_STYLE = "standard"; 33 34 U_CDECL_BEGIN 35 static UBool U_CALLCONV uprv_listformatter_cleanup() { 36 delete listPatternHash; 37 listPatternHash = NULL; 38 return TRUE; 39 } 40 41 static void U_CALLCONV 42 uprv_deleteListFormatData(void *obj) { 43 delete static_cast<ListFormatData *>(obj); 44 } 45 46 U_CDECL_END 47 48 static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode); 49 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode); 50 51 ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) { 52 } 53 54 ListFormatter& ListFormatter::operator=(const ListFormatter& other) { 55 data = other.data; 56 return *this; 57 } 58 59 void ListFormatter::initializeHash(UErrorCode& errorCode) { 60 if (U_FAILURE(errorCode)) { 61 return; 62 } 63 64 listPatternHash = new Hashtable(); 65 if (listPatternHash == NULL) { 66 errorCode = U_MEMORY_ALLOCATION_ERROR; 67 return; 68 } 69 70 listPatternHash->setValueDeleter(uprv_deleteListFormatData); 71 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup); 72 73 } 74 75 const ListFormatData* ListFormatter::getListFormatData( 76 const Locale& locale, const char *style, UErrorCode& errorCode) { 77 if (U_FAILURE(errorCode)) { 78 return NULL; 79 } 80 CharString keyBuffer(locale.getName(), errorCode); 81 keyBuffer.append(':', errorCode).append(style, errorCode); 82 UnicodeString key(keyBuffer.data(), -1, US_INV); 83 ListFormatData* result = NULL; 84 { 85 Mutex m(&listFormatterMutex); 86 if (listPatternHash == NULL) { 87 initializeHash(errorCode); 88 if (U_FAILURE(errorCode)) { 89 return NULL; 90 } 91 } 92 result = static_cast<ListFormatData*>(listPatternHash->get(key)); 93 } 94 if (result != NULL) { 95 return result; 96 } 97 result = loadListFormatData(locale, style, errorCode); 98 if (U_FAILURE(errorCode)) { 99 return NULL; 100 } 101 102 { 103 Mutex m(&listFormatterMutex); 104 ListFormatData* temp = static_cast<ListFormatData*>(listPatternHash->get(key)); 105 if (temp != NULL) { 106 delete result; 107 result = temp; 108 } else { 109 listPatternHash->put(key, result, errorCode); 110 if (U_FAILURE(errorCode)) { 111 return NULL; 112 } 113 } 114 } 115 return result; 116 } 117 118 static ListFormatData* loadListFormatData( 119 const Locale& locale, const char * style, UErrorCode& errorCode) { 120 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode); 121 if (U_FAILURE(errorCode)) { 122 ures_close(rb); 123 return NULL; 124 } 125 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode); 126 rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode); 127 128 // TODO(Travis Keep): This is a hack until fallbacks can be added for 129 // listPattern/duration and listPattern/duration-narrow in CLDR. 130 if (errorCode == U_MISSING_RESOURCE_ERROR) { 131 errorCode = U_ZERO_ERROR; 132 rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode); 133 } 134 if (U_FAILURE(errorCode)) { 135 ures_close(rb); 136 return NULL; 137 } 138 UnicodeString two, start, middle, end; 139 getStringByKey(rb, "2", two, errorCode); 140 getStringByKey(rb, "start", start, errorCode); 141 getStringByKey(rb, "middle", middle, errorCode); 142 getStringByKey(rb, "end", end, errorCode); 143 ures_close(rb); 144 if (U_FAILURE(errorCode)) { 145 return NULL; 146 } 147 ListFormatData* result = new ListFormatData(two, start, middle, end); 148 if (result == NULL) { 149 errorCode = U_MEMORY_ALLOCATION_ERROR; 150 return NULL; 151 } 152 return result; 153 } 154 155 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) { 156 int32_t len; 157 const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode); 158 if (U_FAILURE(errorCode)) { 159 return; 160 } 161 result.setTo(ustr, len); 162 } 163 164 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) { 165 Locale locale; // The default locale. 166 return createInstance(locale, errorCode); 167 } 168 169 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) { 170 return createInstance(locale, STANDARD_STYLE, errorCode); 171 } 172 173 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) { 174 Locale tempLocale = locale; 175 const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode); 176 if (U_FAILURE(errorCode)) { 177 return NULL; 178 } 179 ListFormatter* p = new ListFormatter(listFormatData); 180 if (p == NULL) { 181 errorCode = U_MEMORY_ALLOCATION_ERROR; 182 return NULL; 183 } 184 return p; 185 } 186 187 188 ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) { 189 } 190 191 ListFormatter::~ListFormatter() {} 192 193 UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems, 194 UnicodeString& appendTo, UErrorCode& errorCode) const { 195 if (U_FAILURE(errorCode)) { 196 return appendTo; 197 } 198 if (data == NULL) { 199 errorCode = U_INVALID_STATE_ERROR; 200 return appendTo; 201 } 202 203 if (nItems > 0) { 204 UnicodeString newString = items[0]; 205 if (nItems == 2) { 206 addNewString(data->twoPattern, newString, items[1], errorCode); 207 } else if (nItems > 2) { 208 addNewString(data->startPattern, newString, items[1], errorCode); 209 int32_t i; 210 for (i = 2; i < nItems - 1; ++i) { 211 addNewString(data->middlePattern, newString, items[i], errorCode); 212 } 213 addNewString(data->endPattern, newString, items[nItems - 1], errorCode); 214 } 215 if (U_SUCCESS(errorCode)) { 216 appendTo += newString; 217 } 218 } 219 return appendTo; 220 } 221 222 /** 223 * Joins originalString and nextString using the pattern pat and puts the result in 224 * originalString. 225 */ 226 void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString, 227 const UnicodeString& nextString, UErrorCode& errorCode) const { 228 if (U_FAILURE(errorCode)) { 229 return; 230 } 231 232 int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0); 233 if (p0Offset < 0) { 234 errorCode = U_ILLEGAL_ARGUMENT_ERROR; 235 return; 236 } 237 int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0); 238 if (p1Offset < 0) { 239 errorCode = U_ILLEGAL_ARGUMENT_ERROR; 240 return; 241 } 242 243 int32_t i, j; 244 245 const UnicodeString* firstString; 246 const UnicodeString* secondString; 247 if (p0Offset < p1Offset) { 248 i = p0Offset; 249 j = p1Offset; 250 firstString = &originalString; 251 secondString = &nextString; 252 } else { 253 i = p1Offset; 254 j = p0Offset; 255 firstString = &nextString; 256 secondString = &originalString; 257 } 258 259 UnicodeString result = UnicodeString(pat, 0, i) + *firstString; 260 result += UnicodeString(pat, i+3, j-i-3); 261 result += *secondString; 262 result += UnicodeString(pat, j+3); 263 originalString = result; 264 } 265 266 U_NAMESPACE_END 267