2 *******************************************************************************
4 * Copyright (C) 2013-2014, 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 "simplepatternformatter.h"
29 struct ListFormatInternal
: public UMemory
{
30 SimplePatternFormatter twoPattern
;
31 SimplePatternFormatter startPattern
;
32 SimplePatternFormatter middlePattern
;
33 SimplePatternFormatter endPattern
;
36 const UnicodeString
& two
,
37 const UnicodeString
& start
,
38 const UnicodeString
& middle
,
39 const UnicodeString
& end
) :
42 middlePattern(middle
),
45 ListFormatInternal(const ListFormatData
&data
) :
46 twoPattern(data
.twoPattern
),
47 startPattern(data
.startPattern
),
48 middlePattern(data
.middlePattern
),
49 endPattern(data
.endPattern
) { }
51 ListFormatInternal(const ListFormatInternal
&other
) :
52 twoPattern(other
.twoPattern
),
53 startPattern(other
.startPattern
),
54 middlePattern(other
.middlePattern
),
55 endPattern(other
.endPattern
) { }
60 static Hashtable
* listPatternHash
= NULL
;
61 static UMutex listFormatterMutex
= U_MUTEX_INITIALIZER
;
62 static const char *STANDARD_STYLE
= "standard";
65 static UBool U_CALLCONV
uprv_listformatter_cleanup() {
66 delete listPatternHash
;
67 listPatternHash
= NULL
;
71 static void U_CALLCONV
72 uprv_deleteListFormatInternal(void *obj
) {
73 delete static_cast<ListFormatInternal
*>(obj
);
78 static ListFormatInternal
* loadListFormatInternal(
81 UErrorCode
& errorCode
);
83 static void getStringByKey(
84 const UResourceBundle
* rb
,
86 UnicodeString
& result
,
87 UErrorCode
& errorCode
);
89 ListFormatter::ListFormatter(const ListFormatter
& other
) :
90 owned(other
.owned
), data(other
.data
) {
91 if (other
.owned
!= NULL
) {
92 owned
= new ListFormatInternal(*other
.owned
);
97 ListFormatter
& ListFormatter::operator=(const ListFormatter
& other
) {
103 owned
= new ListFormatInternal(*other
.owned
);
112 void ListFormatter::initializeHash(UErrorCode
& errorCode
) {
113 if (U_FAILURE(errorCode
)) {
117 listPatternHash
= new Hashtable();
118 if (listPatternHash
== NULL
) {
119 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
123 listPatternHash
->setValueDeleter(uprv_deleteListFormatInternal
);
124 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER
, uprv_listformatter_cleanup
);
128 const ListFormatInternal
* ListFormatter::getListFormatInternal(
129 const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
130 if (U_FAILURE(errorCode
)) {
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
;
138 Mutex
m(&listFormatterMutex
);
139 if (listPatternHash
== NULL
) {
140 initializeHash(errorCode
);
141 if (U_FAILURE(errorCode
)) {
145 result
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
147 if (result
!= NULL
) {
150 result
= loadListFormatInternal(locale
, style
, errorCode
);
151 if (U_FAILURE(errorCode
)) {
156 Mutex
m(&listFormatterMutex
);
157 ListFormatInternal
* temp
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
162 listPatternHash
->put(key
, result
, errorCode
);
163 if (U_FAILURE(errorCode
)) {
171 static ListFormatInternal
* loadListFormatInternal(
172 const Locale
& locale
, const char * style
, UErrorCode
& errorCode
) {
173 UResourceBundle
* rb
= ures_open(NULL
, locale
.getName(), &errorCode
);
174 if (U_FAILURE(errorCode
)) {
178 rb
= ures_getByKeyWithFallback(rb
, "listPattern", rb
, &errorCode
);
179 rb
= ures_getByKeyWithFallback(rb
, style
, rb
, &errorCode
);
181 if (U_FAILURE(errorCode
)) {
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
);
191 if (U_FAILURE(errorCode
)) {
194 ListFormatInternal
* result
= new ListFormatInternal(two
, start
, middle
, end
);
195 if (result
== NULL
) {
196 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
202 static void getStringByKey(const UResourceBundle
* rb
, const char* key
, UnicodeString
& result
, UErrorCode
& errorCode
) {
204 const UChar
* ustr
= ures_getStringByKeyWithFallback(rb
, key
, &len
, &errorCode
);
205 if (U_FAILURE(errorCode
)) {
208 result
.setTo(ustr
, len
);
211 ListFormatter
* ListFormatter::createInstance(UErrorCode
& errorCode
) {
212 Locale locale
; // The default locale.
213 return createInstance(locale
, errorCode
);
216 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, UErrorCode
& errorCode
) {
217 return createInstance(locale
, STANDARD_STYLE
, errorCode
);
220 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
221 Locale tempLocale
= locale
;
222 const ListFormatInternal
* listFormatInternal
= getListFormatInternal(tempLocale
, style
, errorCode
);
223 if (U_FAILURE(errorCode
)) {
226 ListFormatter
* p
= new ListFormatter(listFormatInternal
);
228 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
234 ListFormatter::ListFormatter(const ListFormatData
& listFormatData
) {
235 owned
= new ListFormatInternal(listFormatData
);
239 ListFormatter::ListFormatter(const ListFormatInternal
* listFormatterInternal
) : owned(NULL
), data(listFormatterInternal
) {
242 ListFormatter::~ListFormatter() {
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
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.
254 static void joinStringsAndReplace(
255 const SimplePatternFormatter
& pat
,
256 const UnicodeString
& first
,
257 const UnicodeString
& second
,
258 UnicodeString
&result
,
261 UErrorCode
& errorCode
) {
262 if (U_FAILURE(errorCode
)) {
265 const UnicodeString
*params
[2] = {&first
, &second
};
267 pat
.formatAndReplace(
269 UPRV_LENGTHOF(params
),
272 UPRV_LENGTHOF(offsets
),
274 if (U_FAILURE(errorCode
)) {
277 if (offsets
[0] == -1 || offsets
[1] == -1) {
278 errorCode
= U_INVALID_FORMAT_ERROR
;
283 } else if (offset
>= 0) {
284 offset
+= offsets
[0];
288 UnicodeString
& ListFormatter::format(
289 const UnicodeString items
[],
291 UnicodeString
& appendTo
,
292 UErrorCode
& errorCode
) const {
294 return format(items
, nItems
, appendTo
, -1, offset
, errorCode
);
297 UnicodeString
& ListFormatter::format(
298 const UnicodeString items
[],
300 UnicodeString
& appendTo
,
303 UErrorCode
& errorCode
) const {
305 if (U_FAILURE(errorCode
)) {
309 errorCode
= U_INVALID_STATE_ERROR
;
318 offset
= appendTo
.length();
320 appendTo
.append(items
[0]);
323 UnicodeString
result(items
[0]);
327 joinStringsAndReplace(
328 nItems
== 2 ? data
->twoPattern
: data
->startPattern
,
336 for (int32_t i
= 2; i
< nItems
- 1; ++i
) {
337 joinStringsAndReplace(
346 joinStringsAndReplace(
355 if (U_SUCCESS(errorCode
)) {
357 offset
+= appendTo
.length();