| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "DateTimeFormat.h" |
| |
| #if ENABLE(DATE_AND_TIME_INPUT_TYPES) |
| #include <wtf/ASCIICType.h> |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace WebCore { |
| |
| static const DateTimeFormat::FieldType lowerCaseToFieldTypeMap[26] = { |
| DateTimeFormat::FieldTypePeriod, // a |
| DateTimeFormat::FieldTypeInvalid, // b |
| DateTimeFormat::FieldTypeLocalDayOfWeekStandAlon, // c |
| DateTimeFormat::FieldTypeDayOfMonth, // d |
| DateTimeFormat::FieldTypeLocalDayOfWeek, // e |
| DateTimeFormat::FieldTypeInvalid, // f |
| DateTimeFormat::FieldTypeModifiedJulianDay, // g |
| DateTimeFormat::FieldTypeHour12, // h |
| DateTimeFormat::FieldTypeInvalid, // i |
| DateTimeFormat::FieldTypeInvalid, // j |
| DateTimeFormat::FieldTypeHour24, // k |
| DateTimeFormat::FieldTypeInvalid, // l |
| DateTimeFormat::FieldTypeMinute, // m |
| DateTimeFormat::FieldTypeInvalid, // n |
| DateTimeFormat::FieldTypeInvalid, // o |
| DateTimeFormat::FieldTypeInvalid, // p |
| DateTimeFormat::FieldTypeQuaterStandAlone, // q |
| DateTimeFormat::FieldTypeInvalid, // r |
| DateTimeFormat::FieldTypeSecond, // s |
| DateTimeFormat::FieldTypeInvalid, // t |
| DateTimeFormat::FieldTypeExtendedYear, // u |
| DateTimeFormat::FieldTypeNonLocationZone, // v |
| DateTimeFormat::FieldTypeWeekOfYear, // w |
| DateTimeFormat::FieldTypeInvalid, // x |
| DateTimeFormat::FieldTypeYear, // y |
| DateTimeFormat::FieldTypeZone, // z |
| }; |
| |
| static const DateTimeFormat::FieldType upperCaseToFieldTypeMap[26] = { |
| DateTimeFormat::FieldTypeMillisecondsInDay, // A |
| DateTimeFormat::FieldTypeInvalid, // B |
| DateTimeFormat::FieldTypeInvalid, // C |
| DateTimeFormat::FieldTypeDayOfYear, // D |
| DateTimeFormat::FieldTypeDayOfWeek, // E |
| DateTimeFormat::FieldTypeDayOfWeekInMonth, // F |
| DateTimeFormat::FieldTypeEra, // G |
| DateTimeFormat::FieldTypeHour23, // H |
| DateTimeFormat::FieldTypeInvalid, // I |
| DateTimeFormat::FieldTypeInvalid, // J |
| DateTimeFormat::FieldTypeHour11, // K |
| DateTimeFormat::FieldTypeMonthStandAlone, // L |
| DateTimeFormat::FieldTypeMonth, // M |
| DateTimeFormat::FieldTypeInvalid, // N |
| DateTimeFormat::FieldTypeInvalid, // O |
| DateTimeFormat::FieldTypeInvalid, // P |
| DateTimeFormat::FieldTypeQuater, // Q |
| DateTimeFormat::FieldTypeInvalid, // R |
| DateTimeFormat::FieldTypeFractionalSecond, // S |
| DateTimeFormat::FieldTypeInvalid, // T |
| DateTimeFormat::FieldTypeInvalid, // U |
| DateTimeFormat::FieldTypeInvalid, // V |
| DateTimeFormat::FieldTypeWeekOfMonth, // W |
| DateTimeFormat::FieldTypeInvalid, // X |
| DateTimeFormat::FieldTypeYearOfWeekOfYear, // Y |
| DateTimeFormat::FieldTypeRFC822Zone, // Z |
| }; |
| |
| static DateTimeFormat::FieldType mapCharacterToFieldType(const UChar ch) |
| { |
| if (isASCIIUpper(ch)) |
| return upperCaseToFieldTypeMap[ch - 'A']; |
| |
| if (isASCIILower(ch)) |
| return lowerCaseToFieldTypeMap[ch - 'a']; |
| |
| return DateTimeFormat::FieldTypeLiteral; |
| } |
| |
| bool DateTimeFormat::parse(const String& source, TokenHandler& tokenHandler) |
| { |
| enum State { |
| StateInQuote, |
| StateInQuoteQuote, |
| StateLiteral, |
| StateQuote, |
| StateSymbol, |
| } state = StateLiteral; |
| |
| FieldType fieldType = FieldTypeLiteral; |
| StringBuilder literalBuffer; |
| int fieldCounter = 0; |
| |
| for (unsigned int index = 0; index < source.length(); ++index) { |
| const UChar ch = source[index]; |
| switch (state) { |
| case StateInQuote: |
| if (ch == '\'') { |
| state = StateInQuoteQuote; |
| break; |
| } |
| |
| literalBuffer.append(ch); |
| break; |
| |
| case StateInQuoteQuote: |
| if (ch == '\'') { |
| literalBuffer.append('\''); |
| state = StateInQuote; |
| break; |
| } |
| |
| fieldType = mapCharacterToFieldType(ch); |
| if (fieldType == FieldTypeInvalid) |
| return false; |
| |
| if (fieldType == FieldTypeLiteral) { |
| literalBuffer.append(ch); |
| state = StateLiteral; |
| break; |
| } |
| |
| if (literalBuffer.length()) { |
| tokenHandler.visitLiteral(literalBuffer.toString()); |
| literalBuffer.clear(); |
| } |
| |
| fieldCounter = 1; |
| state = StateSymbol; |
| break; |
| |
| case StateLiteral: |
| if (ch == '\'') { |
| state = StateQuote; |
| break; |
| } |
| |
| fieldType = mapCharacterToFieldType(ch); |
| if (fieldType == FieldTypeInvalid) |
| return false; |
| |
| if (fieldType == FieldTypeLiteral) { |
| literalBuffer.append(ch); |
| break; |
| } |
| |
| if (literalBuffer.length()) { |
| tokenHandler.visitLiteral(literalBuffer.toString()); |
| literalBuffer.clear(); |
| } |
| |
| fieldCounter = 1; |
| state = StateSymbol; |
| break; |
| |
| case StateQuote: |
| literalBuffer.append(ch); |
| state = ch == '\'' ? StateLiteral : StateInQuote; |
| break; |
| |
| case StateSymbol: { |
| ASSERT(fieldType != FieldTypeInvalid); |
| ASSERT(fieldType != FieldTypeLiteral); |
| ASSERT(literalBuffer.isEmpty()); |
| |
| FieldType fieldType2 = mapCharacterToFieldType(ch); |
| if (fieldType2 == FieldTypeInvalid) |
| return false; |
| |
| if (fieldType == fieldType2) { |
| ++fieldCounter; |
| break; |
| } |
| |
| tokenHandler.visitField(fieldType, fieldCounter); |
| |
| if (fieldType2 == FieldTypeLiteral) { |
| if (ch == '\'') |
| state = StateQuote; |
| else { |
| literalBuffer.append(ch); |
| state = StateLiteral; |
| } |
| break; |
| } |
| |
| fieldCounter = 1; |
| fieldType = fieldType2; |
| break; |
| } |
| } |
| } |
| |
| ASSERT(fieldType != FieldTypeInvalid); |
| |
| switch (state) { |
| case StateLiteral: |
| case StateInQuoteQuote: |
| if (literalBuffer.length()) |
| tokenHandler.visitLiteral(literalBuffer.toString()); |
| return true; |
| |
| case StateQuote: |
| case StateInQuote: |
| if (literalBuffer.length()) |
| tokenHandler.visitLiteral(literalBuffer.toString()); |
| return false; |
| |
| case StateSymbol: |
| ASSERT(fieldType != FieldTypeLiteral); |
| ASSERT(!literalBuffer.length()); |
| tokenHandler.visitField(fieldType, fieldCounter); |
| return true; |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return false; |
| } |
| |
| static bool isASCIIAlphabetOrQuote(UChar ch) |
| { |
| return isASCIIAlpha(ch) || ch == '\''; |
| } |
| |
| void DateTimeFormat::quoteAndAppendLiteral(const String& literal, StringBuilder& buffer) |
| { |
| if (literal.length() <= 0) |
| return; |
| |
| if (literal.find(isASCIIAlphabetOrQuote) == notFound) { |
| buffer.append(literal); |
| return; |
| } |
| |
| if (literal.find('\'') == notFound) { |
| buffer.append('\''); |
| buffer.append(literal); |
| buffer.append('\''); |
| return; |
| } |
| |
| for (unsigned i = 0; i < literal.length(); ++i) { |
| if (literal[i] == '\'') |
| buffer.appendLiteral("''"); |
| else { |
| String escaped = literal.substring(i); |
| escaped.replace('\'', "''"); |
| buffer.append('\''); |
| buffer.append(escaped); |
| buffer.append('\''); |
| return; |
| } |
| } |
| } |
| |
| } // namespace WebCore |
| |
| #endif |