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