]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/listformatter.cpp
ICU-59173.0.1.tar.gz
[apple/icu.git] / icuSources / common / listformatter.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
51004dcb
A
3/*
4*******************************************************************************
5*
2ca993e8 6* Copyright (C) 2013-2016, International Business Machines
51004dcb
A
7* Corporation and others. All Rights Reserved.
8*
9*******************************************************************************
10* file name: listformatter.cpp
f3c0d7a5 11* encoding: UTF-8
51004dcb
A
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"
2ca993e8 20#include "unicode/simpleformatter.h"
51004dcb
A
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"
f3c0d7a5 28#include "resource.h"
51004dcb
A
29
30U_NAMESPACE_BEGIN
31
57a6839d 32struct ListFormatInternal : public UMemory {
2ca993e8
A
33 SimpleFormatter twoPattern;
34 SimpleFormatter startPattern;
35 SimpleFormatter middlePattern;
36 SimpleFormatter endPattern;
57a6839d
A
37
38ListFormatInternal(
39 const UnicodeString& two,
40 const UnicodeString& start,
41 const UnicodeString& middle,
2ca993e8
A
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) {}
48
49ListFormatInternal(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) { }
57a6839d
A
54
55ListFormatInternal(const ListFormatInternal &other) :
56 twoPattern(other.twoPattern),
57 startPattern(other.startPattern),
58 middlePattern(other.middlePattern),
59 endPattern(other.endPattern) { }
60};
61
62
63
51004dcb
A
64static Hashtable* listPatternHash = NULL;
65static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
57a6839d 66static const char *STANDARD_STYLE = "standard";
51004dcb
A
67
68U_CDECL_BEGIN
69static UBool U_CALLCONV uprv_listformatter_cleanup() {
70 delete listPatternHash;
71 listPatternHash = NULL;
72 return TRUE;
73}
74
75static void U_CALLCONV
57a6839d
A
76uprv_deleteListFormatInternal(void *obj) {
77 delete static_cast<ListFormatInternal *>(obj);
51004dcb
A
78}
79
80U_CDECL_END
81
57a6839d
A
82ListFormatter::ListFormatter(const ListFormatter& other) :
83 owned(other.owned), data(other.data) {
84 if (other.owned != NULL) {
85 owned = new ListFormatInternal(*other.owned);
86 data = owned;
87 }
88}
89
90ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
91 if (this == &other) {
92 return *this;
93 }
94 delete owned;
95 if (other.owned) {
96 owned = new ListFormatInternal(*other.owned);
97 data = owned;
98 } else {
99 owned = NULL;
100 data = other.data;
101 }
102 return *this;
103}
51004dcb
A
104
105void ListFormatter::initializeHash(UErrorCode& errorCode) {
106 if (U_FAILURE(errorCode)) {
107 return;
108 }
109
110 listPatternHash = new Hashtable();
111 if (listPatternHash == NULL) {
112 errorCode = U_MEMORY_ALLOCATION_ERROR;
113 return;
114 }
115
57a6839d 116 listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
51004dcb
A
117 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
118
119}
120
57a6839d
A
121const ListFormatInternal* ListFormatter::getListFormatInternal(
122 const Locale& locale, const char *style, UErrorCode& errorCode) {
51004dcb
A
123 if (U_FAILURE(errorCode)) {
124 return NULL;
125 }
57a6839d
A
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;
51004dcb
A
130 {
131 Mutex m(&listFormatterMutex);
132 if (listPatternHash == NULL) {
133 initializeHash(errorCode);
134 if (U_FAILURE(errorCode)) {
135 return NULL;
136 }
137 }
57a6839d 138 result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
51004dcb
A
139 }
140 if (result != NULL) {
141 return result;
142 }
57a6839d 143 result = loadListFormatInternal(locale, style, errorCode);
51004dcb
A
144 if (U_FAILURE(errorCode)) {
145 return NULL;
146 }
147
148 {
149 Mutex m(&listFormatterMutex);
57a6839d 150 ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
51004dcb
A
151 if (temp != NULL) {
152 delete result;
153 result = temp;
154 } else {
155 listPatternHash->put(key, result, errorCode);
156 if (U_FAILURE(errorCode)) {
157 return NULL;
158 }
159 }
160 }
161 return result;
162}
163
f3c0d7a5
A
164static const UChar solidus = 0x2F;
165static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
166enum {
167 kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix),
168 kStyleLenMax = 24 // longest currently is 14
169};
170
171struct 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];
175 ListPatternsSink() {
176 uprv_memset(aliasedStyle, 0, kStyleLenMax+1);
177 }
178#else
179 char aliasedStyle[kStyleLenMax+1] = {0};
180
181 ListPatternsSink() {}
182#endif
183 virtual ~ListPatternsSink();
184
185 void setAliasedStyle(UnicodeString alias) {
186 int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0);
187 if (startIndex < 0) {
188 return;
189 }
190 startIndex += kAliasPrefixLen;
191 int32_t endIndex = alias.indexOf(solidus, startIndex);
192 if (endIndex < 0) {
193 endIndex = alias.length();
194 }
195 alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV);
196 aliasedStyle[kStyleLenMax] = 0;
197 }
198
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));
204 }
205 } else {
206 pattern = value.getUnicodeString(errorCode);
207 }
208 }
209 }
210
211 virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
212 UErrorCode &errorCode) {
213 aliasedStyle[0] = 0;
214 if (value.getType() == URES_ALIAS) {
215 setAliasedStyle(value.getAliasUnicodeString(errorCode));
216 return;
217 }
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);
228 }
229 }
230 }
231};
232
233// Virtual destructors must be defined out of line.
234ListFormatter::ListPatternsSink::~ListPatternsSink() {}
235
236ListFormatInternal* ListFormatter::loadListFormatInternal(
57a6839d 237 const Locale& locale, const char * style, UErrorCode& errorCode) {
51004dcb 238 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
f3c0d7a5 239 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
51004dcb
A
240 if (U_FAILURE(errorCode)) {
241 ures_close(rb);
242 return NULL;
243 }
f3c0d7a5
A
244 ListFormatter::ListPatternsSink sink;
245 char currentStyle[kStyleLenMax+1];
246 uprv_strncpy(currentStyle, style, kStyleLenMax);
247 currentStyle[kStyleLenMax] = 0;
57a6839d 248
f3c0d7a5
A
249 for (;;) {
250 ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode);
251 if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) {
252 break;
253 }
254 uprv_strcpy(currentStyle, sink.aliasedStyle);
51004dcb 255 }
51004dcb
A
256 ures_close(rb);
257 if (U_FAILURE(errorCode)) {
258 return NULL;
259 }
f3c0d7a5
A
260 if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
261 errorCode = U_MISSING_RESOURCE_ERROR;
262 return NULL;
263 }
264 ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, errorCode);
51004dcb
A
265 if (result == NULL) {
266 errorCode = U_MEMORY_ALLOCATION_ERROR;
267 return NULL;
268 }
2ca993e8
A
269 if (U_FAILURE(errorCode)) {
270 delete result;
271 return NULL;
272 }
51004dcb
A
273 return result;
274}
275
51004dcb
A
276ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
277 Locale locale; // The default locale.
278 return createInstance(locale, errorCode);
279}
280
281ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
57a6839d
A
282 return createInstance(locale, STANDARD_STYLE, errorCode);
283}
284
285ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
51004dcb 286 Locale tempLocale = locale;
57a6839d 287 const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
51004dcb
A
288 if (U_FAILURE(errorCode)) {
289 return NULL;
290 }
57a6839d 291 ListFormatter* p = new ListFormatter(listFormatInternal);
51004dcb
A
292 if (p == NULL) {
293 errorCode = U_MEMORY_ALLOCATION_ERROR;
294 return NULL;
295 }
296 return p;
297}
298
2ca993e8
A
299ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
300 owned = new ListFormatInternal(listFormatData, errorCode);
57a6839d 301 data = owned;
51004dcb
A
302}
303
57a6839d
A
304ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
305}
51004dcb 306
57a6839d
A
307ListFormatter::~ListFormatter() {
308 delete owned;
51004dcb
A
309}
310
311/**
57a6839d
A
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
b331163b
A
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.
51004dcb 318 */
b331163b 319static void joinStringsAndReplace(
2ca993e8 320 const SimpleFormatter& pat,
57a6839d
A
321 const UnicodeString& first,
322 const UnicodeString& second,
323 UnicodeString &result,
324 UBool recordOffset,
325 int32_t &offset,
326 UErrorCode& errorCode) {
51004dcb
A
327 if (U_FAILURE(errorCode)) {
328 return;
329 }
57a6839d
A
330 const UnicodeString *params[2] = {&first, &second};
331 int32_t offsets[2];
b331163b 332 pat.formatAndReplace(
57a6839d 333 params,
b331163b 334 UPRV_LENGTHOF(params),
57a6839d
A
335 result,
336 offsets,
b331163b 337 UPRV_LENGTHOF(offsets),
57a6839d
A
338 errorCode);
339 if (U_FAILURE(errorCode)) {
51004dcb
A
340 return;
341 }
57a6839d
A
342 if (offsets[0] == -1 || offsets[1] == -1) {
343 errorCode = U_INVALID_FORMAT_ERROR;
51004dcb
A
344 return;
345 }
57a6839d
A
346 if (recordOffset) {
347 offset = offsets[1];
348 } else if (offset >= 0) {
349 offset += offsets[0];
350 }
351}
51004dcb 352
57a6839d
A
353UnicodeString& ListFormatter::format(
354 const UnicodeString items[],
355 int32_t nItems,
356 UnicodeString& appendTo,
357 UErrorCode& errorCode) const {
358 int32_t offset;
359 return format(items, nItems, appendTo, -1, offset, errorCode);
360}
51004dcb 361
57a6839d
A
362UnicodeString& ListFormatter::format(
363 const UnicodeString items[],
364 int32_t nItems,
365 UnicodeString& appendTo,
366 int32_t index,
367 int32_t &offset,
368 UErrorCode& errorCode) const {
369 offset = -1;
370 if (U_FAILURE(errorCode)) {
371 return appendTo;
372 }
373 if (data == NULL) {
374 errorCode = U_INVALID_STATE_ERROR;
375 return appendTo;
51004dcb
A
376 }
377
57a6839d
A
378 if (nItems <= 0) {
379 return appendTo;
380 }
381 if (nItems == 1) {
382 if (index == 0) {
383 offset = appendTo.length();
384 }
385 appendTo.append(items[0]);
386 return appendTo;
387 }
b331163b 388 UnicodeString result(items[0]);
57a6839d
A
389 if (index == 0) {
390 offset = 0;
391 }
b331163b
A
392 joinStringsAndReplace(
393 nItems == 2 ? data->twoPattern : data->startPattern,
394 result,
57a6839d 395 items[1],
b331163b 396 result,
57a6839d
A
397 index == 1,
398 offset,
399 errorCode);
b331163b
A
400 if (nItems > 2) {
401 for (int32_t i = 2; i < nItems - 1; ++i) {
402 joinStringsAndReplace(
403 data->middlePattern,
404 result,
405 items[i],
406 result,
407 index == i,
408 offset,
409 errorCode);
410 }
411 joinStringsAndReplace(
412 data->endPattern,
413 result,
414 items[nItems - 1],
415 result,
416 index == nItems - 1,
417 offset,
418 errorCode);
57a6839d 419 }
57a6839d
A
420 if (U_SUCCESS(errorCode)) {
421 if (offset >= 0) {
422 offset += appendTo.length();
423 }
b331163b 424 appendTo += result;
57a6839d
A
425 }
426 return appendTo;
51004dcb
A
427}
428
429U_NAMESPACE_END