• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 WebCore {
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 
mapCharacterToFieldType(const UChar ch)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 
parse(const String & source,TokenHandler & tokenHandler)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 
isASCIIAlphabetOrQuote(UChar ch)243 static bool isASCIIAlphabetOrQuote(UChar ch)
244 {
245     return isASCIIAlpha(ch) || ch == '\'';
246 }
247 
quoteAndAppendLiteral(const String & literal,StringBuilder & buffer)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.append("''");
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 WebCore
280