]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/choicfmt.cpp
ICU-6.2.14.tar.gz
[apple/icu.git] / icuSources / i18n / choicfmt.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 1997-2004, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
6 *
7 * File CHOICFMT.CPP
8 *
9 * Modification History:
10 *
11 * Date Name Description
12 * 02/19/97 aliu Converted from java.
13 * 03/20/97 helena Finished first cut of implementation and got rid
14 * of nextDouble/previousDouble and replaced with
15 * boolean array.
16 * 4/10/97 aliu Clean up. Modified to work on AIX.
17 * 06/04/97 helena Fixed applyPattern(), toPattern() and not to include
18 * wchar.h.
19 * 07/09/97 helena Made ParsePosition into a class.
20 * 08/06/97 nos removed overloaded constructor, fixed 'format(array)'
21 * 07/22/98 stephen JDK 1.2 Sync - removed UBool array (doubleFlags)
22 * 02/22/99 stephen Removed character literals for EBCDIC safety
23 ********************************************************************************
24 */
25
26 #include "unicode/utypes.h"
27
28 #if !UCONFIG_NO_FORMATTING
29
30 #include "unicode/choicfmt.h"
31 #include "unicode/numfmt.h"
32 #include "unicode/locid.h"
33 #include "cpputils.h"
34 #include "ustrfmt.h"
35 #include "cstring.h"
36 #include "putilimp.h"
37
38 // *****************************************************************************
39 // class ChoiceFormat
40 // *****************************************************************************
41
42 U_NAMESPACE_BEGIN
43
44 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChoiceFormat)
45
46 // Special characters used by ChoiceFormat. There are two characters
47 // used interchangeably to indicate <=. Either is parsed, but only
48 // LESS_EQUAL is generated by toPattern().
49 #define SINGLE_QUOTE ((UChar)0x0027) /*'*/
50 #define LESS_THAN ((UChar)0x003C) /*<*/
51 #define LESS_EQUAL ((UChar)0x0023) /*#*/
52 #define LESS_EQUAL2 ((UChar)0x2264)
53 #define VERTICAL_BAR ((UChar)0x007C) /*|*/
54 #define MINUS ((UChar)0x002D) /*-*/
55 #define INFINITY ((UChar)0x221E)
56
57 static const UChar gPositiveInfinity[] = {INFINITY, 0};
58 static const UChar gNegativeInfinity[] = {MINUS, INFINITY, 0};
59 #define POSITIVE_INF_STRLEN 1
60 #define NEGATIVE_INF_STRLEN 2
61
62 // -------------------------------------
63 // Creates a ChoiceFormat instance based on the pattern.
64
65 ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern,
66 UErrorCode& status)
67 : fChoiceLimits(0),
68 fClosures(0),
69 fChoiceFormats(0),
70 fCount(0)
71 {
72 applyPattern(newPattern, status);
73 }
74
75 // -------------------------------------
76 // Creates a ChoiceFormat instance with the limit array and
77 // format strings for each limit.
78
79 ChoiceFormat::ChoiceFormat(const double* limits,
80 const UnicodeString* formats,
81 int32_t cnt )
82 : fChoiceLimits(0),
83 fClosures(0),
84 fChoiceFormats(0),
85 fCount(0)
86 {
87 setChoices(limits, formats, cnt );
88 }
89
90 // -------------------------------------
91
92 ChoiceFormat::ChoiceFormat(const double* limits,
93 const UBool* closures,
94 const UnicodeString* formats,
95 int32_t cnt )
96 : fChoiceLimits(0),
97 fClosures(0),
98 fChoiceFormats(0),
99 fCount(0)
100 {
101 setChoices(limits, closures, formats, cnt );
102 }
103
104 // -------------------------------------
105 // copy constructor
106
107 ChoiceFormat::ChoiceFormat(const ChoiceFormat& that)
108 : NumberFormat(that),
109 fChoiceLimits(0),
110 fClosures(0),
111 fChoiceFormats(0)
112 {
113 *this = that;
114 }
115
116 // -------------------------------------
117 // Private constructor that creates a
118 // ChoiceFormat instance based on the
119 // pattern and populates UParseError
120
121 ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern,
122 UParseError& parseError,
123 UErrorCode& status)
124 : fChoiceLimits(0),
125 fClosures(0),
126 fChoiceFormats(0),
127 fCount(0)
128 {
129 applyPattern(newPattern,parseError, status);
130 }
131 // -------------------------------------
132
133 UBool
134 ChoiceFormat::operator==(const Format& that) const
135 {
136 if (this == &that) return TRUE;
137 if (!NumberFormat::operator==(that)) return FALSE;
138 ChoiceFormat& thatAlias = (ChoiceFormat&)that;
139 if (fCount != thatAlias.fCount) return FALSE;
140 // Checks the limits, the corresponding format string and LE or LT flags.
141 // LE means less than and equal to, LT means less than.
142 for (int32_t i = 0; i < fCount; i++) {
143 if ((fChoiceLimits[i] != thatAlias.fChoiceLimits[i]) ||
144 (fClosures[i] != thatAlias.fClosures[i]) ||
145 (fChoiceFormats[i] != thatAlias.fChoiceFormats[i]))
146 return FALSE;
147 }
148 return TRUE;
149 }
150
151 // -------------------------------------
152 // copy constructor
153
154 const ChoiceFormat&
155 ChoiceFormat::operator=(const ChoiceFormat& that)
156 {
157 if (this != &that) {
158 NumberFormat::operator=(that);
159 fCount = that.fCount;
160 uprv_free(fChoiceLimits);
161 fChoiceLimits = NULL;
162 uprv_free(fClosures);
163 fClosures = NULL;
164 delete [] fChoiceFormats;
165 fChoiceFormats = NULL;
166
167 fChoiceLimits = (double*) uprv_malloc( sizeof(double) * fCount);
168 fClosures = (UBool*) uprv_malloc( sizeof(UBool) * fCount);
169 fChoiceFormats = new UnicodeString[fCount];
170
171 uprv_arrayCopy(that.fChoiceLimits, fChoiceLimits, fCount);
172 uprv_arrayCopy(that.fClosures, fClosures, fCount);
173 uprv_arrayCopy(that.fChoiceFormats, fChoiceFormats, fCount);
174 }
175 return *this;
176 }
177
178 // -------------------------------------
179
180 ChoiceFormat::~ChoiceFormat()
181 {
182 uprv_free(fChoiceLimits);
183 fChoiceLimits = NULL;
184 uprv_free(fClosures);
185 fClosures = NULL;
186 delete [] fChoiceFormats;
187 fChoiceFormats = NULL;
188 fCount = 0;
189 }
190
191 /**
192 * Convert a string to a double value
193 */
194 double
195 ChoiceFormat::stod(const UnicodeString& string)
196 {
197 char source[256];
198 char* end;
199
200 string.extract(0, string.length(), source, (int32_t)sizeof(source), US_INV); /* invariant codepage */
201 return uprv_strtod(source,&end);
202 }
203
204 // -------------------------------------
205
206 /**
207 * Convert a double value to a string
208 */
209 UnicodeString&
210 ChoiceFormat::dtos(double value,
211 UnicodeString& string)
212 {
213 char temp[256];
214
215 uprv_dtostr(value, temp, 3, TRUE);
216 string = UnicodeString(temp, -1, US_INV); /* invariant codepage */
217 return string;
218 }
219
220 // -------------------------------------
221 // calls the overloaded applyPattern method.
222
223 void
224 ChoiceFormat::applyPattern(const UnicodeString& pattern,
225 UErrorCode& status)
226 {
227 UParseError parseError;
228 applyPattern(pattern, parseError, status);
229 }
230
231 // -------------------------------------
232 // Applies the pattern to this ChoiceFormat instance.
233
234 void
235 ChoiceFormat::applyPattern(const UnicodeString& pattern,
236 UParseError& parseError,
237 UErrorCode& status)
238 {
239 if (U_FAILURE(status))
240 {
241 return;
242 }
243
244 // Clear error struct
245 parseError.offset = -1;
246 parseError.preContext[0] = parseError.postContext[0] = (UChar)0;
247
248 // Perform 2 passes. The first computes the number of limits in
249 // this pattern (fCount), which is 1 more than the number of
250 // literal VERTICAL_BAR characters.
251 int32_t count = 1;
252 int32_t i;
253 for (i=0; i<pattern.length(); ++i) {
254 UChar c = pattern[i];
255 if (c == SINGLE_QUOTE) {
256 // Skip over the entire quote, including embedded
257 // contiguous pairs of SINGLE_QUOTE.
258 for (;;) {
259 do {
260 ++i;
261 } while (i<pattern.length() &&
262 pattern[i] != SINGLE_QUOTE);
263 if ((i+1)<pattern.length() &&
264 pattern[i+1] == SINGLE_QUOTE) {
265 // SINGLE_QUOTE pair; skip over it
266 ++i;
267 } else {
268 break;
269 }
270 }
271 } else if (c == VERTICAL_BAR) {
272 ++count;
273 }
274 }
275
276 // Allocate the required storage.
277 double *newLimits = (double*) uprv_malloc( sizeof(double) * count);
278 /* test for NULL */
279 if (newLimits == 0) {
280 status = U_MEMORY_ALLOCATION_ERROR;
281 return;
282 }
283 UBool *newClosures = (UBool*) uprv_malloc( sizeof(UBool) * count);
284 /* test for NULL */
285 if (newClosures == 0) {
286 status = U_MEMORY_ALLOCATION_ERROR;
287 uprv_free(newLimits);
288 return;
289 }
290 UnicodeString *newFormats = new UnicodeString[count];
291 /* test for NULL */
292 if (newFormats == 0) {
293 status = U_MEMORY_ALLOCATION_ERROR;
294 uprv_free(newLimits);
295 uprv_free(newClosures);
296 return;
297 }
298
299 // Perform the second pass
300 int32_t k = 0; // index into newXxx[] arrays
301 UnicodeString buf; // scratch buffer
302 UBool inQuote = FALSE;
303 UBool inNumber = TRUE; // TRUE before < or #, FALSE after
304
305 for (i=0; i<pattern.length(); ++i) {
306 UChar c = pattern[i];
307 if (c == SINGLE_QUOTE) {
308 // Check for SINGLE_QUOTE pair indicating a literal quote
309 if ((i+1) < pattern.length() &&
310 pattern[i+1] == SINGLE_QUOTE) {
311 buf += SINGLE_QUOTE;
312 ++i;
313 } else {
314 inQuote = !inQuote;
315 }
316 } else if (inQuote) {
317 buf += c;
318 } else if (c == LESS_THAN || c == LESS_EQUAL || c == LESS_EQUAL2) {
319 if (!inNumber || buf.length() == 0) {
320 goto error;
321 }
322 inNumber = FALSE;
323
324 double limit;
325 buf.trim();
326 if (!buf.compare(gPositiveInfinity, POSITIVE_INF_STRLEN)) {
327 limit = uprv_getInfinity();
328 } else if (!buf.compare(gNegativeInfinity, NEGATIVE_INF_STRLEN)) {
329 limit = -uprv_getInfinity();
330 } else {
331 limit = stod(buf);
332 }
333
334 if (k == count) {
335 // This shouldn't happen. If it does, it means that
336 // the count determined in the first pass did not
337 // match the number of elements found in the second
338 // pass.
339 goto error;
340 }
341 newLimits[k] = limit;
342 newClosures[k] = (c == LESS_THAN);
343
344 if (k > 0 && limit <= newLimits[k-1]) {
345 // Each limit must be strictly > than the previous
346 // limit. One exception: Two subsequent limits may be
347 // == if the first closure is FALSE and the second
348 // closure is TRUE. This places the limit value in
349 // the second interval.
350 if (!(limit == newLimits[k-1] &&
351 !newClosures[k-1] &&
352 newClosures[k])) {
353 goto error;
354 }
355 }
356
357 buf.truncate(0);
358 } else if (c == VERTICAL_BAR) {
359 if (inNumber) {
360 goto error;
361 }
362 inNumber = TRUE;
363
364 newFormats[k] = buf;
365 ++k;
366 buf.truncate(0);
367 } else {
368 buf += c;
369 }
370 }
371
372 if (k != (count-1) || inNumber || inQuote) {
373 goto error;
374 }
375 newFormats[k] = buf;
376
377 // Don't modify this object until the parse succeeds
378 uprv_free(fChoiceLimits);
379 uprv_free(fClosures);
380 delete[] fChoiceFormats;
381 fCount = count;
382 fChoiceLimits = newLimits;
383 fClosures = newClosures;
384 fChoiceFormats = newFormats;
385 return;
386
387 error:
388 status = U_ILLEGAL_ARGUMENT_ERROR;
389 syntaxError(pattern,i,parseError);
390 uprv_free(newLimits);
391 uprv_free(newClosures);
392 delete[] newFormats;
393 return;
394
395 }
396 // -------------------------------------
397 // Reconstruct the original input pattern.
398
399 UnicodeString&
400 ChoiceFormat::toPattern(UnicodeString& result) const
401 {
402 result.remove();
403 for (int32_t i = 0; i < fCount; ++i) {
404 if (i != 0) {
405 result += VERTICAL_BAR;
406 }
407 UnicodeString buf;
408 if (uprv_isPositiveInfinity(fChoiceLimits[i])) {
409 result += INFINITY;
410 } else if (uprv_isNegativeInfinity(fChoiceLimits[i])) {
411 result += MINUS;
412 result += INFINITY;
413 } else {
414 result += dtos(fChoiceLimits[i], buf);
415 }
416 if (fClosures[i]) {
417 result += LESS_THAN;
418 } else {
419 result += LESS_EQUAL;
420 }
421 // Append fChoiceFormats[i], using quotes if there are special
422 // characters. Single quotes themselves must be escaped in
423 // either case.
424 const UnicodeString& text = fChoiceFormats[i];
425 UBool needQuote = text.indexOf(LESS_THAN) >= 0
426 || text.indexOf(LESS_EQUAL) >= 0
427 || text.indexOf(LESS_EQUAL2) >= 0
428 || text.indexOf(VERTICAL_BAR) >= 0;
429 if (needQuote) {
430 result += SINGLE_QUOTE;
431 }
432 if (text.indexOf(SINGLE_QUOTE) < 0) {
433 result += text;
434 }
435 else {
436 for (int32_t j = 0; j < text.length(); ++j) {
437 UChar c = text[j];
438 result += c;
439 if (c == SINGLE_QUOTE) {
440 result += c;
441 }
442 }
443 }
444 if (needQuote) {
445 result += SINGLE_QUOTE;
446 }
447 }
448
449 return result;
450 }
451
452 #ifdef U_USE_CHOICE_FORMAT_DEPRECATES
453 // -------------------------------------
454 // Adopts the limit and format arrays.
455
456 void
457 ChoiceFormat::adoptChoices(double *limits,
458 UnicodeString *formats,
459 int32_t cnt )
460 {
461 adoptChoices(limits, (UBool *)0, formats, cnt);
462 }
463
464 // -------------------------------------
465 // Adopts the limit and format arrays.
466
467 void
468 ChoiceFormat::adoptChoices(double *limits,
469 UBool *closures,
470 UnicodeString *formats,
471 int32_t cnt )
472 {
473 if(limits == 0 || formats == 0)
474 return;
475
476 uprv_free(fChoiceLimits);
477 uprv_free(fClosures);
478 delete [] fChoiceFormats;
479 fChoiceLimits = limits;
480 fClosures = closures;
481 fChoiceFormats = formats;
482 fCount = cnt;
483
484 if (fClosures == 0) {
485 fClosures = (UBool*) uprv_malloc( sizeof(UBool) * fCount);
486 int32_t i;
487 for (i=0; i<fCount; ++i) {
488 fClosures[i] = FALSE;
489 }
490 }
491 }
492 #endif
493
494 // -------------------------------------
495 // Sets the limit and format arrays.
496 void
497 ChoiceFormat::setChoices( const double* limits,
498 const UnicodeString* formats,
499 int32_t cnt )
500 {
501 setChoices(limits, 0, formats, cnt);
502 }
503
504 // -------------------------------------
505 // Sets the limit and format arrays.
506 void
507 ChoiceFormat::setChoices( const double* limits,
508 const UBool* closures,
509 const UnicodeString* formats,
510 int32_t cnt )
511 {
512 if(limits == 0 || formats == 0)
513 return;
514
515 uprv_free(fChoiceLimits);
516 uprv_free(fClosures);
517 delete [] fChoiceFormats;
518
519 // Note that the old arrays are deleted and this owns
520 // the created array.
521 fCount = cnt;
522 fChoiceLimits = (double*) uprv_malloc( sizeof(double) * fCount);
523 fClosures = (UBool*) uprv_malloc( sizeof(UBool) * fCount);
524 fChoiceFormats = new UnicodeString[fCount];
525
526 uprv_arrayCopy(limits, fChoiceLimits, fCount);
527 uprv_arrayCopy(formats, fChoiceFormats, fCount);
528
529 if (closures != 0) {
530 uprv_arrayCopy(closures, fClosures, fCount);
531 } else {
532 int32_t i;
533 for (i=0; i<fCount; ++i) {
534 fClosures[i] = FALSE;
535 }
536 }
537 }
538
539 // -------------------------------------
540 // Gets the limit array.
541
542 const double*
543 ChoiceFormat::getLimits(int32_t& cnt) const
544 {
545 cnt = fCount;
546 return fChoiceLimits;
547 }
548
549 // -------------------------------------
550 // Gets the closures array.
551
552 const UBool*
553 ChoiceFormat::getClosures(int32_t& cnt) const
554 {
555 cnt = fCount;
556 return fClosures;
557 }
558
559 // -------------------------------------
560 // Gets the format array.
561
562 const UnicodeString*
563 ChoiceFormat::getFormats(int32_t& cnt) const
564 {
565 cnt = fCount;
566 return fChoiceFormats;
567 }
568
569 // -------------------------------------
570 // Formats an int64 number, it's actually formatted as
571 // a double. The returned format string may differ
572 // from the input number because of this.
573
574 UnicodeString&
575 ChoiceFormat::format(int64_t number,
576 UnicodeString& appendTo,
577 FieldPosition& status) const
578 {
579 return format((double) number, appendTo, status);
580 }
581
582 // -------------------------------------
583 // Formats a long number, it's actually formatted as
584 // a double. The returned format string may differ
585 // from the input number because of this.
586
587 UnicodeString&
588 ChoiceFormat::format(int32_t number,
589 UnicodeString& appendTo,
590 FieldPosition& status) const
591 {
592 return format((double) number, appendTo, status);
593 }
594
595 // -------------------------------------
596 // Formats a double number.
597
598 UnicodeString&
599 ChoiceFormat::format(double number,
600 UnicodeString& appendTo,
601 FieldPosition& /*pos*/) const
602 {
603 // find the number
604 int32_t i;
605 for (i = 0; i < fCount; ++i) {
606 if (fClosures[i]) {
607 if (!(number > fChoiceLimits[i])) {
608 // same as number <= fChoiceLimits, except catches NaN
609 break;
610 }
611 } else if (!(number >= fChoiceLimits[i])) {
612 // same as number < fChoiceLimits, except catches NaN
613 break;
614 }
615 }
616 --i;
617 if (i < 0) {
618 i = 0;
619 }
620 // return either a formatted number, or a string
621 appendTo += fChoiceFormats[i];
622 return appendTo;
623 }
624
625 // -------------------------------------
626 // Formats an array of objects. Checks if the data type of the objects
627 // to get the right value for formatting.
628
629 UnicodeString&
630 ChoiceFormat::format(const Formattable* objs,
631 int32_t cnt,
632 UnicodeString& appendTo,
633 FieldPosition& pos,
634 UErrorCode& status) const
635 {
636 if(cnt < 0) {
637 status = U_ILLEGAL_ARGUMENT_ERROR;
638 return appendTo;
639 }
640
641 UnicodeString buffer;
642 for (int32_t i = 0; i < cnt; i++) {
643 double objDouble = objs[i].getDouble(status);
644 if (U_SUCCESS(status)) {
645 buffer.remove();
646 appendTo += format(objDouble, buffer, pos);
647 }
648 }
649
650 return appendTo;
651 }
652
653 // -------------------------------------
654 // Formats an array of objects. Checks if the data type of the objects
655 // to get the right value for formatting.
656
657 UnicodeString&
658 ChoiceFormat::format(const Formattable& obj,
659 UnicodeString& appendTo,
660 FieldPosition& pos,
661 UErrorCode& status) const
662 {
663 return NumberFormat::format(obj, appendTo, pos, status);
664 }
665 // -------------------------------------
666
667 void
668 ChoiceFormat::parse(const UnicodeString& text,
669 Formattable& result,
670 ParsePosition& status) const
671 {
672 // find the best number (defined as the one with the longest parse)
673 int32_t start = status.getIndex();
674 int32_t furthest = start;
675 double bestNumber = uprv_getNaN();
676 double tempNumber = 0.0;
677 for (int i = 0; i < fCount; ++i) {
678 int32_t len = fChoiceFormats[i].length();
679 if (text.compare(start, len, fChoiceFormats[i]) == 0) {
680 status.setIndex(start + len);
681 tempNumber = fChoiceLimits[i];
682 if (status.getIndex() > furthest) {
683 furthest = status.getIndex();
684 bestNumber = tempNumber;
685 if (furthest == text.length())
686 break;
687 }
688 }
689 }
690 status.setIndex(furthest);
691 if (status.getIndex() == start) {
692 status.setErrorIndex(furthest);
693 }
694 result.setDouble(bestNumber);
695 }
696
697 // -------------------------------------
698 // Parses the text and return the Formattable object.
699
700 void
701 ChoiceFormat::parse(const UnicodeString& text,
702 Formattable& result,
703 UErrorCode& status) const
704 {
705 NumberFormat::parse(text, result, status);
706 }
707
708 // -------------------------------------
709
710 Format*
711 ChoiceFormat::clone() const
712 {
713 ChoiceFormat *aCopy = new ChoiceFormat(*this);
714 return aCopy;
715 }
716
717 U_NAMESPACE_END
718
719 #endif /* #if !UCONFIG_NO_FORMATTING */
720
721 //eof