Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / text / DateTimeFormat.cpp
1 /*
2  * Copyright (C) 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
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "platform/text/DateTimeFormat.h"
28
29 #include "wtf/ASCIICType.h"
30 #include "wtf/text/StringBuilder.h"
31
32 namespace blink {
33
34 static const DateTimeFormat::FieldType lowerCaseToFieldTypeMap[26] = {
35     DateTimeFormat::FieldTypePeriod, // a
36     DateTimeFormat::FieldTypeInvalid, // b
37     DateTimeFormat::FieldTypeLocalDayOfWeekStandAlon, // c
38     DateTimeFormat::FieldTypeDayOfMonth, // d
39     DateTimeFormat::FieldTypeLocalDayOfWeek, // e
40     DateTimeFormat::FieldTypeInvalid, // f
41     DateTimeFormat::FieldTypeModifiedJulianDay, // g
42     DateTimeFormat::FieldTypeHour12, // h
43     DateTimeFormat::FieldTypeInvalid, // i
44     DateTimeFormat::FieldTypeInvalid, // j
45     DateTimeFormat::FieldTypeHour24, // k
46     DateTimeFormat::FieldTypeInvalid, // l
47     DateTimeFormat::FieldTypeMinute, // m
48     DateTimeFormat::FieldTypeInvalid, // n
49     DateTimeFormat::FieldTypeInvalid, // o
50     DateTimeFormat::FieldTypeInvalid, // p
51     DateTimeFormat::FieldTypeQuaterStandAlone, // q
52     DateTimeFormat::FieldTypeInvalid, // r
53     DateTimeFormat::FieldTypeSecond, // s
54     DateTimeFormat::FieldTypeInvalid, // t
55     DateTimeFormat::FieldTypeExtendedYear, // u
56     DateTimeFormat::FieldTypeNonLocationZone, // v
57     DateTimeFormat::FieldTypeWeekOfYear, // w
58     DateTimeFormat::FieldTypeInvalid, // x
59     DateTimeFormat::FieldTypeYear, // y
60     DateTimeFormat::FieldTypeZone, // z
61 };
62
63 static const DateTimeFormat::FieldType upperCaseToFieldTypeMap[26] = {
64     DateTimeFormat::FieldTypeMillisecondsInDay, // A
65     DateTimeFormat::FieldTypeInvalid, // B
66     DateTimeFormat::FieldTypeInvalid, // C
67     DateTimeFormat::FieldTypeDayOfYear, // D
68     DateTimeFormat::FieldTypeDayOfWeek, // E
69     DateTimeFormat::FieldTypeDayOfWeekInMonth, // F
70     DateTimeFormat::FieldTypeEra, // G
71     DateTimeFormat::FieldTypeHour23, // H
72     DateTimeFormat::FieldTypeInvalid, // I
73     DateTimeFormat::FieldTypeInvalid, // J
74     DateTimeFormat::FieldTypeHour11, // K
75     DateTimeFormat::FieldTypeMonthStandAlone, // L
76     DateTimeFormat::FieldTypeMonth, // M
77     DateTimeFormat::FieldTypeInvalid, // N
78     DateTimeFormat::FieldTypeInvalid, // O
79     DateTimeFormat::FieldTypeInvalid, // P
80     DateTimeFormat::FieldTypeQuater, // Q
81     DateTimeFormat::FieldTypeInvalid, // R
82     DateTimeFormat::FieldTypeFractionalSecond, // S
83     DateTimeFormat::FieldTypeInvalid, // T
84     DateTimeFormat::FieldTypeInvalid, // U
85     DateTimeFormat::FieldTypeInvalid, // V
86     DateTimeFormat::FieldTypeWeekOfMonth, // W
87     DateTimeFormat::FieldTypeInvalid, // X
88     DateTimeFormat::FieldTypeYearOfWeekOfYear, // Y
89     DateTimeFormat::FieldTypeRFC822Zone, // Z
90 };
91
92 static DateTimeFormat::FieldType mapCharacterToFieldType(const UChar ch)
93 {
94     if (isASCIIUpper(ch))
95         return upperCaseToFieldTypeMap[ch - 'A'];
96
97     if (isASCIILower(ch))
98         return lowerCaseToFieldTypeMap[ch - 'a'];
99
100     return DateTimeFormat::FieldTypeLiteral;
101 }
102
103 bool DateTimeFormat::parse(const String& source, TokenHandler& tokenHandler)
104 {
105     enum State {
106         StateInQuote,
107         StateInQuoteQuote,
108         StateLiteral,
109         StateQuote,
110         StateSymbol,
111     } state = StateLiteral;
112
113     FieldType fieldType = FieldTypeLiteral;
114     StringBuilder literalBuffer;
115     int fieldCounter = 0;
116
117     for (unsigned index = 0; index < source.length(); ++index) {
118         const UChar ch = source[index];
119         switch (state) {
120         case StateInQuote:
121             if (ch == '\'') {
122                 state = StateInQuoteQuote;
123                 break;
124             }
125
126             literalBuffer.append(ch);
127             break;
128
129         case StateInQuoteQuote:
130             if (ch == '\'') {
131                 literalBuffer.append('\'');
132                 state = StateInQuote;
133                 break;
134             }
135
136             fieldType = mapCharacterToFieldType(ch);
137             if (fieldType == FieldTypeInvalid)
138                 return false;
139
140             if (fieldType == FieldTypeLiteral) {
141                 literalBuffer.append(ch);
142                 state = StateLiteral;
143                 break;
144             }
145
146             if (literalBuffer.length()) {
147                 tokenHandler.visitLiteral(literalBuffer.toString());
148                 literalBuffer.clear();
149             }
150
151             fieldCounter = 1;
152             state = StateSymbol;
153             break;
154
155         case StateLiteral:
156             if (ch == '\'') {
157                 state = StateQuote;
158                 break;
159             }
160
161             fieldType = mapCharacterToFieldType(ch);
162             if (fieldType == FieldTypeInvalid)
163                 return false;
164
165             if (fieldType == FieldTypeLiteral) {
166                 literalBuffer.append(ch);
167                 break;
168             }
169
170             if (literalBuffer.length()) {
171                 tokenHandler.visitLiteral(literalBuffer.toString());
172                 literalBuffer.clear();
173             }
174
175             fieldCounter = 1;
176             state = StateSymbol;
177             break;
178
179         case StateQuote:
180             literalBuffer.append(ch);
181             state = ch == '\'' ? StateLiteral : StateInQuote;
182             break;
183
184         case StateSymbol: {
185             ASSERT(fieldType != FieldTypeInvalid);
186             ASSERT(fieldType != FieldTypeLiteral);
187             ASSERT(literalBuffer.isEmpty());
188
189             FieldType fieldType2 = mapCharacterToFieldType(ch);
190             if (fieldType2 == FieldTypeInvalid)
191                 return false;
192
193             if (fieldType == fieldType2) {
194                 ++fieldCounter;
195                 break;
196             }
197
198             tokenHandler.visitField(fieldType, fieldCounter);
199
200             if (fieldType2 == FieldTypeLiteral) {
201                 if (ch == '\'') {
202                     state = StateQuote;
203                 } else {
204                     literalBuffer.append(ch);
205                     state = StateLiteral;
206                 }
207                 break;
208             }
209
210             fieldCounter = 1;
211             fieldType = fieldType2;
212             break;
213         }
214         }
215     }
216
217     ASSERT(fieldType != FieldTypeInvalid);
218
219     switch (state) {
220     case StateLiteral:
221     case StateInQuoteQuote:
222         if (literalBuffer.length())
223             tokenHandler.visitLiteral(literalBuffer.toString());
224         return true;
225
226     case StateQuote:
227     case StateInQuote:
228         if (literalBuffer.length())
229             tokenHandler.visitLiteral(literalBuffer.toString());
230         return false;
231
232     case StateSymbol:
233         ASSERT(fieldType != FieldTypeLiteral);
234         ASSERT(!literalBuffer.length());
235         tokenHandler.visitField(fieldType, fieldCounter);
236         return true;
237     }
238
239     ASSERT_NOT_REACHED();
240     return false;
241 }
242
243 static bool isASCIIAlphabetOrQuote(UChar ch)
244 {
245     return isASCIIAlpha(ch) || ch == '\'';
246 }
247
248 void DateTimeFormat::quoteAndAppendLiteral(const String& literal, StringBuilder& buffer)
249 {
250     if (literal.length() <= 0)
251         return;
252
253     if (literal.find(isASCIIAlphabetOrQuote) == kNotFound) {
254         buffer.append(literal);
255         return;
256     }
257
258     if (literal.find('\'') == kNotFound) {
259         buffer.append('\'');
260         buffer.append(literal);
261         buffer.append('\'');
262         return;
263     }
264
265     for (unsigned i = 0; i < literal.length(); ++i) {
266         if (literal[i] == '\'') {
267             buffer.appendLiteral("''");
268         } else {
269             String escaped = literal.substring(i);
270             escaped.replace("'", "''");
271             buffer.append('\'');
272             buffer.append(escaped);
273             buffer.append('\'');
274             return;
275         }
276     }
277 }
278
279 } // namespace blink