From bdfad5d5a764323cfc3f865555e65da16d3827b4 Mon Sep 17 00:00:00 2001 From: Adeel Kazmi Date: Wed, 30 Sep 2020 11:47:46 +0100 Subject: [PATCH] (JSON Parser State) Reduce Cyclomatic Complexity of ParseJson Change-Id: Iba20de994253d3dfaac8f92b9381c18aaeb76861 --- .../internal/builder/json-parser-state.cpp | 561 ++++++++++++--------- dali-toolkit/internal/builder/json-parser-state.h | 180 +++++-- 2 files changed, 478 insertions(+), 263 deletions(-) diff --git a/dali-toolkit/internal/builder/json-parser-state.cpp b/dali-toolkit/internal/builder/json-parser-state.cpp index 636bac1..40ec632 100644 --- a/dali-toolkit/internal/builder/json-parser-state.cpp +++ b/dali-toolkit/internal/builder/json-parser-state.cpp @@ -249,11 +249,11 @@ bool IsNumber(char c) JsonParserState::JsonParserState(TreeNode* _root) : mRoot(_root), mCurrent(_root), - mErrorDescription(NULL), mErrorNewLine(0), mErrorColumn(0), mErrorPosition(0), + mErrorDescription(nullptr), mErrorNewLine(0), mErrorColumn(0), mErrorPosition(0), mNumberOfParsedChars(0), mNumberOfCreatedNodes(0), mFirstParse(false), mState(STATE_START) { - if(_root == NULL) + if(_root == nullptr) { mFirstParse = true; } @@ -261,13 +261,13 @@ JsonParserState::JsonParserState(TreeNode* _root) TreeNode* JsonParserState::CreateNewNode(const char* name, TreeNode::NodeType type) { - TreeNode* node = NULL; + TreeNode* node = nullptr; node = TreeNodeManipulator::NewTreeNode(); TreeNodeManipulator modifyNew(node); modifyNew.SetType(type); modifyNew.SetName(name); - if(mRoot == NULL) + if(mRoot == nullptr) { mRoot = node; mCurrent = TreeNodeManipulator(mRoot); @@ -286,7 +286,7 @@ TreeNode* JsonParserState::CreateNewNode(const char* name, TreeNode::NodeType ty TreeNode* JsonParserState::NewNode(const char* name, TreeNode::NodeType type) { - TreeNode* node = NULL; + TreeNode* node = nullptr; if(mFirstParse) { @@ -299,7 +299,7 @@ TreeNode* JsonParserState::NewNode(const char* name, TreeNode::NodeType type) if(name) { const TreeNode* found = mCurrent.GetChild(name); - if( NULL != found ) + if( nullptr != found ) { node = const_cast(found); } @@ -307,7 +307,7 @@ TreeNode* JsonParserState::NewNode(const char* name, TreeNode::NodeType type) else { // if root node - if( mCurrent.GetParent() == NULL ) + if( mCurrent.GetParent() == nullptr ) { node = mRoot; } @@ -550,7 +550,7 @@ char* JsonParserState::EncodeString() if (static_cast(*mIter) < '\x20') { static_cast( Error("Control characters not allowed in strings") ); - return NULL; + return nullptr; } else if (*mIter == '\\' && AtLeast(2)) { @@ -602,12 +602,12 @@ char* JsonParserState::EncodeString() if( !AtLeast(6) ) { static_cast( Error("Bad unicode codepoint; not enough characters") ); - return NULL; + return nullptr; } if ( !HexStringToUnsignedInteger(&(*(mIter + 2)), &(*(mIter + 6)), codepoint) ) { static_cast( Error("Bad unicode codepoint") ); - return NULL; + return nullptr; } if (codepoint <= 0x7F) @@ -633,7 +633,7 @@ char* JsonParserState::EncodeString() default: { static_cast( Error("Unrecognized escape sequence") ); - return NULL; + return nullptr; } } @@ -682,6 +682,172 @@ char* JsonParserState::EncodeString() } // ParseString() +bool JsonParserState::HandleStartState(const char* name, const char currentChar) +{ + if( '{' == currentChar ) + { + NewNode(name, TreeNode::OBJECT); + mState = STATE_OBJECT; + } + else if( '[' == currentChar ) + { + NewNode(name, TreeNode::ARRAY); + mState = STATE_VALUE; + } + else + { + return Error("Json must start with object {} or array []"); + } + + AdvanceSkipWhiteSpace(1); + return true; +} + +bool JsonParserState::HandleObjectState(const char currentChar, const char lastCharacter) +{ + if( '}' == currentChar ) + { + if(',' == lastCharacter) + { + return Error("Unexpected comma"); + } + + if( !UpToParent() ) + { + return false; + } + mState = STATE_VALUE; + } + else if ( '"' == currentChar ) + { + mState = STATE_KEY; + } + else + { + return Error("Unexpected character"); + } + + AdvanceSkipWhiteSpace(1); + return true; +} + +bool JsonParserState::HandleKeyState(char*& name) +{ + name = EncodeString(); + if( nullptr == name ) + { + return false; + } + if( !ParseWhiteSpace() ) + { + return false; + } + if( ':' != Char()) + { + return Error("Expected ':'"); + } + if( !ParseWhiteSpace() ) + { + return false; + } + mState = STATE_VALUE; + + AdvanceSkipWhiteSpace(1); + return true; +} + +bool JsonParserState::HandleCharacterQuote(char*& name) +{ + Advance(1); + NewNode(name, TreeNode::STRING); + if( char* value = EncodeString() ) + { + mCurrent.SetString(value); + } + else + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + return true; +} + +bool JsonParserState::HandleCharacterNumberOrHyphen(const char* name) +{ + NewNode(name, TreeNode::IS_NULL); + if( !ParseNumber() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + return true; +} + +bool JsonParserState::HandleValueState(char*& name, const char currentChar, const char lastCharacter) +{ + bool handled = true; + + if( '"' == currentChar ) + { + handled = HandleCharacterQuote(name); + } + else if( IsNumber(currentChar) || currentChar == '-' ) + { + handled = HandleCharacterNumberOrHyphen(name); + } + else if( '{' == currentChar ) + { + handled = HandleCharacterBracesStart(name, lastCharacter); + } + else if( '}' == currentChar ) + { + handled = HandleCharacterBracesEnd(lastCharacter); + } + else if( '[' == currentChar ) + { + handled = HandleCharacterSquareBracketStart(name); + } + else if( ']' == currentChar ) + { + handled = HandleCharacterSquareBracketEnd(lastCharacter); + } + else if( 't' == currentChar ) + { + handled = HandleCharacterLowercaseT(name); + } + else if( 'n' == currentChar ) + { + handled = HandleCharacterLowercaseN(name); + } + else if( 'f' == currentChar) + { + handled = HandleCharacterLowercaseF(name); + } + else if( ',' == currentChar ) + { + handled = HandleCharacterComma(name); + } + else + { + handled = Error("Unexpected character"); + } + + if(handled) + { + name = nullptr; + } + + return handled; +} + bool JsonParserState::ParseJson(VectorChar& source) { Reset(); @@ -694,7 +860,7 @@ bool JsonParserState::ParseJson(VectorChar& source) mIter = source.begin(); mEnd = source.end(); - char* name = NULL; + char* name = nullptr; char currentChar = 0; char lastCharacter = 0; @@ -712,244 +878,34 @@ bool JsonParserState::ParseJson(VectorChar& source) { case STATE_START: { - if( '{' == currentChar ) - { - NewNode(name, TreeNode::OBJECT); - mState = STATE_OBJECT; - } - else if( '[' == currentChar ) + if(!HandleStartState(name, currentChar)) { - NewNode(name, TreeNode::ARRAY); - mState = STATE_VALUE; - } - else - { - return Error("Json must start with object {} or array []"); + return false; } - - AdvanceSkipWhiteSpace(1); break; } case STATE_OBJECT: { - if( '}' == currentChar ) + if(!HandleObjectState(currentChar, lastCharacter)) { - if(',' == lastCharacter) - { - return Error("Unexpected comma"); - } - - if( !UpToParent() ) - { - return false; - } - mState = STATE_VALUE; - } - else if ( '"' == currentChar ) - { - mState = STATE_KEY; - } - else - { - return Error("Unexpected character"); + return false; } - - AdvanceSkipWhiteSpace(1); break; } case STATE_KEY: { - name = EncodeString(); - if( NULL == name ) + if(!HandleKeyState(name)) { return false; } - if( !ParseWhiteSpace() ) - { - return false; - } - if( ':' != Char()) - { - return Error("Expected ':'"); - } - if( !ParseWhiteSpace() ) - { - return false; - } - mState = STATE_VALUE; - - AdvanceSkipWhiteSpace(1); break; } case STATE_VALUE: { - if( '"' == currentChar ) + if(!HandleValueState(name, currentChar, lastCharacter)) { - Advance(1); - NewNode(name, TreeNode::STRING); - if( char* value = EncodeString() ) - { - mCurrent.SetString(value); - } - else - { - return false; - } - if( !UpToParent() ) - { - return false; - } - AdvanceSkipWhiteSpace(0); - } - else if( IsNumber(currentChar) || currentChar == '-' ) - { - NewNode(name, TreeNode::IS_NULL); - if( !ParseNumber() ) - { - return false; - } - if( !UpToParent() ) - { - return false; - } - AdvanceSkipWhiteSpace(0); - } - else if( '{' == currentChar ) - { - if( '}' == lastCharacter ) - { - return Error("Expected a comma"); - } - else - { - NewNode(name, TreeNode::OBJECT); - mState = STATE_OBJECT; - AdvanceSkipWhiteSpace(1); - } - } - else if( '}' == currentChar ) - { - if(',' == lastCharacter) - { - return Error("Expected another value"); - } - - if(mCurrent.GetType() != TreeNode::OBJECT) - { - return Error("Mismatched array definition"); - } - - if(mCurrent.GetParent() == NULL) - { - mState = STATE_END; - } - else - { - if( !UpToParent() ) - { - return false; - } - } - AdvanceSkipWhiteSpace(1); - } - else if( '[' == currentChar ) - { - NewNode(name, TreeNode::ARRAY); - mState = STATE_VALUE; - AdvanceSkipWhiteSpace(1); - } - else if( ']' == currentChar ) - { - if(',' == lastCharacter) - { - return Error("Expected a value"); - } - - if(mCurrent.GetType() != TreeNode::ARRAY) - { - return Error("Mismatched braces in object definition"); - } - - if(mCurrent.GetParent() == NULL) - { - mState = STATE_END; - } - else - { - if( !UpToParent() ) - { - return false; - } - } - AdvanceSkipWhiteSpace(1); - } - else if( 't' == currentChar ) - { - NewNode(name, TreeNode::BOOLEAN); - if( !ParseTrue() ) - { - return false; - } - if( !UpToParent() ) - { - return false; - } - AdvanceSkipWhiteSpace(0); - } - else if( 'n' == currentChar ) - { - NewNode(name, TreeNode::IS_NULL); - if( !ParseNULL() ) - { - return false; - } - if( !UpToParent() ) - { - return false; - } - AdvanceSkipWhiteSpace(0); - } - else if( 'f' == currentChar) - { - NewNode(name, TreeNode::BOOLEAN); - if( !ParseFalse() ) - { - return false; - } - if( !UpToParent() ) - { - return false; - } - AdvanceSkipWhiteSpace(0); - } - else if( ',' == currentChar ) - { - if( 0 == mCurrent.Size() ) - { - return Error("Missing Value"); - } - - if(mCurrent.GetType() == TreeNode::OBJECT) - { - mState = STATE_OBJECT; // to get '"' in '"key":val' - } - else if(mCurrent.GetType() == TreeNode::ARRAY) - { - mState = STATE_VALUE; // array so just get next value - } - else - { - return Error("Unexpected character"); - } - AdvanceSkipWhiteSpace(1); - } - else - { - return Error("Unexpected character"); + return false; } - - name = NULL; - break; } // case STATE_VALUE case STATE_END: @@ -973,17 +929,160 @@ bool JsonParserState::ParseJson(VectorChar& source) } // ParseJson - void JsonParserState::Reset() { mCurrent = TreeNodeManipulator(mRoot); - mErrorDescription = NULL; + mErrorDescription = nullptr; mErrorNewLine = 0; mErrorColumn = 0; mErrorPosition = 0; } +bool JsonParserState::HandleCharacterBracesStart(const char* name, const char lastCharacter) +{ + if( '}' == lastCharacter ) + { + return Error("Expected a comma"); + } + else + { + NewNode(name, TreeNode::OBJECT); + mState = STATE_OBJECT; + AdvanceSkipWhiteSpace(1); + } + return true; +} + +bool JsonParserState::HandleCharacterBracesEnd(const char lastCharacter) +{ + if(',' == lastCharacter) + { + return Error("Expected another value"); + } + + if(mCurrent.GetType() != TreeNode::OBJECT) + { + return Error("Mismatched array definition"); + } + + if(mCurrent.GetParent() == nullptr) + { + mState = STATE_END; + } + else + { + if( !UpToParent() ) + { + return false; + } + } + AdvanceSkipWhiteSpace(1); + return true; +} + +bool JsonParserState::HandleCharacterSquareBracketStart(const char* name) +{ + NewNode(name, TreeNode::ARRAY); + mState = STATE_VALUE; + AdvanceSkipWhiteSpace(1); + return true; +} + +bool JsonParserState::HandleCharacterSquareBracketEnd(const char lastCharacter) +{ + if(',' == lastCharacter) + { + return Error("Expected a value"); + } + + if(mCurrent.GetType() != TreeNode::ARRAY) + { + return Error("Mismatched braces in object definition"); + } + + if(mCurrent.GetParent() == nullptr) + { + mState = STATE_END; + } + else + { + if( !UpToParent() ) + { + return false; + } + } + AdvanceSkipWhiteSpace(1); + return true; +} + +bool JsonParserState::HandleCharacterLowercaseT(const char* name) +{ + NewNode(name, TreeNode::BOOLEAN); + if( !ParseTrue() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + return true; +} + +bool JsonParserState::HandleCharacterLowercaseN(const char* name) +{ + NewNode(name, TreeNode::IS_NULL); + if( !ParseNULL() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + return true; +} + +bool JsonParserState::HandleCharacterLowercaseF(const char* name) +{ + NewNode(name, TreeNode::BOOLEAN); + if( !ParseFalse() ) + { + return false; + } + if( !UpToParent() ) + { + return false; + } + AdvanceSkipWhiteSpace(0); + return true; +} + +bool JsonParserState::HandleCharacterComma(const char* name) +{ + if( 0 == mCurrent.Size() ) + { + return Error("Missing Value"); + } + + if(mCurrent.GetType() == TreeNode::OBJECT) + { + mState = STATE_OBJECT; // to get '"' in '"key":val' + } + else if(mCurrent.GetType() == TreeNode::ARRAY) + { + mState = STATE_VALUE; // array so just get next value + } + else + { + return Error("Unexpected character"); + } + AdvanceSkipWhiteSpace(1); + return true; +} } // namespace Internal diff --git a/dali-toolkit/internal/builder/json-parser-state.h b/dali-toolkit/internal/builder/json-parser-state.h index 210c11f..4d7e7a4 100644 --- a/dali-toolkit/internal/builder/json-parser-state.h +++ b/dali-toolkit/internal/builder/json-parser-state.h @@ -2,7 +2,7 @@ #define DALI_JSON_PARSE_STATE_H /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ namespace Toolkit namespace Internal { -/* +/** * A safer std::advance() */ template @@ -52,7 +52,7 @@ inline int AdvanceIter(IteratorType& iter, EndIteratorType& end, int n) return n; } -/* +/** * Maintains parser state machine * * If a NULL root node is passed in the constructor then a faster non merging parse is performed (the first pass). @@ -61,13 +61,13 @@ inline int AdvanceIter(IteratorType& iter, EndIteratorType& end, int n) class JsonParserState { public: - /* + /** * Constructor * @param tree Tree to start with, pass NULL if no existing tree */ explicit JsonParserState(TreeNode* tree); - /* + /** * Parse json source * The source is modified in place * @param source The vector buffer to parse @@ -75,43 +75,43 @@ public: */ bool ParseJson(VectorChar& source); - /* + /** * Get the root node * @return The root TreeNode */ TreeNode* GetRoot(); - /* + /** * Get the error description of the last parse * @return The error description or NULL if no error */ const char* GetErrorDescription() { return mErrorDescription; } - /* + /** * Get the error line number * @return The line number of the error */ int GetErrorLineNumber() { return mErrorNewLine; } - /* + /** * Get the error column * @return The error column */ int GetErrorColumn() { return mErrorColumn; } - /* + /** * Get the error position * @return The error position */ int GetErrorPosition() { return mErrorPosition; } - /* + /** * Get the size of the string data that has been parsed * @return The size of string data */ int GetParsedStringSize() { return mNumberOfParsedChars; }; - /* + /** * Get the number of nodes created * @return The number of nodes */ @@ -131,7 +131,7 @@ private: int mNumberOfCreatedNodes; ///< The number of nodes created bool mFirstParse; ///< Flag if first parse - /* + /** * The current parse state */ enum State @@ -149,49 +149,49 @@ private: JsonParserState(const JsonParserState&); const JsonParserState& operator=(const JsonParserState&); - /* + /** * Parse over white space * Increments the current position * @return true if no parse errors */ bool ParseWhiteSpace(); - /* + /** * Parse over a number, setting the current node if found * Increments the current position. Sets error data if parse error. * @return true if found, false if parse error */ bool ParseNumber(); - /* + /** * Parse over a symbol * Increments the current position. Sets error data if parse error. * @return true if found, false if parse error */ bool ParseSymbol(const std::string& symbol); - /* + /** * Parse over 'true' symbol, setting the current node if found * Increments the current position. Sets error data if parse error. * @return true if found, false if parse error */ bool ParseTrue(); - /* + /** * Parse over 'false' symbol, setting the current node if found * Increments the current position. Sets error data if parse error. * @return true if found, false if parse error */ bool ParseFalse(); - /* + /** * Parse over 'null' symbol, setting the current node if found * Increments the current position. Sets error data if parse error. * @return true if found, false if parse error */ bool ParseNULL(); - /* + /** * Parse over a string from the current position and insert escaped * control characters in place in the string and a null terminator. * This function works from and modifes the current buffer position. @@ -199,29 +199,29 @@ private: */ char* EncodeString(); - /* + /** * Create a new node with name and type */ TreeNode* CreateNewNode(const char* name, TreeNode::NodeType type); - /* + /** * Create a new node if first parse, else check if the node already * exists and set it to a new type */ TreeNode* NewNode(const char* name, TreeNode::NodeType type); - /* + /** * Set error meta data * @returns always false. */ bool Error(const char* description); - /* + /** * Reset state for another parse */ void Reset(); - /* + /** * Set current to its parent * @return true if we had a parent, false and error otherwise */ @@ -235,7 +235,7 @@ private: return true; } - /* + /** * Get the current character */ inline char Char() @@ -243,7 +243,7 @@ private: return *mIter; } - /* + /** * @return True if there are at least n character left */ inline bool AtLeast(int n) @@ -254,7 +254,7 @@ private: return (mEnd - mIter) > n; } - /* + /** * @return True if at the end of the data to parse */ inline bool AtEnd() @@ -262,7 +262,7 @@ private: return mEnd == mIter; } - /* + /** * Advance current position by n characters or stop at mEnd */ inline void Advance(int n) @@ -272,7 +272,7 @@ private: mErrorColumn += c; } - /* + /** * Advance by n charaters and return true if we reached the end */ inline bool AdvanceEnded(int n) @@ -283,7 +283,7 @@ private: return mEnd == mIter; } - /* + /** * Advance by at least n characters (stopping at mEnd) and skip any whitespace after n. */ inline void AdvanceSkipWhiteSpace(int n) @@ -294,7 +294,7 @@ private: static_cast( ParseWhiteSpace() ); } - /* + /** * Increment new line counters */ inline void NewLine() @@ -303,6 +303,122 @@ private: mErrorColumn = 0; } + /** + * @brief Called by ParseJson if we are in STATE_START. + * + * @param[in] name The current name + * @param[in] currentChar The current character being parsed + * @return true if successfully parsed + */ + bool HandleStartState(const char* name, const char currentChar); + + /** + * @brief Called by ParseJson if we are in STATE_OBJECT. + * + * @param[in] currentChar The current character being parsed + * @param[in] lastCharacter The last character we parsed + * @return true if successfully parsed + */ + bool HandleObjectState(const char currentChar, const char lastCharacter); + + /** + * @brief Called by ParseJson if we are in STATE_KEY. + * + * @param[in/out] name A reference to the name variable + * @return true if successfully parsed + */ + bool HandleKeyState(char*& name); + + /** + * @brief Called by ParseJson if we are in STATE_VALUE. + * + * @param[in/out] name A reference to the name variable + * @param[in] currentChar The current character being parsed + * @param[in] lastCharacter The last character we parsed + * @return true if successfully parsed + */ + bool HandleValueState(char*& name, const char currentChar, const char lastCharacter); + + /** + * @brief Called by HandleValueState to parse a '"' character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterQuote(char*& name); + + /** + * @brief Called by HandleValueState to parse a number or hyphen character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterNumberOrHyphen(const char* name); + + /** + * @brief Called by HandleValueState to parse a '{' character. + * + * @param[in] name The current name + * @param[in] lastCharacter The last character we parsed + * @return true if successfully parsed + */ + bool HandleCharacterBracesStart(const char* name, const char lastCharacter); + + /** + * @brief Called by HandleValueState to parse a '}' character. + * + * @param[in] lastCharacter The last character we parsed + * @return true if successfully parsed + */ + bool HandleCharacterBracesEnd(const char lastCharacter); + + /** + * @brief Called by HandleValueState to parse a '[' character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterSquareBracketStart(const char* name); + + /** + * @brief Called by HandleValueState to parse a ']' character. + * + * @param[in] lastCharacter The last character we parsed + * @return true if successfully parsed + */ + bool HandleCharacterSquareBracketEnd(const char lastCharacter); + + /** + * @brief Called by HandleValueState to parse a 't' character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterLowercaseT(const char* name); + + /** + * @brief Called by HandleValueState to parse a 'n' character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterLowercaseN(const char* name); + + /** + * @brief Called by HandleValueState to parse a 'f' character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterLowercaseF(const char* name); + + /** + * @brief Called by HandleValueState to parse a ',' character. + * + * @param[in] name The current name + * @return true if successfully parsed + */ + bool HandleCharacterComma(const char* name); }; -- 2.7.4