Home | History | Annotate | Download | only in text
      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