|
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 |