1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
6 * Copyright (C) 2013-2016, International Business Machines
7 * Corporation and others. All Rights Reserved.
9 *******************************************************************************
10 * file name: listformatter.cpp
12 * tab size: 8 (not used)
15 * created on: 2012aug27
16 * created by: Umesh P. Nair
19 #include "unicode/listformatter.h"
20 #include "unicode/simpleformatter.h"
32 struct ListFormatInternal
: public UMemory
{
33 SimpleFormatter twoPattern
;
34 SimpleFormatter startPattern
;
35 SimpleFormatter middlePattern
;
36 SimpleFormatter endPattern
;
39 const UnicodeString
& two
,
40 const UnicodeString
& start
,
41 const UnicodeString
& middle
,
42 const UnicodeString
& end
,
43 UErrorCode
&errorCode
) :
44 twoPattern(two
, 2, 2, errorCode
),
45 startPattern(start
, 2, 2, errorCode
),
46 middlePattern(middle
, 2, 2, errorCode
),
47 endPattern(end
, 2, 2, errorCode
) {}
49 ListFormatInternal(const ListFormatData
&data
, UErrorCode
&errorCode
) :
50 twoPattern(data
.twoPattern
, errorCode
),
51 startPattern(data
.startPattern
, errorCode
),
52 middlePattern(data
.middlePattern
, errorCode
),
53 endPattern(data
.endPattern
, errorCode
) { }
55 ListFormatInternal(const ListFormatInternal
&other
) :
56 twoPattern(other
.twoPattern
),
57 startPattern(other
.startPattern
),
58 middlePattern(other
.middlePattern
),
59 endPattern(other
.endPattern
) { }
64 static Hashtable
* listPatternHash
= NULL
;
65 static UMutex listFormatterMutex
= U_MUTEX_INITIALIZER
;
66 static const char *STANDARD_STYLE
= "standard";
69 static UBool U_CALLCONV
uprv_listformatter_cleanup() {
70 delete listPatternHash
;
71 listPatternHash
= NULL
;
75 static void U_CALLCONV
76 uprv_deleteListFormatInternal(void *obj
) {
77 delete static_cast<ListFormatInternal
*>(obj
);
82 ListFormatter::ListFormatter(const ListFormatter
& other
) :
83 owned(other
.owned
), data(other
.data
) {
84 if (other
.owned
!= NULL
) {
85 owned
= new ListFormatInternal(*other
.owned
);
90 ListFormatter
& ListFormatter::operator=(const ListFormatter
& other
) {
96 owned
= new ListFormatInternal(*other
.owned
);
105 void ListFormatter::initializeHash(UErrorCode
& errorCode
) {
106 if (U_FAILURE(errorCode
)) {
110 listPatternHash
= new Hashtable();
111 if (listPatternHash
== NULL
) {
112 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
116 listPatternHash
->setValueDeleter(uprv_deleteListFormatInternal
);
117 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER
, uprv_listformatter_cleanup
);
121 const ListFormatInternal
* ListFormatter::getListFormatInternal(
122 const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
123 if (U_FAILURE(errorCode
)) {
126 CharString
keyBuffer(locale
.getName(), errorCode
);
127 keyBuffer
.append(':', errorCode
).append(style
, errorCode
);
128 UnicodeString
key(keyBuffer
.data(), -1, US_INV
);
129 ListFormatInternal
* result
= NULL
;
131 Mutex
m(&listFormatterMutex
);
132 if (listPatternHash
== NULL
) {
133 initializeHash(errorCode
);
134 if (U_FAILURE(errorCode
)) {
138 result
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
140 if (result
!= NULL
) {
143 result
= loadListFormatInternal(locale
, style
, errorCode
);
144 if (U_FAILURE(errorCode
)) {
149 Mutex
m(&listFormatterMutex
);
150 ListFormatInternal
* temp
= static_cast<ListFormatInternal
*>(listPatternHash
->get(key
));
155 listPatternHash
->put(key
, result
, errorCode
);
156 if (U_FAILURE(errorCode
)) {
164 static const UChar solidus
= 0x2F;
165 static const UChar aliasPrefix
[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
167 kAliasPrefixLen
= UPRV_LENGTHOF(aliasPrefix
),
168 kStyleLenMax
= 24 // longest currently is 14
171 struct ListFormatter::ListPatternsSink
: public ResourceSink
{
172 UnicodeString two
, start
, middle
, end
;
173 #if ((U_PLATFORM == U_PF_AIX) || (U_PLATFORM == U_PF_OS390)) && (U_CPLUSPLUS_VERSION < 11)
174 char aliasedStyle
[kStyleLenMax
+1];
176 uprv_memset(aliasedStyle
, 0, kStyleLenMax
+1);
179 char aliasedStyle
[kStyleLenMax
+1] = {0};
181 ListPatternsSink() {}
183 virtual ~ListPatternsSink();
185 void setAliasedStyle(UnicodeString alias
) {
186 int32_t startIndex
= alias
.indexOf(aliasPrefix
, kAliasPrefixLen
, 0);
187 if (startIndex
< 0) {
190 startIndex
+= kAliasPrefixLen
;
191 int32_t endIndex
= alias
.indexOf(solidus
, startIndex
);
193 endIndex
= alias
.length();
195 alias
.extract(startIndex
, endIndex
-startIndex
, aliasedStyle
, kStyleLenMax
+1, US_INV
);
196 aliasedStyle
[kStyleLenMax
] = 0;
199 void handleValueForPattern(ResourceValue
&value
, UnicodeString
&pattern
, UErrorCode
&errorCode
) {
200 if (pattern
.isEmpty()) {
201 if (value
.getType() == URES_ALIAS
) {
202 if (aliasedStyle
[0] == 0) {
203 setAliasedStyle(value
.getAliasUnicodeString(errorCode
));
206 pattern
= value
.getUnicodeString(errorCode
);
211 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
212 UErrorCode
&errorCode
) {
214 if (value
.getType() == URES_ALIAS
) {
215 setAliasedStyle(value
.getAliasUnicodeString(errorCode
));
218 ResourceTable listPatterns
= value
.getTable(errorCode
);
219 for (int i
= 0; U_SUCCESS(errorCode
) && listPatterns
.getKeyAndValue(i
, key
, value
); ++i
) {
220 if (uprv_strcmp(key
, "2") == 0) {
221 handleValueForPattern(value
, two
, errorCode
);
222 } else if (uprv_strcmp(key
, "end") == 0) {
223 handleValueForPattern(value
, end
, errorCode
);
224 } else if (uprv_strcmp(key
, "middle") == 0) {
225 handleValueForPattern(value
, middle
, errorCode
);
226 } else if (uprv_strcmp(key
, "start") == 0) {
227 handleValueForPattern(value
, start
, errorCode
);
233 // Virtual destructors must be defined out of line.
234 ListFormatter::ListPatternsSink::~ListPatternsSink() {}
236 ListFormatInternal
* ListFormatter::loadListFormatInternal(
237 const Locale
& locale
, const char * style
, UErrorCode
& errorCode
) {
238 UResourceBundle
* rb
= ures_open(NULL
, locale
.getName(), &errorCode
);
239 rb
= ures_getByKeyWithFallback(rb
, "listPattern", rb
, &errorCode
);
240 if (U_FAILURE(errorCode
)) {
244 ListFormatter::ListPatternsSink sink
;
245 char currentStyle
[kStyleLenMax
+1];
246 uprv_strncpy(currentStyle
, style
, kStyleLenMax
);
247 currentStyle
[kStyleLenMax
] = 0;
250 ures_getAllItemsWithFallback(rb
, currentStyle
, sink
, errorCode
);
251 if (U_FAILURE(errorCode
) || sink
.aliasedStyle
[0] == 0 || uprv_strcmp(currentStyle
, sink
.aliasedStyle
) == 0) {
254 uprv_strcpy(currentStyle
, sink
.aliasedStyle
);
257 if (U_FAILURE(errorCode
)) {
260 if (sink
.two
.isEmpty() || sink
.start
.isEmpty() || sink
.middle
.isEmpty() || sink
.end
.isEmpty()) {
261 errorCode
= U_MISSING_RESOURCE_ERROR
;
264 ListFormatInternal
* result
= new ListFormatInternal(sink
.two
, sink
.start
, sink
.middle
, sink
.end
, errorCode
);
265 if (result
== NULL
) {
266 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
269 if (U_FAILURE(errorCode
)) {
276 ListFormatter
* ListFormatter::createInstance(UErrorCode
& errorCode
) {
277 Locale locale
; // The default locale.
278 return createInstance(locale
, errorCode
);
281 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, UErrorCode
& errorCode
) {
282 return createInstance(locale
, STANDARD_STYLE
, errorCode
);
285 ListFormatter
* ListFormatter::createInstance(const Locale
& locale
, const char *style
, UErrorCode
& errorCode
) {
286 Locale tempLocale
= locale
;
287 const ListFormatInternal
* listFormatInternal
= getListFormatInternal(tempLocale
, style
, errorCode
);
288 if (U_FAILURE(errorCode
)) {
291 ListFormatter
* p
= new ListFormatter(listFormatInternal
);
293 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
299 ListFormatter::ListFormatter(const ListFormatData
& listFormatData
, UErrorCode
&errorCode
) {
300 owned
= new ListFormatInternal(listFormatData
, errorCode
);
304 ListFormatter::ListFormatter(const ListFormatInternal
* listFormatterInternal
) : owned(NULL
), data(listFormatterInternal
) {
307 ListFormatter::~ListFormatter() {
312 * Joins first and second using the pattern pat.
313 * On entry offset is an offset into first or -1 if offset unspecified.
314 * On exit offset is offset of second in result if recordOffset was set
315 * Otherwise if it was >=0 it is set to point into result where it used
316 * to point into first. On exit, result is the join of first and second
317 * according to pat. Any previous value of result gets replaced.
319 static void joinStringsAndReplace(
320 const SimpleFormatter
& pat
,
321 const UnicodeString
& first
,
322 const UnicodeString
& second
,
323 UnicodeString
&result
,
326 UErrorCode
& errorCode
) {
327 if (U_FAILURE(errorCode
)) {
330 const UnicodeString
*params
[2] = {&first
, &second
};
332 pat
.formatAndReplace(
334 UPRV_LENGTHOF(params
),
337 UPRV_LENGTHOF(offsets
),
339 if (U_FAILURE(errorCode
)) {
342 if (offsets
[0] == -1 || offsets
[1] == -1) {
343 errorCode
= U_INVALID_FORMAT_ERROR
;
348 } else if (offset
>= 0) {
349 offset
+= offsets
[0];
353 UnicodeString
& ListFormatter::format(
354 const UnicodeString items
[],
356 UnicodeString
& appendTo
,
357 UErrorCode
& errorCode
) const {
359 return format(items
, nItems
, appendTo
, -1, offset
, errorCode
);
362 UnicodeString
& ListFormatter::format(
363 const UnicodeString items
[],
365 UnicodeString
& appendTo
,
368 UErrorCode
& errorCode
) const {
370 if (U_FAILURE(errorCode
)) {
374 errorCode
= U_INVALID_STATE_ERROR
;
383 offset
= appendTo
.length();
385 appendTo
.append(items
[0]);
388 UnicodeString
result(items
[0]);
392 joinStringsAndReplace(
393 nItems
== 2 ? data
->twoPattern
: data
->startPattern
,
401 for (int32_t i
= 2; i
< nItems
- 1; ++i
) {
402 joinStringsAndReplace(
411 joinStringsAndReplace(
420 if (U_SUCCESS(errorCode
)) {
422 offset
+= appendTo
.length();