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