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
26U_NAMESPACE_BEGIN
27
28static Hashtable* listPatternHash = NULL;
29static UMutex listFormatterMutex = U_MUTEX_INITIALIZER;
30static UChar FIRST_PARAMETER[] = { 0x7b, 0x30, 0x7d };  // "{0}"
31static UChar SECOND_PARAMETER[] = { 0x7b, 0x31, 0x7d };  // "{0}"
32
33U_CDECL_BEGIN
34static UBool U_CALLCONV uprv_listformatter_cleanup() {
35    delete listPatternHash;
36    listPatternHash = NULL;
37    return TRUE;
38}
39
40static void U_CALLCONV
41uprv_deleteListFormatData(void *obj) {
42    delete static_cast<ListFormatData *>(obj);
43}
44
45U_CDECL_END
46
47static ListFormatData* loadListFormatData(const Locale& locale, UErrorCode& errorCode);
48static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode);
49
50void 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
66const 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
107static 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
136static 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
145ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
146    Locale locale;  // The default locale.
147    return createInstance(locale, errorCode);
148}
149
150ListFormatter* 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
164ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) {
165}
166
167ListFormatter::~ListFormatter() {}
168
169UnicodeString& 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 */
198void 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
238U_NAMESPACE_END
239