• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 *
4 *   Copyright (C) 2013-2014, 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 "simplepatternformatter.h"
19 #include "mutex.h"
20 #include "hash.h"
21 #include "cstring.h"
22 #include "ulocimp.h"
23 #include "charstr.h"
24 #include "ucln_cmn.h"
25 #include "uresimp.h"
26 
27 #define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
28 
29 U_NAMESPACE_BEGIN
30 
31 struct ListFormatInternal : public UMemory {
32     SimplePatternFormatter twoPattern;
33     SimplePatternFormatter startPattern;
34     SimplePatternFormatter middlePattern;
35     SimplePatternFormatter endPattern;
36 
ListFormatInternalListFormatInternal37 ListFormatInternal(
38         const UnicodeString& two,
39         const UnicodeString& start,
40         const UnicodeString& middle,
41         const UnicodeString& end) :
42         twoPattern(two),
43         startPattern(start),
44         middlePattern(middle),
45         endPattern(end) {}
46 
ListFormatInternalListFormatInternal47 ListFormatInternal(const ListFormatData &data) :
48         twoPattern(data.twoPattern),
49         startPattern(data.startPattern),
50         middlePattern(data.middlePattern),
51         endPattern(data.endPattern) { }
52 
ListFormatInternalListFormatInternal53 ListFormatInternal(const ListFormatInternal &other) :
54     twoPattern(other.twoPattern),
55     startPattern(other.startPattern),
56     middlePattern(other.middlePattern),
57     endPattern(other.endPattern) { }
58 };
59 
60 
61 
62 static Hashtable* listPatternHash = NULL;
63 static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
64 static const char *STANDARD_STYLE = "standard";
65 
66 U_CDECL_BEGIN
uprv_listformatter_cleanup()67 static UBool U_CALLCONV uprv_listformatter_cleanup() {
68     delete listPatternHash;
69     listPatternHash = NULL;
70     return TRUE;
71 }
72 
73 static void U_CALLCONV
uprv_deleteListFormatInternal(void * obj)74 uprv_deleteListFormatInternal(void *obj) {
75     delete static_cast<ListFormatInternal *>(obj);
76 }
77 
78 U_CDECL_END
79 
80 static ListFormatInternal* loadListFormatInternal(
81         const Locale& locale,
82         const char* style,
83         UErrorCode& errorCode);
84 
85 static void getStringByKey(
86         const UResourceBundle* rb,
87         const char* key,
88         UnicodeString& result,
89         UErrorCode& errorCode);
90 
ListFormatter(const ListFormatter & other)91 ListFormatter::ListFormatter(const ListFormatter& other) :
92         owned(other.owned), data(other.data) {
93     if (other.owned != NULL) {
94         owned = new ListFormatInternal(*other.owned);
95         data = owned;
96     }
97 }
98 
operator =(const ListFormatter & other)99 ListFormatter& ListFormatter::operator=(const ListFormatter& other) {
100     if (this == &other) {
101         return *this;
102     }
103     delete owned;
104     if (other.owned) {
105         owned = new ListFormatInternal(*other.owned);
106         data = owned;
107     } else {
108         owned = NULL;
109         data = other.data;
110     }
111     return *this;
112 }
113 
initializeHash(UErrorCode & errorCode)114 void ListFormatter::initializeHash(UErrorCode& errorCode) {
115     if (U_FAILURE(errorCode)) {
116         return;
117     }
118 
119     listPatternHash = new Hashtable();
120     if (listPatternHash == NULL) {
121         errorCode = U_MEMORY_ALLOCATION_ERROR;
122         return;
123     }
124 
125     listPatternHash->setValueDeleter(uprv_deleteListFormatInternal);
126     ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
127 
128 }
129 
getListFormatInternal(const Locale & locale,const char * style,UErrorCode & errorCode)130 const ListFormatInternal* ListFormatter::getListFormatInternal(
131         const Locale& locale, const char *style, UErrorCode& errorCode) {
132     if (U_FAILURE(errorCode)) {
133         return NULL;
134     }
135     CharString keyBuffer(locale.getName(), errorCode);
136     keyBuffer.append(':', errorCode).append(style, errorCode);
137     UnicodeString key(keyBuffer.data(), -1, US_INV);
138     ListFormatInternal* result = NULL;
139     {
140         Mutex m(&listFormatterMutex);
141         if (listPatternHash == NULL) {
142             initializeHash(errorCode);
143             if (U_FAILURE(errorCode)) {
144                 return NULL;
145             }
146         }
147         result = static_cast<ListFormatInternal*>(listPatternHash->get(key));
148     }
149     if (result != NULL) {
150         return result;
151     }
152     result = loadListFormatInternal(locale, style, errorCode);
153     if (U_FAILURE(errorCode)) {
154         return NULL;
155     }
156 
157     {
158         Mutex m(&listFormatterMutex);
159         ListFormatInternal* temp = static_cast<ListFormatInternal*>(listPatternHash->get(key));
160         if (temp != NULL) {
161             delete result;
162             result = temp;
163         } else {
164             listPatternHash->put(key, result, errorCode);
165             if (U_FAILURE(errorCode)) {
166                 return NULL;
167             }
168         }
169     }
170     return result;
171 }
172 
loadListFormatInternal(const Locale & locale,const char * style,UErrorCode & errorCode)173 static ListFormatInternal* loadListFormatInternal(
174         const Locale& locale, const char * style, UErrorCode& errorCode) {
175     UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
176     if (U_FAILURE(errorCode)) {
177         ures_close(rb);
178         return NULL;
179     }
180     rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
181     rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
182 
183     // TODO(Travis Keep): This is a hack until fallbacks can be added for
184     // listPattern/duration and listPattern/duration-narrow in CLDR.
185     if (errorCode == U_MISSING_RESOURCE_ERROR) {
186         errorCode = U_ZERO_ERROR;
187         rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
188     }
189     if (U_FAILURE(errorCode)) {
190         ures_close(rb);
191         return NULL;
192     }
193     UnicodeString two, start, middle, end;
194     getStringByKey(rb, "2", two, errorCode);
195     getStringByKey(rb, "start", start, errorCode);
196     getStringByKey(rb, "middle", middle, errorCode);
197     getStringByKey(rb, "end", end, errorCode);
198     ures_close(rb);
199     if (U_FAILURE(errorCode)) {
200         return NULL;
201     }
202     ListFormatInternal* result = new ListFormatInternal(two, start, middle, end);
203     if (result == NULL) {
204         errorCode = U_MEMORY_ALLOCATION_ERROR;
205         return NULL;
206     }
207     return result;
208 }
209 
getStringByKey(const UResourceBundle * rb,const char * key,UnicodeString & result,UErrorCode & errorCode)210 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
211     int32_t len;
212     const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
213     if (U_FAILURE(errorCode)) {
214       return;
215     }
216     result.setTo(ustr, len);
217 }
218 
createInstance(UErrorCode & errorCode)219 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
220     Locale locale;  // The default locale.
221     return createInstance(locale, errorCode);
222 }
223 
createInstance(const Locale & locale,UErrorCode & errorCode)224 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
225     return createInstance(locale, STANDARD_STYLE, errorCode);
226 }
227 
createInstance(const Locale & locale,const char * style,UErrorCode & errorCode)228 ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *style, UErrorCode& errorCode) {
229     Locale tempLocale = locale;
230     const ListFormatInternal* listFormatInternal = getListFormatInternal(tempLocale, style, errorCode);
231     if (U_FAILURE(errorCode)) {
232         return NULL;
233     }
234     ListFormatter* p = new ListFormatter(listFormatInternal);
235     if (p == NULL) {
236         errorCode = U_MEMORY_ALLOCATION_ERROR;
237         return NULL;
238     }
239     return p;
240 }
241 
ListFormatter(const ListFormatData & listFormatData)242 ListFormatter::ListFormatter(const ListFormatData& listFormatData) {
243     owned = new ListFormatInternal(listFormatData);
244     data = owned;
245 }
246 
ListFormatter(const ListFormatInternal * listFormatterInternal)247 ListFormatter::ListFormatter(const ListFormatInternal* listFormatterInternal) : owned(NULL), data(listFormatterInternal) {
248 }
249 
~ListFormatter()250 ListFormatter::~ListFormatter() {
251     delete owned;
252 }
253 
254 /**
255  * Joins first and second using the pattern pat.
256  * On entry offset is an offset into first or -1 if offset unspecified.
257  * On exit offset is offset of second in result if recordOffset was set
258  * Otherwise if it was >=0 it is set to point into result where it used
259  * to point into first.
260  */
joinStrings(const SimplePatternFormatter & pat,const UnicodeString & first,const UnicodeString & second,UnicodeString & result,UBool recordOffset,int32_t & offset,UErrorCode & errorCode)261 static void joinStrings(
262         const SimplePatternFormatter& pat,
263         const UnicodeString& first,
264         const UnicodeString& second,
265         UnicodeString &result,
266         UBool recordOffset,
267         int32_t &offset,
268         UErrorCode& errorCode) {
269     if (U_FAILURE(errorCode)) {
270         return;
271     }
272     const UnicodeString *params[2] = {&first, &second};
273     int32_t offsets[2];
274     pat.format(
275             params,
276             LENGTHOF(params),
277             result,
278             offsets,
279             LENGTHOF(offsets),
280             errorCode);
281     if (U_FAILURE(errorCode)) {
282         return;
283     }
284     if (offsets[0] == -1 || offsets[1] == -1) {
285         errorCode = U_INVALID_FORMAT_ERROR;
286         return;
287     }
288     if (recordOffset) {
289         offset = offsets[1];
290     } else if (offset >= 0) {
291         offset += offsets[0];
292     }
293 }
294 
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,UErrorCode & errorCode) const295 UnicodeString& ListFormatter::format(
296         const UnicodeString items[],
297         int32_t nItems,
298         UnicodeString& appendTo,
299         UErrorCode& errorCode) const {
300     int32_t offset;
301     return format(items, nItems, appendTo, -1, offset, errorCode);
302 }
303 
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,int32_t index,int32_t & offset,UErrorCode & errorCode) const304 UnicodeString& ListFormatter::format(
305         const UnicodeString items[],
306         int32_t nItems,
307         UnicodeString& appendTo,
308         int32_t index,
309         int32_t &offset,
310         UErrorCode& errorCode) const {
311     offset = -1;
312     if (U_FAILURE(errorCode)) {
313         return appendTo;
314     }
315     if (data == NULL) {
316         errorCode = U_INVALID_STATE_ERROR;
317         return appendTo;
318     }
319 
320     if (nItems <= 0) {
321         return appendTo;
322     }
323     if (nItems == 1) {
324         if (index == 0) {
325             offset = appendTo.length();
326         }
327         appendTo.append(items[0]);
328         return appendTo;
329     }
330     if (nItems == 2) {
331         if (index == 0) {
332             offset = 0;
333         }
334         joinStrings(
335                 data->twoPattern,
336                 items[0],
337                 items[1],
338                 appendTo,
339                 index == 1,
340                 offset,
341                 errorCode);
342         return appendTo;
343     }
344     UnicodeString temp[2];
345     if (index == 0) {
346         offset = 0;
347     }
348     joinStrings(
349             data->startPattern,
350             items[0],
351             items[1],
352             temp[0],
353             index == 1,
354             offset,
355             errorCode);
356     int32_t i;
357     int32_t pos = 0;
358     int32_t npos = 1;
359     for (i = 2; i < nItems - 1; ++i) {
360          temp[npos].remove();
361          joinStrings(
362                  data->middlePattern,
363                  temp[pos],
364                  items[i],
365                  temp[npos],
366                  index == i,
367                  offset,
368                  errorCode);
369          pos = npos;
370          npos = (pos + 1) & 1;
371     }
372     temp[npos].remove();
373     joinStrings(
374             data->endPattern,
375             temp[pos],
376             items[nItems - 1],
377             temp[npos],
378             index == nItems - 1,
379             offset,
380             errorCode);
381     if (U_SUCCESS(errorCode)) {
382         if (offset >= 0) {
383             offset += appendTo.length();
384         }
385         appendTo += temp[npos];
386     }
387     return appendTo;
388 }
389 
390 U_NAMESPACE_END
391