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