/** * Digital Voice Modem - Host Software * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * @package DVM / Host Software * */ // // Based on code from the mini-yaml project. (https://github.com/jimmiebergmann/mini-yaml) // Licensed under the MIT License (https://opensource.org/licenses/MIT) // /* * Copyright(c) 2018 Jimmie Bergmann * Copyright (C) 2020 Bryan Biedenkapp N2PLL * * MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files(the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions : * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include "yaml/Yaml.h" #include #include #include #include #include #include #include // Implementation access definitions. #define NODE_IMP static_cast(m_pImp) #define NODE_IMP_EXT(node) static_cast(node.m_pImp) #define TYPE_IMP static_cast(m_pImp)->m_pImp #define IT_IMP static_cast(m_pImp) namespace yaml { class ReaderLine; // Exception message definitions. static const std::string g_ErrorInvalidCharacter = "Invalid character found."; static const std::string g_ErrorKeyMissing = "Missing key."; static const std::string g_ErrorKeyIncorrect = "Incorrect key."; static const std::string g_ErrorValueIncorrect = "Incorrect value."; static const std::string g_ErrorTabInOffset = "Tab found in offset."; static const std::string g_ErrorBlockSequenceNotAllowed = "Sequence entries are not allowed in this context."; static const std::string g_ErrorUnexpectedDocumentEnd = "Unexpected document end."; static const std::string g_ErrorDiffEntryNotAllowed = "Different entry is not allowed in this context."; static const std::string g_ErrorIncorrectOffset = "Incorrect offset."; static const std::string g_ErrorSequenceError = "Error in sequence node."; static const std::string g_ErrorCannotOpenFile = "Cannot open file."; static const std::string g_ErrorIndentation = "Space indentation is less than 2."; static const std::string g_ErrorInvalidBlockScalar = "Invalid block scalar."; static const std::string g_ErrorInvalidQuote = "Invalid quote."; static const std::string g_EmptyString = ""; static yaml::Node g_NoneNode; // Global function definitions. Implemented at end of this source file. static std::string ExceptionMessage(const std::string& message, ReaderLine& line); static std::string ExceptionMessage(const std::string& message, ReaderLine& line, const size_t errorPos); static std::string ExceptionMessage(const std::string& message, const size_t errorLine, const size_t errorPos); static std::string ExceptionMessage(const std::string& message, const size_t errorLine, const std::string& data); static bool FindQuote(const std::string& input, size_t& start, size_t& end, size_t searchPos = 0); static size_t FindNotCited(const std::string& input, char token, size_t& preQuoteCount); static size_t FindNotCited(const std::string& input, char token); static bool ValidateQuote(const std::string& input); static void CopyNode(const Node& from, Node& to); static bool ShouldBeCited(const std::string& key); static void AddEscapeTokens(std::string& input, const std::string& tokens); static void RemoveAllEscapeTokens(std::string& input); // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the Exception class. /// /// /// Exception::Exception(const std::string& message, const eType type) : std::runtime_error(message), m_Type(type) { /* stub */ } /// /// Get type of exception. /// /// Exception::eType Exception::type() const { return m_Type; } /// /// Get message of exception. /// /// const char* Exception::message() const { return what(); } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the InternalException class. /// /// InternalException::InternalException(const std::string& message) : Exception(message, InternalError) { /* stub */ } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the ParsingException class. /// /// ParsingException::ParsingException(const std::string& message) : Exception(message, ParsingError) { /* stub */ } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the OperationException class. /// /// OperationException::OperationException(const std::string & message) : Exception(message, OperationError) { /* stub */ } // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class TypeImp { public: /// /// Finalizes a new instance of the TypeImp class. /// virtual ~TypeImp() { } /// /// virtual const std::string& getData() const = 0; /// /// /// virtual bool setData(const std::string& data) = 0; /// /// virtual size_t size() const = 0; /// /// /// virtual Node* getNode(const size_t index) = 0; /// /// /// virtual Node* getNode(const std::string& key) = 0; /// /// /// virtual Node* insert(const size_t index) = 0; /// /// virtual Node* push_front() = 0; /// /// virtual Node* push_back() = 0; /// /// /// virtual void erase(const size_t index) = 0; /// /// /// virtual void erase(const std::string& key) = 0; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class SequenceImp : public TypeImp { public: /// /// Finalizes a new instance of the SequenceImp class. /// ~SequenceImp() { for (auto it = m_Sequence.begin(); it != m_Sequence.end(); it++) { delete it->second; } } /// /// virtual const std::string & getData() const { return g_EmptyString; } /// /// /// virtual bool setData(const std::string & data) { return false; } /// /// virtual size_t size() const { return m_Sequence.size(); } /// /// /// virtual Node* getNode(const size_t index) { auto it = m_Sequence.find(index); if (it != m_Sequence.end()) { return it->second; } return nullptr; } /// /// /// virtual Node* getNode(const std::string& key) { return nullptr; } /// /// /// virtual Node* insert(const size_t index) { if (m_Sequence.size() == 0) { Node* pNode = new Node; m_Sequence.insert({ 0, pNode }); return pNode; } if (index >= m_Sequence.size()) { auto it = m_Sequence.end(); --it; Node* pNode = new Node; m_Sequence.insert({ it->first, pNode }); return pNode; } auto it = m_Sequence.cbegin(); while (it != m_Sequence.cend()) { m_Sequence[it->first + 1] = it->second; if (it->first == index) { break; } } Node* pNode = new Node; m_Sequence.insert({ index, pNode }); return pNode; } /// /// virtual Node* push_front() { for (auto it = m_Sequence.cbegin(); it != m_Sequence.cend(); it++) { m_Sequence[it->first + 1] = it->second; } Node* pNode = new Node; m_Sequence.insert({ 0, pNode }); return pNode; } /// /// virtual Node* push_back() { size_t index = 0; if (m_Sequence.size()) { auto it = m_Sequence.end(); --it; index = it->first + 1; } Node* pNode = new Node; m_Sequence.insert({ index, pNode }); return pNode; } /// /// /// virtual void erase(const size_t index) { auto it = m_Sequence.find(index); if (it == m_Sequence.end()) { return; } delete it->second; m_Sequence.erase(index); } /// /// /// virtual void erase(const std::string& key) { /* stub */ } std::map m_Sequence; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class MapImp : public TypeImp { public: /// /// Finalizes a new instance of the SequenceImp class. /// ~MapImp() { for (auto it = m_Map.begin(); it != m_Map.end(); it++) { delete it->second; } } /// /// virtual const std::string& getData() const { return g_EmptyString; } /// /// /// virtual bool setData(const std::string& data) { return false; } /// /// virtual size_t size() const { return m_Map.size(); } /// /// /// virtual Node* getNode(const size_t index) { return nullptr; } /// /// /// virtual Node* getNode(const std::string& key) { auto it = m_Map.find(key); if (it == m_Map.end()) { Node* pNode = new Node; m_Map.insert({ key, pNode }); return pNode; } return it->second; } /// /// /// virtual Node* insert(const size_t index) { return nullptr; } /// /// virtual Node* push_front() { return nullptr; } /// /// virtual Node* push_back() { return nullptr; } /// /// /// virtual void erase(const size_t index) { /* stub */ } /// /// /// virtual void erase(const std::string& key) { auto it = m_Map.find(key); if (it == m_Map.end()) { return; } delete it->second; m_Map.erase(key); } std::map m_Map; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class ScalarImp : public TypeImp { public: /// /// Finalizes a new instance of the ScalarImp class. /// ~ScalarImp() { /* stub */ } /// /// virtual const std::string& getData() const { return m_Value; } /// /// /// virtual bool setData(const std::string& data) { m_Value = data; return true; } /// /// virtual size_t size() const { return 0; } /// /// /// virtual Node* getNode(const size_t index) { return nullptr; } /// /// /// virtual Node* getNode(const std::string& key) { return nullptr; } /// /// /// virtual Node* insert(const size_t index) { return nullptr; } /// /// virtual Node* push_front() { return nullptr; } /// /// virtual Node* push_back() { return nullptr; } /// /// /// virtual void erase(const size_t index) { /* stub */ } /// /// /// virtual void erase(const std::string& key) { /* stub */ } std::string m_Value; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class NodeImp { public: /// /// Initializes a new instance of the NodeImp class. /// NodeImp() : m_Type(Node::None), m_pImp(nullptr) { /* stub */ } /// /// Finalizes a new instance of the NodeImp class. /// ~NodeImp() { clear(); } /// Completely clear node. void clear() { if (m_pImp != nullptr) { delete m_pImp; m_pImp = nullptr; } m_Type = Node::None; } /// void initSequence() { if (m_Type != Node::SequenceType || m_pImp == nullptr) { if (m_pImp) { delete m_pImp; } m_pImp = new SequenceImp; m_Type = Node::SequenceType; } } /// void initMap() { if (m_Type != Node::MapType || m_pImp == nullptr) { if (m_pImp) { delete m_pImp; } m_pImp = new MapImp; m_Type = Node::MapType; } } /// void initScalar() { if (m_Type != Node::ScalarType || m_pImp == nullptr) { if (m_pImp) { delete m_pImp; } m_pImp = new ScalarImp; m_Type = Node::ScalarType; } } Node::eType m_Type; TypeImp* m_pImp; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class IteratorImp { public: /// /// Finalizes a new instance of the IteratorImp class. /// virtual ~IteratorImp() { /* stub */ } /// /// virtual Node::eType type() const = 0; /// /// virtual void initBegin(SequenceImp* pSequenceImp) = 0; /// /// virtual void initEnd(SequenceImp* pSequenceImp) = 0; /// /// virtual void initBegin(MapImp* pMapImp) = 0; /// /// virtual void initEnd(MapImp* pMapImp) = 0; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class SequenceIteratorImp : public IteratorImp { public: /// /// virtual Node::eType type() const { return Node::SequenceType; } /// /// virtual void initBegin(SequenceImp* pSequenceImp) { m_Iterator = pSequenceImp->m_Sequence.begin(); } /// /// virtual void initEnd(SequenceImp* pSequenceImp) { m_Iterator = pSequenceImp->m_Sequence.end(); } /// /// virtual void initBegin(MapImp* pMapImp) { /* stub */ } /// /// virtual void initEnd(MapImp* pMapImp) { /* stub */ } /// /// void copy(const SequenceIteratorImp& it) { m_Iterator = it.m_Iterator; } std::map::iterator m_Iterator; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class MapIteratorImp : public IteratorImp { public: /// /// virtual Node::eType type() const { return Node::MapType; } /// /// virtual void initBegin(SequenceImp* pSequenceImp) { /* stub */ } /// /// virtual void initEnd(SequenceImp* pSequenceImp) { /* stub */ } /// /// virtual void initBegin(MapImp* pMapImp) { m_Iterator = pMapImp->m_Map.begin(); } /// /// virtual void initEnd(MapImp* pMapImp) { m_Iterator = pMapImp->m_Map.end(); } /// /// void copy(const MapIteratorImp& it) { m_Iterator = it.m_Iterator; } std::map::iterator m_Iterator; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class SequenceConstIteratorImp : public IteratorImp { public: /// /// virtual Node::eType type() const { return Node::SequenceType; } /// /// virtual void initBegin(SequenceImp* pSequenceImp) { m_Iterator = pSequenceImp->m_Sequence.begin(); } /// /// virtual void initEnd(SequenceImp* pSequenceImp) { m_Iterator = pSequenceImp->m_Sequence.end(); } /// /// virtual void initBegin(MapImp* pMapImp) { /* stub */ } /// /// virtual void initEnd(MapImp* pMapImp) { /* stub */ } /// /// void copy(const SequenceConstIteratorImp & it) { m_Iterator = it.m_Iterator; } std::map::const_iterator m_Iterator; }; // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class MapConstIteratorImp : public IteratorImp { public: /// /// virtual Node::eType type() const { return Node::MapType; } /// /// virtual void initBegin(SequenceImp* pSequenceImp) { /* stub */ } /// /// virtual void initEnd(SequenceImp* pSequenceImp) { /* stub */ } /// /// virtual void initBegin(MapImp* pMapImp) { m_Iterator = pMapImp->m_Map.begin(); } /// /// virtual void initEnd(MapImp* pMapImp) { m_Iterator = pMapImp->m_Map.end(); } /// /// void copy(const MapConstIteratorImp & it) { m_Iterator = it.m_Iterator; } std::map::const_iterator m_Iterator; }; // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the Iterator class. /// Iterator::Iterator() : m_Type(None), m_pImp(nullptr) { /* stub */ } /// /// Copies an instance of the Iterator class to a new instance of the Iterator class. /// /// Iterator::Iterator(const Iterator& it) : m_Type(None), m_pImp(nullptr) { *this = it; } /// /// Finalizes a instance of the Iterator class. /// Iterator::~Iterator() { if (m_pImp) { switch (m_Type) { case SequenceType: delete static_cast(m_pImp); break; case MapType: delete static_cast(m_pImp); break; default: break; } } } /// Assignment operator. Iterator& Iterator::operator = (const Iterator& it) { if (m_pImp) { switch (m_Type) { case SequenceType: delete static_cast(m_pImp); break; case MapType: delete static_cast(m_pImp); break; default: break; } m_pImp = nullptr; m_Type = None; } IteratorImp* pNewImp = nullptr; switch (it.m_Type) { case SequenceType: m_Type = SequenceType; pNewImp = new SequenceIteratorImp; static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; break; case MapType: m_Type = MapType; pNewImp = new MapIteratorImp; static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; break; default: break; } m_pImp = pNewImp; return *this; } /// Get node of iterator. First pair item is the key of map value, empty if type is sequence. std::pair Iterator::operator *() { switch (m_Type) { case SequenceType: return { g_EmptyString, *(static_cast(m_pImp)->m_Iterator->second) }; break; case MapType: return { static_cast(m_pImp)->m_Iterator->first, *(static_cast(m_pImp)->m_Iterator->second) }; break; default: break; } g_NoneNode.clear(); return { g_EmptyString, g_NoneNode }; } /// Post-increment operator. Iterator& Iterator::operator ++ (int dummy) { switch (m_Type) { case SequenceType: static_cast(m_pImp)->m_Iterator++; break; case MapType: static_cast(m_pImp)->m_Iterator++; break; default: break; } return *this; } /// Post-decrement operator. Iterator& Iterator::operator -- (int dummy) { switch(m_Type) { case SequenceType: static_cast(m_pImp)->m_Iterator--; break; case MapType: static_cast(m_pImp)->m_Iterator--; break; default: break; } return *this; } /// Check if iterator is equal to other iterator. bool Iterator::operator == (const Iterator& it) { if (m_Type != it.m_Type) { return false; } switch (m_Type) { case SequenceType: return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; break; case MapType: return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; break; default: break; } return false; } /// Check if iterator is not equal to other iterator. bool Iterator::operator != (const Iterator& it) { return !(*this == it); } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the ConstIterator class. /// ConstIterator::ConstIterator() : m_Type(None), m_pImp(nullptr) { /* stub */ } /// /// Copies an instance of the ConstIterator class to a new instance of the ConstIterator class. /// /// ConstIterator::ConstIterator(const ConstIterator& it) : m_Type(None), m_pImp(nullptr) { *this = it; } /// /// Finalizes a instance of the ConstIterator class. /// ConstIterator::~ConstIterator() { if (m_pImp) { switch (m_Type) { case SequenceType: delete static_cast(m_pImp); break; case MapType: delete static_cast(m_pImp); break; default: break; } } } /// Assignment operator. ConstIterator& ConstIterator::operator = (const ConstIterator& it) { if (m_pImp) { switch (m_Type) { case SequenceType: delete static_cast(m_pImp); break; case MapType: delete static_cast(m_pImp); break; default: break; } m_pImp = nullptr; m_Type = None; } IteratorImp* pNewImp = nullptr; switch (it.m_Type) { case SequenceType: m_Type = SequenceType; pNewImp = new SequenceConstIteratorImp; static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; break; case MapType: m_Type = MapType; pNewImp = new MapConstIteratorImp; static_cast(pNewImp)->m_Iterator = static_cast(it.m_pImp)->m_Iterator; break; default: break; } m_pImp = pNewImp; return *this; } /// Get node of iterator. First pair item is the key of map value, empty if type is sequence. std::pair ConstIterator::operator *() { switch (m_Type) { case SequenceType: return { g_EmptyString, *(static_cast(m_pImp)->m_Iterator->second) }; break; case MapType: return { static_cast(m_pImp)->m_Iterator->first, *(static_cast(m_pImp)->m_Iterator->second) }; break; default: break; } g_NoneNode.clear(); return { g_EmptyString, g_NoneNode }; } /// Post-increment operator. ConstIterator& ConstIterator::operator ++ (int dummy) { switch (m_Type) { case SequenceType: static_cast(m_pImp)->m_Iterator++; break; case MapType: static_cast(m_pImp)->m_Iterator++; break; default: break; } return *this; } /// Post-decrement operator. ConstIterator& ConstIterator::operator -- (int dummy) { switch (m_Type) { case SequenceType: static_cast(m_pImp)->m_Iterator--; break; case MapType: static_cast(m_pImp)->m_Iterator--; break; default: break; } return *this; } /// Check if iterator is equal to other iterator. bool ConstIterator::operator == (const ConstIterator& it) { if (m_Type != it.m_Type) { return false; } switch (m_Type) { case SequenceType: return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; break; case MapType: return static_cast(m_pImp)->m_Iterator == static_cast(it.m_pImp)->m_Iterator; break; default: break; } return false; } /// Check if iterator is not equal to other iterator. bool ConstIterator::operator != (const ConstIterator & it) { return !(*this == it); } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the Node class. /// Node::Node() : m_pImp(new NodeImp) { /* stub */ } /// /// Copies an instance of the Node class to a new instance of the Node class. /// /// Node::Node(const Node& node) : Node() { *this = node; } /// /// Initializes a new instance of the Node class. /// /// Node::Node(const std::string& value) : Node() { *this = value; } /// /// Initializes a new instance of the Node class. /// Node::Node(const char* value) : Node() { *this = value; } /// /// Finalizes a instance of the Node class. /// Node::~Node() { delete static_cast(m_pImp); } /// Gets the type of node. /// Node::eType Node::type() const { return NODE_IMP->m_Type; } /// Checks if the node contains nothing. /// bool Node::isNone() const { return NODE_IMP->m_Type == Node::None; } /// Checks if the node is a sequence node. /// bool Node::isSequence() const { return NODE_IMP->m_Type == Node::SequenceType; } /// Checks if the node is a map node. /// bool Node::isMap() const { return NODE_IMP->m_Type == Node::MapType; } /// Checks if the node is a scalar node. /// bool Node::isScalar() const { return NODE_IMP->m_Type == Node::ScalarType; } /// Completely clear node. void Node::clear() { NODE_IMP->clear(); } /// Get size of node. Nodes of type None or Scalar will return 0. /// size_t Node::size() const { if (TYPE_IMP == nullptr) { return 0; } return TYPE_IMP->size(); } /// /// Insert sequence item at given index. Converts node to sequence type if needed. /// Adding new item to end of sequence if index is larger than sequence size. /// /// /// Node& Node::insert(const size_t index) { NODE_IMP->initSequence(); return *TYPE_IMP->insert(index); } /// Add new sequence index to back. Converts node to sequence type if needed. /// Node& Node::push_front() { NODE_IMP->initSequence(); return *TYPE_IMP->push_front(); } /// Add new sequence index to front. Converts node to sequence type if needed. /// Node& Node::push_back() { NODE_IMP->initSequence(); return *TYPE_IMP->push_back(); } /// Get sequence/map item. Converts node to sequence/map type if needed. /// /// Node& Node::operator[](const size_t index) { NODE_IMP->initSequence(); Node* pNode = TYPE_IMP->getNode(index); if (pNode == nullptr) { g_NoneNode.clear(); return g_NoneNode; } return *pNode; } /// Get sequence/map item. Converts node to sequence/map type if needed. /// /// Node& Node::operator[](const std::string& key) { NODE_IMP->initMap(); return *TYPE_IMP->getNode(key); } /// Erase item. No action if node is not a sequence or map. /// void Node::erase(const size_t index) { if (TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::SequenceType) { return; } return TYPE_IMP->erase(index); } /// Erase item. No action if node is not a sequence or map. /// void Node::erase(const std::string& key) { if (TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::MapType) { return; } return TYPE_IMP->erase(key); } /// Assignment operator. Node& Node::operator = (const Node& node) { NODE_IMP->clear(); CopyNode(node, *this); return *this; } /// Assignment operator. Node& Node::operator = (const std::string& value) { NODE_IMP->initScalar(); TYPE_IMP->setData(value); return *this; } /// Assignment operator. Node& Node::operator = (const char* value) { NODE_IMP->initScalar(); TYPE_IMP->setData(value ? std::string(value) : ""); return *this; } /// Get start iterator. /// Iterator Node::begin() { Iterator it; if (TYPE_IMP != nullptr) { IteratorImp* pItImp = nullptr; switch (NODE_IMP->m_Type) { case Node::SequenceType: it.m_Type = Iterator::SequenceType; pItImp = new SequenceIteratorImp; pItImp->initBegin(static_cast(TYPE_IMP)); break; case Node::MapType: it.m_Type = Iterator::MapType; pItImp = new MapIteratorImp; pItImp->initBegin(static_cast(TYPE_IMP)); break; default: break; } it.m_pImp = pItImp; } return it; } /// Get start constant iterator. /// ConstIterator Node::begin() const { ConstIterator it; if (TYPE_IMP != nullptr) { IteratorImp* pItImp = nullptr; switch (NODE_IMP->m_Type) { case Node::SequenceType: it.m_Type = ConstIterator::SequenceType; pItImp = new SequenceConstIteratorImp; pItImp->initBegin(static_cast(TYPE_IMP)); break; case Node::MapType: it.m_Type = ConstIterator::MapType; pItImp = new MapConstIteratorImp; pItImp->initBegin(static_cast(TYPE_IMP)); break; default: break; } it.m_pImp = pItImp; } return it; } /// Get end iterator. /// Iterator Node::end() { Iterator it; if (TYPE_IMP != nullptr) { IteratorImp* pItImp = nullptr; switch (NODE_IMP->m_Type) { case Node::SequenceType: it.m_Type = Iterator::SequenceType; pItImp = new SequenceIteratorImp; pItImp->initEnd(static_cast(TYPE_IMP)); break; case Node::MapType: it.m_Type = Iterator::MapType; pItImp = new MapIteratorImp; pItImp->initEnd(static_cast(TYPE_IMP)); break; default: break; } it.m_pImp = pItImp; } return it; } /// Get end constant iterator. /// ConstIterator Node::end() const { ConstIterator it; if (TYPE_IMP != nullptr) { IteratorImp* pItImp = nullptr; switch (NODE_IMP->m_Type) { case Node::SequenceType: it.m_Type = ConstIterator::SequenceType; pItImp = new SequenceConstIteratorImp; pItImp->initEnd(static_cast(TYPE_IMP)); break; case Node::MapType: it.m_Type = ConstIterator::MapType; pItImp = new MapConstIteratorImp; pItImp->initEnd(static_cast(TYPE_IMP)); break; default: break; } it.m_pImp = pItImp; } return it; } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- /// /// const std::string& Node::asString() const { if (TYPE_IMP == nullptr) { return g_EmptyString; } return TYPE_IMP->getData(); } // Reader implementations // --------------------------------------------------------------------------- // Class Declaration // // --------------------------------------------------------------------------- class ReaderLine { public: /// /// Initializes a new instance of the ReaderLine class. /// /// /// /// /// /// ReaderLine(const std::string& data = "", const size_t no = 0, const size_t offset = 0, const Node::eType type = Node::None, const unsigned char flags = 0) : Data(data), No(no), Offset(offset), Type(type), Flags(flags), NextLine(nullptr) { /* stub */ } enum eFlag { LiteralScalarFlag, // Literal scalar type, defined as "|". FoldedScalarFlag, // Folded scalar type, defined as "<". ScalarNewlineFlag // Scalar ends with a newline. }; /// Set flag. /// void setFlag(const eFlag flag) { Flags |= FlagMask[static_cast(flag)]; } /// Set flags by mask value. /// void setFlags(const unsigned char flags) { Flags |= flags; } /// Unset flag. /// void unsetFlag(const eFlag flag) { Flags &= ~FlagMask[static_cast(flag)]; } /// Unset flags by mask value. /// void unsetFlags(const unsigned char flags) { Flags &= ~flags; } /// Get flag value. /// /// bool getFlag(const eFlag flag) const { return Flags & FlagMask[static_cast(flag)]; } /// Copy and replace scalar flags from another ReaderLine. /// void copyScalarFlags(ReaderLine * from) { if (from == nullptr) { return; } unsigned char newFlags = from->Flags & (FlagMask[0] | FlagMask[1] | FlagMask[2]); Flags |= newFlags; } static const unsigned char FlagMask[3]; std::string Data; // Data of line. size_t No; // Line number. size_t Offset; // Offset to first character in data. Node::eType Type; // Type of line. unsigned char Flags; // Flags of line. ReaderLine* NextLine; // Pointer to next line. }; const unsigned char ReaderLine::FlagMask[3] = { 0x01, 0x02, 0x04 }; // --------------------------------------------------------------------------- // Class Declaration // Implementation class of Yaml parsing. // Parsing incoming streamand outputs a root node. // --------------------------------------------------------------------------- class ParseImp { public: /// /// Initializes a new instance of the ParseImp class. /// ParseImp() { /* stub */ } /// /// Finalizes a new instance of the ParseImp class. /// ~ParseImp() { clearLines(); } /// Run full parsing procedure. /// /// void parse(Node& root, std::iostream& stream) { try { root.clear(); readLines(stream); postProcessLines(); parseRoot(root); } catch (Exception e) { root.clear(); throw; } } private: /// /// Copies a instance of the ParseImp class to new instance of the ParseImp class. /// /// ParseImp(const ParseImp& copy) { /* stub */ } /// Read all lines. /// void readLines(std::iostream& stream) { std::string line = ""; size_t lineNo = 0; bool documentStartFound = false; bool foundFirstNotEmpty = false; std::streampos streamPos = 0; // read all lines, as long as the stream is ok while (!stream.eof() && !stream.fail()) { // read line streamPos = stream.tellg(); std::getline(stream, line); lineNo++; // remove comment const size_t commentPos = FindNotCited(line, '#'); if (commentPos != std::string::npos) { line.resize(commentPos); } // start of document if (documentStartFound == false && line == "---") { // erase all lines before this line clearLines(); documentStartFound = true; continue; } // end of document if (line == "...") { break; } else if (line == "---") { stream.seekg(streamPos); break; } // remove trailing return if (line.size()) { if (line[line.size() - 1] == '\r') { line.resize(line.size() - 1); } } // validate characters for (size_t i = 0; i < line.size(); i++) { if (line[i] != '\t' && (line[i] < 32 || line[i] > 125)) { throw ParsingException(ExceptionMessage(g_ErrorInvalidCharacter, lineNo, i + 1)); } } // validate tabs const size_t firstTabPos = line.find_first_of('\t'); size_t startOffset = line.find_first_not_of(" \t"); // make sure no tabs are in the very front if (startOffset != std::string::npos) { if (firstTabPos < startOffset) { throw ParsingException(ExceptionMessage(g_ErrorTabInOffset, lineNo, firstTabPos)); } // remove front spaces line = line.substr(startOffset); } else { startOffset = 0; line = ""; } // add line if (foundFirstNotEmpty == false) { if (line.size()) { foundFirstNotEmpty = true; } else { continue; } } ReaderLine* pLine = new ReaderLine(line, lineNo, startOffset); m_Lines.push_back(pLine); } } /// Run post-processing on all lines. Basically split lines into multiple lines if needed, to follow the parsing algorithm. void postProcessLines() { for (auto it = m_Lines.begin(); it != m_Lines.end();) { // sequence if (postProcessSequenceLine(it) == true) { continue; } // mapping if (postProcessMappingLine(it) == true) { continue; } // scalar postProcessScalarLine(it); } // set next line of all lines if (m_Lines.size()) { if (m_Lines.back()->Type != Node::ScalarType) { throw ParsingException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *m_Lines.back())); } if (m_Lines.size() > 1) { auto prevEnd = m_Lines.end(); --prevEnd; for (auto it = m_Lines.begin(); it != prevEnd; it++) { auto nextIt = it; ++nextIt; (*it)->NextLine = *nextIt; } } } } /// Run post-processing and check for sequence. Split line into two lines if sequence token is not on it's own line. /// /// True if line is sequence, else false. bool postProcessSequenceLine(std::list::iterator & it) { ReaderLine* pLine = *it; // sequence split if (isSequenceStart(pLine->Data) == false) { return false; } pLine->Type = Node::SequenceType; clearTrailingEmptyLines(++it); const size_t valueStart = pLine->Data.find_first_not_of(" \t", 1); if (valueStart == std::string::npos) { return true; } // create new line and insert std::string newLine = pLine->Data.substr(valueStart); it = m_Lines.insert(it, new ReaderLine(newLine, pLine->No, pLine->Offset + valueStart)); pLine->Data = ""; return false; } /// Run post-processing and check for mapping. Split line into two lines if mapping value is not on it's own line. /// /// True if line is mapping, else move on to scalar parsing. bool postProcessMappingLine(std::list::iterator & it) { ReaderLine* pLine = *it; // find map key size_t preKeyQuotes = 0; size_t tokenPos = FindNotCited(pLine->Data, ':', preKeyQuotes); if (tokenPos == std::string::npos) { return false; } if (preKeyQuotes > 1) { throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine)); } pLine->Type = Node::MapType; // get key std::string key = pLine->Data.substr(0, tokenPos); const size_t keyEnd = key.find_last_not_of(" \t"); if (keyEnd == std::string::npos) { throw ParsingException(ExceptionMessage(g_ErrorKeyMissing, *pLine)); } key.resize(keyEnd + 1); // handle cited key if (preKeyQuotes == 1) { if (key.front() != '"' || key.back() != '"') { throw ParsingException(ExceptionMessage(g_ErrorKeyIncorrect, *pLine)); } key = key.substr(1, key.size() - 2); } RemoveAllEscapeTokens(key); // get value std::string value = ""; size_t valueStart = std::string::npos; if (tokenPos + 1 != pLine->Data.size()) { valueStart = pLine->Data.find_first_not_of(" \t", tokenPos + 1); if (valueStart != std::string::npos) { value = pLine->Data.substr(valueStart); } } // make sure the value is not a sequence start if (isSequenceStart(value) == true) { throw ParsingException(ExceptionMessage(g_ErrorBlockSequenceNotAllowed, *pLine, valueStart)); } pLine->Data = key; // remove all empty lines after map key clearTrailingEmptyLines(++it); // add new empty line? size_t newLineOffset = valueStart; if (newLineOffset == std::string::npos) { if (it != m_Lines.end() && (*it)->Offset > pLine->Offset) { return true; } newLineOffset = tokenPos + 2; } else { newLineOffset += pLine->Offset; } // add new line with value unsigned char dummyBlockFlags = 0; if (isBlockScalar(value, pLine->No, dummyBlockFlags) == true) { newLineOffset = pLine->Offset; } ReaderLine* pNewLine = new ReaderLine(value, pLine->No, newLineOffset, Node::ScalarType); it = m_Lines.insert(it, pNewLine); // return false in order to handle next line(scalar value) return false; } /// Run post-processing and check for scalar. Checking for multi-line scalars. /// void postProcessScalarLine(std::list::iterator & it) { ReaderLine* pLine = *it; pLine->Type = Node::ScalarType; size_t parentOffset = pLine->Offset; if (pLine != m_Lines.front()) { std::list::iterator lastIt = it; --lastIt; parentOffset = (*lastIt)->Offset; } std::list::iterator lastNotEmpty = it++; // find last empty lines while (it != m_Lines.end()) { pLine = *it; pLine->Type = Node::ScalarType; if (pLine->Data.size()) { if (pLine->Offset <= parentOffset) { break; } else { lastNotEmpty = it; } } ++it; } clearTrailingEmptyLines(++lastNotEmpty); } /// Process root node and start of document. /// void parseRoot(Node & root) { // get first line and start type auto it = m_Lines.begin(); if (it == m_Lines.end()) { return; } Node::eType type = (*it)->Type; ReaderLine* pLine = *it; // handle next line switch (type) { case Node::SequenceType: parseSequence(root, it); break; case Node::MapType: parseMap(root, it); break; case Node::ScalarType: parseScalar(root, it); break; default: break; } if (it != m_Lines.end()) { throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); } } /// Process sequence node. /// /// void parseSequence(Node & node, std::list::iterator & it) { ReaderLine* pNextLine = nullptr; while (it != m_Lines.end()) { ReaderLine* pLine = *it; Node& childNode = node.push_back(); // move to next line, error check ++it; if (it == m_Lines.end()) { throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); } // handle value of map Node::eType valueType = (*it)->Type; switch (valueType) { case Node::SequenceType: parseSequence(childNode, it); break; case Node::MapType: parseMap(childNode, it); break; case Node::ScalarType: parseScalar(childNode, it); break; default: break; } // check next line; if sequence and correct level, go on, else exit // if same level but but of type map = error if (it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset)) { break; } if (pNextLine->Offset > pLine->Offset) { throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine)); } if (pNextLine->Type != Node::SequenceType) { throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine)); } } } /// Process map node. /// /// void parseMap(Node & node, std::list::iterator & it) { ReaderLine* pNextLine = nullptr; while (it != m_Lines.end()) { ReaderLine* pLine = *it; Node& childNode = node[pLine->Data]; // move to next line, error check ++it; if (it == m_Lines.end()) { throw InternalException(ExceptionMessage(g_ErrorUnexpectedDocumentEnd, *pLine)); } // handle value of map Node::eType valueType = (*it)->Type; switch (valueType) { case Node::SequenceType: parseSequence(childNode, it); break; case Node::MapType: parseMap(childNode, it); break; case Node::ScalarType: parseScalar(childNode, it); break; default: break; } // check next line; if map and correct level, go on, else exit // if same level but but of type map = error if (it == m_Lines.end() || ((pNextLine = *it)->Offset < pLine->Offset)) { break; } if (pNextLine->Offset > pLine->Offset) { throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pNextLine)); } if (pNextLine->Type != pLine->Type) { throw InternalException(ExceptionMessage(g_ErrorDiffEntryNotAllowed, *pNextLine)); } } } /// Process scalar node. /// /// void parseScalar(Node & node, std::list::iterator & it) { std::string data = ""; ReaderLine* pFirstLine = *it; ReaderLine* pLine = *it; // check if current line is a block scalar unsigned char blockFlags = 0; bool blockScalar = isBlockScalar(pLine->Data, pLine->No, blockFlags); const bool newLineFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]); const bool foldedFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::FoldedScalarFlag)]); const bool literalFlag = static_cast(blockFlags & ReaderLine::FlagMask[static_cast(ReaderLine::LiteralScalarFlag)]); size_t parentOffset = 0; // find parent offset if (it != m_Lines.begin()) { std::list::iterator parentIt = it; --parentIt; parentOffset = (*parentIt)->Offset; } // move to next iterator/line if current line is a block scalar if (blockScalar) { ++it; if (it == m_Lines.end() || (pLine = *it)->Type != Node::ScalarType) { return; } } // not a block scalar, cut end spaces/tabs if (blockScalar == false) { while (true) { pLine = *it; if (parentOffset != 0 && pLine->Offset <= parentOffset) { throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); } const size_t endOffset = pLine->Data.find_last_not_of(" \t"); if (endOffset == std::string::npos) { data += "\n"; } else { data += pLine->Data.substr(0, endOffset + 1); } // Move to next line ++it; if (it == m_Lines.end() || (*it)->Type != Node::ScalarType) { break; } data += " "; } if (ValidateQuote(data) == false) { throw ParsingException(ExceptionMessage(g_ErrorInvalidQuote, *pFirstLine)); } } else { // block scalar pLine = *it; size_t blockOffset = pLine->Offset; if (blockOffset <= parentOffset) { throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); } bool addedSpace = false; while (it != m_Lines.end() && (*it)->Type == Node::ScalarType) { pLine = *it; const size_t endOffset = pLine->Data.find_last_not_of(" \t"); if (endOffset != std::string::npos && pLine->Offset < blockOffset) { throw ParsingException(ExceptionMessage(g_ErrorIncorrectOffset, *pLine)); } if (endOffset == std::string::npos) { if (addedSpace) { data[data.size() - 1] = '\n'; addedSpace = false; } else { data += "\n"; } ++it; continue; } else { if (blockOffset != pLine->Offset && foldedFlag) { if (addedSpace) { data[data.size() - 1] = '\n'; addedSpace = false; } else { data += "\n"; } } data += std::string(pLine->Offset - blockOffset, ' '); data += pLine->Data; } // move to next line ++it; if (it == m_Lines.end() || (*it)->Type != Node::ScalarType) { if (newLineFlag) { data += "\n"; } break; } if (foldedFlag) { data += " "; addedSpace = true; } else if (literalFlag && endOffset != std::string::npos) { data += "\n"; } } } if (data.size() && (data[0] == '"' || data[0] == '\'')) { data = data.substr(1, data.size() - 2); } node = data; } /// Clear all read lines. /// void clearTrailingEmptyLines(std::list::iterator & it) { while (it != m_Lines.end()) { ReaderLine* pLine = *it; if (pLine->Data.size() == 0) { delete* it; it = m_Lines.erase(it); } else { return; } } } /// /// /// static bool isSequenceStart(const std::string & data) { if (data.size() == 0 || data[0] != '-') { return false; } if (data.size() >= 2 && data[1] != ' ') { return false; } return true; } /// /// /// /// static bool isBlockScalar(const std::string & data, const size_t line, unsigned char& flags) { flags = 0; if (data.size() == 0) { return false; } if (data[0] == '|') { if (data.size() >= 2) { if (data[1] != '-' && data[1] != ' ' && data[1] != '\t') { throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data)); } } else { flags |= ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]; } flags |= ReaderLine::FlagMask[static_cast(ReaderLine::LiteralScalarFlag)]; return true; } if (data[0] == '>') { if (data.size() >= 2) { if (data[1] != '-' && data[1] != ' ' && data[1] != '\t') { throw ParsingException(ExceptionMessage(g_ErrorInvalidBlockScalar, line, data)); } } else { flags |= ReaderLine::FlagMask[static_cast(ReaderLine::ScalarNewlineFlag)]; } flags |= ReaderLine::FlagMask[static_cast(ReaderLine::FoldedScalarFlag)]; return true; } return false; } std::list m_Lines; // List of lines. }; // --------------------------------------------------------------------------- // Parsing Functions // --------------------------------------------------------------------------- /// Populate given root node with deserialized data. /// Root node to populate. /// Path of input file. bool Parse(Node& root, const char* filename) { std::ifstream f(filename, std::ifstream::binary); if (f.is_open() == false) { throw OperationException(g_ErrorCannotOpenFile); } f.seekg(0, f.end); size_t fileSize = static_cast(f.tellg()); f.seekg(0, f.beg); std::unique_ptr data(new char[fileSize]); f.read(data.get(), fileSize); f.close(); return Parse(root, data.get(), fileSize); } /// Populate given root node with deserialized data. /// Root node to populate. /// Input stream. bool Parse(Node& root, std::iostream& stream) { ParseImp* pImp = nullptr; try { pImp = new ParseImp; pImp->parse(root, stream); delete pImp; return true; } catch (const Exception e) { delete pImp; return false; } } /// Populate given root node with deserialized data. /// Root node to populate. /// String of input data. bool Parse(Node& root, const std::string& string) { std::stringstream ss(string); return Parse(root, ss); } /// Populate given root node with deserialized data. /// Character array of input data. /// Buffer size. bool Parse(Node& root, const char* buffer, const size_t size) { std::stringstream ss(std::string(buffer, size)); return Parse(root, ss); } // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the SerializeConfig struct. /// /// Number of spaces per indentation. /// Maximum length of scalars. Serialized as folder scalars if exceeded. Ignored if equal to 0. /// Put maps on a new line if parent node is a sequence. /// Put scalars on a new line if parent node is a map. SerializeConfig::SerializeConfig(const size_t spaceIndentation, const size_t scalarMaxLength, const bool sequenceMapNewline, const bool mapScalarNewline) : SpaceIndentation(spaceIndentation), ScalarMaxLength(scalarMaxLength), SequenceMapNewline(sequenceMapNewline), MapScalarNewline(mapScalarNewline) { /* stub */ } // --------------------------------------------------------------------------- // Serialization Functions // --------------------------------------------------------------------------- /// Serialize node data. /// Root node to serialize. /// Path of output file. /// Serialization configuration. void Serialize(const Node& root, const char* filename, const SerializeConfig& config) { std::stringstream stream; Serialize(root, stream, config); std::ofstream f(filename); if (f.is_open() == false) { throw OperationException(g_ErrorCannotOpenFile); } f.write(stream.str().c_str(), stream.str().size()); f.close(); } /// /// /// /// /// size_t LineFolding(const std::string& input, std::vector& folded, const size_t maxLength) { folded.clear(); if (input.size() == 0) { return 0; } size_t currentPos = 0; size_t lastPos = 0; size_t spacePos = std::string::npos; while (currentPos < input.size()) { currentPos = lastPos + maxLength; if (currentPos < input.size()) { spacePos = input.find_first_of(' ', currentPos); } if (spacePos == std::string::npos || currentPos >= input.size()) { const std::string endLine = input.substr(lastPos); if (endLine.size()) { folded.push_back(endLine); } return folded.size(); } folded.push_back(input.substr(lastPos, spacePos - lastPos)); lastPos = spacePos + 1; } return folded.size(); } /// /// /// /// /// /// static void SerializeLoop(const Node& node, std::iostream& stream, bool useLevel, const size_t level, const SerializeConfig& config) { const size_t indention = config.SpaceIndentation; switch (node.type()) { case Node::SequenceType: { for (auto it = node.begin(); it != node.end(); it++) { const Node& value = (*it).second; if (value.isNone()) { continue; } stream << std::string(level, ' ') << "- "; useLevel = false; if (value.isSequence() || (value.isMap() && config.SequenceMapNewline == true)) { useLevel = true; stream << "\n"; } SerializeLoop(value, stream, useLevel, level + 2, config); } } break; case Node::MapType: { size_t count = 0; for (auto it = node.begin(); it != node.end(); it++) { const Node& value = (*it).second; if (value.isNone()) { continue; } if (useLevel || count > 0) { stream << std::string(level, ' '); } std::string key = (*it).first; AddEscapeTokens(key, "\\\""); if (ShouldBeCited(key)) { stream << "\"" << key << "\"" << ": "; } else { stream << key << ": "; } useLevel = false; if (value.isScalar() == false || (value.isScalar() && config.MapScalarNewline)) { useLevel = true; stream << "\n"; } SerializeLoop(value, stream, useLevel, level + indention, config); useLevel = true; count++; } } break; case Node::ScalarType: { const std::string value = node.as(); // empty scalar if (value.size() == 0) { stream << "\n"; break; } // get lines of scalar std::string line = ""; std::vector lines; std::istringstream iss(value); while (iss.eof() == false) { std::getline(iss, line); lines.push_back(line); } // block scalar const std::string& lastLine = lines.back(); const bool endNewline = lastLine.size() == 0; if (endNewline) { lines.pop_back(); } // literal if (lines.size() > 1) { stream << "|"; } // folded/plain else { const std::string frontLine = lines.front(); if (config.ScalarMaxLength == 0 || lines.front().size() <= config.ScalarMaxLength || LineFolding(frontLine, lines, config.ScalarMaxLength) == 1) { if (useLevel) { stream << std::string(level, ' '); } if (ShouldBeCited(value)) { stream << "\"" << value << "\"\n"; break; } stream << value << "\n"; break; } else { stream << ">"; } } if (endNewline == false) { stream << "-"; } stream << "\n"; for (auto it = lines.begin(); it != lines.end(); it++) { stream << std::string(level, ' ') << (*it) << "\n"; } } break; default: break; } } /// Serialize node data. /// Root node to serialize. /// Output stream. /// Serialization configuration. void Serialize(const Node& root, std::iostream& stream, const SerializeConfig& config) { if (config.SpaceIndentation < 2) { throw OperationException(g_ErrorIndentation); } SerializeLoop(root, stream, false, 0, config); } /// Serialize node data. /// Root node to serialize. /// String of output data. /// Serialization configuration. void Serialize(const Node& root, std::string& string, const SerializeConfig& config) { std::stringstream stream; Serialize(root, stream, config); string = stream.str(); } // --------------------------------------------------------------------------- // Global Functions // --------------------------------------------------------------------------- std::string ExceptionMessage(const std::string& message, ReaderLine& line) { return message + std::string(" Line ") + std::to_string(line.No) + std::string(": ") + line.Data; } std::string ExceptionMessage(const std::string& message, ReaderLine& line, const size_t errorPos) { return message + std::string(" Line ") + std::to_string(line.No) + std::string(" column ") + std::to_string(errorPos + 1) + std::string(": ") + line.Data; } std::string ExceptionMessage(const std::string& message, const size_t errorLine, const size_t errorPos) { return message + std::string(" Line ") + std::to_string(errorLine) + std::string(" column ") + std::to_string(errorPos); } std::string ExceptionMessage(const std::string& message, const size_t errorLine, const std::string& data) { return message + std::string(" Line ") + std::to_string(errorLine) + std::string(": ") + data; } bool FindQuote(const std::string& input, size_t& start, size_t& end, size_t searchPos) { start = end = std::string::npos; size_t qPos = searchPos; bool foundStart = false; while (qPos != std::string::npos) { // find first quote qPos = input.find_first_of("\"'", qPos); if(qPos == std::string::npos) { return false; } const char token = input[qPos]; if (token == '"' && (qPos == 0 || input[qPos-1] != '\\')) { // found start quote if (foundStart == false) { start = qPos; foundStart = true; } // found end quote else { end = qPos; return true; } } // check if it's possible for another loop if (qPos + 1 == input.size()) { return false; } qPos++; } return false; } size_t FindNotCited(const std::string& input, char token, size_t& preQuoteCount) { preQuoteCount = 0; size_t tokenPos = input.find_first_of(token); if (tokenPos == std::string::npos) { return std::string::npos; } // find all quotes std::vector> quotes; size_t quoteStart = 0; size_t quoteEnd = 0; while (FindQuote(input, quoteStart, quoteEnd, quoteEnd)) { quotes.push_back({quoteStart, quoteEnd}); if (quoteEnd + 1 == input.size()) { break; } quoteEnd++; } if (quotes.size() == 0) { return tokenPos; } size_t currentQuoteIndex = 0; std::pair currentQuote = {0, 0}; while (currentQuoteIndex < quotes.size()) { currentQuote = quotes[currentQuoteIndex]; if (tokenPos < currentQuote.first) { return tokenPos; } preQuoteCount++; if (tokenPos <= currentQuote.second) { // find next token if (tokenPos + 1 == input.size()) { return std::string::npos; } tokenPos = input.find_first_of(token, tokenPos + 1); if (tokenPos == std::string::npos) { return std::string::npos; } } currentQuoteIndex++; } return tokenPos; } size_t FindNotCited(const std::string& input, char token) { size_t dummy = 0; return FindNotCited(input, token, dummy); } bool ValidateQuote(const std::string& input) { if (input.size() == 0) { return true; } char token = 0; size_t searchPos = 0; if (input[0] == '\"' || input[0] == '\'') { if (input.size() == 1) { return false; } token = input[0]; searchPos = 1; } while (searchPos != std::string::npos && searchPos < input.size() - 1) { searchPos = input.find_first_of("\"'", searchPos + 1); if(searchPos == std::string::npos) { break; } const char foundToken = input[searchPos]; if (input[searchPos] == '\"' || input[searchPos] == '\'') { if (token == 0 && input[searchPos-1] != '\\') { return false; } if (foundToken == token && input[searchPos-1] != '\\') { if(searchPos == input.size() - 1) { return true; } return false; } } } return token == 0; } void CopyNode(const Node& from, Node& to) { const Node::eType type = from.type(); switch(type) { case Node::SequenceType: for (auto it = from.begin(); it != from.end(); it++) { const Node & currentNode = (*it).second; Node & newNode = to.push_back(); CopyNode(currentNode, newNode); } break; case Node::MapType: for (auto it = from.begin(); it != from.end(); it++) { const Node & currentNode = (*it).second; Node & newNode = to[(*it).first]; CopyNode(currentNode, newNode); } break; case Node::ScalarType: to = from.as(); break; case Node::None: break; } } bool ShouldBeCited(const std::string& key) { return key.find_first_of("\":{}[],&*#?|-<>=!%@") != std::string::npos; } void AddEscapeTokens(std::string& input, const std::string& tokens) { for (auto it = tokens.begin(); it != tokens.end(); it++) { const char token = *it; const std::string replace = std::string("\\") + std::string(1, token); size_t found = input.find_first_of(token); while (found != std::string::npos) { input.replace(found, 1, replace); found = input.find_first_of(token, found + 2); } } } void RemoveAllEscapeTokens(std::string & input) { size_t found = input.find_first_of("\\"); while (found != std::string::npos) { if (found + 1 == input.size()) { return; } std::string replace(1, input[found + 1]); input.replace(found, 2, replace); found = input.find_first_of("\\", found + 1); } } } // namespace yaml