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 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.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