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