Home | History | Annotate | Download | only in indexeddb
      1 /*
      2  * Copyright (C) 2010 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "modules/indexeddb/IDBKeyPath.h"
     28 
     29 #include "wtf/ASCIICType.h"
     30 #include "wtf/dtoa.h"
     31 #include "wtf/unicode/CharacterNames.h"
     32 #include "wtf/unicode/Unicode.h"
     33 
     34 using namespace WTF::Unicode;
     35 
     36 namespace WebCore {
     37 
     38 class IDBKeyPathLexer {
     39 public:
     40     enum TokenType {
     41         TokenIdentifier,
     42         TokenDot,
     43         TokenEnd,
     44         TokenError
     45     };
     46 
     47     explicit IDBKeyPathLexer(const String& s)
     48         : m_string(s)
     49         , m_length(s.length())
     50         , m_index(0)
     51         , m_currentTokenType(TokenError)
     52     {
     53     }
     54 
     55     TokenType currentTokenType() const { return m_currentTokenType; }
     56 
     57     TokenType nextTokenType()
     58     {
     59         m_currentTokenType = lex(m_currentElement);
     60         return m_currentTokenType;
     61     }
     62 
     63     const String& currentElement() { return m_currentElement; }
     64 
     65 private:
     66     TokenType lex(String&);
     67     TokenType lexIdentifier(String&);
     68     String m_currentElement;
     69     const String m_string;
     70     const unsigned m_length;
     71     unsigned m_index;
     72     TokenType m_currentTokenType;
     73 };
     74 
     75 IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(String& element)
     76 {
     77     if (m_index >= m_length)
     78         return TokenEnd;
     79     ASSERT(m_index < m_length);
     80 
     81     if (m_string[m_index] == '.') {
     82         ++m_index;
     83         return TokenDot;
     84     }
     85     return lexIdentifier(element);
     86 }
     87 
     88 namespace {
     89 
     90 using namespace WTF::Unicode;
     91 
     92 // The following correspond to grammar in ECMA-262.
     93 const uint32_t unicodeLetter = Letter_Uppercase | Letter_Lowercase | Letter_Titlecase | Letter_Modifier | Letter_Other | Number_Letter;
     94 const uint32_t unicodeCombiningMark = Mark_NonSpacing | Mark_SpacingCombining;
     95 const uint32_t unicodeDigit = Number_DecimalDigit;
     96 const uint32_t unicodeConnectorPunctuation = Punctuation_Connector;
     97 
     98 static inline bool isIdentifierStartCharacter(UChar c)
     99 {
    100     return (category(c) & unicodeLetter) || (c == '$') || (c == '_');
    101 }
    102 
    103 static inline bool isIdentifierCharacter(UChar c)
    104 {
    105     return (category(c) & (unicodeLetter | unicodeCombiningMark | unicodeDigit | unicodeConnectorPunctuation)) || (c == '$') || (c == '_') || (c == zeroWidthNonJoiner) || (c == zeroWidthJoiner);
    106 }
    107 
    108 } // namespace
    109 
    110 IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(String& element)
    111 {
    112     unsigned start = m_index;
    113     if (m_index < m_length && isIdentifierStartCharacter(m_string[m_index]))
    114         ++m_index;
    115     else
    116         return TokenError;
    117 
    118     while (m_index < m_length && isIdentifierCharacter(m_string[m_index]))
    119         ++m_index;
    120 
    121     element = m_string.substring(start, m_index - start);
    122     return TokenIdentifier;
    123 }
    124 
    125 bool IDBIsValidKeyPath(const String& keyPath)
    126 {
    127     IDBKeyPathParseError error;
    128     Vector<String> keyPathElements;
    129     IDBParseKeyPath(keyPath, keyPathElements, error);
    130     return error == IDBKeyPathParseErrorNone;
    131 }
    132 
    133 void IDBParseKeyPath(const String& keyPath, Vector<String>& elements, IDBKeyPathParseError& error)
    134 {
    135     // IDBKeyPath ::= EMPTY_STRING | identifier ('.' identifier)*
    136     // The basic state machine is:
    137     //   Start => {Identifier, End}
    138     //   Identifier => {Dot, End}
    139     //   Dot => {Identifier}
    140     // It bails out as soon as it finds an error, but doesn't discard the bits it managed to parse.
    141     enum ParserState { Identifier, Dot, End };
    142 
    143     IDBKeyPathLexer lexer(keyPath);
    144     IDBKeyPathLexer::TokenType tokenType = lexer.nextTokenType();
    145     ParserState state;
    146     if (tokenType == IDBKeyPathLexer::TokenIdentifier)
    147         state = Identifier;
    148     else if (tokenType == IDBKeyPathLexer::TokenEnd)
    149         state = End;
    150     else {
    151         error = IDBKeyPathParseErrorStart;
    152         return;
    153     }
    154 
    155     while (1) {
    156         switch (state) {
    157         case Identifier : {
    158             IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType();
    159             ASSERT(tokenType == IDBKeyPathLexer::TokenIdentifier);
    160 
    161             String element = lexer.currentElement();
    162             elements.append(element);
    163 
    164             tokenType = lexer.nextTokenType();
    165             if (tokenType == IDBKeyPathLexer::TokenDot)
    166                 state = Dot;
    167             else if (tokenType == IDBKeyPathLexer::TokenEnd)
    168                 state = End;
    169             else {
    170                 error = IDBKeyPathParseErrorIdentifier;
    171                 return;
    172             }
    173             break;
    174         }
    175         case Dot: {
    176             IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType();
    177             ASSERT(tokenType == IDBKeyPathLexer::TokenDot);
    178 
    179             tokenType = lexer.nextTokenType();
    180             if (tokenType == IDBKeyPathLexer::TokenIdentifier)
    181                 state = Identifier;
    182             else {
    183                 error = IDBKeyPathParseErrorDot;
    184                 return;
    185             }
    186             break;
    187         }
    188         case End: {
    189             error = IDBKeyPathParseErrorNone;
    190             return;
    191         }
    192         }
    193     }
    194 }
    195 
    196 IDBKeyPath::IDBKeyPath(const String& string)
    197     : m_type(StringType)
    198     , m_string(string)
    199 {
    200     ASSERT(!m_string.isNull());
    201 }
    202 
    203 IDBKeyPath::IDBKeyPath(const Vector<String>& array)
    204     : m_type(ArrayType)
    205     , m_array(array)
    206 {
    207 #ifndef NDEBUG
    208     for (size_t i = 0; i < m_array.size(); ++i)
    209         ASSERT(!m_array[i].isNull());
    210 #endif
    211 }
    212 
    213 bool IDBKeyPath::isValid() const
    214 {
    215     switch (m_type) {
    216     case NullType:
    217         return false;
    218 
    219     case StringType:
    220         return IDBIsValidKeyPath(m_string);
    221 
    222     case ArrayType:
    223         if (m_array.isEmpty())
    224             return false;
    225         for (size_t i = 0; i < m_array.size(); ++i) {
    226             if (!IDBIsValidKeyPath(m_array[i]))
    227                 return false;
    228         }
    229         return true;
    230     }
    231     ASSERT_NOT_REACHED();
    232     return false;
    233 }
    234 
    235 bool IDBKeyPath::operator==(const IDBKeyPath& other) const
    236 {
    237     if (m_type != other.m_type)
    238         return false;
    239 
    240     switch (m_type) {
    241     case NullType:
    242         return true;
    243     case StringType:
    244         return m_string == other.m_string;
    245     case ArrayType:
    246         return m_array == other.m_array;
    247     }
    248     ASSERT_NOT_REACHED();
    249     return false;
    250 }
    251 
    252 } // namespace WebCore
    253