]> git.saurik.com Git - apple/icu.git/blob - icuSources/common/listformatter.cpp
ICU-531.31.tar.gz
[apple/icu.git] / icuSources / common / listformatter.cpp
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