diff options
Diffstat (limited to 'Source/WebCore/storage/IDBKeyPath.cpp')
-rw-r--r-- | Source/WebCore/storage/IDBKeyPath.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/Source/WebCore/storage/IDBKeyPath.cpp b/Source/WebCore/storage/IDBKeyPath.cpp new file mode 100644 index 0000000..8833da0 --- /dev/null +++ b/Source/WebCore/storage/IDBKeyPath.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2010 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 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 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 "IDBKeyPath.h" + +#if ENABLE(INDEXED_DATABASE) + +#include <wtf/ASCIICType.h> +#include <wtf/dtoa.h> + +namespace WebCore { + +class IDBKeyPathLexer { +public: + enum TokenType { + TokenLeftBracket, + TokenRightBracket, + TokenIdentifier, + TokenNumber, + TokenDot, + TokenEnd, + TokenError + }; + + explicit IDBKeyPathLexer(const String& s) + : m_string(s) + , m_ptr(s.characters()) + , m_end(s.characters() + s.length()) + , m_currentTokenType(TokenError) + { + } + + TokenType currentTokenType() const { return m_currentTokenType; } + + TokenType nextTokenType() + { + m_currentTokenType = lex(m_currentElement); + return m_currentTokenType; + } + + const IDBKeyPathElement& currentElement() { return m_currentElement; } + +private: + TokenType lex(IDBKeyPathElement&); + TokenType lexIdentifier(IDBKeyPathElement&); + TokenType lexNumber(IDBKeyPathElement&); + IDBKeyPathElement m_currentElement; + String m_string; + const UChar* m_ptr; + const UChar* m_end; + TokenType m_currentTokenType; +}; + +IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(IDBKeyPathElement& element) +{ + while (m_ptr < m_end && isASCIISpace(*m_ptr)) + ++m_ptr; + + if (m_ptr >= m_end) + return TokenEnd; + + ASSERT(m_ptr < m_end); + switch (*m_ptr) { + case '[': + ++m_ptr; + return TokenLeftBracket; + case ']': + ++m_ptr; + return TokenRightBracket; + case '.': + ++m_ptr; + return TokenDot; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return lexNumber(element); + default: + return lexIdentifier(element); + } + return TokenError; +} + +static inline bool isSafeIdentifierStartCharacter(UChar c) +{ + return isASCIIAlpha(c) || (c == '_') || (c == '$'); +} + +static inline bool isSafeIdentifierCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || (c == '_') || (c == '$'); +} + +IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(IDBKeyPathElement& element) +{ + const UChar* start = m_ptr; + if (m_ptr < m_end && isSafeIdentifierStartCharacter(*m_ptr)) + ++m_ptr; + else + return TokenError; + + while (m_ptr < m_end && isSafeIdentifierCharacter(*m_ptr)) + ++m_ptr; + + element.type = IDBKeyPathElement::IsNamed; + element.identifier = String(start, m_ptr - start); + return TokenIdentifier; +} + +IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexNumber(IDBKeyPathElement& element) +{ + if (m_ptr >= m_end) + return TokenError; + + const UChar* start = m_ptr; + // [0-9]* + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + + String numberAsString; + numberAsString = String(start, m_ptr - start); + bool ok = false; + unsigned number = numberAsString.toUIntStrict(&ok); + if (!ok) + return TokenError; + + element.type = IDBKeyPathElement::IsIndexed; + element.index = number; + return TokenNumber; +} + +void IDBParseKeyPath(const String& keyPath, Vector<IDBKeyPathElement>& elements, IDBKeyPathParseError& error) +{ + // This is a simplified parser loosely based on LiteralParser. + // An IDBKeyPath is defined as a sequence of: + // identifierA{.identifierB{[numeric_value]} + // where "{}" represents an optional part + // The basic state machine is: + // Start => {Identifier, Array} + // Identifier => {Dot, Array, End} + // Array => {Start, Dot, End} + // Dot => {Identifier} + // It bails out as soon as it finds an error, but doesn't discard the bits it managed to parse. + enum ParserState { Identifier, Array, Dot, End }; + + IDBKeyPathLexer lexer(keyPath); + IDBKeyPathLexer::TokenType tokenType = lexer.nextTokenType(); + ParserState state; + if (tokenType == IDBKeyPathLexer::TokenIdentifier) + state = Identifier; + else if (tokenType == IDBKeyPathLexer::TokenLeftBracket) + state = Array; + else if (tokenType == IDBKeyPathLexer::TokenEnd) + state = End; + else { + error = IDBKeyPathParseErrorStart; + return; + } + + while (1) { + switch (state) { + case Identifier : { + IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType(); + ASSERT(tokenType == IDBKeyPathLexer::TokenIdentifier); + + IDBKeyPathElement element = lexer.currentElement(); + ASSERT(element.type == IDBKeyPathElement::IsNamed); + elements.append(element); + + tokenType = lexer.nextTokenType(); + if (tokenType == IDBKeyPathLexer::TokenDot) + state = Dot; + else if (tokenType == IDBKeyPathLexer::TokenLeftBracket) + state = Array; + else if (tokenType == IDBKeyPathLexer::TokenEnd) + state = End; + else { + error = IDBKeyPathParseErrorIdentifier; + return; + } + break; + } + case Array : { + IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType(); + ASSERT(tokenType == IDBKeyPathLexer::TokenLeftBracket); + + tokenType = lexer.nextTokenType(); + if (tokenType != IDBKeyPathLexer::TokenNumber) { + error = IDBKeyPathParseErrorArrayIndex; + return; + } + + ASSERT(tokenType == IDBKeyPathLexer::TokenNumber); + IDBKeyPathElement element = lexer.currentElement(); + ASSERT(element.type == IDBKeyPathElement::IsIndexed); + elements.append(element); + + tokenType = lexer.nextTokenType(); + if (tokenType != IDBKeyPathLexer::TokenRightBracket) { + error = IDBKeyPathParseErrorArrayIndex; + return; + } + + tokenType = lexer.nextTokenType(); + if (tokenType == IDBKeyPathLexer::TokenDot) + state = Dot; + else if (tokenType == IDBKeyPathLexer::TokenLeftBracket) + state = Array; + else if (tokenType == IDBKeyPathLexer::TokenEnd) + state = End; + else { + error = IDBKeyPathParseErrorAfterArray; + return; + } + break; + } + case Dot: { + IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType(); + ASSERT(tokenType == IDBKeyPathLexer::TokenDot); + + tokenType = lexer.nextTokenType(); + if (tokenType != IDBKeyPathLexer::TokenIdentifier) { + error = IDBKeyPathParseErrorDot; + return; + } + + state = Identifier; + break; + } + case End: { + error = IDBKeyPathParseErrorNone; + return; + } + } + } +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) |