1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 * Copyright (C) 2013-2016, International Business Machines
7 * Corporation and others. All Rights Reserved.
8 *
9 *******************************************************************************
10 * file name: listformatter.cpp
11 * encoding: US-ASCII
12 * tab size: 8 (not used)
13 * indentation:4
14 *
15 * created on: 2012aug27
16 * created by: Umesh P. Nair
17 */
18
19 #include "unicode/listformatter.h"
20 #include "unicode/simpleformatter.h"
21 #include "mutex.h"
22 #include "hash.h"
23 #include "cstring.h"
24 #include "ulocimp.h"
25 #include "charstr.h"
26 #include "ucln_cmn.h"
27 #include "uresimp.h"
28
29 U_NAMESPACE_BEGIN
30
31 struct ListFormatInternal : public UMemory {
32 SimpleFormatter twoPattern;
33 SimpleFormatter startPattern;
34 SimpleFormatter middlePattern;
35 SimpleFormatter endPattern;
36
ListFormatInternalListFormatInternal37 ListFormatInternal(
38 const UnicodeString& two,
39 const UnicodeString& start,
40 const UnicodeString& middle,
41 const UnicodeString& end,
42 UErrorCode &errorCode) :
43 twoPattern(two, 2, 2, errorCode),
44 startPattern(start, 2, 2, errorCode),
45 middlePattern(middle, 2, 2, errorCode),
46 endPattern(end, 2, 2, errorCode) {}
47
ListFormatInternalListFormatInternal48 ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) :
49 twoPattern(data.twoPattern, errorCode),
50 startPattern(data.startPattern, errorCode),
51 middlePattern(data.middlePattern, errorCode),
52 endPattern(data.endPattern, errorCode) { }
53
ListFormatInternalListFormatInternal54 ListFormatInternal(const ListFormatInternal &other) :
55 twoPattern(other.twoPattern),
56 startPattern(other.startPattern),
57 middlePattern(other.middlePattern),
58 endPattern(other.endPattern) { }
59 };
60
61
62
63 static Hashtable* listPatternHash = NULL;
64 static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
65 static const char *STANDARD_STYLE = "standard";
66
67 U_CDECL_BEGIN
uprv_listformatter_cleanup()68 static UBool U_CALLCONV uprv_listformatter_cleanup() {
69 delete listPatternHash;
70 listPatternHash = NULL;
71 return TRUE;
72 }
73
74 static void U_CALLCONV
uprv_deleteListFormatInternal(void * obj)75 uprv_deleteListFormatInternal(void *obj) {
76 delete static_cast<ListFormatInternal *>(obj);
77 }
78
79 U_CDECL_END
80
81 static ListFormatInternal* loadListFormatInternal(
82 const Locale& locale,
83 const char* style,
84 UErrorCode& errorCode);
85
86 static void getStringByKey(
87 const UResourceBundle* rb,
88 const char* key,
89 UnicodeString& result,
90 UErrorCode& errorCode);
91
ListFormatter(const ListFormatter & other)92 ListFormatter::ListFormatter(const ListFormatter& other) :
93 owned(other.owned), data(other.data) {
94 if (other.owned != NULL) {
95 owned = new ListFormatInternal(*other.owned);
96 data = owned;
97 }
98 }
99
operator =(const ListFormatter & other)100 ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
101 if (this == &other) {
102 return *this;
103 }
104 delete owned;
105 if (other.owned) {
106 owned = new ListFormatInternal(*other.owned);
107 data = owned;
108 } else {
109 owned = NULL;
110 data = other.data;
111 }
112 return *this;
113 }
114
initializeHash(UErrorCode & errorCode)115 void ListFormatter::initializeHash(UErrorCode& errorCode) {
116 if (U_FAILURE(errorCode)) {
117 return;
118 }
119
120 listPatternHash = new Hashtable();
121 if (listPatternHash == NULL) {
122 errorCode = U_MEMORY_ALLOCATION_ERROR;
123 return;
124 }
125
126 listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
127 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
128
129 }
130
getListFormatInternal(const Locale & locale,const char * style,UErrorCode & errorCode)131 const ListFormatInternal* ListFormatter::getListFormatInternal(
132 const Locale& locale, const char *style, UErrorCode& errorCode) {
133 if (U_FAILURE(errorCode)) {
134 return NULL;
135 }
136 CharString keyBuffer(locale.getName(), errorCode);
137 keyBuffer.append(':', errorCode).append(style, errorCode);
138 UnicodeString key(keyBuffer.data(), -1, US_INV);
139 ListFormatInternal* result = NULL;
140 {
141 Mutex m(&listFormatterMutex);
142 if (listPatternHash == NULL) {
143 initializeHash(errorCode);
144 if (U_FAILURE(errorCode)) {
145 return NULL;
146 }
147 }
148 result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
149 }
150 if (result != NULL) {
151 return result;
152 }
153 result = loadListFormatInternal(locale, style, errorCode);
154 if (U_FAILURE(errorCode)) {
155 return NULL;
156 }
157
158 {
159 Mutex m(&listFormatterMutex);
160 ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
161 if (temp != NULL) {
162 delete result;
163 result = temp;
164 } else {
165 listPatternHash->put(key, result, errorCode);
166 if (U_FAILURE(errorCode)) {
167 return NULL;
168 }
169 }
170 }
171 return result;
172 }
173
loadListFormatInternal(const Locale & locale,const char * style,UErrorCode & errorCode)174 static ListFormatInternal* loadListFormatInternal(
175 const Locale& locale, const char * style, UErrorCode& errorCode) {
176 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
177 if (U_FAILURE(errorCode)) {
178 ures_close(rb);
179 return NULL;
180 }
181 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
182 rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
183
184 if (U_FAILURE(errorCode)) {
185 ures_close(rb);
186 return NULL;
187 }
188 UnicodeString two, start, middle, end;
189 getStringByKey(rb, "2", two, errorCode);
190 getStringByKey(rb, "start", start, errorCode);
191 getStringByKey(rb, "middle", middle, errorCode);
192 getStringByKey(rb, "end", end, errorCode);
193 ures_close(rb);
194 if (U_FAILURE(errorCode)) {
195 return NULL;
196 }
197 ListFormatInternal* result = new ListFormatInternal(two, start, middle, end, errorCode);
198 if (result == NULL) {
199 errorCode = U_MEMORY_ALLOCATION_ERROR;
200 return NULL;
201 }
202 if (U_FAILURE(errorCode)) {
203 delete result;
204 return NULL;
205 }
206 return result;
207 }
208
getStringByKey(const UResourceBundle * rb,const char * key,UnicodeString & result,UErrorCode & errorCode)209 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
210 int32_t len;
211 const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
212 if (U_FAILURE(errorCode)) {
213 return;
214 }
215 result.setTo(ustr, len);
216 }
217
createInstance(UErrorCode & errorCode)218 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
219 Locale locale; // The default locale.
220 return createInstance(locale, errorCode);
221 }
222
createInstance(const Locale & locale,UErrorCode & errorCode)223 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
224 return createInstance(locale, STANDARD_STYLE, errorCode);
225 }
226
createInstance(const Locale & locale,const char * style,UErrorCode & errorCode)227 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
228 Locale tempLocale = locale;
229 const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
230 if (U_FAILURE(errorCode)) {
231 return NULL;
232 }
233 ListFormatter* p = new ListFormatter(listFormatInternal);
234 if (p == NULL) {
235 errorCode = U_MEMORY_ALLOCATION_ERROR;
236 return NULL;
237 }
238 return p;
239 }
240
ListFormatter(const ListFormatData & listFormatData,UErrorCode & errorCode)241 ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
242 owned = new ListFormatInternal(listFormatData, errorCode);
243 data = owned;
244 }
245
ListFormatter(const ListFormatInternal * listFormatterInternal)246 ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
247 }
248
~ListFormatter()249 ListFormatter::~ListFormatter() {
250 delete owned;
251 }
252
253 /**
254 * Joins first and second using the pattern pat.
255 * On entry offset is an offset into first or -1 if offset unspecified.
256 * On exit offset is offset of second in result if recordOffset was set
257 * Otherwise if it was >=0 it is set to point into result where it used
258 * to point into first. On exit, result is the join of first and second
259 * according to pat. Any previous value of result gets replaced.
260 */
joinStringsAndReplace(const SimpleFormatter & pat,const UnicodeString & first,const UnicodeString & second,UnicodeString & result,UBool recordOffset,int32_t & offset,UErrorCode & errorCode)261 static void joinStringsAndReplace(
262 const SimpleFormatter& 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.formatAndReplace(
275 params,
276 UPRV_LENGTHOF(params),
277 result,
278 offsets,
279 UPRV_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
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,UErrorCode & errorCode) const295 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
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,int32_t index,int32_t & offset,UErrorCode & errorCode) const304 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 UnicodeString result(items[0]);
331 if (index == 0) {
332 offset = 0;
333 }
334 joinStringsAndReplace(
335 nItems == 2 ? data->twoPattern : data->startPattern,
336 result,
337 items[1],
338 result,
339 index == 1,
340 offset,
341 errorCode);
342 if (nItems > 2) {
343 for (int32_t i = 2; i < nItems - 1; ++i) {
344 joinStringsAndReplace(
345 data->middlePattern,
346 result,
347 items[i],
348 result,
349 index == i,
350 offset,
351 errorCode);
352 }
353 joinStringsAndReplace(
354 data->endPattern,
355 result,
356 items[nItems - 1],
357 result,
358 index == nItems - 1,
359 offset,
360 errorCode);
361 }
362 if (U_SUCCESS(errorCode)) {
363 if (offset >= 0) {
364 offset += appendTo.length();
365 }
366 appendTo += result;
367 }
368 return appendTo;
369 }
370
371 U_NAMESPACE_END
372