intl/icu/source/common/listformatter.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:04dd2fc5c765
1 /*
2 *******************************************************************************
3 *
4 * Copyright (C) 2013, International Business Machines
5 * Corporation and others. All Rights Reserved.
6 *
7 *******************************************************************************
8 * file name: listformatter.cpp
9 * encoding: US-ASCII
10 * tab size: 8 (not used)
11 * indentation:4
12 *
13 * created on: 2012aug27
14 * created by: Umesh P. Nair
15 */
16
17 #include "unicode/listformatter.h"
18 #include "mutex.h"
19 #include "hash.h"
20 #include "cstring.h"
21 #include "ulocimp.h"
22 #include "charstr.h"
23 #include "ucln_cmn.h"
24 #include "uresimp.h"
25
26 U_NAMESPACE_BEGIN
27
28 static Hashtable* listPatternHash = NULL;
29 static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
30 static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d }; // "{0}"
31 static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d }; // "{0}"
32 static const char *STANDARD_STYLE = "standard";
33
34 U_CDECL_BEGIN
35 static UBool U_CALLCONV uprv_listformatter_cleanup() {
36 delete listPatternHash;
37 listPatternHash = NULL;
38 return TRUE;
39 }
40
41 static void U_CALLCONV
42 uprv_deleteListFormatData(void *obj) {
43 delete static_cast<ListFormatData *>(obj);
44 }
45
46 U_CDECL_END
47
48 static ListFormatData* loadListFormatData(const Locale& locale, const char* style, UErrorCode& errorCode);
49 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
50
51 ListFormatter::ListFormatter(const ListFormatter& other) : data(other.data) {
52 }
53
54 ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
55 data = other.data;
56 return *this;
57 }
58
59 void ListFormatter::initializeHash(UErrorCode& errorCode) {
60 if (U_FAILURE(errorCode)) {
61 return;
62 }
63
64 listPatternHash = new Hashtable();
65 if (listPatternHash == NULL) {
66 errorCode = U_MEMORY_ALLOCATION_ERROR;
67 return;
68 }
69
70 listPatternHash->setValueDeleter(uprv_deleteListFormatData);
71 ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
72
73 }
74
75 const ListFormatData* ListFormatter::getListFormatData(
76 const Locale& locale, const char *style, UErrorCode& errorCode) {
77 if (U_FAILURE(errorCode)) {
78 return NULL;
79 }
80 CharString keyBuffer(locale.getName(), errorCode);
81 keyBuffer.append(':', errorCode).append(style, errorCode);
82 UnicodeString key(keyBuffer.data(), -1, US_INV);
83 ListFormatData* result = NULL;
84 {
85 Mutex m(&listFormatterMutex);
86 if (listPatternHash == NULL) {
87 initializeHash(errorCode);
88 if (U_FAILURE(errorCode)) {
89 return NULL;
90 }
91 }
92 result = static_cast<ListFormatData*>(listPatternHash->get(key));
93 }
94 if (result != NULL) {
95 return result;
96 }
97 result = loadListFormatData(locale, style, errorCode);
98 if (U_FAILURE(errorCode)) {
99 return NULL;
100 }
101
102 {
103 Mutex m(&listFormatterMutex);
104 ListFormatData* temp = static_cast<ListFormatData*>(listPatternHash->get(key));
105 if (temp != NULL) {
106 delete result;
107 result = temp;
108 } else {
109 listPatternHash->put(key, result, errorCode);
110 if (U_FAILURE(errorCode)) {
111 return NULL;
112 }
113 }
114 }
115 return result;
116 }
117
118 static ListFormatData* loadListFormatData(
119 const Locale& locale, const char * style, UErrorCode& errorCode) {
120 UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
121 if (U_FAILURE(errorCode)) {
122 ures_close(rb);
123 return NULL;
124 }
125 rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
126 rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
127
128 // TODO(Travis Keep): This is a hack until fallbacks can be added for
129 // listPattern/duration and listPattern/duration-narrow in CLDR.
130 if (errorCode == U_MISSING_RESOURCE_ERROR) {
131 errorCode = U_ZERO_ERROR;
132 rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
133 }
134 if (U_FAILURE(errorCode)) {
135 ures_close(rb);
136 return NULL;
137 }
138 UnicodeString two, start, middle, end;
139 getStringByKey(rb, "2", two, errorCode);
140 getStringByKey(rb, "start", start, errorCode);
141 getStringByKey(rb, "middle", middle, errorCode);
142 getStringByKey(rb, "end", end, errorCode);
143 ures_close(rb);
144 if (U_FAILURE(errorCode)) {
145 return NULL;
146 }
147 ListFormatData* result = new ListFormatData(two, start, middle, end);
148 if (result == NULL) {
149 errorCode = U_MEMORY_ALLOCATION_ERROR;
150 return NULL;
151 }
152 return result;
153 }
154
155 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
156 int32_t len;
157 const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
158 if (U_FAILURE(errorCode)) {
159 return;
160 }
161 result.setTo(ustr, len);
162 }
163
164 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
165 Locale locale; // The default locale.
166 return createInstance(locale, errorCode);
167 }
168
169 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
170 return createInstance(locale, STANDARD_STYLE, errorCode);
171 }
172
173 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
174 Locale tempLocale = locale;
175 const ListFormatData* listFormatData = getListFormatData(tempLocale, style, errorCode);
176 if (U_FAILURE(errorCode)) {
177 return NULL;
178 }
179 ListFormatter* p = new ListFormatter(listFormatData);
180 if (p == NULL) {
181 errorCode = U_MEMORY_ALLOCATION_ERROR;
182 return NULL;
183 }
184 return p;
185 }
186
187
188 ListFormatter::ListFormatter(const ListFormatData* listFormatterData) : data(listFormatterData) {
189 }
190
191 ListFormatter::~ListFormatter() {}
192
193 UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems,
194 UnicodeString& appendTo, UErrorCode& errorCode) const {
195 if (U_FAILURE(errorCode)) {
196 return appendTo;
197 }
198 if (data == NULL) {
199 errorCode = U_INVALID_STATE_ERROR;
200 return appendTo;
201 }
202
203 if (nItems > 0) {
204 UnicodeString newString = items[0];
205 if (nItems == 2) {
206 addNewString(data->twoPattern, newString, items[1], errorCode);
207 } else if (nItems > 2) {
208 addNewString(data->startPattern, newString, items[1], errorCode);
209 int32_t i;
210 for (i = 2; i < nItems - 1; ++i) {
211 addNewString(data->middlePattern, newString, items[i], errorCode);
212 }
213 addNewString(data->endPattern, newString, items[nItems - 1], errorCode);
214 }
215 if (U_SUCCESS(errorCode)) {
216 appendTo += newString;
217 }
218 }
219 return appendTo;
220 }
221
222 /**
223 * Joins originalString and nextString using the pattern pat and puts the result in
224 * originalString.
225 */
226 void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString,
227 const UnicodeString& nextString, UErrorCode& errorCode) const {
228 if (U_FAILURE(errorCode)) {
229 return;
230 }
231
232 int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0);
233 if (p0Offset < 0) {
234 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
235 return;
236 }
237 int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0);
238 if (p1Offset < 0) {
239 errorCode = U_ILLEGAL_ARGUMENT_ERROR;
240 return;
241 }
242
243 int32_t i, j;
244
245 const UnicodeString* firstString;
246 const UnicodeString* secondString;
247 if (p0Offset < p1Offset) {
248 i = p0Offset;
249 j = p1Offset;
250 firstString = &originalString;
251 secondString = &nextString;
252 } else {
253 i = p1Offset;
254 j = p0Offset;
255 firstString = &nextString;
256 secondString = &originalString;
257 }
258
259 UnicodeString result = UnicodeString(pat, 0, i) + *firstString;
260 result += UnicodeString(pat, i+3, j-i-3);
261 result += *secondString;
262 result += UnicodeString(pat, j+3);
263 originalString = result;
264 }
265
266 U_NAMESPACE_END

mercurial