• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
deleteHashStrings(void * obj)27 deleteHashStrings(void *obj) {
28     delete (UnicodeString *)obj;
29 }
30 U_CDECL_END
31 
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)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 
PluralFormat(const Locale & loc,UErrorCode & status)40 PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) {
41     init(NULL, loc, status);
42 }
43 
PluralFormat(const PluralRules & rules,UErrorCode & status)44 PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) {
45     init(&rules, Locale::getDefault(), status);
46 }
47 
PluralFormat(const Locale & loc,const PluralRules & rules,UErrorCode & status)48 PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, UErrorCode& status) {
49     init(&rules, loc, status);
50 }
51 
PluralFormat(const UnicodeString & pat,UErrorCode & status)52 PluralFormat::PluralFormat(const UnicodeString& pat, UErrorCode& status) {
53     init(NULL, Locale::getDefault(), status);
54     applyPattern(pat, status);
55 }
56 
PluralFormat(const Locale & loc,const UnicodeString & pat,UErrorCode & status)57 PluralFormat::PluralFormat(const Locale& loc, const UnicodeString& pat, UErrorCode& status) {
58     init(NULL, loc, status);
59     applyPattern(pat, status);
60 }
61 
PluralFormat(const PluralRules & rules,const UnicodeString & pat,UErrorCode & status)62 PluralFormat::PluralFormat(const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) {
63     init(&rules, Locale::getDefault(), status);
64     applyPattern(pat, status);
65 }
66 
PluralFormat(const Locale & loc,const PluralRules & rules,const UnicodeString & pat,UErrorCode & status)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 
PluralFormat(const PluralFormat & other)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 
~PluralFormat()94 PluralFormat::~PluralFormat() {
95     delete pluralRules;
96     delete fParsedValuesHash;
97     delete numberFormat;
98 }
99 
100 void
init(const PluralRules * rules,const Locale & curLocale,UErrorCode & status)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
applyPattern(const UnicodeString & newPattern,UErrorCode & status)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&
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const250 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
format(int32_t number,UErrorCode & status) const276 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
format(double number,UErrorCode & status) const287 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&
format(int32_t number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const299 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&
format(double number,UnicodeString & appendTo,FieldPosition & pos,UErrorCode &) const307 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&
toPattern(UnicodeString & appendTo)331 PluralFormat::toPattern(UnicodeString& appendTo) {
332     appendTo+= pattern;
333     return appendTo;
334 }
335 
336 UBool
inRange(UChar ch,fmtToken & type)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
checkSufficientDefinition()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
setLocale(const Locale & loc,UErrorCode & status)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
setNumberFormat(const NumberFormat * format,UErrorCode &)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*
clone() const406 PluralFormat::clone() const
407 {
408     return new PluralFormat(*this);
409 }
410 
411 PluralFormat&
operator =(const PluralFormat & other)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
operator ==(const Format & other) const445 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
operator !=(const Format & other) const456 PluralFormat::operator!=(const Format& other) const {
457     return  !operator==(other);
458 }
459 
460 void
parseObject(const UnicodeString &,Formattable &,ParsePosition &) const461 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
insertFormattedNumber(double number,UnicodeString & message,UnicodeString & appendTo,FieldPosition & pos) const469 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
copyHashtable(Hashtable * other,UErrorCode & status)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