blob: aecc0ee4d267b8a7e863fd130c43c7a515da905e [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 "third_party/blink/renderer/platform/text/date_time_format.h"
#include "third_party/blink/renderer/platform/wtf/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
static const DateTimeFormat::FieldType kLowerCaseToFieldTypeMap[26] = {
DateTimeFormat::kFieldTypePeriod, // a
DateTimeFormat::kFieldTypeInvalid, // b
DateTimeFormat::kFieldTypeLocalDayOfWeekStandAlon, // c
DateTimeFormat::kFieldTypeDayOfMonth, // d
DateTimeFormat::kFieldTypeLocalDayOfWeek, // e
DateTimeFormat::kFieldTypeInvalid, // f
DateTimeFormat::kFieldTypeModifiedJulianDay, // g
DateTimeFormat::kFieldTypeHour12, // h
DateTimeFormat::kFieldTypeInvalid, // i
DateTimeFormat::kFieldTypeInvalid, // j
DateTimeFormat::kFieldTypeHour24, // k
DateTimeFormat::kFieldTypeInvalid, // l
DateTimeFormat::kFieldTypeMinute, // m
DateTimeFormat::kFieldTypeInvalid, // n
DateTimeFormat::kFieldTypeInvalid, // o
DateTimeFormat::kFieldTypeInvalid, // p
DateTimeFormat::kFieldTypeQuaterStandAlone, // q
DateTimeFormat::kFieldTypeInvalid, // r
DateTimeFormat::kFieldTypeSecond, // s
DateTimeFormat::kFieldTypeInvalid, // t
DateTimeFormat::kFieldTypeExtendedYear, // u
DateTimeFormat::kFieldTypeNonLocationZone, // v
DateTimeFormat::kFieldTypeWeekOfYear, // w
DateTimeFormat::kFieldTypeInvalid, // x
DateTimeFormat::kFieldTypeYear, // y
DateTimeFormat::kFieldTypeZone, // z
};
static const DateTimeFormat::FieldType kUpperCaseToFieldTypeMap[26] = {
DateTimeFormat::kFieldTypeMillisecondsInDay, // A
DateTimeFormat::kFieldTypeInvalid, // B
DateTimeFormat::kFieldTypeInvalid, // C
DateTimeFormat::kFieldTypeDayOfYear, // D
DateTimeFormat::kFieldTypeDayOfWeek, // E
DateTimeFormat::kFieldTypeDayOfWeekInMonth, // F
DateTimeFormat::kFieldTypeEra, // G
DateTimeFormat::kFieldTypeHour23, // H
DateTimeFormat::kFieldTypeInvalid, // I
DateTimeFormat::kFieldTypeInvalid, // J
DateTimeFormat::kFieldTypeHour11, // K
DateTimeFormat::kFieldTypeMonthStandAlone, // L
DateTimeFormat::kFieldTypeMonth, // M
DateTimeFormat::kFieldTypeInvalid, // N
DateTimeFormat::kFieldTypeInvalid, // O
DateTimeFormat::kFieldTypeInvalid, // P
DateTimeFormat::kFieldTypeQuater, // Q
DateTimeFormat::kFieldTypeInvalid, // R
DateTimeFormat::kFieldTypeFractionalSecond, // S
DateTimeFormat::kFieldTypeInvalid, // T
DateTimeFormat::kFieldTypeInvalid, // U
DateTimeFormat::kFieldTypeInvalid, // V
DateTimeFormat::kFieldTypeWeekOfMonth, // W
DateTimeFormat::kFieldTypeInvalid, // X
DateTimeFormat::kFieldTypeYearOfWeekOfYear, // Y
DateTimeFormat::kFieldTypeRFC822Zone, // Z
};
static DateTimeFormat::FieldType MapCharacterToFieldType(const UChar ch) {
if (IsASCIIUpper(ch))
return kUpperCaseToFieldTypeMap[ch - 'A'];
if (IsASCIILower(ch))
return kLowerCaseToFieldTypeMap[ch - 'a'];
return DateTimeFormat::kFieldTypeLiteral;
}
bool DateTimeFormat::Parse(const String& source, TokenHandler& token_handler) {
enum State {
kStateInQuote,
kStateInQuoteQuote,
kStateLiteral,
kStateQuote,
kStateSymbol,
} state = kStateLiteral;
FieldType field_type = kFieldTypeLiteral;
StringBuilder literal_buffer;
int field_counter = 0;
for (unsigned index = 0; index < source.length(); ++index) {
const UChar ch = source[index];
switch (state) {
case kStateInQuote:
if (ch == '\'') {
state = kStateInQuoteQuote;
break;
}
literal_buffer.Append(ch);
break;
case kStateInQuoteQuote:
if (ch == '\'') {
literal_buffer.Append('\'');
state = kStateInQuote;
break;
}
field_type = MapCharacterToFieldType(ch);
if (field_type == kFieldTypeInvalid)
return false;
if (field_type == kFieldTypeLiteral) {
literal_buffer.Append(ch);
state = kStateLiteral;
break;
}
if (literal_buffer.length()) {
token_handler.VisitLiteral(literal_buffer.ToString());
literal_buffer.Clear();
}
field_counter = 1;
state = kStateSymbol;
break;
case kStateLiteral:
if (ch == '\'') {
state = kStateQuote;
break;
}
field_type = MapCharacterToFieldType(ch);
if (field_type == kFieldTypeInvalid)
return false;
if (field_type == kFieldTypeLiteral) {
literal_buffer.Append(ch);
break;
}
if (literal_buffer.length()) {
token_handler.VisitLiteral(literal_buffer.ToString());
literal_buffer.Clear();
}
field_counter = 1;
state = kStateSymbol;
break;
case kStateQuote:
literal_buffer.Append(ch);
state = ch == '\'' ? kStateLiteral : kStateInQuote;
break;
case kStateSymbol: {
DCHECK_NE(field_type, kFieldTypeInvalid);
DCHECK_NE(field_type, kFieldTypeLiteral);
DCHECK(literal_buffer.IsEmpty());
FieldType field_type2 = MapCharacterToFieldType(ch);
if (field_type2 == kFieldTypeInvalid)
return false;
if (field_type == field_type2) {
++field_counter;
break;
}
token_handler.VisitField(field_type, field_counter);
if (field_type2 == kFieldTypeLiteral) {
if (ch == '\'') {
state = kStateQuote;
} else {
literal_buffer.Append(ch);
state = kStateLiteral;
}
break;
}
field_counter = 1;
field_type = field_type2;
break;
}
}
}
DCHECK_NE(field_type, kFieldTypeInvalid);
switch (state) {
case kStateLiteral:
case kStateInQuoteQuote:
if (literal_buffer.length())
token_handler.VisitLiteral(literal_buffer.ToString());
return true;
case kStateQuote:
case kStateInQuote:
if (literal_buffer.length())
token_handler.VisitLiteral(literal_buffer.ToString());
return false;
case kStateSymbol:
DCHECK_NE(field_type, kFieldTypeLiteral);
DCHECK(!literal_buffer.length());
token_handler.VisitField(field_type, field_counter);
return true;
}
NOTREACHED();
return false;
}
static bool IsASCIIAlphabetOrQuote(UChar ch) {
return IsASCIIAlpha(ch) || ch == '\'';
}
void DateTimeFormat::QuoteAndappend(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.Append("''");
} else {
String escaped = literal.Substring(i);
escaped.Replace("'", "''");
buffer.Append('\'');
buffer.Append(escaped);
buffer.Append('\'');
return;
}
}
}
} // namespace blink