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