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