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