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