Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / icu / source / i18n / choicfmt.cpp
1 /*
2 *******************************************************************************
3 * Copyright (C) 1997-2013, 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 "messageimpl.h"
36 #include "putilimp.h"
37 #include "uassert.h"
38 #include <stdio.h>
39 #include <float.h>
40
41 // *****************************************************************************
42 // class ChoiceFormat
43 // *****************************************************************************
44
45 U_NAMESPACE_BEGIN
46
47 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(ChoiceFormat)
48
49 // Special characters used by ChoiceFormat.  There are two characters
50 // used interchangeably to indicate <=.  Either is parsed, but only
51 // LESS_EQUAL is generated by toPattern().
52 #define SINGLE_QUOTE ((UChar)0x0027)   /*'*/
53 #define LESS_THAN    ((UChar)0x003C)   /*<*/
54 #define LESS_EQUAL   ((UChar)0x0023)   /*#*/
55 #define LESS_EQUAL2  ((UChar)0x2264)
56 #define VERTICAL_BAR ((UChar)0x007C)   /*|*/
57 #define MINUS        ((UChar)0x002D)   /*-*/
58
59 static const UChar LEFT_CURLY_BRACE = 0x7B;     /*{*/
60 static const UChar RIGHT_CURLY_BRACE = 0x7D;    /*}*/
61
62 #ifdef INFINITY
63 #undef INFINITY
64 #endif
65 #define INFINITY     ((UChar)0x221E)
66
67 //static const UChar gPositiveInfinity[] = {INFINITY, 0};
68 //static const UChar gNegativeInfinity[] = {MINUS, INFINITY, 0};
69 #define POSITIVE_INF_STRLEN 1
70 #define NEGATIVE_INF_STRLEN 2
71
72 // -------------------------------------
73 // Creates a ChoiceFormat instance based on the pattern.
74
75 ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern,
76                            UErrorCode& status)
77 : constructorErrorCode(status),
78   msgPattern(status)
79 {
80     applyPattern(newPattern, status);
81 }
82
83 // -------------------------------------
84 // Creates a ChoiceFormat instance with the limit array and 
85 // format strings for each limit.
86
87 ChoiceFormat::ChoiceFormat(const double* limits, 
88                            const UnicodeString* formats, 
89                            int32_t cnt )
90 : constructorErrorCode(U_ZERO_ERROR),
91   msgPattern(constructorErrorCode)
92 {
93     setChoices(limits, NULL, formats, cnt, constructorErrorCode);
94 }
95
96 // -------------------------------------
97
98 ChoiceFormat::ChoiceFormat(const double* limits, 
99                            const UBool* closures,
100                            const UnicodeString* formats, 
101                            int32_t cnt )
102 : constructorErrorCode(U_ZERO_ERROR),
103   msgPattern(constructorErrorCode)
104 {
105     setChoices(limits, closures, formats, cnt, constructorErrorCode);
106 }
107
108 // -------------------------------------
109 // copy constructor
110
111 ChoiceFormat::ChoiceFormat(const    ChoiceFormat&   that) 
112 : NumberFormat(that),
113   constructorErrorCode(that.constructorErrorCode),
114   msgPattern(that.msgPattern)
115 {
116 }
117
118 // -------------------------------------
119 // Private constructor that creates a 
120 // ChoiceFormat instance based on the 
121 // pattern and populates UParseError
122
123 ChoiceFormat::ChoiceFormat(const UnicodeString& newPattern,
124                            UParseError& parseError,
125                            UErrorCode& status)
126 : constructorErrorCode(status),
127   msgPattern(status)
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     return msgPattern == thatAlias.msgPattern;
140 }
141
142 // -------------------------------------
143 // copy constructor
144
145 const ChoiceFormat&
146 ChoiceFormat::operator=(const   ChoiceFormat& that)
147 {
148     if (this != &that) {
149         NumberFormat::operator=(that);
150         constructorErrorCode = that.constructorErrorCode;
151         msgPattern = that.msgPattern;
152     }
153     return *this;
154 }
155
156 // -------------------------------------
157
158 ChoiceFormat::~ChoiceFormat()
159 {
160 }
161
162 // -------------------------------------
163
164 /**
165  * Convert a double value to a string without the overhead of NumberFormat.
166  */
167 UnicodeString&
168 ChoiceFormat::dtos(double value,
169                    UnicodeString& string)
170 {
171     /* Buffer to contain the digits and any extra formatting stuff. */
172     char temp[DBL_DIG + 16];
173     char *itrPtr = temp;
174     char *expPtr;
175
176     sprintf(temp, "%.*g", DBL_DIG, value);
177
178     /* Find and convert the decimal point.
179        Using setlocale on some machines will cause sprintf to use a comma for certain locales.
180     */
181     while (*itrPtr && (*itrPtr == '-' || isdigit(*itrPtr))) {
182         itrPtr++;
183     }
184     if (*itrPtr != 0 && *itrPtr != 'e') {
185         /* We reached something that looks like a decimal point.
186         In case someone used setlocale(), which changes the decimal point. */
187         *itrPtr = '.';
188         itrPtr++;
189     }
190     /* Search for the exponent */
191     while (*itrPtr && *itrPtr != 'e') {
192         itrPtr++;
193     }
194     if (*itrPtr == 'e') {
195         itrPtr++;
196         /* Verify the exponent sign */
197         if (*itrPtr == '+' || *itrPtr == '-') {
198             itrPtr++;
199         }
200         /* Remove leading zeros. You will see this on Windows machines. */
201         expPtr = itrPtr;
202         while (*itrPtr == '0') {
203             itrPtr++;
204         }
205         if (*itrPtr && expPtr != itrPtr) {
206             /* Shift the exponent without zeros. */
207             while (*itrPtr) {
208                 *(expPtr++)  = *(itrPtr++);
209             }
210             // NULL terminate
211             *expPtr = 0;
212         }
213     }
214
215     string = UnicodeString(temp, -1, US_INV);    /* 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     msgPattern.parseChoiceStyle(pattern, NULL, status);
227     constructorErrorCode = 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     msgPattern.parseChoiceStyle(pattern, &parseError, status);
239     constructorErrorCode = status;
240 }
241 // -------------------------------------
242 // Returns the input pattern string.
243
244 UnicodeString&
245 ChoiceFormat::toPattern(UnicodeString& result) const
246 {
247     return result = msgPattern.getPatternString();
248 }
249
250 // -------------------------------------
251 // Sets the limit and format arrays. 
252 void
253 ChoiceFormat::setChoices(  const double* limits, 
254                            const UnicodeString* formats, 
255                            int32_t cnt )
256 {
257     UErrorCode errorCode = U_ZERO_ERROR;
258     setChoices(limits, NULL, formats, cnt, errorCode);
259 }
260
261 // -------------------------------------
262 // Sets the limit and format arrays. 
263 void
264 ChoiceFormat::setChoices(  const double* limits, 
265                            const UBool* closures,
266                            const UnicodeString* formats, 
267                            int32_t cnt )
268 {
269     UErrorCode errorCode = U_ZERO_ERROR;
270     setChoices(limits, closures, formats, cnt, errorCode);
271 }
272
273 void
274 ChoiceFormat::setChoices(const double* limits,
275                          const UBool* closures,
276                          const UnicodeString* formats,
277                          int32_t count,
278                          UErrorCode &errorCode) {
279     if (U_FAILURE(errorCode)) {
280         return;
281     }
282     if (limits == NULL || formats == NULL) {
283         errorCode = U_ILLEGAL_ARGUMENT_ERROR;
284         return;
285     }
286     // Reconstruct the original input pattern.
287     // Modified version of the pre-ICU 4.8 toPattern() implementation.
288     UnicodeString result;
289     for (int32_t i = 0; i < count; ++i) {
290         if (i != 0) {
291             result += VERTICAL_BAR;
292         }
293         UnicodeString buf;
294         if (uprv_isPositiveInfinity(limits[i])) {
295             result += INFINITY;
296         } else if (uprv_isNegativeInfinity(limits[i])) {
297             result += MINUS;
298             result += INFINITY;
299         } else {
300             result += dtos(limits[i], buf);
301         }
302         if (closures != NULL && closures[i]) {
303             result += LESS_THAN;
304         } else {
305             result += LESS_EQUAL;
306         }
307         // Append formats[i], using quotes if there are special
308         // characters.  Single quotes themselves must be escaped in
309         // either case.
310         const UnicodeString& text = formats[i];
311         int32_t textLength = text.length();
312         int32_t nestingLevel = 0;
313         for (int32_t j = 0; j < textLength; ++j) {
314             UChar c = text[j];
315             if (c == SINGLE_QUOTE && nestingLevel == 0) {
316                 // Double each top-level apostrophe.
317                 result.append(c);
318             } else if (c == VERTICAL_BAR && nestingLevel == 0) {
319                 // Surround each pipe symbol with apostrophes for quoting.
320                 // If the next character is an apostrophe, then that will be doubled,
321                 // and although the parser will see the apostrophe pairs beginning
322                 // and ending one character earlier than our doubling, the result
323                 // is as desired.
324                 //   | -> '|'
325                 //   |' -> '|'''
326                 //   |'' -> '|''''' etc.
327                 result.append(SINGLE_QUOTE).append(c).append(SINGLE_QUOTE);
328                 continue;  // Skip the append(c) at the end of the loop body.
329             } else if (c == LEFT_CURLY_BRACE) {
330                 ++nestingLevel;
331             } else if (c == RIGHT_CURLY_BRACE && nestingLevel > 0) {
332                 --nestingLevel;
333             }
334             result.append(c);
335         }
336     }
337     // Apply the reconstructed pattern.
338     applyPattern(result, errorCode);
339 }
340
341 // -------------------------------------
342 // Gets the limit array.
343
344 const double*
345 ChoiceFormat::getLimits(int32_t& cnt) const 
346 {
347     cnt = 0;
348     return NULL;
349 }
350
351 // -------------------------------------
352 // Gets the closures array.
353
354 const UBool*
355 ChoiceFormat::getClosures(int32_t& cnt) const 
356 {
357     cnt = 0;
358     return NULL;
359 }
360
361 // -------------------------------------
362 // Gets the format array.
363
364 const UnicodeString*
365 ChoiceFormat::getFormats(int32_t& cnt) const
366 {
367     cnt = 0;
368     return NULL;
369 }
370
371 // -------------------------------------
372 // Formats an int64 number, it's actually formatted as
373 // a double.  The returned format string may differ
374 // from the input number because of this.
375
376 UnicodeString&
377 ChoiceFormat::format(int64_t number, 
378                      UnicodeString& appendTo, 
379                      FieldPosition& status) const
380 {
381     return format((double) number, appendTo, status);
382 }
383
384 // -------------------------------------
385 // Formats an int32_t number, it's actually formatted as
386 // a double.
387
388 UnicodeString&
389 ChoiceFormat::format(int32_t number, 
390                      UnicodeString& appendTo, 
391                      FieldPosition& status) const
392 {
393     return format((double) number, appendTo, status);
394 }
395
396 // -------------------------------------
397 // Formats a double number.
398
399 UnicodeString&
400 ChoiceFormat::format(double number, 
401                      UnicodeString& appendTo, 
402                      FieldPosition& /*pos*/) const
403 {
404     if (msgPattern.countParts() == 0) {
405         // No pattern was applied, or it failed.
406         return appendTo;
407     }
408     // Get the appropriate sub-message.
409     int32_t msgStart = findSubMessage(msgPattern, 0, number);
410     if (!MessageImpl::jdkAposMode(msgPattern)) {
411         int32_t patternStart = msgPattern.getPart(msgStart).getLimit();
412         int32_t msgLimit = msgPattern.getLimitPartIndex(msgStart);
413         appendTo.append(msgPattern.getPatternString(),
414                         patternStart,
415                         msgPattern.getPatternIndex(msgLimit) - patternStart);
416         return appendTo;
417     }
418     // JDK compatibility mode: Remove SKIP_SYNTAX.
419     return MessageImpl::appendSubMessageWithoutSkipSyntax(msgPattern, msgStart, appendTo);
420 }
421
422 int32_t
423 ChoiceFormat::findSubMessage(const MessagePattern &pattern, int32_t partIndex, double number) {
424     int32_t count = pattern.countParts();
425     int32_t msgStart;
426     // Iterate over (ARG_INT|DOUBLE, ARG_SELECTOR, message) tuples
427     // until ARG_LIMIT or end of choice-only pattern.
428     // Ignore the first number and selector and start the loop on the first message.
429     partIndex += 2;
430     for (;;) {
431         // Skip but remember the current sub-message.
432         msgStart = partIndex;
433         partIndex = pattern.getLimitPartIndex(partIndex);
434         if (++partIndex >= count) {
435             // Reached the end of the choice-only pattern.
436             // Return with the last sub-message.
437             break;
438         }
439         const MessagePattern::Part &part = pattern.getPart(partIndex++);
440         UMessagePatternPartType type = part.getType();
441         if (type == UMSGPAT_PART_TYPE_ARG_LIMIT) {
442             // Reached the end of the ChoiceFormat style.
443             // Return with the last sub-message.
444             break;
445         }
446         // part is an ARG_INT or ARG_DOUBLE
447         U_ASSERT(MessagePattern::Part::hasNumericValue(type));
448         double boundary = pattern.getNumericValue(part);
449         // Fetch the ARG_SELECTOR character.
450         int32_t selectorIndex = pattern.getPatternIndex(partIndex++);
451         UChar boundaryChar = pattern.getPatternString().charAt(selectorIndex);
452         if (boundaryChar == LESS_THAN ? !(number > boundary) : !(number >= boundary)) {
453             // The number is in the interval between the previous boundary and the current one.
454             // Return with the sub-message between them.
455             // The !(a>b) and !(a>=b) comparisons are equivalent to
456             // (a<=b) and (a<b) except they "catch" NaN.
457             break;
458         }
459     }
460     return msgStart;
461 }
462
463 // -------------------------------------
464 // Formats an array of objects. Checks if the data type of the objects
465 // to get the right value for formatting.  
466
467 UnicodeString&
468 ChoiceFormat::format(const Formattable* objs,
469                      int32_t cnt,
470                      UnicodeString& appendTo,
471                      FieldPosition& pos,
472                      UErrorCode& status) const
473 {
474     if(cnt < 0) {
475         status = U_ILLEGAL_ARGUMENT_ERROR;
476         return appendTo;
477     }
478     if (msgPattern.countParts() == 0) {
479         status = U_INVALID_STATE_ERROR;
480         return appendTo;
481     }
482
483     for (int32_t i = 0; i < cnt; i++) {
484         double objDouble = objs[i].getDouble(status);
485         if (U_SUCCESS(status)) {
486             format(objDouble, appendTo, pos);
487         }
488     }
489
490     return appendTo;
491 }
492
493 // -------------------------------------
494
495 void
496 ChoiceFormat::parse(const UnicodeString& text, 
497                     Formattable& result,
498                     ParsePosition& pos) const
499 {
500     result.setDouble(parseArgument(msgPattern, 0, text, pos));
501 }
502
503 double
504 ChoiceFormat::parseArgument(
505         const MessagePattern &pattern, int32_t partIndex,
506         const UnicodeString &source, ParsePosition &pos) {
507     // find the best number (defined as the one with the longest parse)
508     int32_t start = pos.getIndex();
509     int32_t furthest = start;
510     double bestNumber = uprv_getNaN();
511     double tempNumber = 0.0;
512     int32_t count = pattern.countParts();
513     while (partIndex < count && pattern.getPartType(partIndex) != UMSGPAT_PART_TYPE_ARG_LIMIT) {
514         tempNumber = pattern.getNumericValue(pattern.getPart(partIndex));
515         partIndex += 2;  // skip the numeric part and ignore the ARG_SELECTOR
516         int32_t msgLimit = pattern.getLimitPartIndex(partIndex);
517         int32_t len = matchStringUntilLimitPart(pattern, partIndex, msgLimit, source, start);
518         if (len >= 0) {
519             int32_t newIndex = start + len;
520             if (newIndex > furthest) {
521                 furthest = newIndex;
522                 bestNumber = tempNumber;
523                 if (furthest == source.length()) {
524                     break;
525                 }
526             }
527         }
528         partIndex = msgLimit + 1;
529     }
530     if (furthest == start) {
531         pos.setErrorIndex(start);
532     } else {
533         pos.setIndex(furthest);
534     }
535     return bestNumber;
536 }
537
538 int32_t
539 ChoiceFormat::matchStringUntilLimitPart(
540         const MessagePattern &pattern, int32_t partIndex, int32_t limitPartIndex,
541         const UnicodeString &source, int32_t sourceOffset) {
542     int32_t matchingSourceLength = 0;
543     const UnicodeString &msgString = pattern.getPatternString();
544     int32_t prevIndex = pattern.getPart(partIndex).getLimit();
545     for (;;) {
546         const MessagePattern::Part &part = pattern.getPart(++partIndex);
547         if (partIndex == limitPartIndex || part.getType() == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
548             int32_t index = part.getIndex();
549             int32_t length = index - prevIndex;
550             if (length != 0 && 0 != source.compare(sourceOffset, length, msgString, prevIndex, length)) {
551                 return -1;  // mismatch
552             }
553             matchingSourceLength += length;
554             if (partIndex == limitPartIndex) {
555                 return matchingSourceLength;
556             }
557             prevIndex = part.getLimit();  // SKIP_SYNTAX
558         }
559     }
560 }
561
562 // -------------------------------------
563
564 Format*
565 ChoiceFormat::clone() const
566 {
567     ChoiceFormat *aCopy = new ChoiceFormat(*this);
568     return aCopy;
569 }
570
571 U_NAMESPACE_END
572
573 #endif /* #if !UCONFIG_NO_FORMATTING */
574
575 //eof