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