Move the content of LocalizedCalendarICU.cpp and LocalizedDateICU.cpp to ICULocale...
[WebKit-https.git] / Source / WebCore / platform / text / ICULocale.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "ICULocale.h"
33
34 #include <limits>
35 #include <wtf/PassOwnPtr.h>
36 #include <wtf/text/StringBuilder.h>
37
38 using namespace icu;
39 using namespace std;
40
41 namespace WebCore {
42
43 ICULocale::ICULocale(const char* locale)
44     : m_locale(locale)
45     , m_numberFormat(0)
46     , m_shortDateFormat(0)
47     , m_didCreateDecimalFormat(false)
48     , m_didCreateShortDateFormat(false)
49 #if ENABLE(CALENDAR_PICKER)
50     , m_firstDayOfWeek(0)
51 #endif
52 {
53 }
54
55 ICULocale::~ICULocale()
56 {
57     unum_close(m_numberFormat);
58     udat_close(m_shortDateFormat);
59 }
60
61 PassOwnPtr<ICULocale> ICULocale::create(const char* localeString)
62 {
63     return adoptPtr(new ICULocale(localeString));
64 }
65
66 PassOwnPtr<ICULocale> ICULocale::createForCurrentLocale()
67 {
68     return adoptPtr(new ICULocale(0));
69 }
70
71 ICULocale* ICULocale::currentLocale()
72 {
73     static ICULocale* currentICULocale = ICULocale::createForCurrentLocale().leakPtr();
74     return currentICULocale;
75 }
76
77 void ICULocale::setDecimalSymbol(unsigned index, UNumberFormatSymbol symbol)
78 {
79     UErrorCode status = U_ZERO_ERROR;
80     int32_t bufferLength = unum_getSymbol(m_numberFormat, symbol, 0, 0, &status);
81     ASSERT(U_SUCCESS(status) || status == U_BUFFER_OVERFLOW_ERROR);
82     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
83         return;
84     Vector<UChar> buffer(bufferLength);
85     status = U_ZERO_ERROR;
86     unum_getSymbol(m_numberFormat, symbol, buffer.data(), bufferLength, &status);
87     if (U_FAILURE(status))
88         return;
89     m_decimalSymbols[index] = String::adopt(buffer);
90 }
91
92 void ICULocale::setDecimalTextAttribute(String& destination, UNumberFormatTextAttribute tag)
93 {
94     UErrorCode status = U_ZERO_ERROR;
95     int32_t bufferLength = unum_getTextAttribute(m_numberFormat, tag, 0, 0, &status);
96     ASSERT(U_SUCCESS(status) || status == U_BUFFER_OVERFLOW_ERROR);
97     if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR)
98         return;
99     Vector<UChar> buffer(bufferLength);
100     status = U_ZERO_ERROR;
101     unum_getTextAttribute(m_numberFormat, tag, buffer.data(), bufferLength, &status);
102     ASSERT(U_SUCCESS(status));
103     if (U_FAILURE(status))
104         return;
105     destination = String::adopt(buffer);
106 }
107
108 void ICULocale::initializeDecimalFormat()
109 {
110     if (m_didCreateDecimalFormat)
111         return;
112     m_didCreateDecimalFormat = true;
113     UErrorCode status = U_ZERO_ERROR;
114     m_numberFormat = unum_open(UNUM_DECIMAL, 0, 0, m_locale.data(), 0, &status);
115     if (!U_SUCCESS(status))
116         return;
117
118     setDecimalSymbol(0, UNUM_ZERO_DIGIT_SYMBOL);
119     setDecimalSymbol(1, UNUM_ONE_DIGIT_SYMBOL);
120     setDecimalSymbol(2, UNUM_TWO_DIGIT_SYMBOL);
121     setDecimalSymbol(3, UNUM_THREE_DIGIT_SYMBOL);
122     setDecimalSymbol(4, UNUM_FOUR_DIGIT_SYMBOL);
123     setDecimalSymbol(5, UNUM_FIVE_DIGIT_SYMBOL);
124     setDecimalSymbol(6, UNUM_SIX_DIGIT_SYMBOL);
125     setDecimalSymbol(7, UNUM_SEVEN_DIGIT_SYMBOL);
126     setDecimalSymbol(8, UNUM_EIGHT_DIGIT_SYMBOL);
127     setDecimalSymbol(9, UNUM_NINE_DIGIT_SYMBOL);
128     setDecimalSymbol(DecimalSeparatorIndex, UNUM_DECIMAL_SEPARATOR_SYMBOL);
129     setDecimalSymbol(GroupSeparatorIndex, UNUM_GROUPING_SEPARATOR_SYMBOL);
130     setDecimalTextAttribute(m_positivePrefix, UNUM_POSITIVE_PREFIX);
131     setDecimalTextAttribute(m_positiveSuffix, UNUM_POSITIVE_SUFFIX);
132     setDecimalTextAttribute(m_negativePrefix, UNUM_NEGATIVE_PREFIX);
133     setDecimalTextAttribute(m_negativeSuffix, UNUM_NEGATIVE_SUFFIX);
134     ASSERT(!m_positivePrefix.isEmpty() || !m_positiveSuffix.isEmpty() || !m_negativePrefix.isEmpty() || !m_negativeSuffix.isEmpty());
135 }
136
137 String ICULocale::convertToLocalizedNumber(const String& input)
138 {
139     initializeDecimalFormat();
140     if (!m_numberFormat || input.isEmpty())
141         return input;
142
143     unsigned i = 0;
144     bool isNegative = false;
145     UnicodeString ustring;
146     StringBuilder builder;
147     builder.reserveCapacity(input.length());
148
149     if (input[0] == '-') {
150         ++i;
151         isNegative = true;
152         builder.append(m_negativePrefix);
153     } else
154         builder.append(m_positivePrefix);
155
156     for (; i < input.length(); ++i) {
157         switch (input[i]) {
158         case '0':
159         case '1':
160         case '2':
161         case '3':
162         case '4':
163         case '5':
164         case '6':
165         case '7':
166         case '8':
167         case '9':
168             builder.append(m_decimalSymbols[input[i] - '0']);
169             break;
170         case '.':
171             builder.append(m_decimalSymbols[DecimalSeparatorIndex]);
172             break;
173         default:
174             ASSERT_NOT_REACHED();
175         }
176     }
177
178     builder.append(isNegative ? m_negativeSuffix : m_positiveSuffix);
179
180     return builder.toString();
181 }
182
183 static bool matches(const String& text, unsigned position, const String& part)
184 {
185     if (part.isEmpty())
186         return true;
187     if (position + part.length() > text.length())
188         return false;
189     for (unsigned i = 0; i < part.length(); ++i) {
190         if (text[position + i] != part[i])
191             return false;
192     }
193     return true;
194 }
195
196 bool ICULocale::detectSignAndGetDigitRange(const String& input, bool& isNegative, unsigned& startIndex, unsigned& endIndex)
197 {
198     startIndex = 0;
199     endIndex = input.length();
200     if (m_negativePrefix.isEmpty() && m_negativeSuffix.isEmpty()) {
201         if (input.startsWith(m_positivePrefix) && input.endsWith(m_positiveSuffix)) {
202             isNegative = false;
203             startIndex = m_positivePrefix.length();
204             endIndex -= m_positiveSuffix.length();
205         } else
206             isNegative = true;
207     } else {
208         if (input.startsWith(m_negativePrefix) && input.endsWith(m_negativeSuffix)) {
209             isNegative = true;
210             startIndex = m_negativePrefix.length();
211             endIndex -= m_negativeSuffix.length();
212         } else {
213             isNegative = false;
214             if (input.startsWith(m_positivePrefix) && input.endsWith(m_positiveSuffix)) {
215                 startIndex = m_positivePrefix.length();
216                 endIndex -= m_positiveSuffix.length();
217             } else
218                 return false;
219         }
220     }
221     return true;
222 }
223
224 unsigned ICULocale::matchedDecimalSymbolIndex(const String& input, unsigned& position)
225 {
226     for (unsigned symbolIndex = 0; symbolIndex < DecimalSymbolsSize; ++symbolIndex) {
227         if (m_decimalSymbols[symbolIndex].length() && matches(input, position, m_decimalSymbols[symbolIndex])) {
228             position += m_decimalSymbols[symbolIndex].length();
229             return symbolIndex;
230         }
231     }
232     return DecimalSymbolsSize;
233 }
234
235 String ICULocale::convertFromLocalizedNumber(const String& localized)
236 {
237     initializeDecimalFormat();
238     String input = localized.stripWhiteSpace();
239     if (!m_numberFormat || input.isEmpty())
240         return input;
241
242     bool isNegative;
243     unsigned startIndex;
244     unsigned endIndex;
245     if (!detectSignAndGetDigitRange(input, isNegative, startIndex, endIndex)) {
246         // Input is broken. Returning an invalid number string.
247         return "*";
248     }
249
250     StringBuilder builder;
251     builder.reserveCapacity(input.length());
252     if (isNegative)
253         builder.append("-");
254     for (unsigned i = startIndex; i < endIndex;) {
255         unsigned symbolIndex = matchedDecimalSymbolIndex(input, i);
256         if (symbolIndex >= DecimalSymbolsSize)
257             return "*";
258         if (symbolIndex == DecimalSeparatorIndex)
259             builder.append('.');
260         else if (symbolIndex == GroupSeparatorIndex) {
261             // Ignore group separators.
262
263         } else
264             builder.append(static_cast<UChar>('0' + symbolIndex));
265     }
266     return builder.toString();
267 }
268
269 bool ICULocale::initializeShortDateFormat()
270 {
271     if (m_didCreateShortDateFormat)
272         return m_shortDateFormat;
273     const UChar gmtTimezone[3] = {'G', 'M', 'T'};
274     UErrorCode status = U_ZERO_ERROR;
275     m_shortDateFormat = udat_open(UDAT_NONE, UDAT_SHORT, m_locale.data(), gmtTimezone, WTF_ARRAY_LENGTH(gmtTimezone), 0, -1, &status);
276     m_didCreateShortDateFormat = true;
277     return m_shortDateFormat;
278 }
279
280 double ICULocale::parseLocalizedDate(const String& input)
281 {
282     if (!initializeShortDateFormat())
283         return numeric_limits<double>::quiet_NaN();
284     if (input.length() > static_cast<unsigned>(numeric_limits<int32_t>::max()))
285         return numeric_limits<double>::quiet_NaN();
286     int32_t inputLength = static_cast<int32_t>(input.length());
287     UErrorCode status = U_ZERO_ERROR;
288     int32_t parsePosition = 0;
289     UDate date = udat_parse(m_shortDateFormat, input.characters(), inputLength, &parsePosition, &status);
290     if (parsePosition != inputLength || U_FAILURE(status))
291         return numeric_limits<double>::quiet_NaN();
292     // UDate, which is an alias of double, is compatible with our expectation.
293     return date;
294 }
295
296 String ICULocale::formatLocalizedDate(const DateComponents& dateComponents)
297 {
298     if (!initializeShortDateFormat())
299         return String();
300     double input = dateComponents.millisecondsSinceEpoch();
301     UErrorCode status = U_ZERO_ERROR;
302     int32_t length = udat_format(m_shortDateFormat, input, 0, 0, 0, &status);
303     if (status != U_BUFFER_OVERFLOW_ERROR)
304         return String();
305     Vector<UChar> buffer(length);
306     status = U_ZERO_ERROR;
307     udat_format(m_shortDateFormat, input, buffer.data(), length, 0, &status);
308     if (U_FAILURE(status))
309         return String();
310     return String::adopt(buffer);
311 }
312
313 #if ENABLE(CALENDAR_PICKER)
314 PassOwnPtr<Vector<String> > ICULocale::createLabelVector(UDateFormatSymbolType type, int32_t startIndex, int32_t size)
315 {
316     if (!m_shortDateFormat)
317         return PassOwnPtr<Vector<String> >();
318     if (udat_countSymbols(m_shortDateFormat, type) != startIndex + size)
319         return PassOwnPtr<Vector<String> >();
320
321     OwnPtr<Vector<String> > labels = adoptPtr(new Vector<String>());
322     labels->reserveCapacity(size);
323     for (int32_t i = 0; i < size; ++i) {
324         UErrorCode status = U_ZERO_ERROR;
325         int32_t length = udat_getSymbols(m_shortDateFormat, type, startIndex + i, 0, 0, &status);
326         if (status != U_BUFFER_OVERFLOW_ERROR)
327             return PassOwnPtr<Vector<String> >();
328         Vector<UChar> buffer(length);
329         status = U_ZERO_ERROR;
330         udat_getSymbols(m_shortDateFormat, type, startIndex + i, buffer.data(), length, &status);
331         if (U_FAILURE(status))
332             return PassOwnPtr<Vector<String> >();
333         labels->append(String::adopt(buffer));
334     }
335     return labels.release();
336 }
337
338 static PassOwnPtr<Vector<String> > createFallbackMonthLabels()
339 {
340     OwnPtr<Vector<String> > labels = adoptPtr(new Vector<String>());
341     labels->reserveCapacity(12);
342     labels->append("January");
343     labels->append("February");
344     labels->append("March");
345     labels->append("April");
346     labels->append("May");
347     labels->append("June");
348     labels->append("July");
349     labels->append("August");
350     labels->append("September");
351     labels->append("October");
352     labels->append("November");
353     labels->append("December");
354     return labels.release();
355 }
356
357 static PassOwnPtr<Vector<String> > createFallbackWeekDayShortLabels()
358 {
359     OwnPtr<Vector<String> > labels = adoptPtr(new Vector<String>());
360     labels->reserveCapacity(7);
361     labels->append("Sun");
362     labels->append("Mon");
363     labels->append("Tue");
364     labels->append("Wed");
365     labels->append("Thu");
366     labels->append("Fri");
367     labels->append("Sat");
368     return labels.release();
369 }
370
371 void ICULocale::initializeCalendar()
372 {
373     if (m_monthLabels && m_weekDayShortLabels)
374         return;
375
376     if (!initializeShortDateFormat()) {
377         m_firstDayOfWeek = 0;
378         m_monthLabels = createFallbackMonthLabels();
379         m_weekDayShortLabels = createFallbackWeekDayShortLabels();
380         return;
381     }
382     m_firstDayOfWeek = ucal_getAttribute(udat_getCalendar(m_shortDateFormat), UCAL_FIRST_DAY_OF_WEEK) - UCAL_SUNDAY;
383
384     m_monthLabels = createLabelVector(UDAT_MONTHS, UCAL_JANUARY, 12);
385     if (!m_monthLabels)
386         m_monthLabels = createFallbackMonthLabels();
387
388     m_weekDayShortLabels = createLabelVector(UDAT_SHORT_WEEKDAYS, UCAL_SUNDAY, 7);
389     if (!m_weekDayShortLabels)
390         m_weekDayShortLabels = createFallbackWeekDayShortLabels();
391 }
392
393 const Vector<String>& ICULocale::monthLabels()
394 {
395     initializeCalendar();
396     return *m_monthLabels;
397 }
398
399 const Vector<String>& ICULocale::weekDayShortLabels()
400 {
401     initializeCalendar();
402     return *m_weekDayShortLabels;
403 }
404
405 unsigned ICULocale::firstDayOfWeek()
406 {
407     initializeCalendar();
408     return m_firstDayOfWeek;
409 }
410 #endif
411
412 } // namespace WebCore
413