2 *******************************************************************************
4 * Copyright (C) 2013-2016, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 *******************************************************************************
8 * file name: listformatter.cpp
10 * tab size: 8 (not used)
13 * created on: 2012aug27
14 * created by: Umesh P. Nair
17 #include "unicode/listformatter.h"
18 #include "unicode/simpleformatter.h"
29 struct ListFormatInternal
: public UMemory
{
30 SimpleFormatter twoPattern
;
31 SimpleFormatter startPattern
;
32 SimpleFormatter middlePattern
;
33 SimpleFormatter endPattern
;
36 const UnicodeString
& two
,
37 const UnicodeString
& start
,
38 const UnicodeString
& middle
,
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
) {}
46 ListFormatInternal(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
) { }
52 ListFormatInternal(const ListFormatInternal
&other
) :
53 twoPattern(other
.twoPattern
),
54 startPattern(other
.startPattern
),
55 middlePattern(other
.middlePattern
),
56 endPattern(other
.endPattern
) { }
61 static Hashtable
* listPatternHash
= NULL
;
62 static UMutex listFormatterMutex
= U_MUTEX_INITIALIZER
;
63 static const char *STANDARD_STYLE
= "standard";
66 static UBool U_CALLCONV
uprv_listformatter_cleanup() {
67 delete listPatternHash
;
68 listPatternHash
= NULL
;
72 static void U_CALLCONV
73 uprv_deleteListFormatInternal(void *obj
) {
74 delete static_cast<ListFormatInternal
*>(obj
);
79 static ListFormatInternal
* loadListFormatInternal(
82 UErrorCode
& errorCode
);
84 static void getStringByKey(
85 const UResourceBundle
* rb
,
87 UnicodeString
& result
,
88 UErrorCode
& errorCode
);
90 ListFormatter::ListFormatter(const ListFormatter
& other
) :
91 owned(other
.owned
), data(other
.data
) {
92 if (other
.owned
!= NULL
) {
93 owned
= new ListFormatInternal(*other
.owned
);
98 ListFormatter
& ListFormatter::operator=(const ListFormatter
& other
) {
104 owned
= new ListFormatInternal(*other
.owned
);
113 void ListFormatter::initializeHash(UErrorCode
& errorCode
) {
114 if (U_FAILURE(errorCode
)) {
118 listPatternHash
= new Hashtable();
119 if (listPatternHash
== NULL
) {
120 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
124 listPatternHash
->setValueDeleter(uprv_deleteListFormatInternal
);
125 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER
, uprv_listformatter_cleanup
);
129 const ListFormatInternal
* ListFormatter::getListFormatInternal(
130 const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
131 if (U_FAILURE(errorCode
)) {
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
;
139 Mutex
m(&listFormatterMutex
);
140 if (listPatternHash
== NULL
) {
141 initializeHash(errorCode
);
142 if (U_FAILURE(errorCode
)) {
146 result
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
148 if (result
!= NULL
) {
151 result
= loadListFormatInternal(locale
, style
, errorCode
);
152 if (U_FAILURE(errorCode
)) {
157 Mutex
m(&listFormatterMutex
);
158 ListFormatInternal
* temp
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
163 listPatternHash
->put(key
, result
, errorCode
);
164 if (U_FAILURE(errorCode
)) {
172 static ListFormatInternal
* loadListFormatInternal(
173 const Locale
& locale
, const char * style
, UErrorCode
& errorCode
) {
174 UResourceBundle
* rb
= ures_open(NULL
, locale
.getName(), &errorCode
);
175 if (U_FAILURE(errorCode
)) {
179 rb
= ures_getByKeyWithFallback(rb
, "listPattern", rb
, &errorCode
);
180 rb
= ures_getByKeyWithFallback(rb
, style
, rb
, &errorCode
);
182 if (U_FAILURE(errorCode
)) {
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
);
192 if (U_FAILURE(errorCode
)) {
195 ListFormatInternal
* result
= new ListFormatInternal(two
, start
, middle
, end
, errorCode
);
196 if (result
== NULL
) {
197 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
200 if (U_FAILURE(errorCode
)) {
207 static void getStringByKey(const UResourceBundle
* rb
, const char* key
, UnicodeString
& result
, UErrorCode
& errorCode
) {
209 const UChar
* ustr
= ures_getStringByKeyWithFallback(rb
, key
, &len
, &errorCode
);
210 if (U_FAILURE(errorCode
)) {
213 result
.setTo(ustr
, len
);
216 ListFormatter
* ListFormatter::createInstance(UErrorCode
& errorCode
) {
217 Locale locale
; // The default locale.
218 return createInstance(locale
, errorCode
);
221 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, UErrorCode
& errorCode
) {
222 return createInstance(locale
, STANDARD_STYLE
, errorCode
);
225 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
226 Locale tempLocale
= locale
;
227 const ListFormatInternal
* listFormatInternal
= getListFormatInternal(tempLocale
, style
, errorCode
);
228 if (U_FAILURE(errorCode
)) {
231 ListFormatter
* p
= new ListFormatter(listFormatInternal
);
233 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
239 ListFormatter::ListFormatter(const ListFormatData
& listFormatData
, UErrorCode
&errorCode
) {
240 owned
= new ListFormatInternal(listFormatData
, errorCode
);
244 ListFormatter::ListFormatter(const ListFormatInternal
* listFormatterInternal
) : owned(NULL
), data(listFormatterInternal
) {
247 ListFormatter::~ListFormatter() {
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
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.
259 static void joinStringsAndReplace(
260 const SimpleFormatter
& pat
,
261 const UnicodeString
& first
,
262 const UnicodeString
& second
,
263 UnicodeString
&result
,
266 UErrorCode
& errorCode
) {
267 if (U_FAILURE(errorCode
)) {
270 const UnicodeString
*params
[2] = {&first
, &second
};
272 pat
.formatAndReplace(
274 UPRV_LENGTHOF(params
),
277 UPRV_LENGTHOF(offsets
),
279 if (U_FAILURE(errorCode
)) {
282 if (offsets
[0] == -1 || offsets
[1] == -1) {
283 errorCode
= U_INVALID_FORMAT_ERROR
;
288 } else if (offset
>= 0) {
289 offset
+= offsets
[0];
293 UnicodeString
& ListFormatter::format(
294 const UnicodeString items
[],
296 UnicodeString
& appendTo
,
297 UErrorCode
& errorCode
) const {
299 return format(items
, nItems
, appendTo
, -1, offset
, errorCode
);
302 UnicodeString
& ListFormatter::format(
303 const UnicodeString items
[],
305 UnicodeString
& appendTo
,
308 UErrorCode
& errorCode
) const {
310 if (U_FAILURE(errorCode
)) {
314 errorCode
= U_INVALID_STATE_ERROR
;
323 offset
= appendTo
.length();
325 appendTo
.append(items
[0]);
328 UnicodeString
result(items
[0]);
332 joinStringsAndReplace(
333 nItems
== 2 ? data
->twoPattern
: data
->startPattern
,
341 for (int32_t i
= 2; i
< nItems
- 1; ++i
) {
342 joinStringsAndReplace(
351 joinStringsAndReplace(
360 if (U_SUCCESS(errorCode
)) {
362 offset
+= appendTo
.length();