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