blob: 512163345b02792d9340ca632d33a17de29ae090 [file] [log] [blame]
/*
* 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 "sky/engine/platform/text/DateTimeFormat.h"
#include "sky/engine/wtf/ASCIICType.h"
#include "sky/engine/wtf/text/StringBuilder.h"
namespace blink {
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 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) == kNotFound) {
buffer.append(literal);
return;
}
if (literal.find('\'') == kNotFound) {
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 blink