]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/plurfmt.cpp
ICU-461.18.tar.gz
[apple/icu.git] / icuSources / i18n / plurfmt.cpp
CommitLineData
46f4442e
A
1/*
2*******************************************************************************
729e4ab9 3* Copyright (C) 2009, International Business Machines Corporation and
46f4442e
A
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
23U_NAMESPACE_BEGIN
24
25U_CDECL_BEGIN
26static void U_CALLCONV
27deleteHashStrings(void *obj) {
28 delete (UnicodeString *)obj;
29}
30U_CDECL_END
31
32UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PluralFormat)
33
34#define MAX_KEYWORD_SIZE 30
35
36PluralFormat::PluralFormat(UErrorCode& status) {
37 init(NULL, Locale::getDefault(), status);
38}
39
40PluralFormat::PluralFormat(const Locale& loc, UErrorCode& status) {
41 init(NULL, loc, status);
42}
43
44PluralFormat::PluralFormat(const PluralRules& rules, UErrorCode& status) {
45 init(&rules, Locale::getDefault(), status);
46}
47
48PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, UErrorCode& status) {
49 init(&rules, loc, status);
50}
51
52PluralFormat::PluralFormat(const UnicodeString& pat, UErrorCode& status) {
53 init(NULL, Locale::getDefault(), status);
54 applyPattern(pat, status);
55}
56
57PluralFormat::PluralFormat(const Locale& loc, const UnicodeString& pat, UErrorCode& status) {
58 init(NULL, loc, status);
59 applyPattern(pat, status);
60}
61
62PluralFormat::PluralFormat(const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) {
63 init(&rules, Locale::getDefault(), status);
64 applyPattern(pat, status);
65}
66
67PluralFormat::PluralFormat(const Locale& loc, const PluralRules& rules, const UnicodeString& pat, UErrorCode& status) {
68 init(&rules, loc, status);
69 applyPattern(pat, status);
70}
71
72PluralFormat::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);
729e4ab9
A
78 if (U_FAILURE(status)) {
79 delete pluralRules;
80 pluralRules = NULL;
81 return;
82 }
46f4442e 83 numberFormat=NumberFormat::createInstance(locale, status);
729e4ab9
A
84 if (U_FAILURE(status)) {
85 delete pluralRules;
86 pluralRules = NULL;
87 delete fParsedValuesHash;
88 fParsedValuesHash = NULL;
89 return;
90 }
46f4442e
A
91 replacedNumberFormat=other.replacedNumberFormat;
92}
93
94PluralFormat::~PluralFormat() {
95 delete pluralRules;
96 delete fParsedValuesHash;
97 delete numberFormat;
98}
99
100void
101PluralFormat::init(const PluralRules* rules, const Locale& curLocale, UErrorCode& status) {
729e4ab9
A
102 if (U_FAILURE(status)) {
103 return;
104 }
46f4442e
A
105 locale = curLocale;
106 if ( rules==NULL) {
107 pluralRules = PluralRules::forLocale(locale, status);
729e4ab9
A
108 if (U_FAILURE(status)) {
109 return;
110 }
46f4442e
A
111 }
112 else {
113 pluralRules = rules->clone();
114 }
115 fParsedValuesHash=NULL;
116 pattern.remove();
117 numberFormat= NumberFormat::createInstance(curLocale, status);
729e4ab9
A
118 if (U_FAILURE(status)) {
119 delete pluralRules;
120 pluralRules = NULL;
121 return;
122 }
46f4442e
A
123 replacedNumberFormat=NULL;
124}
125
126void
127PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
729e4ab9
A
128 if (U_FAILURE(status)) {
129 return;
130 }
46f4442e
A
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);
729e4ab9
A
213 if (U_FAILURE(status)) {
214 return;
215 }
46f4442e
A
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
249UnicodeString&
250PluralFormat::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
275UnicodeString
276PluralFormat::format(int32_t number, UErrorCode& status) const {
729e4ab9
A
277 if (U_FAILURE(status)) {
278 return UnicodeString();
279 }
46f4442e
A
280 FieldPosition fpos(0);
281 UnicodeString result;
282
283 return format(number, result, fpos, status);
284}
285
286UnicodeString
287PluralFormat::format(double number, UErrorCode& status) const {
729e4ab9
A
288 if (U_FAILURE(status)) {
289 return UnicodeString();
290 }
46f4442e
A
291 FieldPosition fpos(0);
292 UnicodeString result;
293
294 return format(number, result, fpos, status);
295}
296
297
298UnicodeString&
299PluralFormat::format(int32_t number,
300 UnicodeString& appendTo,
301 FieldPosition& pos,
302 UErrorCode& status) const {
303 return format((double)number, appendTo, pos, status);
304}
305
306UnicodeString&
307PluralFormat::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
330UnicodeString&
331PluralFormat::toPattern(UnicodeString& appendTo) {
332 appendTo+= pattern;
333 return appendTo;
334}
335
336UBool
337PluralFormat::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
365UBool
366PluralFormat::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
377void
378PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
729e4ab9
A
379 if (U_FAILURE(status)) {
380 return;
381 }
46f4442e
A
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
398void
399PluralFormat::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
405Format*
406PluralFormat::clone() const
407{
408 return new PluralFormat(*this);
409}
410
411PluralFormat&
412PluralFormat::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);
729e4ab9
A
422 if (U_FAILURE(status)) {
423 delete pluralRules;
424 pluralRules = NULL;
425 fParsedValuesHash = NULL;
426 numberFormat = NULL;
427 return *this;
428 }
46f4442e 429 numberFormat=NumberFormat::createInstance(locale, status);
729e4ab9
A
430 if (U_FAILURE(status)) {
431 delete pluralRules;
432 delete fParsedValuesHash;
433 pluralRules = NULL;
434 fParsedValuesHash = NULL;
435 numberFormat = NULL;
436 return *this;
437 }
46f4442e
A
438 replacedNumberFormat=other.replacedNumberFormat;
439 }
440
441 return *this;
442}
443
444UBool
445PluralFormat::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
455UBool
456PluralFormat::operator!=(const Format& other) const {
457 return !operator==(other);
458}
459
460void
461PluralFormat::parseObject(const UnicodeString& /*source*/,
462 Formattable& /*result*/,
463 ParsePosition& /*pos*/) const
464{
465 // TODO: not yet supported in icu4j and icu4c
466}
467
468UnicodeString
469PluralFormat::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
505void
506PluralFormat::copyHashtable(Hashtable *other, UErrorCode& status) {
729e4ab9 507 if (other == NULL || U_FAILURE(status)) {
46f4442e
A
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
532U_NAMESPACE_END
533
534
535#endif /* #if !UCONFIG_NO_FORMATTING */
536
537//eof