[Platform] Implement Date Time format parser
authoryosin@chromium.org <yosin@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jun 2012 07:26:55 +0000 (07:26 +0000)
committeryosin@chromium.org <yosin@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 29 Jun 2012 07:26:55 +0000 (07:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=89963

Reviewed by Kent Tamura.

Source/WebCore:

This patch introduces Unicode TR35 LDML date time format parser for
input type "time" if ENABLE(INPUT_TYPE_TIME_MULTIPLE_FIELDS) is true.

Test: WebKit/chromium/tests/DateTimeFormatTest.cpp

* CMakeLists.txt: Added DateTimeFormat.cpp
* GNUmakefile.list.am: Added DateTimeFormat.{cpp,h}
* Target.pri: ditto
* WebCore.gypi: ditto
* WebCore.vcproj/WebCore.vcproj: ditto
* WebCore.xcodeproj/product.pbxproj: ditto
* platform/text/DateTimeFormat.cpp: Added.
(WebCore::mapCharacterToFieldTypeInternal):
(WebCore::DateTimeFormat::DateTimeFormat):
(WebCore::DateTimeFormat::mapCharacterToFieldType):
(WebCore::DateTimeFormat::parse):
* platform/text/DateTimeFormat.h: Added.
(DateTimeFormat):
(TokenHandler):
(WebCore::DateTimeFormat::TokenHandler::~TokenHandler):

Source/WebKit/chromium:

This patch adds an unit test for date time format parser if
ENABLE(INPUT_TYPE_TIME_MULTIPLE_FIELDS) is true.

* tests/DateTimeFormatTest.cpp: Added.
(DateTimeFormatTest):
(Token):
(DateTimeFormatTest::Token::Token):
(DateTimeFormatTest::Token::operator==):
(DateTimeFormatTest::Token::toString):
(Tokens):
(DateTimeFormatTest::Tokens::Tokens):
(DateTimeFormatTest::Tokens::operator==):
(DateTimeFormatTest::Tokens::toString):
(DateTimeFormatTest::parse):
(DateTimeFormatTest::single):
(TokenHandler):
(DateTimeFormatTest::TokenHandler::~TokenHandler):
(DateTimeFormatTest::TokenHandler::fieldType):
(DateTimeFormatTest::TokenHandler::tokens):
(DateTimeFormatTest::TokenHandler::visitField):
(DateTimeFormatTest::TokenHandler::visitLiteral):
(operator<<):
(TEST_F):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@121525 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Target.pri
Source/WebCore/WebCore.gypi
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/text/DateTimeFormat.cpp [new file with mode: 0644]
Source/WebCore/platform/text/DateTimeFormat.h [new file with mode: 0644]
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/WebKit.gypi
Source/WebKit/chromium/tests/DateTimeFormatTest.cpp [new file with mode: 0644]

index 0348fa3..5c56495 100644 (file)
@@ -1296,6 +1296,7 @@ SET(WebCore_SOURCES
 
     platform/text/Base64.cpp
     platform/text/BidiContext.cpp
+    platform/text/DateTimeFormat.cpp
     platform/text/Hyphenation.cpp
     platform/text/LineEnding.cpp
     platform/text/LocaleToScriptMappingDefault.cpp
index 8589ab2..e4083bd 100644 (file)
@@ -1,3 +1,31 @@
+2012-06-29  Yoshifumi Inoue  <yosin@chromium.org>
+
+        [Platform] Implement Date Time format parser
+        https://bugs.webkit.org/show_bug.cgi?id=89963
+
+        Reviewed by Kent Tamura.
+
+        This patch introduces Unicode TR35 LDML date time format parser for
+        input type "time" if ENABLE(INPUT_TYPE_TIME_MULTIPLE_FIELDS) is true.
+
+        Test: WebKit/chromium/tests/DateTimeFormatTest.cpp
+
+        * CMakeLists.txt: Added DateTimeFormat.cpp
+        * GNUmakefile.list.am: Added DateTimeFormat.{cpp,h}
+        * Target.pri: ditto
+        * WebCore.gypi: ditto
+        * WebCore.vcproj/WebCore.vcproj: ditto
+        * WebCore.xcodeproj/product.pbxproj: ditto
+        * platform/text/DateTimeFormat.cpp: Added.
+        (WebCore::mapCharacterToFieldTypeInternal):
+        (WebCore::DateTimeFormat::DateTimeFormat):
+        (WebCore::DateTimeFormat::mapCharacterToFieldType):
+        (WebCore::DateTimeFormat::parse):
+        * platform/text/DateTimeFormat.h: Added.
+        (DateTimeFormat):
+        (TokenHandler):
+        (WebCore::DateTimeFormat::TokenHandler::~TokenHandler):
+
 2012-06-29  Eric Seidel  <eric@webkit.org>
 
         Remove more BUILDING_ON_LEOPARD usage in PLATFORM(MAC) code
index 066b94c..fcaccac 100644 (file)
@@ -3681,6 +3681,8 @@ webcore_sources += \
        Source/WebCore/platform/text/BidiContext.h \
        Source/WebCore/platform/text/BidiResolver.h \
        Source/WebCore/platform/text/BidiRunList.h \
+       Source/WebCore/platform/text/DateTimeFormat.cpp \
+       Source/WebCore/platform/text/DateTimeFormat.h \
        Source/WebCore/platform/text/DecodeEscapeSequences.h \
        Source/WebCore/platform/text/Hyphenation.cpp \
        Source/WebCore/platform/text/Hyphenation.h \
index 31bd7b2..e541435 100644 (file)
@@ -1076,6 +1076,7 @@ SOURCES += \
     platform/Arena.cpp \
     platform/text/Base64.cpp \
     platform/text/BidiContext.cpp \
+    platform/text/DateTimeFormat.cpp \
     platform/text/Hyphenation.cpp \
     platform/text/LocaleToScriptMappingDefault.cpp \
     platform/text/LocalizedDateNone.cpp \
@@ -2414,6 +2415,7 @@ HEADERS += \
     platform/sql/SQLValue.h \
     platform/text/Base64.h \
     platform/text/BidiContext.h \
+    platform/text/DateTimeFormat.h \
     platform/text/DecodeEscapeSequences.h \
     platform/text/Hyphenation.h \
     platform/text/QuotedPrintable.h \
index 3ff9fd4..077f0e0 100644 (file)
             'platform/text/BidiRunList.h',
             'platform/text/BidiContext.h',
             'platform/text/BidiResolver.h',
+            'platform/text/DateTimeFormat.h',
             'platform/text/DecodeEscapeSequences.h',
             'platform/text/LineBreakIteratorPoolICU.h',
             'platform/text/LineEnding.h',
             'platform/text/AtomicStringKeyedMRUCache.h',
             'platform/text/Base64.cpp',
             'platform/text/BidiContext.cpp',
+            'platform/text/DateTimeFormat.cpp',
             'platform/text/Hyphenation.cpp',
             'platform/text/Hyphenation.h',
             'platform/text/LineEnding.cpp',
index fee6bc9..3d265f9 100755 (executable)
                                        >
                                </File>
                                <File
+                                       RelativePath="..\platform\text\DateTimeFormat.cpp"
+                                       >
+                               </File>
+                               <File
+                                       RelativePath="..\platform\text\DateTimeFormat.h"
+                                       >
+                               </File>
+                               <File
                                        RelativePath="..\platform\text\DecodeEscapeSequences.h"
                                        >
                                </File>
index 6ea00e3..882cc6b 100644 (file)
                45099C411370A7800058D513 /* IconURL.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45099C401370A7800058D513 /* IconURL.cpp */; };
                450CEBF015073BBE002BB149 /* LabelableElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 450CEBEE15073BBE002BB149 /* LabelableElement.cpp */; };
                450CEBF115073BBE002BB149 /* LabelableElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 450CEBEF15073BBE002BB149 /* LabelableElement.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               453EB636159C570400001BB7 /* DateTimeFormat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 453EB634159C570400001BB7 /* DateTimeFormat.cpp */; };
+               453EB637159C570400001BB7 /* DateTimeFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 453EB635159C570400001BB7 /* DateTimeFormat.h */; settings = {ATTRIBUTES = (Private, ); }; };
                458FE4091589DF0B005609E6 /* RenderSearchField.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 458FE4071589DF0B005609E6 /* RenderSearchField.cpp */; };
                458FE40A1589DF0B005609E6 /* RenderSearchField.h in Headers */ = {isa = PBXBuildFile; fileRef = 458FE4081589DF0B005609E6 /* RenderSearchField.h */; };
                45BAC2B01360BBAB005DA258 /* IconURL.h in Headers */ = {isa = PBXBuildFile; fileRef = 45BAC2AF1360BBAB005DA258 /* IconURL.h */; settings = {ATTRIBUTES = (Private, ); }; };
                45099C401370A7800058D513 /* IconURL.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IconURL.cpp; sourceTree = "<group>"; };
                450CEBEE15073BBE002BB149 /* LabelableElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LabelableElement.cpp; sourceTree = "<group>"; };
                450CEBEF15073BBE002BB149 /* LabelableElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LabelableElement.h; sourceTree = "<group>"; };
+               453EB634159C570400001BB7 /* DateTimeFormat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DateTimeFormat.cpp; sourceTree = "<group>"; };
+               453EB635159C570400001BB7 /* DateTimeFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateTimeFormat.h; sourceTree = "<group>"; };
                458FE4071589DF0B005609E6 /* RenderSearchField.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderSearchField.cpp; sourceTree = "<group>"; };
                458FE4081589DF0B005609E6 /* RenderSearchField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderSearchField.h; sourceTree = "<group>"; };
                45BAC2AF1360BBAB005DA258 /* IconURL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IconURL.h; sourceTree = "<group>"; };
                                B2C3D9F30D006C1D00EF6F26 /* BidiContext.h */,
                                B2C3D9F40D006C1D00EF6F26 /* BidiResolver.h */,
                                A8C402921348B2220063F1E5 /* BidiRunList.h */,
+                               453EB634159C570400001BB7 /* DateTimeFormat.cpp */,
+                               453EB635159C570400001BB7 /* DateTimeFormat.h */,
                                CECCFC3A141973D5002A0AC1 /* DecodeEscapeSequences.h */,
                                375CD231119D43C800A2A859 /* Hyphenation.h */,
                                A5ABB78613B904BC00F197E3 /* LineBreakIteratorPoolICU.h */,
                                81AC599A131636E60009A7E0 /* DataTransferItemList.h in Headers */,
                                2E97CCEB12939CB800C5C8FF /* DataView.h in Headers */,
                                A5732B0B136A161D005C8D7C /* DateComponents.h in Headers */,
+                               453EB637159C570400001BB7 /* DateTimeFormat.h in Headers */,
                                F55B3DB61251F12D003EF269 /* DateInputType.h in Headers */,
                                F55B3DB81251F12D003EF269 /* DateTimeInputType.h in Headers */,
                                F55B3DBA1251F12D003EF269 /* DateTimeLocalInputType.h in Headers */,
                                2E97CCEA12939CB800C5C8FF /* DataView.cpp in Sources */,
                                A5732B0A136A161D005C8D7C /* DateComponents.cpp in Sources */,
                                F55B3DB51251F12D003EF269 /* DateInputType.cpp in Sources */,
+                               453EB636159C570400001BB7 /* DateTimeFormat.cpp in Sources */,
                                F55B3DB71251F12D003EF269 /* DateTimeInputType.cpp in Sources */,
                                F55B3DB91251F12D003EF269 /* DateTimeLocalInputType.cpp in Sources */,
                                45FEA5CF156DDE8C00654101 /* Decimal.cpp in Sources */,
diff --git a/Source/WebCore/platform/text/DateTimeFormat.cpp b/Source/WebCore/platform/text/DateTimeFormat.cpp
new file mode 100644 (file)
index 0000000..6de7fdc
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * 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(INPUT_TYPE_TIME_MULTIPLE_FIELDS)
+#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::FieldTypeWeekOfMonth, // 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::FieldTypeWeekOfYear, // 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;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/platform/text/DateTimeFormat.h b/Source/WebCore/platform/text/DateTimeFormat.h
new file mode 100644 (file)
index 0000000..e7d539f
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#ifndef DateTimeFormat_h
+#define DateTimeFormat_h
+
+#if ENABLE(INPUT_TYPE_TIME_MULTIPLE_FIELDS)
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+// DateTimeFormat parses date time format defined in Unicode Technical
+// standard 35, Locale Data Markup Language (LDML)[1].
+// [1] LDML http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
+class DateTimeFormat {
+public:
+    enum FieldType {
+        FieldTypeInvalid,
+        FieldTypeLiteral,
+
+        // Era: AD
+        FieldTypeEra = 'G',
+
+        // Year: 1996
+        FieldTypeYear = 'y',
+        FieldTypeYearOfWeekOfYear = 'Y',
+        FieldTypeExtendedYear = 'u',
+
+        // Quater: Q2
+        FieldTypeQuater = 'Q',
+        FieldTypeQuaterStandAlone = 'q',
+
+        // Month: September
+        FieldTypeMonth = 'M',
+        FieldTypeMonthStandAlone = 'L',
+
+        // Week: 42
+        FieldTypeWeekOfYear = 'W',
+        FieldTypeWeekOfMonth = 'w',
+
+        // Day: 12
+        FieldTypeDayOfMonth = 'd',
+        FieldTypeDayOfYear = 'D',
+        FieldTypeDayOfWeekInMonth = 'F',
+        FieldTypeModifiedJulianDay = 'g',
+
+        // Week Day: Tuesday
+        FieldTypeDayOfWeek = 'E',
+        FieldTypeLocalDayOfWeek = 'e',
+        FieldTypeLocalDayOfWeekStandAlon = 'c',
+
+        // Period: AM or PM
+        FieldTypePeriod = 'a',
+
+        // Hour: 7
+        FieldTypeHour12 = 'h',
+        FieldTypeHour23 = 'H',
+        FieldTypeHour11 = 'K',
+        FieldTypeHour24 = 'k',
+
+        // Minute: 59
+        FieldTypeMinute = 'm',
+
+        // Second: 12
+        FieldTypeSecond = 's',
+        FieldTypeFractionalSecond = 'S',
+        FieldTypeMillisecondsInDay = 'A',
+
+        // Zone: PDT
+        FieldTypeZone = 'z',
+        FieldTypeRFC822Zone = 'Z',
+        FieldTypeNonLocationZone = 'v',
+    };
+
+    class TokenHandler {
+    public:
+        virtual ~TokenHandler() { }
+        virtual void visitField(FieldType, int numberOfPatternCharacters) = 0;
+        virtual void visitLiteral(const String&) = 0;
+    };
+
+    // Returns true if succeeded, false if failed.
+    static bool parse(const String&, TokenHandler&);
+};
+
+} // namespace WebCore
+
+#endif
+
+#endif // DateTimeFormat_h
index 8861395..74a3345 100644 (file)
@@ -1,3 +1,34 @@
+2012-06-29  Yoshifumi Inoue  <yosin@chromium.org>
+
+        [Platform] Implement Date Time format parser
+        https://bugs.webkit.org/show_bug.cgi?id=89963
+
+        Reviewed by Kent Tamura.
+
+        This patch adds an unit test for date time format parser if
+        ENABLE(INPUT_TYPE_TIME_MULTIPLE_FIELDS) is true.
+
+        * tests/DateTimeFormatTest.cpp: Added.
+        (DateTimeFormatTest):
+        (Token):
+        (DateTimeFormatTest::Token::Token):
+        (DateTimeFormatTest::Token::operator==):
+        (DateTimeFormatTest::Token::toString):
+        (Tokens):
+        (DateTimeFormatTest::Tokens::Tokens):
+        (DateTimeFormatTest::Tokens::operator==):
+        (DateTimeFormatTest::Tokens::toString):
+        (DateTimeFormatTest::parse):
+        (DateTimeFormatTest::single):
+        (TokenHandler):
+        (DateTimeFormatTest::TokenHandler::~TokenHandler):
+        (DateTimeFormatTest::TokenHandler::fieldType):
+        (DateTimeFormatTest::TokenHandler::tokens):
+        (DateTimeFormatTest::TokenHandler::visitField):
+        (DateTimeFormatTest::TokenHandler::visitLiteral):
+        (operator<<):
+        (TEST_F):
+
 2012-06-28  Joshua Bell  <jsbell@chromium.org>
 
         IndexedDB: Implement IDBTransaction internal active flag
index a8dd3a6..27c37b6 100644 (file)
@@ -97,6 +97,7 @@
             'tests/CCTimerTest.cpp',
             'tests/ClipboardChromiumTest.cpp',
             'tests/CompositorFakeWebGraphicsContext3D.h',
+            'tests/DateTimeFormatTest.cpp',
             'tests/DecimalTest.cpp',
             'tests/DragImageTest.cpp',
             'tests/EventListenerTest.cpp',
diff --git a/Source/WebKit/chromium/tests/DateTimeFormatTest.cpp b/Source/WebKit/chromium/tests/DateTimeFormatTest.cpp
new file mode 100644 (file)
index 0000000..1c006ab
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * 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(INPUT_TYPE_TIME_MULTIPLE_FIELDS)
+#include <gtest/gtest.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+using namespace WebCore;
+
+class DateTimeFormatTest : public ::testing::Test {
+public:
+    typedef DateTimeFormat::FieldType FieldType;
+
+    struct Token {
+        String string;
+        int count;
+        FieldType fieldType;
+
+        Token(FieldType fieldType, int count = 1)
+            : count(count)
+            , fieldType(fieldType)
+        {
+            ASSERT(fieldType != DateTimeFormat::FieldTypeLiteral);
+        }
+
+        Token(const String& string)
+            : string(string)
+            , count(0)
+            , fieldType(DateTimeFormat::FieldTypeLiteral)
+        {
+        }
+
+        bool operator==(const Token& other) const
+        {
+            return fieldType == other.fieldType && count == other.count && string == other.string;
+        }
+
+        String toString() const
+        {
+            switch (fieldType) {
+            case DateTimeFormat::FieldTypeInvalid:
+                return "*invalid*";
+            case DateTimeFormat::FieldTypeLiteral: {
+                StringBuilder builder;
+                builder.append('"');
+                builder.append(string);
+                builder.append('"');
+                return builder.toString();
+            }
+            default:
+                return String::format("Token(%d, %d)", fieldType, count);
+            }
+        }
+    };
+
+    class Tokens {
+    public:
+        Tokens() { }
+
+        explicit Tokens(const Vector<Token> tokens)
+            : m_tokens(tokens)
+        {
+        }
+
+        explicit Tokens(const String& string)
+        {
+            m_tokens.append(Token(string));
+        }
+
+        explicit Tokens(Token token1)
+        {
+            m_tokens.append(token1);
+        }
+
+        Tokens(Token token1, Token token2)
+        {
+            m_tokens.append(token1);
+            m_tokens.append(token2);
+        }
+
+        Tokens(Token token1, Token token2, Token token3)
+        {
+            m_tokens.append(token1);
+            m_tokens.append(token2);
+            m_tokens.append(token3);
+        }
+
+        Tokens(Token token1, Token token2, Token token3, Token token4)
+        {
+            m_tokens.append(token1);
+            m_tokens.append(token2);
+            m_tokens.append(token3);
+            m_tokens.append(token4);
+        }
+
+        Tokens(Token token1, Token token2, Token token3, Token token4, Token token5)
+        {
+            m_tokens.append(token1);
+            m_tokens.append(token2);
+            m_tokens.append(token3);
+            m_tokens.append(token4);
+            m_tokens.append(token5);
+        }
+
+        Tokens(Token token1, Token token2, Token token3, Token token4, Token token5, Token token6)
+        {
+            m_tokens.append(token1);
+            m_tokens.append(token2);
+            m_tokens.append(token3);
+            m_tokens.append(token4);
+            m_tokens.append(token5);
+            m_tokens.append(token6);
+        }
+
+        bool operator==(const Tokens& other) const
+        {
+            return m_tokens == other.m_tokens;
+        }
+
+        String toString() const
+        {
+            StringBuilder builder;
+            builder.append("Tokens(");
+            for (unsigned index = 0; index < m_tokens.size(); ++index) {
+                if (index)
+                    builder.append(",");
+                builder.append(m_tokens[index].toString());
+            }
+            builder.append(")");
+            return builder.toString();
+        }
+
+    private:
+        Vector<Token> m_tokens;
+    };
+
+protected:
+    Tokens parse(const String& formatString)
+    {
+        TokenHandler handler;
+        if (!DateTimeFormat::parse(formatString, handler))
+            return Tokens(Token("*failed*"));
+        return handler.tokens();
+    }
+
+    FieldType single(const char ch)
+    {
+        char formatString[2];
+        formatString[0] = ch;
+        formatString[1] = 0;
+        TokenHandler handler;
+        if (!DateTimeFormat::parse(formatString, handler))
+            return DateTimeFormat::FieldTypeInvalid;
+        return handler.fieldType(0);
+    }
+
+private:
+    class TokenHandler : public DateTimeFormat::TokenHandler {
+    public:
+        virtual ~TokenHandler() { }
+
+        FieldType fieldType(int index) const
+        {
+            return index >=0 && index < static_cast<int>(m_tokens.size()) ? m_tokens[index].fieldType : DateTimeFormat::FieldTypeInvalid;
+        }
+
+        Tokens tokens() const { return Tokens(m_tokens); }
+
+    private:
+        virtual void visitField(FieldType fieldType, int count) OVERRIDE
+        {
+            m_tokens.append(Token(fieldType, count));
+        }
+
+        virtual void visitLiteral(const String& string) OVERRIDE
+        {
+            m_tokens.append(Token(string));
+        }
+
+        Vector<Token> m_tokens;
+    };
+};
+
+std::ostream& operator<<(std::ostream& os, const DateTimeFormatTest::Tokens& tokens)
+{
+    return os << tokens.toString().ascii().data();
+}
+
+TEST_F(DateTimeFormatTest, CommonPattern)
+{
+    EXPECT_EQ(Tokens(), parse(""));
+
+    EXPECT_EQ(
+        Tokens(
+            Token(DateTimeFormat::FieldTypeYear, 4), Token("-"),
+            Token(DateTimeFormat::FieldTypeMonth, 2), Token("-"),
+            Token(DateTimeFormat::FieldTypeDayOfMonth, 2)),
+        parse("yyyy-MM-dd"));
+
+    EXPECT_EQ(
+        Tokens(
+            Token(DateTimeFormat::FieldTypeHour24, 2), Token(":"),
+            Token(DateTimeFormat::FieldTypeMinute, 2), Token(":"),
+            Token(DateTimeFormat::FieldTypeSecond, 2)),
+        parse("kk:mm:ss"));
+
+    EXPECT_EQ(
+        Tokens(
+            Token(DateTimeFormat::FieldTypeHour12), Token(":"),
+            Token(DateTimeFormat::FieldTypeMinute), Token(" "),
+            Token(DateTimeFormat::FieldTypePeriod)),
+        parse("h:m a"));
+
+    EXPECT_EQ(
+        Tokens(
+            Token(DateTimeFormat::FieldTypeYear), Token("Nen "),
+            Token(DateTimeFormat::FieldTypeMonth), Token("Getsu "),
+            Token(DateTimeFormat::FieldTypeDayOfMonth), Token("Nichi")),
+        parse("y'Nen' M'Getsu' d'Nichi'"));
+}
+
+TEST_F(DateTimeFormatTest, MissingClosingQuote)
+{
+    EXPECT_EQ(Tokens("*failed*"), parse("'foo"));
+    EXPECT_EQ(Tokens("*failed*"), parse("fo'o"));
+    EXPECT_EQ(Tokens("*failed*"), parse("foo'"));
+}
+
+TEST_F(DateTimeFormatTest, Quote)
+{
+    EXPECT_EQ(Tokens("FooBar"), parse("'FooBar'"));
+    EXPECT_EQ(Tokens("'"), parse("''"));
+    EXPECT_EQ(Tokens("'-'"), parse("''-''"));
+    EXPECT_EQ(Tokens("Foo'Bar"), parse("'Foo''Bar'"));
+    EXPECT_EQ(
+        Tokens(Token(DateTimeFormat::FieldTypeEra), Token("'s")),
+        parse("G'''s'"));
+    EXPECT_EQ(
+        Tokens(Token(DateTimeFormat::FieldTypeEra), Token("'"), Token(DateTimeFormat::FieldTypeSecond)),
+        parse("G''s"));
+}
+
+TEST_F(DateTimeFormatTest, SingleLowerCaseCharacter)
+{
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('b'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeLocalDayOfWeekStandAlon, single('c'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeDayOfMonth, single('d'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeLocalDayOfWeek, single('e'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeModifiedJulianDay, single('g'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeHour12, single('h'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeHour24, single('k'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeMinute, single('m'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeQuaterStandAlone, single('q'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeSecond, single('s'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeExtendedYear, single('u'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeNonLocationZone, single('v'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeWeekOfMonth, single('w'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeYear, single('y'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeZone, single('z'));
+}
+
+TEST_F(DateTimeFormatTest, SingleLowerCaseInvalid)
+{
+    EXPECT_EQ(DateTimeFormat::FieldTypePeriod, single('a'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('f'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('i'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('j'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('l'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('n'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('o'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('p'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('r'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('t'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('x'));
+}
+
+TEST_F(DateTimeFormatTest, SingleUpperCaseCharacter)
+{
+    EXPECT_EQ(DateTimeFormat::FieldTypeMillisecondsInDay, single('A'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeDayOfYear, single('D'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeDayOfWeek, single('E'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeDayOfWeekInMonth, single('F'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeEra, single('G'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeHour23, single('H'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeHour11, single('K'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeMonthStandAlone, single('L'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeMonth, single('M'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeQuater, single('Q'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeFractionalSecond, single('S'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeWeekOfYear, single('W'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeYearOfWeekOfYear, single('Y'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeRFC822Zone, single('Z'));
+}
+
+TEST_F(DateTimeFormatTest, SingleUpperCaseInvalid)
+{
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('B'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('C'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('I'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('J'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('N'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('O'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('P'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('R'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('T'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('U'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('V'));
+    EXPECT_EQ(DateTimeFormat::FieldTypeInvalid, single('X'));
+}
+
+#endif