Home | History | Annotate | Download | only in common
      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
     34 static UBool U_CALLCONV uprv_listformatter_cleanup() {
     35     delete listPatternHash;
     36     listPatternHash = NULL;
     37     return TRUE;
     38 }
     39 
     40 static void U_CALLCONV
     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 
     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 
     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 
    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 
    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 
    145 ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
    146     Locale locale;  // The default locale.
    147     return createInstance(locale, errorCode);
    148 }
    149 
    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 
    164 ListFormatter::ListFormatter(const ListFormatData& listFormatterData) : data(listFormatterData) {
    165 }
    166 
    167 ListFormatter::~ListFormatter() {}
    168 
    169 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  */
    198 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