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.append("''"); 269 else { 270 String escaped = literal.substring(i); 271 escaped.replace(ASCIILiteral("'"), ASCIILiteral("''")); 272 buffer.append("'"); 273 buffer.append(escaped); 274 buffer.append("'"); 275 return; 276 } 277 } 278} 279 280} // namespace WebCore 281 282#endif 283