2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "ICULocale.h"
35 #include <wtf/PassOwnPtr.h>
36 #include <wtf/text/StringBuilder.h>
43 ICULocale::ICULocale(const char* locale)
46 , m_shortDateFormat(0)
47 , m_didCreateDecimalFormat(false)
48 , m_didCreateShortDateFormat(false)
49 #if ENABLE(CALENDAR_PICKER)
55 ICULocale::~ICULocale()
57 unum_close(m_numberFormat);
58 udat_close(m_shortDateFormat);
61 PassOwnPtr<ICULocale> ICULocale::create(const char* localeString)
63 return adoptPtr(new ICULocale(localeString));
66 PassOwnPtr<ICULocale> ICULocale::createForCurrentLocale()
68 return adoptPtr(new ICULocale(0));
71 ICULocale* ICULocale::currentLocale()
73 static ICULocale* currentICULocale = ICULocale::createForCurrentLocale().leakPtr();
74 return currentICULocale;
77 void ICULocale::setDecimalSymbol(unsigned index, UNumberFormatSymbol symbol)
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)
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))
89 m_decimalSymbols[index] = String::adopt(buffer);
92 void ICULocale::setDecimalTextAttribute(String& destination, UNumberFormatTextAttribute tag)
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)
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))
105 destination = String::adopt(buffer);
108 void ICULocale::initializeDecimalFormat()
110 if (m_didCreateDecimalFormat)
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))
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());
137 String ICULocale::convertToLocalizedNumber(const String& input)
139 initializeDecimalFormat();
140 if (!m_numberFormat || input.isEmpty())
144 bool isNegative = false;
145 UnicodeString ustring;
146 StringBuilder builder;
147 builder.reserveCapacity(input.length());
149 if (input[0] == '-') {
152 builder.append(m_negativePrefix);
154 builder.append(m_positivePrefix);
156 for (; i < input.length(); ++i) {
168 builder.append(m_decimalSymbols[input[i] - '0']);
171 builder.append(m_decimalSymbols[DecimalSeparatorIndex]);
174 ASSERT_NOT_REACHED();
178 builder.append(isNegative ? m_negativeSuffix : m_positiveSuffix);
180 return builder.toString();
183 static bool matches(const String& text, unsigned position, const String& part)
187 if (position + part.length() > text.length())
189 for (unsigned i = 0; i < part.length(); ++i) {
190 if (text[position + i] != part[i])
196 bool ICULocale::detectSignAndGetDigitRange(const String& input, bool& isNegative, unsigned& startIndex, unsigned& endIndex)
199 endIndex = input.length();
200 if (m_negativePrefix.isEmpty() && m_negativeSuffix.isEmpty()) {
201 if (input.startsWith(m_positivePrefix) && input.endsWith(m_positiveSuffix)) {
203 startIndex = m_positivePrefix.length();
204 endIndex -= m_positiveSuffix.length();
208 if (input.startsWith(m_negativePrefix) && input.endsWith(m_negativeSuffix)) {
210 startIndex = m_negativePrefix.length();
211 endIndex -= m_negativeSuffix.length();
214 if (input.startsWith(m_positivePrefix) && input.endsWith(m_positiveSuffix)) {
215 startIndex = m_positivePrefix.length();
216 endIndex -= m_positiveSuffix.length();
224 unsigned ICULocale::matchedDecimalSymbolIndex(const String& input, unsigned& position)
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();
232 return DecimalSymbolsSize;
235 String ICULocale::convertFromLocalizedNumber(const String& localized)
237 initializeDecimalFormat();
238 String input = localized.stripWhiteSpace();
239 if (!m_numberFormat || input.isEmpty())
245 if (!detectSignAndGetDigitRange(input, isNegative, startIndex, endIndex)) {
246 // Input is broken. Returning an invalid number string.
250 StringBuilder builder;
251 builder.reserveCapacity(input.length());
254 for (unsigned i = startIndex; i < endIndex;) {
255 unsigned symbolIndex = matchedDecimalSymbolIndex(input, i);
256 if (symbolIndex >= DecimalSymbolsSize)
258 if (symbolIndex == DecimalSeparatorIndex)
260 else if (symbolIndex == GroupSeparatorIndex) {
261 // Ignore group separators.
264 builder.append(static_cast<UChar>('0' + symbolIndex));
266 return builder.toString();
269 bool ICULocale::initializeShortDateFormat()
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;
280 double ICULocale::parseLocalizedDate(const String& input)
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.
296 String ICULocale::formatLocalizedDate(const DateComponents& dateComponents)
298 if (!initializeShortDateFormat())
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)
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))
310 return String::adopt(buffer);
313 #if ENABLE(CALENDAR_PICKER)
314 PassOwnPtr<Vector<String> > ICULocale::createLabelVector(UDateFormatSymbolType type, int32_t startIndex, int32_t size)
316 if (!m_shortDateFormat)
317 return PassOwnPtr<Vector<String> >();
318 if (udat_countSymbols(m_shortDateFormat, type) != startIndex + size)
319 return PassOwnPtr<Vector<String> >();
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));
335 return labels.release();
338 static PassOwnPtr<Vector<String> > createFallbackMonthLabels()
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();
357 static PassOwnPtr<Vector<String> > createFallbackWeekDayShortLabels()
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();
371 void ICULocale::initializeCalendar()
373 if (m_monthLabels && m_weekDayShortLabels)
376 if (!initializeShortDateFormat()) {
377 m_firstDayOfWeek = 0;
378 m_monthLabels = createFallbackMonthLabels();
379 m_weekDayShortLabels = createFallbackWeekDayShortLabels();
382 m_firstDayOfWeek = ucal_getAttribute(udat_getCalendar(m_shortDateFormat), UCAL_FIRST_DAY_OF_WEEK) - UCAL_SUNDAY;
384 m_monthLabels = createLabelVector(UDAT_MONTHS, UCAL_JANUARY, 12);
386 m_monthLabels = createFallbackMonthLabels();
388 m_weekDayShortLabels = createLabelVector(UDAT_SHORT_WEEKDAYS, UCAL_SUNDAY, 7);
389 if (!m_weekDayShortLabels)
390 m_weekDayShortLabels = createFallbackWeekDayShortLabels();
393 const Vector<String>& ICULocale::monthLabels()
395 initializeCalendar();
396 return *m_monthLabels;
399 const Vector<String>& ICULocale::weekDayShortLabels()
401 initializeCalendar();
402 return *m_weekDayShortLabels;
405 unsigned ICULocale::firstDayOfWeek()
407 initializeCalendar();
408 return m_firstDayOfWeek;
412 } // namespace WebCore