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"
27 #define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
31 struct ListFormatInternal
: public UMemory
{
32 SimplePatternFormatter twoPattern
;
33 SimplePatternFormatter startPattern
;
34 SimplePatternFormatter middlePattern
;
35 SimplePatternFormatter endPattern
;
38 const UnicodeString
& two
,
39 const UnicodeString
& start
,
40 const UnicodeString
& middle
,
41 const UnicodeString
& end
) :
44 middlePattern(middle
),
47 ListFormatInternal(const ListFormatData
&data
) :
48 twoPattern(data
.twoPattern
),
49 startPattern(data
.startPattern
),
50 middlePattern(data
.middlePattern
),
51 endPattern(data
.endPattern
) { }
53 ListFormatInternal(const ListFormatInternal
&other
) :
54 twoPattern(other
.twoPattern
),
55 startPattern(other
.startPattern
),
56 middlePattern(other
.middlePattern
),
57 endPattern(other
.endPattern
) { }
62 static Hashtable
* listPatternHash
= NULL
;
63 static UMutex listFormatterMutex
= U_MUTEX_INITIALIZER
;
64 static const char *STANDARD_STYLE
= "standard";
67 static UBool U_CALLCONV
uprv_listformatter_cleanup() {
68 delete listPatternHash
;
69 listPatternHash
= NULL
;
73 static void U_CALLCONV
74 uprv_deleteListFormatInternal(void *obj
) {
75 delete static_cast<ListFormatInternal
*>(obj
);
80 static ListFormatInternal
* loadListFormatInternal(
83 UErrorCode
& errorCode
);
85 static void getStringByKey(
86 const UResourceBundle
* rb
,
88 UnicodeString
& result
,
89 UErrorCode
& errorCode
);
91 ListFormatter::ListFormatter(const ListFormatter
& other
) :
92 owned(other
.owned
), data(other
.data
) {
93 if (other
.owned
!= NULL
) {
94 owned
= new ListFormatInternal(*other
.owned
);
99 ListFormatter
& ListFormatter::operator=(const ListFormatter
& other
) {
100 if (this == &other
) {
105 owned
= new ListFormatInternal(*other
.owned
);
114 void ListFormatter::initializeHash(UErrorCode
& errorCode
) {
115 if (U_FAILURE(errorCode
)) {
119 listPatternHash
= new Hashtable();
120 if (listPatternHash
== NULL
) {
121 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
125 listPatternHash
->setValueDeleter(uprv_deleteListFormatInternal
);
126 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER
, uprv_listformatter_cleanup
);
130 const ListFormatInternal
* ListFormatter::getListFormatInternal(
131 const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
132 if (U_FAILURE(errorCode
)) {
135 CharString
keyBuffer(locale
.getName(), errorCode
);
136 keyBuffer
.append(':', errorCode
).append(style
, errorCode
);
137 UnicodeString
key(keyBuffer
.data(), -1, US_INV
);
138 ListFormatInternal
* result
= NULL
;
140 Mutex
m(&listFormatterMutex
);
141 if (listPatternHash
== NULL
) {
142 initializeHash(errorCode
);
143 if (U_FAILURE(errorCode
)) {
147 result
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
149 if (result
!= NULL
) {
152 result
= loadListFormatInternal(locale
, style
, errorCode
);
153 if (U_FAILURE(errorCode
)) {
158 Mutex
m(&listFormatterMutex
);
159 ListFormatInternal
* temp
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
164 listPatternHash
->put(key
, result
, errorCode
);
165 if (U_FAILURE(errorCode
)) {
173 static ListFormatInternal
* loadListFormatInternal(
174 const Locale
& locale
, const char * style
, UErrorCode
& errorCode
) {
175 UResourceBundle
* rb
= ures_open(NULL
, locale
.getName(), &errorCode
);
176 if (U_FAILURE(errorCode
)) {
180 rb
= ures_getByKeyWithFallback(rb
, "listPattern", rb
, &errorCode
);
181 rb
= ures_getByKeyWithFallback(rb
, style
, rb
, &errorCode
);
183 // TODO(Travis Keep): This is a hack until fallbacks can be added for
184 // listPattern/duration and listPattern/duration-narrow in CLDR.
185 if (errorCode
== U_MISSING_RESOURCE_ERROR
) {
186 errorCode
= U_ZERO_ERROR
;
187 rb
= ures_getByKeyWithFallback(rb
, "standard", rb
, &errorCode
);
189 if (U_FAILURE(errorCode
)) {
193 UnicodeString two
, start
, middle
, end
;
194 getStringByKey(rb
, "2", two
, errorCode
);
195 getStringByKey(rb
, "start", start
, errorCode
);
196 getStringByKey(rb
, "middle", middle
, errorCode
);
197 getStringByKey(rb
, "end", end
, errorCode
);
199 if (U_FAILURE(errorCode
)) {
202 ListFormatInternal
* result
= new ListFormatInternal(two
, start
, middle
, end
);
203 if (result
== NULL
) {
204 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
210 static void getStringByKey(const UResourceBundle
* rb
, const char* key
, UnicodeString
& result
, UErrorCode
& errorCode
) {
212 const UChar
* ustr
= ures_getStringByKeyWithFallback(rb
, key
, &len
, &errorCode
);
213 if (U_FAILURE(errorCode
)) {
216 result
.setTo(ustr
, len
);
219 ListFormatter
* ListFormatter::createInstance(UErrorCode
& errorCode
) {
220 Locale locale
; // The default locale.
221 return createInstance(locale
, errorCode
);
224 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, UErrorCode
& errorCode
) {
225 return createInstance(locale
, STANDARD_STYLE
, errorCode
);
228 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
229 Locale tempLocale
= locale
;
230 const ListFormatInternal
* listFormatInternal
= getListFormatInternal(tempLocale
, style
, errorCode
);
231 if (U_FAILURE(errorCode
)) {
234 ListFormatter
* p
= new ListFormatter(listFormatInternal
);
236 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
242 ListFormatter::ListFormatter(const ListFormatData
& listFormatData
) {
243 owned
= new ListFormatInternal(listFormatData
);
247 ListFormatter::ListFormatter(const ListFormatInternal
* listFormatterInternal
) : owned(NULL
), data(listFormatterInternal
) {
250 ListFormatter::~ListFormatter() {
255 * Joins first and second using the pattern pat.
256 * On entry offset is an offset into first or -1 if offset unspecified.
257 * On exit offset is offset of second in result if recordOffset was set
258 * Otherwise if it was >=0 it is set to point into result where it used
259 * to point into first.
261 static void joinStrings(
262 const SimplePatternFormatter
& pat
,
263 const UnicodeString
& first
,
264 const UnicodeString
& second
,
265 UnicodeString
&result
,
268 UErrorCode
& errorCode
) {
269 if (U_FAILURE(errorCode
)) {
272 const UnicodeString
*params
[2] = {&first
, &second
};
281 if (U_FAILURE(errorCode
)) {
284 if (offsets
[0] == -1 || offsets
[1] == -1) {
285 errorCode
= U_INVALID_FORMAT_ERROR
;
290 } else if (offset
>= 0) {
291 offset
+= offsets
[0];
295 UnicodeString
& ListFormatter::format(
296 const UnicodeString items
[],
298 UnicodeString
& appendTo
,
299 UErrorCode
& errorCode
) const {
301 return format(items
, nItems
, appendTo
, -1, offset
, errorCode
);
304 UnicodeString
& ListFormatter::format(
305 const UnicodeString items
[],
307 UnicodeString
& appendTo
,
310 UErrorCode
& errorCode
) const {
312 if (U_FAILURE(errorCode
)) {
316 errorCode
= U_INVALID_STATE_ERROR
;
325 offset
= appendTo
.length();
327 appendTo
.append(items
[0]);
344 UnicodeString temp
[2];
359 for (i
= 2; i
< nItems
- 1; ++i
) {
370 npos
= (pos
+ 1) & 1;
381 if (U_SUCCESS(errorCode
)) {
383 offset
+= appendTo
.length();
385 appendTo
+= temp
[npos
];