]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/plurfmt.cpp
ICU-400.37.tar.gz
[apple/icu.git] / icuSources / i18n / plurfmt.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 2008, 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 numberFormat=NumberFormat::createInstance(locale, status);
79 replacedNumberFormat=other.replacedNumberFormat;
80 }
81
82 PluralFormat::~PluralFormat() {
83 delete pluralRules;
84 delete fParsedValuesHash;
85 delete numberFormat;
86 }
87
88 void
89 PluralFormat::init(const PluralRules* rules, const Locale& curLocale, UErrorCode& status) {
90 locale = curLocale;
91 if ( rules==NULL) {
92 pluralRules = PluralRules::forLocale(locale, status);
93 }
94 else {
95 pluralRules = rules->clone();
96 }
97 fParsedValuesHash=NULL;
98 pattern.remove();
99 numberFormat= NumberFormat::createInstance(curLocale, status);
100 replacedNumberFormat=NULL;
101 }
102
103 void
104 PluralFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
105 this->pattern = newPattern;
106 UnicodeString token;
107 int32_t braceCount=0;
108 fmtToken type;
109 UBool spaceIncluded=FALSE;
110
111 if (fParsedValuesHash==NULL) {
112 fParsedValuesHash = new Hashtable(TRUE, status);
113 if (U_FAILURE(status)) {
114 return;
115 }
116 fParsedValuesHash->setValueDeleter(deleteHashStrings);
117 }
118
119 UBool getKeyword=TRUE;
120 UnicodeString hashKeyword;
121 UnicodeString *hashPattern;
122
123 for (int32_t i=0; i<pattern.length(); ++i) {
124 UChar ch=pattern.charAt(i);
125
126 if ( !inRange(ch, type) ) {
127 if (getKeyword) {
128 status = U_ILLEGAL_CHARACTER;
129 return;
130 }
131 else {
132 token += ch;
133 continue;
134 }
135 }
136 switch (type) {
137 case tSpace:
138 if (token.length()==0) {
139 continue;
140 }
141 if (getKeyword) {
142 // space after keyword
143 spaceIncluded = TRUE;
144 }
145 else {
146 token += ch;
147 }
148 break;
149 case tLeftBrace:
150 if ( getKeyword ) {
151 if (fParsedValuesHash->get(token)!= NULL) {
152 status = U_DUPLICATE_KEYWORD;
153 return;
154 }
155 if (token.length()==0) {
156 status = U_PATTERN_SYNTAX_ERROR;
157 return;
158 }
159 if (!pluralRules->isKeyword(token)) {
160 status = U_UNDEFINED_KEYWORD;
161 return;
162 }
163 hashKeyword = token;
164 getKeyword = FALSE;
165 token.remove();
166 }
167 else {
168 if (braceCount==0) {
169 status = U_UNEXPECTED_TOKEN;
170 return;
171 }
172 else {
173 token += ch;
174 }
175 }
176 braceCount++;
177 spaceIncluded = FALSE;
178 break;
179 case tRightBrace:
180 if ( getKeyword ) {
181 status = U_UNEXPECTED_TOKEN;
182 return;
183 }
184 else {
185 hashPattern = new UnicodeString(token);
186 fParsedValuesHash->put(hashKeyword, hashPattern, status);
187 braceCount--;
188 if ( braceCount==0 ) {
189 getKeyword=TRUE;
190 hashKeyword.remove();
191 hashPattern=NULL;
192 token.remove();
193 }
194 else {
195 token += ch;
196 }
197 }
198 spaceIncluded = FALSE;
199 break;
200 case tLetter:
201 case tNumberSign:
202 if (spaceIncluded) {
203 status = U_PATTERN_SYNTAX_ERROR;
204 return;
205 }
206 default:
207 token+=ch;
208 break;
209 }
210 }
211 if ( checkSufficientDefinition() ) {
212 return;
213 }
214 else {
215 status = U_DEFAULT_KEYWORD_MISSING;
216 return;
217 }
218 }
219
220 UnicodeString&
221 PluralFormat::format(const Formattable& obj,
222 UnicodeString& appendTo,
223 FieldPosition& pos,
224 UErrorCode& status) const
225 {
226 if (U_FAILURE(status)) return appendTo;
227 int32_t number;
228
229 switch (obj.getType())
230 {
231 case Formattable::kDouble:
232 return format((int32_t)obj.getDouble(), appendTo, pos, status);
233 break;
234 case Formattable::kLong:
235 number = (int32_t)obj.getLong();
236 return format(number, appendTo, pos, status);
237 break;
238 case Formattable::kInt64:
239 return format((int32_t)obj.getInt64(), appendTo, pos, status);
240 default:
241 status = U_ILLEGAL_ARGUMENT_ERROR;
242 return appendTo;
243 }
244 }
245
246 UnicodeString
247 PluralFormat::format(int32_t number, UErrorCode& status) const {
248 FieldPosition fpos(0);
249 UnicodeString result;
250
251 return format(number, result, fpos, status);
252 }
253
254 UnicodeString
255 PluralFormat::format(double number, UErrorCode& status) const {
256 FieldPosition fpos(0);
257 UnicodeString result;
258
259 return format(number, result, fpos, status);
260 }
261
262
263 UnicodeString&
264 PluralFormat::format(int32_t number,
265 UnicodeString& appendTo,
266 FieldPosition& pos,
267 UErrorCode& status) const {
268 return format((double)number, appendTo, pos, status);
269 }
270
271 UnicodeString&
272 PluralFormat::format(double number,
273 UnicodeString& appendTo,
274 FieldPosition& pos,
275 UErrorCode& /*status*/) const {
276
277 if (fParsedValuesHash==NULL) {
278 if ( replacedNumberFormat== NULL ) {
279 return numberFormat->format(number, appendTo, pos);
280 }
281 else {
282 replacedNumberFormat->format(number, appendTo, pos);
283 }
284 }
285 UnicodeString selectedRule = pluralRules->select(number);
286 UnicodeString *selectedPattern = (UnicodeString *)fParsedValuesHash->get(selectedRule);
287 if (selectedPattern==NULL) {
288 selectedPattern = (UnicodeString *)fParsedValuesHash->get(pluralRules->getKeywordOther());
289 }
290 appendTo = insertFormattedNumber(number, *selectedPattern, appendTo, pos);
291
292 return appendTo;
293 }
294
295 UnicodeString&
296 PluralFormat::toPattern(UnicodeString& appendTo) {
297 appendTo+= pattern;
298 return appendTo;
299 }
300
301 UBool
302 PluralFormat::inRange(UChar ch, fmtToken& type) {
303 if ((ch>=CAP_A) && (ch<=CAP_Z)) {
304 // we assume all characters are in lower case already.
305 return FALSE;
306 }
307 if ((ch>=LOW_A) && (ch<=LOW_Z)) {
308 type = tLetter;
309 return TRUE;
310 }
311 switch (ch) {
312 case LEFTBRACE:
313 type = tLeftBrace;
314 return TRUE;
315 case SPACE:
316 type = tSpace;
317 return TRUE;
318 case RIGHTBRACE:
319 type = tRightBrace;
320 return TRUE;
321 case NUMBER_SIGN:
322 type = tNumberSign;
323 return TRUE;
324 default :
325 type = none;
326 return FALSE;
327 }
328 }
329
330 UBool
331 PluralFormat::checkSufficientDefinition() {
332 // Check that at least the default rule is defined.
333 if (fParsedValuesHash==NULL) return FALSE;
334 if (fParsedValuesHash->get(pluralRules->getKeywordOther()) == NULL) {
335 return FALSE;
336 }
337 else {
338 return TRUE;
339 }
340 }
341
342 void
343 PluralFormat::setLocale(const Locale& loc, UErrorCode& status) {
344 if (pluralRules!=NULL) {
345 delete pluralRules;
346 pluralRules=NULL;
347 }
348 if (fParsedValuesHash!= NULL) {
349 delete fParsedValuesHash;
350 fParsedValuesHash = NULL;
351 }
352 if (numberFormat!=NULL) {
353 delete numberFormat;
354 numberFormat = NULL;
355 replacedNumberFormat=NULL;
356 }
357 init(NULL, loc, status);
358 }
359
360 void
361 PluralFormat::setNumberFormat(const NumberFormat* format, UErrorCode& /*status*/) {
362 // TODO: The copy constructor and assignment op of NumberFormat class are protected.
363 // create a pointer as the workaround.
364 replacedNumberFormat = (NumberFormat *)format;
365 }
366
367 Format*
368 PluralFormat::clone() const
369 {
370 return new PluralFormat(*this);
371 }
372
373 PluralFormat&
374 PluralFormat::operator=(const PluralFormat& other) {
375 if (this != &other) {
376 UErrorCode status = U_ZERO_ERROR;
377 delete pluralRules;
378 delete fParsedValuesHash;
379 delete numberFormat;
380 locale = other.locale;
381 pluralRules = other.pluralRules->clone();
382 pattern = other.pattern;
383 copyHashtable(other.fParsedValuesHash, status);
384 numberFormat=NumberFormat::createInstance(locale, status);
385 replacedNumberFormat=other.replacedNumberFormat;
386 }
387
388 return *this;
389 }
390
391 UBool
392 PluralFormat::operator==(const Format& other) const {
393 // This protected comparison operator should only be called by subclasses
394 // which have confirmed that the other object being compared against is
395 // an instance of a sublcass of PluralFormat. THIS IS IMPORTANT.
396 // Format::operator== guarantees that this cast is safe
397 PluralFormat* fmt = (PluralFormat*)&other;
398 return ((*pluralRules == *(fmt->pluralRules)) &&
399 (*numberFormat == *(fmt->numberFormat)));
400 }
401
402 UBool
403 PluralFormat::operator!=(const Format& other) const {
404 return !operator==(other);
405 }
406
407 void
408 PluralFormat::parseObject(const UnicodeString& /*source*/,
409 Formattable& /*result*/,
410 ParsePosition& /*pos*/) const
411 {
412 // TODO: not yet supported in icu4j and icu4c
413 }
414
415 UnicodeString
416 PluralFormat::insertFormattedNumber(double number,
417 UnicodeString& message,
418 UnicodeString& appendTo,
419 FieldPosition& pos) const {
420 UnicodeString result;
421 int32_t braceStack=0;
422 int32_t startIndex=0;
423
424 if (message.length()==0) {
425 return result;
426 }
427 appendTo = numberFormat->format(number, appendTo, pos);
428 for(int32_t i=0; i<message.length(); ++i) {
429 switch(message.charAt(i)) {
430 case LEFTBRACE:
431 ++braceStack;
432 break;
433 case RIGHTBRACE:
434 --braceStack;
435 break;
436 case NUMBER_SIGN:
437 if (braceStack==0) {
438 result += UnicodeString(message, startIndex, i);
439 result += appendTo;
440 startIndex = i + 1;
441 }
442 break;
443 }
444 }
445 if ( startIndex < message.length() ) {
446 result += UnicodeString(message, startIndex, message.length()-startIndex);
447 }
448 appendTo = result;
449 return result;
450 }
451
452 void
453 PluralFormat::copyHashtable(Hashtable *other, UErrorCode& status) {
454 if (other == NULL) {
455 fParsedValuesHash = NULL;
456 return;
457 }
458 fParsedValuesHash = new Hashtable(TRUE, status);
459 if(U_FAILURE(status)){
460 return;
461 }
462 fParsedValuesHash->setValueDeleter(deleteHashStrings);
463 int32_t pos = -1;
464 const UHashElement* elem = NULL;
465 // walk through the hash table and create a deep clone
466 while((elem = other->nextElement(pos))!= NULL){
467 const UHashTok otherKeyTok = elem->key;
468 UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer;
469 const UHashTok otherKeyToVal = elem->value;
470 UnicodeString* otherValue = (UnicodeString*)otherKeyToVal.pointer;
471 fParsedValuesHash->put(*otherKey, new UnicodeString(*otherValue), status);
472 if(U_FAILURE(status)){
473 return;
474 }
475 }
476 }
477
478
479 U_NAMESPACE_END
480
481
482 #endif /* #if !UCONFIG_NO_FORMATTING */
483
484 //eof