]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/listformatter.cpp
ICU-57163.0.1.tar.gz
[apple/icu.git] / icuSources / common / listformatter.cpp
CommitLineData
51004dcb
A
1/*
2*******************************************************************************
3*
2ca993e8 4* Copyright (C) 2013-2016, International Business Machines
51004dcb
A
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"
2ca993e8 18#include "unicode/simpleformatter.h"
51004dcb
A
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
27U_NAMESPACE_BEGIN
28
57a6839d 29struct ListFormatInternal : public UMemory {
2ca993e8
A
30 SimpleFormatter twoPattern;
31 SimpleFormatter startPattern;
32 SimpleFormatter middlePattern;
33 SimpleFormatter endPattern;
57a6839d
A
34
35ListFormatInternal(
36 const UnicodeString& two,
37 const UnicodeString& start,
38 const UnicodeString& middle,
2ca993e8
A
39 const UnicodeString& end,
40 UErrorCode &errorCode) :
41 twoPattern(two, 2, 2, errorCode),
42 startPattern(start, 2, 2, errorCode),
43 middlePattern(middle, 2, 2, errorCode),
44 endPattern(end, 2, 2, errorCode) {}
45
46ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) :
47 twoPattern(data.twoPattern, errorCode),
48 startPattern(data.startPattern, errorCode),
49 middlePattern(data.middlePattern, errorCode),
50 endPattern(data.endPattern, errorCode) { }
57a6839d
A
51
52ListFormatInternal(const ListFormatInternal &other) :
53 twoPattern(other.twoPattern),
54 startPattern(other.startPattern),
55 middlePattern(other.middlePattern),
56 endPattern(other.endPattern) { }
57};
58
59
60
51004dcb
A
61static Hashtable* listPatternHash = NULL;
62static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
57a6839d 63static const char *STANDARD_STYLE = "standard";
51004dcb
A
64
65U_CDECL_BEGIN
66static UBool U_CALLCONV uprv_listformatter_cleanup() {
67 delete listPatternHash;
68 listPatternHash = NULL;
69 return TRUE;
70}
71
72static void U_CALLCONV
57a6839d
A
73uprv_deleteListFormatInternal(void *obj) {
74 delete static_cast<ListFormatInternal *>(obj);
51004dcb
A
75}
76
77U_CDECL_END
78
57a6839d
A
79static ListFormatInternal* loadListFormatInternal(
80 const Locale& locale,
81 const char* style,
82 UErrorCode& errorCode);
83
84static void getStringByKey(
85 const UResourceBundle* rb,
86 const char* key,
87 UnicodeString& result,
88 UErrorCode& errorCode);
89
90ListFormatter::ListFormatter(const ListFormatter& other) :
91 owned(other.owned), data(other.data) {
92 if (other.owned != NULL) {
93 owned = new ListFormatInternal(*other.owned);
94 data = owned;
95 }
96}
97
98ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
99 if (this == &other) {
100 return *this;
101 }
102 delete owned;
103 if (other.owned) {
104 owned = new ListFormatInternal(*other.owned);
105 data = owned;
106 } else {
107 owned = NULL;
108 data = other.data;
109 }
110 return *this;
111}
51004dcb
A
112
113void ListFormatter::initializeHash(UErrorCode& errorCode) {
114 if (U_FAILURE(errorCode)) {
115 return;
116 }
117
118 listPatternHash = new Hashtable();
119 if (listPatternHash == NULL) {
120 errorCode = U_MEMORY_ALLOCATION_ERROR;
121 return;
122 }
123
57a6839d 124 listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
51004dcb
A
125 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
126
127}
128
57a6839d
A
129const ListFormatInternal* ListFormatter::getListFormatInternal(
130 const Locale& locale, const char *style, UErrorCode& errorCode) {
51004dcb
A
131 if (U_FAILURE(errorCode)) {
132 return NULL;
133 }
57a6839d
A
134 CharString keyBuffer(locale.getName(), errorCode);
135 keyBuffer.append(':', errorCode).append(style, errorCode);
136 UnicodeString key(keyBuffer.data(), -1, US_INV);
137 ListFormatInternal* result = NULL;
51004dcb
A
138 {
139 Mutex m(&listFormatterMutex);
140 if (listPatternHash == NULL) {
141 initializeHash(errorCode);
142 if (U_FAILURE(errorCode)) {
143 return NULL;
144 }
145 }
57a6839d 146 result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
51004dcb
A
147 }
148 if (result != NULL) {
149 return result;
150 }
57a6839d 151 result = loadListFormatInternal(locale, style, errorCode);
51004dcb
A
152 if (U_FAILURE(errorCode)) {
153 return NULL;
154 }
155
156 {
157 Mutex m(&listFormatterMutex);
57a6839d 158 ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
51004dcb
A
159 if (temp != NULL) {
160 delete result;
161 result = temp;
162 } else {
163 listPatternHash->put(key, result, errorCode);
164 if (U_FAILURE(errorCode)) {
165 return NULL;
166 }
167 }
168 }
169 return result;
170}
171
57a6839d
A
172static ListFormatInternal* loadListFormatInternal(
173 const Locale& locale, const char * style, UErrorCode& errorCode) {
51004dcb
A
174 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
175 if (U_FAILURE(errorCode)) {
176 ures_close(rb);
177 return NULL;
178 }
179 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
57a6839d
A
180 rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
181
51004dcb
A
182 if (U_FAILURE(errorCode)) {
183 ures_close(rb);
184 return NULL;
185 }
186 UnicodeString two, start, middle, end;
187 getStringByKey(rb, "2", two, errorCode);
188 getStringByKey(rb, "start", start, errorCode);
189 getStringByKey(rb, "middle", middle, errorCode);
190 getStringByKey(rb, "end", end, errorCode);
191 ures_close(rb);
192 if (U_FAILURE(errorCode)) {
193 return NULL;
194 }
2ca993e8 195 ListFormatInternal* result = new ListFormatInternal(two, start, middle, end, errorCode);
51004dcb
A
196 if (result == NULL) {
197 errorCode = U_MEMORY_ALLOCATION_ERROR;
198 return NULL;
199 }
2ca993e8
A
200 if (U_FAILURE(errorCode)) {
201 delete result;
202 return NULL;
203 }
51004dcb
A
204 return result;
205}
206
207static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
208 int32_t len;
209 const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
210 if (U_FAILURE(errorCode)) {
211 return;
212 }
213 result.setTo(ustr, len);
214}
215
216ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
217 Locale locale; // The default locale.
218 return createInstance(locale, errorCode);
219}
220
221ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
57a6839d
A
222 return createInstance(locale, STANDARD_STYLE, errorCode);
223}
224
225ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
51004dcb 226 Locale tempLocale = locale;
57a6839d 227 const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
51004dcb
A
228 if (U_FAILURE(errorCode)) {
229 return NULL;
230 }
57a6839d 231 ListFormatter* p = new ListFormatter(listFormatInternal);
51004dcb
A
232 if (p == NULL) {
233 errorCode = U_MEMORY_ALLOCATION_ERROR;
234 return NULL;
235 }
236 return p;
237}
238
2ca993e8
A
239ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
240 owned = new ListFormatInternal(listFormatData, errorCode);
57a6839d 241 data = owned;
51004dcb
A
242}
243
57a6839d
A
244ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
245}
51004dcb 246
57a6839d
A
247ListFormatter::~ListFormatter() {
248 delete owned;
51004dcb
A
249}
250
251/**
57a6839d
A
252 * Joins first and second using the pattern pat.
253 * On entry offset is an offset into first or -1 if offset unspecified.
254 * On exit offset is offset of second in result if recordOffset was set
255 * Otherwise if it was >=0 it is set to point into result where it used
b331163b
A
256 * to point into first. On exit, result is the join of first and second
257 * according to pat. Any previous value of result gets replaced.
51004dcb 258 */
b331163b 259static void joinStringsAndReplace(
2ca993e8 260 const SimpleFormatter& pat,
57a6839d
A
261 const UnicodeString& first,
262 const UnicodeString& second,
263 UnicodeString &result,
264 UBool recordOffset,
265 int32_t &offset,
266 UErrorCode& errorCode) {
51004dcb
A
267 if (U_FAILURE(errorCode)) {
268 return;
269 }
57a6839d
A
270 const UnicodeString *params[2] = {&first, &second};
271 int32_t offsets[2];
b331163b 272 pat.formatAndReplace(
57a6839d 273 params,
b331163b 274 UPRV_LENGTHOF(params),
57a6839d
A
275 result,
276 offsets,
b331163b 277 UPRV_LENGTHOF(offsets),
57a6839d
A
278 errorCode);
279 if (U_FAILURE(errorCode)) {
51004dcb
A
280 return;
281 }
57a6839d
A
282 if (offsets[0] == -1 || offsets[1] == -1) {
283 errorCode = U_INVALID_FORMAT_ERROR;
51004dcb
A
284 return;
285 }
57a6839d
A
286 if (recordOffset) {
287 offset = offsets[1];
288 } else if (offset >= 0) {
289 offset += offsets[0];
290 }
291}
51004dcb 292
57a6839d
A
293UnicodeString& 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}
51004dcb 301
57a6839d
A
302UnicodeString& 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;
310 if (U_FAILURE(errorCode)) {
311 return appendTo;
312 }
313 if (data == NULL) {
314 errorCode = U_INVALID_STATE_ERROR;
315 return appendTo;
51004dcb
A
316 }
317
57a6839d
A
318 if (nItems <= 0) {
319 return appendTo;
320 }
321 if (nItems == 1) {
322 if (index == 0) {
323 offset = appendTo.length();
324 }
325 appendTo.append(items[0]);
326 return appendTo;
327 }
b331163b 328 UnicodeString result(items[0]);
57a6839d
A
329 if (index == 0) {
330 offset = 0;
331 }
b331163b
A
332 joinStringsAndReplace(
333 nItems == 2 ? data->twoPattern : data->startPattern,
334 result,
57a6839d 335 items[1],
b331163b 336 result,
57a6839d
A
337 index == 1,
338 offset,
339 errorCode);
b331163b
A
340 if (nItems > 2) {
341 for (int32_t i = 2; i < nItems - 1; ++i) {
342 joinStringsAndReplace(
343 data->middlePattern,
344 result,
345 items[i],
346 result,
347 index == i,
348 offset,
349 errorCode);
350 }
351 joinStringsAndReplace(
352 data->endPattern,
353 result,
354 items[nItems - 1],
355 result,
356 index == nItems - 1,
357 offset,
358 errorCode);
57a6839d 359 }
57a6839d
A
360 if (U_SUCCESS(errorCode)) {
361 if (offset >= 0) {
362 offset += appendTo.length();
363 }
b331163b 364 appendTo += result;
57a6839d
A
365 }
366 return appendTo;
51004dcb
A
367}
368
369U_NAMESPACE_END