• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 *******************************************************************************
3 *
4 *   Copyright (C) 2012, 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 
33 U_CDECL_BEGIN
uprv_listformatter_cleanup()34 static UBool U_CALLCONV uprv_listformatter_cleanup() {
35     delete listPatternHash;
36     listPatternHash = NULL;
37     return TRUE;
38 }
39 
40 static void U_CALLCONV
uprv_deleteListFormatData(void * obj)41 uprv_deleteListFormatData(void *obj) {
42     delete static_cast<ListFormatData *>(obj);
43 }
44 
45 U_CDECL_END
46 
47 static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode);
48 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
49 
initializeHash(UErrorCode & errorCode)50 void ListFormatter::initializeHash(UErrorCode& errorCode) {
51     if (U_FAILURE(errorCode)) {
52         return;
53     }
54 
55     listPatternHash = new Hashtable();
56     if (listPatternHash == NULL) {
57         errorCode = U_MEMORY_ALLOCATION_ERROR;
58         return;
59     }
60 
61     listPatternHash->setValueDeleter(uprv_deleteListFormatData);
62     ucln_common_registerCleanup(UCLN_COMMON_LIST_FORMATTER, uprv_listformatter_cleanup);
63 
64 }
65 
getListFormatData(const Locale & locale,UErrorCode & errorCode)66 const ListFormatData* ListFormatter::getListFormatData(
67         const Locale& locale, UErrorCode& errorCode) {
68     if (U_FAILURE(errorCode)) {
69         return NULL;
70     }
71     UnicodeString key(locale.getName(), -1, US_INV);
72     ListFormatData* result = NULL;
73     {
74         Mutex m(&listFormatterMutex);
75         if (listPatternHash == NULL) {
76             initializeHash(errorCode);
77             if (U_FAILURE(errorCode)) {
78                 return NULL;
79             }
80         }
81         result = static_cast<ListFormatData*>(listPatternHash->get(key));
82     }
83     if (result != NULL) {
84         return result;
85     }
86     result = loadListFormatData(locale, errorCode);
87     if (U_FAILURE(errorCode)) {
88         return NULL;
89     }
90 
91     {
92         Mutex m(&listFormatterMutex);
93         ListFormatData* temp = static_cast<ListFormatData*>(listPatternHash->get(key));
94         if (temp != NULL) {
95             delete result;
96             result = temp;
97         } else {
98             listPatternHash->put(key, result, errorCode);
99             if (U_FAILURE(errorCode)) {
100                 return NULL;
101             }
102         }
103     }
104     return result;
105 }
106 
loadListFormatData(const Locale & locale,UErrorCode & errorCode)107 static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode) {
108     UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
109     if (U_FAILURE(errorCode)) {
110         ures_close(rb);
111         return NULL;
112     }
113     rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
114     rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
115     if (U_FAILURE(errorCode)) {
116         ures_close(rb);
117         return NULL;
118     }
119     UnicodeString two, start, middle, end;
120     getStringByKey(rb, "2", two, errorCode);
121     getStringByKey(rb, "start", start, errorCode);
122     getStringByKey(rb, "middle", middle, errorCode);
123     getStringByKey(rb, "end", end, errorCode);
124     ures_close(rb);
125     if (U_FAILURE(errorCode)) {
126         return NULL;
127     }
128     ListFormatData* result = new ListFormatData(two, start, middle, end);
129     if (result == NULL) {
130         errorCode = U_MEMORY_ALLOCATION_ERROR;
131         return NULL;
132     }
133     return result;
134 }
135 
getStringByKey(const UResourceBundle * rb,const char * key,UnicodeString & result,UErrorCode & errorCode)136 static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
137     int32_t len;
138     const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
139     if (U_FAILURE(errorCode)) {
140       return;
141     }
142     result.setTo(ustr, len);
143 }
144 
createInstance(UErrorCode & errorCode)145 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
146     Locale locale;  // The default locale.
147     return createInstance(locale, errorCode);
148 }
149 
createInstance(const Locale & locale,UErrorCode & errorCode)150 ListFormatter* ListFormatter::createInstance(const Locale& locale, UErrorCode& errorCode) {
151     Locale tempLocale = locale;
152     const ListFormatData* listFormatData = getListFormatData(tempLocale, errorCode);
153     if (U_FAILURE(errorCode)) {
154         return NULL;
155     }
156     ListFormatter* p = new ListFormatter(*listFormatData);
157     if (p == NULL) {
158         errorCode = U_MEMORY_ALLOCATION_ERROR;
159         return NULL;
160     }
161     return p;
162 }
163 
ListFormatter(const ListFormatData & listFormatterData)164 ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) {
165 }
166 
~ListFormatter()167 ListFormatter::~ListFormatter() {}
168 
format(const UnicodeString items[],int32_t nItems,UnicodeString & appendTo,UErrorCode & errorCode) const169 UnicodeString& ListFormatter::format(const UnicodeString items[], int32_t nItems,
170                       UnicodeString& appendTo, UErrorCode& errorCode) const {
171     if (U_FAILURE(errorCode)) {
172         return appendTo;
173     }
174 
175     if (nItems > 0) {
176         UnicodeString newString = items[0];
177         if (nItems == 2) {
178             addNewString(data.twoPattern, newString, items[1], errorCode);
179         } else if (nItems > 2) {
180             addNewString(data.startPattern, newString, items[1], errorCode);
181             int32_t i;
182             for (i = 2; i < nItems - 1; ++i) {
183                 addNewString(data.middlePattern, newString, items[i], errorCode);
184             }
185             addNewString(data.endPattern, newString, items[nItems - 1], errorCode);
186         }
187         if (U_SUCCESS(errorCode)) {
188             appendTo += newString;
189         }
190     }
191     return appendTo;
192 }
193 
194 /**
195  * Joins originalString and nextString using the pattern pat and puts the result in
196  * originalString.
197  */
addNewString(const UnicodeString & pat,UnicodeString & originalString,const UnicodeString & nextString,UErrorCode & errorCode) const198 void ListFormatter::addNewString(const UnicodeString& pat, UnicodeString& originalString,
199                                  const UnicodeString& nextString, UErrorCode& errorCode) const {
200     if (U_FAILURE(errorCode)) {
201         return;
202     }
203 
204     int32_t p0Offset = pat.indexOf(FIRST_PARAMETER, 3, 0);
205     if (p0Offset < 0) {
206         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
207         return;
208     }
209     int32_t p1Offset = pat.indexOf(SECOND_PARAMETER, 3, 0);
210     if (p1Offset < 0) {
211         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
212         return;
213     }
214 
215     int32_t i, j;
216 
217     const UnicodeString* firstString;
218     const UnicodeString* secondString;
219     if (p0Offset < p1Offset) {
220         i = p0Offset;
221         j = p1Offset;
222         firstString = &originalString;
223         secondString = &nextString;
224     } else {
225         i = p1Offset;
226         j = p0Offset;
227         firstString = &nextString;
228         secondString = &originalString;
229     }
230 
231     UnicodeString result = UnicodeString(pat, 0, i) + *firstString;
232     result += UnicodeString(pat, i+3, j-i-3);
233     result += *secondString;
234     result += UnicodeString(pat, j+3);
235     originalString = result;
236 }
237 
238 U_NAMESPACE_END
239