Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Side by Side Diff: Source/core/platform/text/PlatformLocale.cpp

Issue 26113002: Move core/platform/text/Locale* to platform/text (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2011,2012 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 "core/platform/text/PlatformLocale.h"
33
34 #include "platform/LocalizedStrings.h"
35 #include "platform/text/DateTimeFormat.h"
36 #include "public/platform/Platform.h"
37 #include "wtf/MainThread.h"
38 #include "wtf/text/StringBuilder.h"
39
40 namespace WebCore {
41
42 using WebKit::Platform;
43 using WebKit::WebLocalizedString;
44
45 class DateTimeStringBuilder : private DateTimeFormat::TokenHandler {
46 WTF_MAKE_NONCOPYABLE(DateTimeStringBuilder);
47
48 public:
49 // The argument objects must be alive until this object dies.
50 DateTimeStringBuilder(Locale&, const DateComponents&);
51
52 bool build(const String&);
53 String toString();
54
55 private:
56 // DateTimeFormat::TokenHandler functions.
57 virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
58 virtual void visitLiteral(const String&) OVERRIDE FINAL;
59
60 String zeroPadString(const String&, size_t width);
61 void appendNumber(int number, size_t width);
62
63 StringBuilder m_builder;
64 Locale& m_localizer;
65 const DateComponents& m_date;
66 };
67
68 DateTimeStringBuilder::DateTimeStringBuilder(Locale& localizer, const DateCompon ents& date)
69 : m_localizer(localizer)
70 , m_date(date)
71 {
72 }
73
74 bool DateTimeStringBuilder::build(const String& formatString)
75 {
76 m_builder.reserveCapacity(formatString.length());
77 return DateTimeFormat::parse(formatString, *this);
78 }
79
80 String DateTimeStringBuilder::zeroPadString(const String& string, size_t width)
81 {
82 if (string.length() >= width)
83 return string;
84 StringBuilder zeroPaddedStringBuilder;
85 zeroPaddedStringBuilder.reserveCapacity(width);
86 for (size_t i = string.length(); i < width; ++i)
87 zeroPaddedStringBuilder.append("0");
88 zeroPaddedStringBuilder.append(string);
89 return zeroPaddedStringBuilder.toString();
90 }
91
92 void DateTimeStringBuilder::appendNumber(int number, size_t width)
93 {
94 String zeroPaddedNumberString = zeroPadString(String::number(number), width) ;
95 m_builder.append(m_localizer.convertToLocalizedNumber(zeroPaddedNumberString ));
96 }
97
98 void DateTimeStringBuilder::visitField(DateTimeFormat::FieldType fieldType, int numberOfPatternCharacters)
99 {
100 switch (fieldType) {
101 case DateTimeFormat::FieldTypeYear:
102 // Always use padding width of 4 so it matches DateTimeEditElement.
103 appendNumber(m_date.fullYear(), 4);
104 return;
105 case DateTimeFormat::FieldTypeMonth:
106 if (numberOfPatternCharacters == 3)
107 m_builder.append(m_localizer.shortMonthLabels()[m_date.month()]);
108 else if (numberOfPatternCharacters == 4)
109 m_builder.append(m_localizer.monthLabels()[m_date.month()]);
110 else {
111 // Always use padding width of 2 so it matches DateTimeEditElement.
112 appendNumber(m_date.month() + 1, 2);
113 }
114 return;
115 case DateTimeFormat::FieldTypeMonthStandAlone:
116 if (numberOfPatternCharacters == 3)
117 m_builder.append(m_localizer.shortStandAloneMonthLabels()[m_date.mon th()]);
118 else if (numberOfPatternCharacters == 4)
119 m_builder.append(m_localizer.standAloneMonthLabels()[m_date.month()] );
120 else {
121 // Always use padding width of 2 so it matches DateTimeEditElement.
122 appendNumber(m_date.month() + 1, 2);
123 }
124 return;
125 case DateTimeFormat::FieldTypeDayOfMonth:
126 // Always use padding width of 2 so it matches DateTimeEditElement.
127 appendNumber(m_date.monthDay(), 2);
128 return;
129 case DateTimeFormat::FieldTypeWeekOfYear:
130 // Always use padding width of 2 so it matches DateTimeEditElement.
131 appendNumber(m_date.week(), 2);
132 return;
133 case DateTimeFormat::FieldTypePeriod:
134 m_builder.append(m_localizer.timeAMPMLabels()[(m_date.hour() >= 12 ? 1 : 0)]);
135 return;
136 case DateTimeFormat::FieldTypeHour12: {
137 int hour12 = m_date.hour() % 12;
138 if (!hour12)
139 hour12 = 12;
140 appendNumber(hour12, numberOfPatternCharacters);
141 return;
142 }
143 case DateTimeFormat::FieldTypeHour23:
144 appendNumber(m_date.hour(), numberOfPatternCharacters);
145 return;
146 case DateTimeFormat::FieldTypeHour11:
147 appendNumber(m_date.hour() % 12, numberOfPatternCharacters);
148 return;
149 case DateTimeFormat::FieldTypeHour24: {
150 int hour24 = m_date.hour();
151 if (!hour24)
152 hour24 = 24;
153 appendNumber(hour24, numberOfPatternCharacters);
154 return;
155 }
156 case DateTimeFormat::FieldTypeMinute:
157 appendNumber(m_date.minute(), numberOfPatternCharacters);
158 return;
159 case DateTimeFormat::FieldTypeSecond:
160 if (!m_date.millisecond())
161 appendNumber(m_date.second(), numberOfPatternCharacters);
162 else {
163 double second = m_date.second() + m_date.millisecond() / 1000.0;
164 String zeroPaddedSecondString = zeroPadString(String::format("%.03f" , second), numberOfPatternCharacters + 4);
165 m_builder.append(m_localizer.convertToLocalizedNumber(zeroPaddedSeco ndString));
166 }
167 return;
168 default:
169 return;
170 }
171 }
172
173 void DateTimeStringBuilder::visitLiteral(const String& text)
174 {
175 ASSERT(text.length());
176 m_builder.append(text);
177 }
178
179 String DateTimeStringBuilder::toString()
180 {
181 return m_builder.toString();
182 }
183
184 Locale* Locale::defaultLocale()
185 {
186 static Locale* locale = Locale::create(defaultLanguage()).leakPtr();
187 ASSERT(isMainThread());
188 return locale;
189 }
190
191 Locale::~Locale()
192 {
193 }
194
195 String Locale::queryString(WebLocalizedString::Name name)
196 {
197 // FIXME: Returns a string locazlied for this locale.
198 return Platform::current()->queryLocalizedString(name);
199 }
200
201 String Locale::queryString(WebLocalizedString::Name name, const String& paramete r)
202 {
203 // FIXME: Returns a string locazlied for this locale.
204 return Platform::current()->queryLocalizedString(name, parameter);
205 }
206
207 String Locale::queryString(WebLocalizedString::Name name, const String& paramete r1, const String& parameter2)
208 {
209 // FIXME: Returns a string locazlied for this locale.
210 return Platform::current()->queryLocalizedString(name, parameter1, parameter 2);
211 }
212
213 String Locale::validationMessageTooLongText(unsigned valueLength, int maxLength)
214 {
215 return queryString(WebLocalizedString::ValidationTooLong, convertToLocalized Number(String::number(valueLength)), convertToLocalizedNumber(String::number(max Length)));
216 }
217
218 String Locale::weekFormatInLDML()
219 {
220 String templ = queryString(WebLocalizedString::WeekFormatTemplate);
221 // Converts a string like "Week $2, $1" to an LDML date format pattern like
222 // "'Week 'ww', 'yyyy".
223 StringBuilder builder;
224 unsigned literalStart = 0;
225 unsigned length = templ.length();
226 for (unsigned i = 0; i + 1 < length; ++i) {
227 if (templ[i] == '$' && (templ[i + 1] == '1' || templ[i + 1] == '2')) {
228 if (literalStart < i)
229 DateTimeFormat::quoteAndAppendLiteral(templ.substring(literalSta rt, i - literalStart), builder);
230 builder.append(templ[++i] == '1' ? "yyyy" : "ww");
231 literalStart = i + 1;
232 }
233 }
234 if (literalStart < length)
235 DateTimeFormat::quoteAndAppendLiteral(templ.substring(literalStart, leng th - literalStart), builder);
236 return builder.toString();
237 }
238
239 void Locale::setLocaleData(const Vector<String, DecimalSymbolsSize>& symbols, co nst String& positivePrefix, const String& positiveSuffix, const String& negative Prefix, const String& negativeSuffix)
240 {
241 for (size_t i = 0; i < symbols.size(); ++i) {
242 ASSERT(!symbols[i].isEmpty());
243 m_decimalSymbols[i] = symbols[i];
244 }
245 m_positivePrefix = positivePrefix;
246 m_positiveSuffix = positiveSuffix;
247 m_negativePrefix = negativePrefix;
248 m_negativeSuffix = negativeSuffix;
249 ASSERT(!m_positivePrefix.isEmpty() || !m_positiveSuffix.isEmpty() || !m_nega tivePrefix.isEmpty() || !m_negativeSuffix.isEmpty());
250 m_hasLocaleData = true;
251 }
252
253 String Locale::convertToLocalizedNumber(const String& input)
254 {
255 initializeLocaleData();
256 if (!m_hasLocaleData || input.isEmpty())
257 return input;
258
259 unsigned i = 0;
260 bool isNegative = false;
261 StringBuilder builder;
262 builder.reserveCapacity(input.length());
263
264 if (input[0] == '-') {
265 ++i;
266 isNegative = true;
267 builder.append(m_negativePrefix);
268 } else
269 builder.append(m_positivePrefix);
270
271 for (; i < input.length(); ++i) {
272 switch (input[i]) {
273 case '0':
274 case '1':
275 case '2':
276 case '3':
277 case '4':
278 case '5':
279 case '6':
280 case '7':
281 case '8':
282 case '9':
283 builder.append(m_decimalSymbols[input[i] - '0']);
284 break;
285 case '.':
286 builder.append(m_decimalSymbols[DecimalSeparatorIndex]);
287 break;
288 default:
289 ASSERT_NOT_REACHED();
290 }
291 }
292
293 builder.append(isNegative ? m_negativeSuffix : m_positiveSuffix);
294
295 return builder.toString();
296 }
297
298 static bool matches(const String& text, unsigned position, const String& part)
299 {
300 if (part.isEmpty())
301 return true;
302 if (position + part.length() > text.length())
303 return false;
304 for (unsigned i = 0; i < part.length(); ++i) {
305 if (text[position + i] != part[i])
306 return false;
307 }
308 return true;
309 }
310
311 bool Locale::detectSignAndGetDigitRange(const String& input, bool& isNegative, u nsigned& startIndex, unsigned& endIndex)
312 {
313 startIndex = 0;
314 endIndex = input.length();
315 if (m_negativePrefix.isEmpty() && m_negativeSuffix.isEmpty()) {
316 if (input.startsWith(m_positivePrefix) && input.endsWith(m_positiveSuffi x)) {
317 isNegative = false;
318 startIndex = m_positivePrefix.length();
319 endIndex -= m_positiveSuffix.length();
320 } else
321 isNegative = true;
322 } else {
323 if (input.startsWith(m_negativePrefix) && input.endsWith(m_negativeSuffi x)) {
324 isNegative = true;
325 startIndex = m_negativePrefix.length();
326 endIndex -= m_negativeSuffix.length();
327 } else {
328 isNegative = false;
329 if (input.startsWith(m_positivePrefix) && input.endsWith(m_positiveS uffix)) {
330 startIndex = m_positivePrefix.length();
331 endIndex -= m_positiveSuffix.length();
332 } else
333 return false;
334 }
335 }
336 return true;
337 }
338
339 unsigned Locale::matchedDecimalSymbolIndex(const String& input, unsigned& positi on)
340 {
341 for (unsigned symbolIndex = 0; symbolIndex < DecimalSymbolsSize; ++symbolInd ex) {
342 if (m_decimalSymbols[symbolIndex].length() && matches(input, position, m _decimalSymbols[symbolIndex])) {
343 position += m_decimalSymbols[symbolIndex].length();
344 return symbolIndex;
345 }
346 }
347 return DecimalSymbolsSize;
348 }
349
350 String Locale::convertFromLocalizedNumber(const String& localized)
351 {
352 initializeLocaleData();
353 String input = localized.stripWhiteSpace();
354 if (!m_hasLocaleData || input.isEmpty())
355 return input;
356
357 bool isNegative;
358 unsigned startIndex;
359 unsigned endIndex;
360 if (!detectSignAndGetDigitRange(input, isNegative, startIndex, endIndex))
361 return input;
362
363 StringBuilder builder;
364 builder.reserveCapacity(input.length());
365 if (isNegative)
366 builder.append("-");
367 for (unsigned i = startIndex; i < endIndex;) {
368 unsigned symbolIndex = matchedDecimalSymbolIndex(input, i);
369 if (symbolIndex >= DecimalSymbolsSize)
370 return input;
371 if (symbolIndex == DecimalSeparatorIndex)
372 builder.append('.');
373 else if (symbolIndex == GroupSeparatorIndex)
374 return input;
375 else
376 builder.append(static_cast<UChar>('0' + symbolIndex));
377 }
378 return builder.toString();
379 }
380
381 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
382 String Locale::localizedDecimalSeparator()
383 {
384 initializeLocaleData();
385 return m_decimalSymbols[DecimalSeparatorIndex];
386 }
387 #endif
388
389 String Locale::formatDateTime(const DateComponents& date, FormatType formatType)
390 {
391 if (date.type() == DateComponents::Invalid)
392 return String();
393
394 DateTimeStringBuilder builder(*this, date);
395 switch (date.type()) {
396 case DateComponents::Time:
397 builder.build(formatType == FormatTypeShort ? shortTimeFormat() : timeFo rmat());
398 break;
399 case DateComponents::Date:
400 builder.build(dateFormat());
401 break;
402 case DateComponents::Month:
403 builder.build(formatType == FormatTypeShort ? shortMonthFormat() : month Format());
404 break;
405 case DateComponents::Week:
406 builder.build(weekFormatInLDML());
407 break;
408 case DateComponents::DateTime:
409 case DateComponents::DateTimeLocal:
410 builder.build(formatType == FormatTypeShort ? dateTimeFormatWithoutSecon ds() : dateTimeFormatWithSeconds());
411 break;
412 case DateComponents::Invalid:
413 ASSERT_NOT_REACHED();
414 break;
415 }
416 return builder.toString();
417 }
418
419 }
OLDNEW
« no previous file with comments | « Source/core/platform/text/PlatformLocale.h ('k') | Source/core/rendering/RenderFileUploadControl.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698