Home | History | Annotate | Download | only in i18n
      1 /*
      2 *******************************************************************************
      3 * Copyright (C) 2009, International Business Machines Corporation and
      4 * others. All Rights Reserved.
      5 *******************************************************************************
      6 *
      7 * File PLURFMT.CPP
      8 *
      9 * Modification History:
     10 *
     11 *   Date        Name        Description
     12 *******************************************************************************
     13 */
     14 
     15 
     16 #include "unicode/utypes.h"
     17 #include "unicode/plurfmt.h"
     18 #include "unicode/plurrule.h"
     19 #include "plurrule_impl.h"
     20 
     21 #if !UCONFIG_NO_FORMATTING
     22 
     23 U_NAMESPACE_BEGIN
     24 
     25 U_CDECL_BEGIN
     26 static void U_CALLCONV
     27 deleteHashStrings(void *obj) {
     28     delete (UnicodeString *)obj;
     29 }
     30 U_CDECL_END
     31 
     32 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)
     33 
     34 #define MAX_KEYWORD_SIZE 30
     35 
     36 PluralFormat::PluralFormat(UErrorCode& status) {
     37     init(NULL, Locale::getDefault(), status);
     38 }
     39 
     40 PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) {
     41     init(NULL, loc, status);
     42 }
     43 
     44 PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) {
     45     init(&rules, Locale::getDefault(), status);
     46 }
     47 
     48 PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, UErrorCode& status) {
     49     init(&rules, loc, status);
     50 }
     51 
     52 PluralFormat::PluralFormat(const UnicodeString& pat, UErrorCode& status) {
     53     init(NULL, Locale::getDefault(), status);
     54     applyPattern(pat, status);
     55 }
     56 
     57 PluralFormat::PluralFormat(const Locale& loc, const UnicodeString& pat, UErrorCode& status) {
     58     init(NULL, loc, status);
     59     applyPattern(pat, status);
     60 }
     61 
     62 PluralFormat::PluralFormat(const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) {
     63     init(&rules, Locale::getDefault(), status);
     64     applyPattern(pat, status);
     65 }
     66 
     67 PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) {
     68     init(&rules, loc, status);
     69     applyPattern(pat, status);
     70 }
     71 
     72 PluralFormat::PluralFormat(const PluralFormat& other) : Format(other) {
     73     UErrorCode status = U_ZERO_ERROR;
     74     locale = other.locale;
     75     pluralRules = other.pluralRules->clone();
     76     pattern = other.pattern;
     77     copyHashtable(other.fParsedValuesHash, status);
     78     if (U_FAILURE(status)) {
     79         delete pluralRules;
     80         pluralRules = NULL;
     81         return;
     82     }
     83     numberFormat=NumberFormat::createInstance(locale, status);
     84     if (U_FAILURE(status)) {
     85         delete pluralRules;
     86         pluralRules = NULL;
     87         delete fParsedValuesHash;
     88         fParsedValuesHash = NULL;
     89         return;
     90     }
     91     replacedNumberFormat=other.replacedNumberFormat;
     92 }
     93 
     94 PluralFormat::~PluralFormat() {
     95     delete pluralRules;
     96     delete fParsedValuesHash;
     97     delete numberFormat;
     98 }
     99 
    100 void
    101 PluralFormat::init(const PluralRules* rules, const Locale& curLocale, UErrorCode& status) {
    102     if (U_FAILURE(status)) {
    103         return;
    104     }
    105     locale = curLocale;
    106     if ( rules==NULL) {
    107         pluralRules = PluralRules::forLocale(locale, status);
    108         if (U_FAILURE(status)) {
    109             return;
    110         }
    111     }
    112     else {
    113         pluralRules = rules->clone();
    114     }
    115     fParsedValuesHash=NULL;
    116     pattern.remove();
    117     numberFormat= NumberFormat::createInstance(curLocale, status);
    118     if (U_FAILURE(status)) {
    119         delete pluralRules;
    120         pluralRules = NULL;
    121         return;
    122     }
    123     replacedNumberFormat=NULL;
    124 }
    125 
    126 void
    127 PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
    128     if (U_FAILURE(status)) {
    129         return;
    130     }
    131     this->pattern = newPattern;
    132     UnicodeString token;
    133     int32_t braceCount=0;
    134     fmtToken type;
    135     UBool spaceIncluded=FALSE;
    136 
    137     if (fParsedValuesHash==NULL) {
    138         fParsedValuesHash = new Hashtable(TRUE, status);
    139         if (U_FAILURE(status)) {
    140             return;
    141         }
    142         fParsedValuesHash->setValueDeleter(deleteHashStrings);
    143     }
    144 
    145     UBool getKeyword=TRUE;
    146     UnicodeString hashKeyword;
    147     UnicodeString *hashPattern;
    148 
    149     for (int32_t i=0; i<pattern.length(); ++i) {
    150         UChar ch=pattern.charAt(i);
    151 
    152         if ( !inRange(ch, type) ) {
    153             if (getKeyword) {
    154                 status = U_ILLEGAL_CHARACTER;
    155                 return;
    156             }
    157             else {
    158                 token += ch;
    159                 continue;
    160             }
    161         }
    162         switch (type) {
    163             case tSpace:
    164                 if (token.length()==0) {
    165                     continue;
    166                 }
    167                 if (getKeyword) {
    168                     // space after keyword
    169                     spaceIncluded = TRUE;
    170                 }
    171                 else {
    172                     token += ch;
    173                 }
    174                 break;
    175             case tLeftBrace:
    176                 if ( getKeyword ) {
    177                     if (fParsedValuesHash->get(token)!= NULL) {
    178                         status = U_DUPLICATE_KEYWORD;
    179                         return;
    180                     }
    181                     if (token.length()==0) {
    182                         status = U_PATTERN_SYNTAX_ERROR;
    183                         return;
    184                     }
    185                     if (!pluralRules->isKeyword(token)) {
    186                         status = U_UNDEFINED_KEYWORD;
    187                         return;
    188                     }
    189                     hashKeyword = token;
    190                     getKeyword = FALSE;
    191                     token.remove();
    192                 }
    193                 else  {
    194                     if (braceCount==0) {
    195                         status = U_UNEXPECTED_TOKEN;
    196                         return;
    197                     }
    198                     else {
    199                         token += ch;
    200                     }
    201                 }
    202                 braceCount++;
    203                 spaceIncluded = FALSE;
    204                 break;
    205             case tRightBrace:
    206                 if ( getKeyword ) {
    207                     status = U_UNEXPECTED_TOKEN;
    208                     return;
    209                 }
    210                 else  {
    211                     hashPattern = new UnicodeString(token);
    212                     fParsedValuesHash->put(hashKeyword, hashPattern, status);
    213                     if (U_FAILURE(status)) {
    214                         return;
    215                     }
    216                     braceCount--;
    217                     if ( braceCount==0 ) {
    218                         getKeyword=TRUE;
    219                         hashKeyword.remove();
    220                         hashPattern=NULL;
    221                         token.remove();
    222                     }
    223                     else {
    224                         token += ch;
    225                     }
    226                 }
    227                 spaceIncluded = FALSE;
    228                 break;
    229             case tLetter:
    230             case tNumberSign:
    231                 if (spaceIncluded) {
    232                     status = U_PATTERN_SYNTAX_ERROR;
    233                     return;
    234                 }
    235             default:
    236                 token+=ch;
    237                 break;
    238         }
    239     }
    240     if ( checkSufficientDefinition() ) {
    241         return;
    242     }
    243     else {
    244         status = U_DEFAULT_KEYWORD_MISSING;
    245         return;
    246     }
    247 }
    248 
    249 UnicodeString&
    250 PluralFormat::format(const Formattable& obj,
    251                    UnicodeString& appendTo,
    252                    FieldPosition& pos,
    253                    UErrorCode& status) const
    254 {
    255     if (U_FAILURE(status)) return appendTo;
    256     int32_t number;
    257 
    258     switch (obj.getType())
    259     {
    260     case Formattable::kDouble:
    261         return format((int32_t)obj.getDouble(), appendTo, pos, status);
    262         break;
    263     case Formattable::kLong:
    264         number = (int32_t)obj.getLong();
    265         return format(number, appendTo, pos, status);
    266         break;
    267     case Formattable::kInt64:
    268         return format((int32_t)obj.getInt64(), appendTo, pos, status);
    269     default:
    270         status = U_ILLEGAL_ARGUMENT_ERROR;
    271         return appendTo;
    272     }
    273 }
    274 
    275 UnicodeString
    276 PluralFormat::format(int32_t number, UErrorCode& status) const {
    277     if (U_FAILURE(status)) {
    278         return UnicodeString();
    279     }
    280     FieldPosition fpos(0);
    281     UnicodeString result;
    282 
    283     return format(number, result, fpos, status);
    284 }
    285 
    286 UnicodeString
    287 PluralFormat::format(double number, UErrorCode& status) const {
    288     if (U_FAILURE(status)) {
    289         return UnicodeString();
    290     }
    291     FieldPosition fpos(0);
    292     UnicodeString result;
    293 
    294     return format(number, result, fpos, status);
    295 }
    296 
    297 
    298 UnicodeString&
    299 PluralFormat::format(int32_t number,
    300                      UnicodeString& appendTo,
    301                      FieldPosition& pos,
    302                      UErrorCode& status) const {
    303     return format((double)number, appendTo, pos, status);
    304 }
    305 
    306 UnicodeString&
    307 PluralFormat::format(double number,
    308                      UnicodeString& appendTo,
    309                      FieldPosition& pos,
    310                      UErrorCode& /*status*/) const {
    311 
    312     if (fParsedValuesHash==NULL) {
    313         if ( replacedNumberFormat== NULL ) {
    314             return numberFormat->format(number, appendTo, pos);
    315         }
    316         else {
    317             replacedNumberFormat->format(number, appendTo, pos);
    318         }
    319     }
    320     UnicodeString selectedRule = pluralRules->select(number);
    321     UnicodeString *selectedPattern = (UnicodeString *)fParsedValuesHash->get(selectedRule);
    322     if (selectedPattern==NULL) {
    323         selectedPattern = (UnicodeString *)fParsedValuesHash->get(pluralRules->getKeywordOther());
    324     }
    325     appendTo = insertFormattedNumber(number, *selectedPattern, appendTo, pos);
    326 
    327     return appendTo;
    328 }
    329 
    330 UnicodeString&
    331 PluralFormat::toPattern(UnicodeString& appendTo) {
    332     appendTo+= pattern;
    333     return appendTo;
    334 }
    335 
    336 UBool
    337 PluralFormat::inRange(UChar ch, fmtToken& type) {
    338     if ((ch>=CAP_A) && (ch<=CAP_Z)) {
    339         // we assume all characters are in lower case already.
    340         return FALSE;
    341     }
    342     if ((ch>=LOW_A) && (ch<=LOW_Z)) {
    343         type = tLetter;
    344         return TRUE;
    345     }
    346     switch (ch) {
    347         case LEFTBRACE:
    348             type = tLeftBrace;
    349             return TRUE;
    350         case SPACE:
    351             type = tSpace;
    352             return TRUE;
    353         case RIGHTBRACE:
    354             type = tRightBrace;
    355             return TRUE;
    356         case NUMBER_SIGN:
    357             type = tNumberSign;
    358             return TRUE;
    359         default :
    360             type = none;
    361             return FALSE;
    362     }
    363 }
    364 
    365 UBool
    366 PluralFormat::checkSufficientDefinition() {
    367     // Check that at least the default rule is defined.
    368     if (fParsedValuesHash==NULL)  return FALSE;
    369     if (fParsedValuesHash->get(pluralRules->getKeywordOther()) == NULL) {
    370         return FALSE;
    371     }
    372     else {
    373         return TRUE;
    374     }
    375 }
    376 
    377 void
    378 PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
    379     if (U_FAILURE(status)) {
    380         return;
    381     }
    382     if (pluralRules!=NULL) {
    383         delete pluralRules;
    384         pluralRules=NULL;
    385     }
    386     if (fParsedValuesHash!= NULL) {
    387         delete fParsedValuesHash;
    388         fParsedValuesHash = NULL;
    389     }
    390     if (numberFormat!=NULL) {
    391         delete numberFormat;
    392         numberFormat = NULL;
    393         replacedNumberFormat=NULL;
    394     }
    395     init(NULL, loc, status);
    396 }
    397 
    398 void
    399 PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& /*status*/) {
    400     // TODO: The copy constructor and assignment op of NumberFormat class are protected.
    401     // create a pointer as the workaround.
    402     replacedNumberFormat = (NumberFormat *)format;
    403 }
    404 
    405 Format*
    406 PluralFormat::clone() const
    407 {
    408     return new PluralFormat(*this);
    409 }
    410 
    411 PluralFormat&
    412 PluralFormat::operator=(const PluralFormat& other) {
    413     if (this != &other) {
    414         UErrorCode status = U_ZERO_ERROR;
    415         delete pluralRules;
    416         delete fParsedValuesHash;
    417         delete numberFormat;
    418         locale = other.locale;
    419         pluralRules = other.pluralRules->clone();
    420         pattern = other.pattern;
    421         copyHashtable(other.fParsedValuesHash, status);
    422         if (U_FAILURE(status)) {
    423             delete pluralRules;
    424             pluralRules = NULL;
    425             fParsedValuesHash = NULL;
    426             numberFormat = NULL;
    427             return *this;
    428         }
    429         numberFormat=NumberFormat::createInstance(locale, status);
    430         if (U_FAILURE(status)) {
    431             delete pluralRules;
    432             delete fParsedValuesHash;
    433             pluralRules = NULL;
    434             fParsedValuesHash = NULL;
    435             numberFormat = NULL;
    436             return *this;
    437         }
    438         replacedNumberFormat=other.replacedNumberFormat;
    439     }
    440 
    441     return *this;
    442 }
    443 
    444 UBool
    445 PluralFormat::operator==(const Format& other) const {
    446     // This protected comparison operator should only be called by subclasses
    447     // which have confirmed that the other object being compared against is
    448     // an instance of a sublcass of PluralFormat.  THIS IS IMPORTANT.
    449     // Format::operator== guarantees that this cast is safe
    450     PluralFormat* fmt = (PluralFormat*)&other;
    451     return ((*pluralRules == *(fmt->pluralRules)) &&
    452             (*numberFormat == *(fmt->numberFormat)));
    453 }
    454 
    455 UBool
    456 PluralFormat::operator!=(const Format& other) const {
    457     return  !operator==(other);
    458 }
    459 
    460 void
    461 PluralFormat::parseObject(const UnicodeString& /*source*/,
    462                         Formattable& /*result*/,
    463                         ParsePosition& /*pos*/) const
    464 {
    465     // TODO: not yet supported in icu4j and icu4c
    466 }
    467 
    468 UnicodeString
    469 PluralFormat::insertFormattedNumber(double number,
    470                                     UnicodeString& message,
    471                                     UnicodeString& appendTo,
    472                                     FieldPosition& pos) const {
    473     UnicodeString result;
    474     int32_t braceStack=0;
    475     int32_t startIndex=0;
    476 
    477     if (message.length()==0) {
    478         return result;
    479     }
    480     appendTo = numberFormat->format(number, appendTo, pos);
    481     for(int32_t i=0; i<message.length(); ++i) {
    482         switch(message.charAt(i)) {
    483         case LEFTBRACE:
    484             ++braceStack;
    485             break;
    486         case RIGHTBRACE:
    487             --braceStack;
    488             break;
    489         case NUMBER_SIGN:
    490             if (braceStack==0) {
    491                 result += UnicodeString(message, startIndex, i);
    492                 result += appendTo;
    493                 startIndex = i + 1;
    494             }
    495             break;
    496         }
    497     }
    498     if ( startIndex < message.length() ) {
    499         result += UnicodeString(message, startIndex, message.length()-startIndex);
    500     }
    501     appendTo = result;
    502     return result;
    503 }
    504 
    505 void
    506 PluralFormat::copyHashtable(Hashtable *other, UErrorCode& status) {
    507     if (other == NULL || U_FAILURE(status)) {
    508         fParsedValuesHash = NULL;
    509         return;
    510     }
    511     fParsedValuesHash = new Hashtable(TRUE, status);
    512     if(U_FAILURE(status)){
    513         return;
    514     }
    515     fParsedValuesHash->setValueDeleter(deleteHashStrings);
    516     int32_t pos = -1;
    517     const UHashElement* elem = NULL;
    518     // walk through the hash table and create a deep clone
    519     while((elem = other->nextElement(pos))!= NULL){
    520         const UHashTok otherKeyTok = elem->key;
    521         UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer;
    522         const UHashTok otherKeyToVal = elem->value;
    523         UnicodeString* otherValue = (UnicodeString*)otherKeyToVal.pointer;
    524         fParsedValuesHash->put(*otherKey, new UnicodeString(*otherValue), status);
    525         if(U_FAILURE(status)){
    526             return;
    527         }
    528     }
    529 }
    530 
    531 
    532 U_NAMESPACE_END
    533 
    534 
    535 #endif /* #if !UCONFIG_NO_FORMATTING */
    536 
    537 //eof
    538