]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/choicfmt.cpp
ICU-8.11.4.tar.gz
[apple/icu.git] / icuSources / i18n / choicfmt.cpp
CommitLineData
b75a7d8f
A
1/*
2*******************************************************************************
73c04bcf 3* Copyright (C) 1997-2006, International Business Machines Corporation and *
b75a7d8f
A
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"
374ca955
A
34#include "cstring.h"
35#include "putilimp.h"
73c04bcf
A
36#include <stdio.h>
37#include <float.h>
b75a7d8f
A
38
39// *****************************************************************************
40// class ChoiceFormat
41// *****************************************************************************
42
43U_NAMESPACE_BEGIN
44
374ca955 45UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChoiceFormat)
b75a7d8f
A
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
374ca955
A
58static const UChar gPositiveInfinity[] = {INFINITY, 0};
59static const UChar gNegativeInfinity[] = {MINUS, INFINITY, 0};
b75a7d8f
A
60#define POSITIVE_INF_STRLEN 1
61#define NEGATIVE_INF_STRLEN 2
62
63// -------------------------------------
64// Creates a ChoiceFormat instance based on the pattern.
65
66ChoiceFormat::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
80ChoiceFormat::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
93ChoiceFormat::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
108ChoiceFormat::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
122ChoiceFormat::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
134UBool
135ChoiceFormat::operator==(const Format& that) const
136{
137 if (this == &that) return TRUE;
b75a7d8f
A
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
155const ChoiceFormat&
156ChoiceFormat::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
181ChoiceFormat::~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 */
195double
196ChoiceFormat::stod(const UnicodeString& string)
197{
198 char source[256];
199 char* end;
200
374ca955 201 string.extract(0, string.length(), source, (int32_t)sizeof(source), US_INV); /* invariant codepage */
b75a7d8f
A
202 return uprv_strtod(source,&end);
203}
204
205// -------------------------------------
206
207/**
208 * Convert a double value to a string
209 */
210UnicodeString&
211ChoiceFormat::dtos(double value,
212 UnicodeString& string)
213{
73c04bcf
A
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 }
b75a7d8f 230
73c04bcf
A
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 }
374ca955 241 string = UnicodeString(temp, -1, US_INV); /* invariant codepage */
b75a7d8f
A
242 return string;
243}
244
245// -------------------------------------
246// calls the overloaded applyPattern method.
247
248void
249ChoiceFormat::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
259void
260ChoiceFormat::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();
374ca955 351 if (!buf.compare(gPositiveInfinity, POSITIVE_INF_STRLEN)) {
b75a7d8f 352 limit = uprv_getInfinity();
374ca955 353 } else if (!buf.compare(gNegativeInfinity, NEGATIVE_INF_STRLEN)) {
b75a7d8f
A
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
412error:
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
424UnicodeString&
425ChoiceFormat::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
481void
482ChoiceFormat::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
492void
493ChoiceFormat::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.
521void
522ChoiceFormat::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.
531void
532ChoiceFormat::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
567const double*
568ChoiceFormat::getLimits(int32_t& cnt) const
569{
570 cnt = fCount;
571 return fChoiceLimits;
572}
573
574// -------------------------------------
575// Gets the closures array.
576
577const UBool*
578ChoiceFormat::getClosures(int32_t& cnt) const
579{
580 cnt = fCount;
581 return fClosures;
582}
583
584// -------------------------------------
585// Gets the format array.
586
587const UnicodeString*
588ChoiceFormat::getFormats(int32_t& cnt) const
589{
590 cnt = fCount;
591 return fChoiceFormats;
592}
593
374ca955
A
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
599UnicodeString&
600ChoiceFormat::format(int64_t number,
601 UnicodeString& appendTo,
602 FieldPosition& status) const
603{
604 return format((double) number, appendTo, status);
605}
606
b75a7d8f
A
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
612UnicodeString&
613ChoiceFormat::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
623UnicodeString&
624ChoiceFormat::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
654UnicodeString&
655ChoiceFormat::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++) {
374ca955
A
668 double objDouble = objs[i].getDouble(status);
669 if (U_SUCCESS(status)) {
670 buffer.remove();
671 appendTo += format(objDouble, buffer, pos);
672 }
b75a7d8f
A
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
682UnicodeString&
683ChoiceFormat::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
692void
693ChoiceFormat::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
725void
726ChoiceFormat::parse(const UnicodeString& text,
727 Formattable& result,
728 UErrorCode& status) const
729{
730 NumberFormat::parse(text, result, status);
731}
732
733// -------------------------------------
734
735Format*
736ChoiceFormat::clone() const
737{
738 ChoiceFormat *aCopy = new ChoiceFormat(*this);
739 return aCopy;
740}
741
742U_NAMESPACE_END
743
744#endif /* #if !UCONFIG_NO_FORMATTING */
745
746//eof