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