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 "DateTimeFormat.h"
28
29#if ENABLE(DATE_AND_TIME_INPUT_TYPES)
30#include <wtf/ASCIICType.h>
31#include <wtf/text/StringBuilder.h>
32
33namespace WebCore {
34
35static const DateTimeFormat::FieldType lowerCaseToFieldTypeMap[26] = {
36 DateTimeFormat::FieldTypePeriod, // a
37 DateTimeFormat::FieldTypeInvalid, // b
38 DateTimeFormat::FieldTypeLocalDayOfWeekStandAlon, // c
39 DateTimeFormat::FieldTypeDayOfMonth, // d
40 DateTimeFormat::FieldTypeLocalDayOfWeek, // e
41 DateTimeFormat::FieldTypeInvalid, // f
42 DateTimeFormat::FieldTypeModifiedJulianDay, // g
43 DateTimeFormat::FieldTypeHour12, // h
44 DateTimeFormat::FieldTypeInvalid, // i
45 DateTimeFormat::FieldTypeInvalid, // j
46 DateTimeFormat::FieldTypeHour24, // k
47 DateTimeFormat::FieldTypeInvalid, // l
48 DateTimeFormat::FieldTypeMinute, // m
49 DateTimeFormat::FieldTypeInvalid, // n
50 DateTimeFormat::FieldTypeInvalid, // o
51 DateTimeFormat::FieldTypeInvalid, // p
52 DateTimeFormat::FieldTypeQuaterStandAlone, // q
53 DateTimeFormat::FieldTypeInvalid, // r
54 DateTimeFormat::FieldTypeSecond, // s
55 DateTimeFormat::FieldTypeInvalid, // t
56 DateTimeFormat::FieldTypeExtendedYear, // u
57 DateTimeFormat::FieldTypeNonLocationZone, // v
58 DateTimeFormat::FieldTypeWeekOfYear, // w
59 DateTimeFormat::FieldTypeInvalid, // x
60 DateTimeFormat::FieldTypeYear, // y
61 DateTimeFormat::FieldTypeZone, // z
62};
63
64static const DateTimeFormat::FieldType upperCaseToFieldTypeMap[26] = {
65 DateTimeFormat::FieldTypeMillisecondsInDay, // A
66 DateTimeFormat::FieldTypeInvalid, // B
67 DateTimeFormat::FieldTypeInvalid, // C
68 DateTimeFormat::FieldTypeDayOfYear, // D
69 DateTimeFormat::FieldTypeDayOfWeek, // E
70 DateTimeFormat::FieldTypeDayOfWeekInMonth, // F
71 DateTimeFormat::FieldTypeEra, // G
72 DateTimeFormat::FieldTypeHour23, // H
73 DateTimeFormat::FieldTypeInvalid, // I
74 DateTimeFormat::FieldTypeInvalid, // J
75 DateTimeFormat::FieldTypeHour11, // K
76 DateTimeFormat::FieldTypeMonthStandAlone, // L
77 DateTimeFormat::FieldTypeMonth, // M
78 DateTimeFormat::FieldTypeInvalid, // N
79 DateTimeFormat::FieldTypeInvalid, // O
80 DateTimeFormat::FieldTypeInvalid, // P
81 DateTimeFormat::FieldTypeQuater, // Q
82 DateTimeFormat::FieldTypeInvalid, // R
83 DateTimeFormat::FieldTypeFractionalSecond, // S
84 DateTimeFormat::FieldTypeInvalid, // T
85 DateTimeFormat::FieldTypeInvalid, // U
86 DateTimeFormat::FieldTypeInvalid, // V
87 DateTimeFormat::FieldTypeWeekOfMonth, // W
88 DateTimeFormat::FieldTypeInvalid, // X
89 DateTimeFormat::FieldTypeYearOfWeekOfYear, // Y
90 DateTimeFormat::FieldTypeRFC822Zone, // Z
91};
92
93static DateTimeFormat::FieldType mapCharacterToFieldType(const UChar ch)
94{
95 if (isASCIIUpper(ch))
96 return upperCaseToFieldTypeMap[ch - 'A'];
97
98 if (isASCIILower(ch))
99 return lowerCaseToFieldTypeMap[ch - 'a'];
100
101 return DateTimeFormat::FieldTypeLiteral;
102}
103
104bool DateTimeFormat::parse(const String& source, TokenHandler& tokenHandler)
105{
106 enum State {
107 StateInQuote,
108 StateInQuoteQuote,
109 StateLiteral,
110 StateQuote,
111 StateSymbol,
112 } state = StateLiteral;
113
114 FieldType fieldType = FieldTypeLiteral;
115 StringBuilder literalBuffer;
116 int fieldCounter = 0;
117
118 for (unsigned int index = 0; index < source.length(); ++index) {
119 const UChar ch = source[index];
120 switch (state) {
121 case StateInQuote:
122 if (ch == '\'') {
123 state = StateInQuoteQuote;
124 break;
125 }
126
127 literalBuffer.append(ch);
128 break;
129
130 case StateInQuoteQuote:
131 if (ch == '\'') {
132 literalBuffer.append('\'');
133 state = StateInQuote;
134 break;
135 }
136
137 fieldType = mapCharacterToFieldType(ch);
138 if (fieldType == FieldTypeInvalid)
139 return false;
140
141 if (fieldType == FieldTypeLiteral) {
142 literalBuffer.append(ch);
143 state = StateLiteral;
144 break;
145 }
146
147 if (literalBuffer.length()) {
148 tokenHandler.visitLiteral(literalBuffer.toString());
149 literalBuffer.clear();
150 }
151
152 fieldCounter = 1;
153 state = StateSymbol;
154 break;
155
156 case StateLiteral:
157 if (ch == '\'') {
158 state = StateQuote;
159 break;
160 }
161
162 fieldType = mapCharacterToFieldType(ch);
163 if (fieldType == FieldTypeInvalid)
164 return false;
165
166 if (fieldType == FieldTypeLiteral) {
167 literalBuffer.append(ch);
168 break;
169 }
170
171 if (literalBuffer.length()) {
172 tokenHandler.visitLiteral(literalBuffer.toString());
173 literalBuffer.clear();
174 }
175
176 fieldCounter = 1;
177 state = StateSymbol;
178 break;
179
180 case StateQuote:
181 literalBuffer.append(ch);
182 state = ch == '\'' ? StateLiteral : StateInQuote;
183 break;
184
185 case StateSymbol: {
186 ASSERT(fieldType != FieldTypeInvalid);
187 ASSERT(fieldType != FieldTypeLiteral);
188 ASSERT(literalBuffer.isEmpty());
189
190 FieldType fieldType2 = mapCharacterToFieldType(ch);
191 if (fieldType2 == FieldTypeInvalid)
192 return false;
193
194 if (fieldType == fieldType2) {
195 ++fieldCounter;
196 break;
197 }
198
199 tokenHandler.visitField(fieldType, fieldCounter);
200
201 if (fieldType2 == FieldTypeLiteral) {
202 if (ch == '\'')
203 state = StateQuote;
204 else {
205 literalBuffer.append(ch);
206 state = StateLiteral;
207 }
208 break;
209 }
210
211 fieldCounter = 1;
212 fieldType = fieldType2;
213 break;
214 }
215 }
216 }
217
218 ASSERT(fieldType != FieldTypeInvalid);
219
220 switch (state) {
221 case StateLiteral:
222 case StateInQuoteQuote:
223 if (literalBuffer.length())
224 tokenHandler.visitLiteral(literalBuffer.toString());
225 return true;
226
227 case StateQuote:
228 case StateInQuote:
229 if (literalBuffer.length())
230 tokenHandler.visitLiteral(literalBuffer.toString());
231 return false;
232
233 case StateSymbol:
234 ASSERT(fieldType != FieldTypeLiteral);
235 ASSERT(!literalBuffer.length());
236 tokenHandler.visitField(fieldType, fieldCounter);
237 return true;
238 }
239
240 ASSERT_NOT_REACHED();
241 return false;
242}
243
244static bool isASCIIAlphabetOrQuote(UChar ch)
245{
246 return isASCIIAlpha(ch) || ch == '\'';
247}
248
249void DateTimeFormat::quoteAndAppendLiteral(const String& literal, StringBuilder& buffer)
250{
251 if (literal.length() <= 0)
252 return;
253
254 if (literal.find(isASCIIAlphabetOrQuote) == notFound) {
255 buffer.append(literal);
256 return;
257 }
258
259 if (literal.find('\'') == notFound) {
260 buffer.append('\'');
261 buffer.append(literal);
262 buffer.append('\'');
263 return;
264 }
265
266 for (unsigned i = 0; i < literal.length(); ++i) {
267 if (literal[i] == '\'')
268 buffer.appendLiteral("''");
269 else {
270 String escaped = literal.substring(i);
271 escaped.replace('\'', "''");
272 buffer.append('\'');
273 buffer.append(escaped);
274 buffer.append('\'');
275 return;
276 }
277 }
278}
279
280} // namespace WebCore
281
282#endif
283