You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2766 lines
87 KiB
2766 lines
87 KiB
/**
|
|
* 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 <memory>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <list>
|
|
#include <cstdio>
|
|
#include <stdarg.h>
|
|
|
|
// Implementation access definitions.
|
|
#define NODE_IMP static_cast<NodeImp*>(m_pImp)
|
|
#define NODE_IMP_EXT(node) static_cast<NodeImp*>(node.m_pImp)
|
|
#define TYPE_IMP static_cast<NodeImp*>(m_pImp)->m_pImp
|
|
|
|
#define IT_IMP static_cast<IteratorImp*>(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
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the Exception class.
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <param name="type"></param>
|
|
Exception::Exception(const std::string& message, const eType type) :
|
|
std::runtime_error(message),
|
|
m_Type(type)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get type of exception.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
Exception::eType Exception::type() const
|
|
{
|
|
return m_Type;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get message of exception.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
const char* Exception::message() const
|
|
{
|
|
return what();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the InternalException class.
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
InternalException::InternalException(const std::string& message) :
|
|
Exception(message, InternalError)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the ParsingException class.
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
ParsingException::ParsingException(const std::string& message) :
|
|
Exception(message, ParsingError)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the OperationException class.
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
OperationException::OperationException(const std::string & message) :
|
|
Exception(message, OperationError)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class TypeImp {
|
|
public:
|
|
/// <summary>
|
|
/// Finalizes a new instance of the TypeImp class.
|
|
/// </summary>
|
|
virtual ~TypeImp()
|
|
{
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual const std::string& getData() const = 0;
|
|
/// <summary></summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
virtual bool setData(const std::string& data) = 0;
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual size_t size() const = 0;
|
|
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const size_t index) = 0;
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const std::string& key) = 0;
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* insert(const size_t index) = 0;
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node* push_front() = 0;
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node* push_back() = 0;
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual void erase(const size_t index) = 0;
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
virtual void erase(const std::string& key) = 0;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class SequenceImp : public TypeImp {
|
|
public:
|
|
/// <summary>
|
|
/// Finalizes a new instance of the SequenceImp class.
|
|
/// </summary>
|
|
~SequenceImp()
|
|
{
|
|
for (auto it = m_Sequence.begin(); it != m_Sequence.end(); it++) {
|
|
delete it->second;
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual const std::string & getData() const
|
|
{
|
|
return g_EmptyString;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
virtual bool setData(const std::string & data)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual size_t size() const
|
|
{
|
|
return m_Sequence.size();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const size_t index)
|
|
{
|
|
auto it = m_Sequence.find(index);
|
|
if (it != m_Sequence.end()) {
|
|
return it->second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const std::string& key)
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
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);
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
virtual void erase(const std::string& key)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
std::map<size_t, Node*> m_Sequence;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class MapImp : public TypeImp {
|
|
public:
|
|
/// <summary>
|
|
/// Finalizes a new instance of the SequenceImp class.
|
|
/// </summary>
|
|
~MapImp()
|
|
{
|
|
for (auto it = m_Map.begin(); it != m_Map.end(); it++) {
|
|
delete it->second;
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual const std::string& getData() const
|
|
{
|
|
return g_EmptyString;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
virtual bool setData(const std::string& data)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual size_t size() const
|
|
{
|
|
return m_Map.size();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* insert(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node* push_front()
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node* push_back()
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual void erase(const size_t index)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
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<std::string, Node*> m_Map;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class ScalarImp : public TypeImp {
|
|
public:
|
|
/// <summary>
|
|
/// Finalizes a new instance of the ScalarImp class.
|
|
/// </summary>
|
|
~ScalarImp()
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual const std::string& getData() const
|
|
{
|
|
return m_Value;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
virtual bool setData(const std::string& data)
|
|
{
|
|
m_Value = data;
|
|
return true;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual size_t size() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
virtual Node* getNode(const std::string& key)
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual Node* insert(const size_t index)
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node* push_front()
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node* push_back()
|
|
{
|
|
return nullptr;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
virtual void erase(const size_t index)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
virtual void erase(const std::string& key)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
std::string m_Value;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class NodeImp {
|
|
public:
|
|
/// <summary>
|
|
/// Initializes a new instance of the NodeImp class.
|
|
/// </summary>
|
|
NodeImp() :
|
|
m_Type(Node::None),
|
|
m_pImp(nullptr)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary>
|
|
/// Finalizes a new instance of the NodeImp class.
|
|
/// </summary>
|
|
~NodeImp()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
/// <summary>Completely clear node.</summary>
|
|
void clear()
|
|
{
|
|
if (m_pImp != nullptr) {
|
|
delete m_pImp;
|
|
m_pImp = nullptr;
|
|
}
|
|
m_Type = Node::None;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
void initSequence()
|
|
{
|
|
if (m_Type != Node::SequenceType || m_pImp == nullptr) {
|
|
if (m_pImp) {
|
|
delete m_pImp;
|
|
}
|
|
m_pImp = new SequenceImp;
|
|
m_Type = Node::SequenceType;
|
|
}
|
|
}
|
|
/// <summary></summary>
|
|
void initMap()
|
|
{
|
|
if (m_Type != Node::MapType || m_pImp == nullptr) {
|
|
if (m_pImp) {
|
|
delete m_pImp;
|
|
}
|
|
m_pImp = new MapImp;
|
|
m_Type = Node::MapType;
|
|
}
|
|
}
|
|
/// <summary></summary>
|
|
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:
|
|
/// <summary>
|
|
/// Finalizes a new instance of the IteratorImp class.
|
|
/// </summary>
|
|
virtual ~IteratorImp()
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node::eType type() const = 0;
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initBegin(SequenceImp* pSequenceImp) = 0;
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initEnd(SequenceImp* pSequenceImp) = 0;
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initBegin(MapImp* pMapImp) = 0;
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initEnd(MapImp* pMapImp) = 0;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class SequenceIteratorImp : public IteratorImp {
|
|
public:
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node::eType type() const
|
|
{
|
|
return Node::SequenceType;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initBegin(SequenceImp* pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.begin();
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initEnd(SequenceImp* pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.end();
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initBegin(MapImp* pMapImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initEnd(MapImp* pMapImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="it"></param>
|
|
void copy(const SequenceIteratorImp& it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<size_t, Node *>::iterator m_Iterator;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class MapIteratorImp : public IteratorImp {
|
|
public:
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node::eType type() const
|
|
{
|
|
return Node::MapType;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initBegin(SequenceImp* pSequenceImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initEnd(SequenceImp* pSequenceImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initBegin(MapImp* pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.begin();
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initEnd(MapImp* pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.end();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="it"></param>
|
|
void copy(const MapIteratorImp& it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<std::string, Node *>::iterator m_Iterator;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class SequenceConstIteratorImp : public IteratorImp {
|
|
public:
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node::eType type() const
|
|
{
|
|
return Node::SequenceType;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initBegin(SequenceImp* pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.begin();
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initEnd(SequenceImp* pSequenceImp)
|
|
{
|
|
m_Iterator = pSequenceImp->m_Sequence.end();
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initBegin(MapImp* pMapImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initEnd(MapImp* pMapImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="it"></param>
|
|
void copy(const SequenceConstIteratorImp & it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<size_t, Node *>::const_iterator m_Iterator;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class MapConstIteratorImp : public IteratorImp {
|
|
public:
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
virtual Node::eType type() const
|
|
{
|
|
return Node::MapType;
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initBegin(SequenceImp* pSequenceImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pSequenceImp"></param>
|
|
virtual void initEnd(SequenceImp* pSequenceImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initBegin(MapImp* pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.begin();
|
|
}
|
|
/// <summary></summary>
|
|
/// <param name="pMapImp"></param>
|
|
virtual void initEnd(MapImp* pMapImp)
|
|
{
|
|
m_Iterator = pMapImp->m_Map.end();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="it"></param>
|
|
void copy(const MapConstIteratorImp & it)
|
|
{
|
|
m_Iterator = it.m_Iterator;
|
|
}
|
|
|
|
std::map<std::string, Node *>::const_iterator m_Iterator;
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the Iterator class.
|
|
/// </summary>
|
|
Iterator::Iterator() :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary>
|
|
/// Copies an instance of the Iterator class to a new instance of the Iterator class.
|
|
/// </summary>
|
|
/// <param name="it"></param>
|
|
Iterator::Iterator(const Iterator& it) :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
*this = it;
|
|
}
|
|
/// <summary>
|
|
/// Finalizes a instance of the Iterator class.
|
|
/// </summary>
|
|
Iterator::~Iterator()
|
|
{
|
|
if (m_pImp) {
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
delete static_cast<SequenceIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapIteratorImp*>(m_pImp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>Assignment operator.</summary>
|
|
Iterator& Iterator::operator = (const Iterator& it)
|
|
{
|
|
if (m_pImp) {
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
delete static_cast<SequenceIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapIteratorImp*>(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<SequenceIteratorImp*>(pNewImp)->m_Iterator = static_cast<SequenceIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
m_Type = MapType;
|
|
pNewImp = new MapIteratorImp;
|
|
static_cast<MapIteratorImp*>(pNewImp)->m_Iterator = static_cast<MapIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_pImp = pNewImp;
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Get node of iterator. First pair item is the key of map value, empty if type is sequence.</summary>
|
|
std::pair<const std::string&, Node&> Iterator::operator *()
|
|
{
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
return { g_EmptyString, *(static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator->second) };
|
|
break;
|
|
case MapType:
|
|
return { static_cast<MapIteratorImp*>(m_pImp)->m_Iterator->first,
|
|
*(static_cast<MapIteratorImp*>(m_pImp)->m_Iterator->second) };
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_NoneNode.clear();
|
|
return { g_EmptyString, g_NoneNode };
|
|
}
|
|
|
|
/// <summary>Post-increment operator.</summary>
|
|
Iterator& Iterator::operator ++ (int dummy)
|
|
{
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Post-decrement operator.</summary>
|
|
Iterator& Iterator::operator -- (int dummy)
|
|
{
|
|
switch(m_Type) {
|
|
case SequenceType:
|
|
static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Check if iterator is equal to other iterator.</summary>
|
|
bool Iterator::operator == (const Iterator& it)
|
|
{
|
|
if (m_Type != it.m_Type) {
|
|
return false;
|
|
}
|
|
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
return static_cast<SequenceIteratorImp*>(m_pImp)->m_Iterator == static_cast<SequenceIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
return static_cast<MapIteratorImp*>(m_pImp)->m_Iterator == static_cast<MapIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>Check if iterator is not equal to other iterator.</summary>
|
|
bool Iterator::operator != (const Iterator& it)
|
|
{
|
|
return !(*this == it);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the ConstIterator class.
|
|
/// </summary>
|
|
ConstIterator::ConstIterator() :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary>
|
|
/// Copies an instance of the ConstIterator class to a new instance of the ConstIterator class.
|
|
/// </summary>
|
|
/// <param name="it"></param>
|
|
ConstIterator::ConstIterator(const ConstIterator& it) :
|
|
m_Type(None),
|
|
m_pImp(nullptr)
|
|
{
|
|
*this = it;
|
|
}
|
|
/// <summary>
|
|
/// Finalizes a instance of the ConstIterator class.
|
|
/// </summary>
|
|
ConstIterator::~ConstIterator()
|
|
{
|
|
if (m_pImp) {
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
delete static_cast<SequenceConstIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapConstIteratorImp*>(m_pImp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary>Assignment operator.</summary>
|
|
ConstIterator& ConstIterator::operator = (const ConstIterator& it)
|
|
{
|
|
if (m_pImp) {
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
delete static_cast<SequenceConstIteratorImp*>(m_pImp);
|
|
break;
|
|
case MapType:
|
|
delete static_cast<MapConstIteratorImp*>(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<SequenceConstIteratorImp*>(pNewImp)->m_Iterator = static_cast<SequenceConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
m_Type = MapType;
|
|
pNewImp = new MapConstIteratorImp;
|
|
static_cast<MapConstIteratorImp*>(pNewImp)->m_Iterator = static_cast<MapConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_pImp = pNewImp;
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Get node of iterator. First pair item is the key of map value, empty if type is sequence.</summary>
|
|
std::pair<const std::string&, const Node&> ConstIterator::operator *()
|
|
{
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
return { g_EmptyString, *(static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator->second) };
|
|
break;
|
|
case MapType:
|
|
return { static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator->first,
|
|
*(static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator->second) };
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
g_NoneNode.clear();
|
|
return { g_EmptyString, g_NoneNode };
|
|
}
|
|
|
|
/// <summary>Post-increment operator.</summary>
|
|
ConstIterator& ConstIterator::operator ++ (int dummy)
|
|
{
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Post-decrement operator.</summary>
|
|
ConstIterator& ConstIterator::operator -- (int dummy)
|
|
{
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
case MapType:
|
|
static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator--;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Check if iterator is equal to other iterator.</summary>
|
|
bool ConstIterator::operator == (const ConstIterator& it)
|
|
{
|
|
if (m_Type != it.m_Type) {
|
|
return false;
|
|
}
|
|
|
|
switch (m_Type) {
|
|
case SequenceType:
|
|
return static_cast<SequenceConstIteratorImp*>(m_pImp)->m_Iterator == static_cast<SequenceConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
case MapType:
|
|
return static_cast<MapConstIteratorImp*>(m_pImp)->m_Iterator == static_cast<MapConstIteratorImp*>(it.m_pImp)->m_Iterator;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>Check if iterator is not equal to other iterator.</summary>
|
|
bool ConstIterator::operator != (const ConstIterator & it)
|
|
{
|
|
return !(*this == it);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the Node class.
|
|
/// </summary>
|
|
Node::Node() :
|
|
m_pImp(new NodeImp)
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary>
|
|
/// Copies an instance of the Node class to a new instance of the Node class.
|
|
/// </summary>
|
|
/// <param name="node"></param>
|
|
Node::Node(const Node& node) :
|
|
Node()
|
|
{
|
|
*this = node;
|
|
}
|
|
/// <summary>
|
|
/// Initializes a new instance of the Node class.
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
Node::Node(const std::string& value) :
|
|
Node()
|
|
{
|
|
*this = value;
|
|
}
|
|
/// <summary>
|
|
/// Initializes a new instance of the Node class.
|
|
/// </summary>
|
|
Node::Node(const char* value) :
|
|
Node()
|
|
{
|
|
*this = value;
|
|
}
|
|
/// <summary>
|
|
/// Finalizes a instance of the Node class.
|
|
/// </summary>
|
|
Node::~Node()
|
|
{
|
|
delete static_cast<NodeImp*>(m_pImp);
|
|
}
|
|
|
|
/// <summary>Gets the type of node.</summary>
|
|
/// <returns></returns>
|
|
Node::eType Node::type() const
|
|
{
|
|
return NODE_IMP->m_Type;
|
|
}
|
|
|
|
/// <summary>Checks if the node contains nothing.</summary>
|
|
/// <returns></returns>
|
|
bool Node::isNone() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::None;
|
|
}
|
|
|
|
/// <summary>Checks if the node is a sequence node.</summary>
|
|
/// <returns></returns>
|
|
bool Node::isSequence() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::SequenceType;
|
|
}
|
|
|
|
/// <summary>Checks if the node is a map node.</summary>
|
|
/// <returns></returns>
|
|
bool Node::isMap() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::MapType;
|
|
}
|
|
|
|
/// <summary>Checks if the node is a scalar node.</summary>
|
|
/// <returns></returns>
|
|
bool Node::isScalar() const
|
|
{
|
|
return NODE_IMP->m_Type == Node::ScalarType;
|
|
}
|
|
|
|
/// <summary>Completely clear node.</summary>
|
|
void Node::clear()
|
|
{
|
|
NODE_IMP->clear();
|
|
}
|
|
|
|
/// <summary>Get size of node. Nodes of type None or Scalar will return 0.</summary>
|
|
/// <returns></returns>
|
|
size_t Node::size() const
|
|
{
|
|
if (TYPE_IMP == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
return TYPE_IMP->size();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
Node& Node::insert(const size_t index)
|
|
{
|
|
NODE_IMP->initSequence();
|
|
return *TYPE_IMP->insert(index);
|
|
}
|
|
|
|
/// <summary>Add new sequence index to back. Converts node to sequence type if needed.</summary>
|
|
/// <returns></returns>
|
|
Node& Node::push_front()
|
|
{
|
|
NODE_IMP->initSequence();
|
|
return *TYPE_IMP->push_front();
|
|
}
|
|
/// <summary>Add new sequence index to front. Converts node to sequence type if needed.</summary>
|
|
/// <returns></returns>
|
|
Node& Node::push_back()
|
|
{
|
|
NODE_IMP->initSequence();
|
|
return *TYPE_IMP->push_back();
|
|
}
|
|
|
|
/// <summary>Get sequence/map item. Converts node to sequence/map type if needed.</summary>
|
|
/// <param name="index"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary>Get sequence/map item. Converts node to sequence/map type if needed.</summary>
|
|
/// <param name="key"></param>
|
|
/// <returns></returns>
|
|
Node& Node::operator[](const std::string& key)
|
|
{
|
|
NODE_IMP->initMap();
|
|
return *TYPE_IMP->getNode(key);
|
|
}
|
|
|
|
/// <summary>Erase item. No action if node is not a sequence or map.</summary>
|
|
/// <param name="index"></param>
|
|
void Node::erase(const size_t index)
|
|
{
|
|
if (TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::SequenceType) {
|
|
return;
|
|
}
|
|
|
|
return TYPE_IMP->erase(index);
|
|
}
|
|
/// <summary>Erase item. No action if node is not a sequence or map.</summary>
|
|
/// <param name="key"></param>
|
|
void Node::erase(const std::string& key)
|
|
{
|
|
if (TYPE_IMP == nullptr || NODE_IMP->m_Type != Node::MapType) {
|
|
return;
|
|
}
|
|
|
|
return TYPE_IMP->erase(key);
|
|
}
|
|
|
|
/// <summary>Assignment operator.</summary>
|
|
Node& Node::operator = (const Node& node)
|
|
{
|
|
NODE_IMP->clear();
|
|
CopyNode(node, *this);
|
|
return *this;
|
|
}
|
|
/// <summary>Assignment operator.</summary>
|
|
Node& Node::operator = (const std::string& value)
|
|
{
|
|
NODE_IMP->initScalar();
|
|
TYPE_IMP->setData(value);
|
|
return *this;
|
|
}
|
|
/// <summary>Assignment operator.</summary>
|
|
Node& Node::operator = (const char* value)
|
|
{
|
|
NODE_IMP->initScalar();
|
|
TYPE_IMP->setData(value ? std::string(value) : "");
|
|
return *this;
|
|
}
|
|
|
|
/// <summary>Get start iterator.</summary>
|
|
/// <returns></returns>
|
|
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<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = Iterator::MapType;
|
|
pItImp = new MapIteratorImp;
|
|
pItImp->initBegin(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
/// <summary>Get start constant iterator.</summary>
|
|
/// <returns></returns>
|
|
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<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = ConstIterator::MapType;
|
|
pItImp = new MapConstIteratorImp;
|
|
pItImp->initBegin(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
/// <summary>Get end iterator.</summary>
|
|
/// <returns></returns>
|
|
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<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = Iterator::MapType;
|
|
pItImp = new MapIteratorImp;
|
|
pItImp->initEnd(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
/// <summary>Get end constant iterator.</summary>
|
|
/// <returns></returns>
|
|
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<SequenceImp*>(TYPE_IMP));
|
|
break;
|
|
case Node::MapType:
|
|
it.m_Type = ConstIterator::MapType;
|
|
pItImp = new MapConstIteratorImp;
|
|
pItImp->initEnd(static_cast<MapImp*>(TYPE_IMP));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
it.m_pImp = pItImp;
|
|
}
|
|
|
|
return it;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Private Class Members
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary></summary>
|
|
/// <returns></returns>
|
|
const std::string& Node::asString() const
|
|
{
|
|
if (TYPE_IMP == nullptr) {
|
|
return g_EmptyString;
|
|
}
|
|
|
|
return TYPE_IMP->getData();
|
|
}
|
|
|
|
// Reader implementations
|
|
// ---------------------------------------------------------------------------
|
|
// Class Declaration
|
|
//
|
|
// ---------------------------------------------------------------------------
|
|
|
|
class ReaderLine {
|
|
public:
|
|
/// <summary>
|
|
/// Initializes a new instance of the ReaderLine class.
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <param name="no"></param>
|
|
/// <param name="offset"></param>
|
|
/// <param name="type"></param>
|
|
/// <param name="flags"></param>
|
|
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.
|
|
};
|
|
|
|
/// <summary>Set flag.</summary>
|
|
/// <param name="flag"></param>
|
|
void setFlag(const eFlag flag)
|
|
{
|
|
Flags |= FlagMask[static_cast<size_t>(flag)];
|
|
}
|
|
/// <summary>Set flags by mask value.</summary>
|
|
/// <param name="flags"></param>
|
|
void setFlags(const unsigned char flags)
|
|
{
|
|
Flags |= flags;
|
|
}
|
|
|
|
/// <summary>Unset flag.</summary>
|
|
/// <param name="flag"></param>
|
|
void unsetFlag(const eFlag flag)
|
|
{
|
|
Flags &= ~FlagMask[static_cast<size_t>(flag)];
|
|
}
|
|
/// <summary>Unset flags by mask value.</summary>
|
|
/// <param name="flags"></param>
|
|
void unsetFlags(const unsigned char flags)
|
|
{
|
|
Flags &= ~flags;
|
|
}
|
|
|
|
/// <summary>Get flag value.</summary>
|
|
/// <param name="flag"></param>
|
|
/// <returns></returns>
|
|
bool getFlag(const eFlag flag) const
|
|
{
|
|
return Flags & FlagMask[static_cast<size_t>(flag)];
|
|
}
|
|
|
|
/// <summary>Copy and replace scalar flags from another ReaderLine.</summary>
|
|
/// <param name="from"></param>
|
|
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:
|
|
/// <summary>
|
|
/// Initializes a new instance of the ParseImp class.
|
|
/// </summary>
|
|
ParseImp()
|
|
{
|
|
/* stub */
|
|
}
|
|
/// <summary>
|
|
/// Finalizes a new instance of the ParseImp class.
|
|
/// </summary>
|
|
~ParseImp()
|
|
{
|
|
clearLines();
|
|
}
|
|
|
|
/// <summary>Run full parsing procedure.</summary>
|
|
/// <param name="root"></param>
|
|
/// <param name="stream"></param>
|
|
void parse(Node& root, std::iostream& stream)
|
|
{
|
|
try
|
|
{
|
|
root.clear();
|
|
readLines(stream);
|
|
postProcessLines();
|
|
parseRoot(root);
|
|
}
|
|
catch (Exception const& e)
|
|
{
|
|
root.clear();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private:
|
|
/// <summary>
|
|
/// Copies a instance of the ParseImp class to new instance of the ParseImp class.
|
|
/// </summary>
|
|
/// <param name="copy"></param>
|
|
ParseImp(const ParseImp& copy)
|
|
{
|
|
/* stub */
|
|
}
|
|
|
|
/// <summary>Read all lines.</summary>
|
|
/// <param name="stream"></param>
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>Run post-processing on all lines. Basically split lines into multiple lines if needed, to follow the parsing algorithm.</summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Run post-processing and check for sequence. Split line into two lines if sequence token is not on it's own line.</summary>
|
|
/// <param name="it"></param>
|
|
/// <returns>True if line is sequence, else false.</returns>
|
|
bool postProcessSequenceLine(std::list<ReaderLine*>::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;
|
|
}
|
|
|
|
/// <summary>Run post-processing and check for mapping. Split line into two lines if mapping value is not on it's own line.</summary>
|
|
/// <param name="it"></param>
|
|
/// <returns>True if line is mapping, else move on to scalar parsing.</returns>
|
|
bool postProcessMappingLine(std::list<ReaderLine*>::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;
|
|
}
|
|
|
|
/// <summary>Run post-processing and check for scalar. Checking for multi-line scalars.</summary>
|
|
/// <param name="it"></param>
|
|
void postProcessScalarLine(std::list<ReaderLine*>::iterator & it)
|
|
{
|
|
ReaderLine* pLine = *it;
|
|
pLine->Type = Node::ScalarType;
|
|
|
|
size_t parentOffset = pLine->Offset;
|
|
if (pLine != m_Lines.front()) {
|
|
std::list<ReaderLine*>::iterator lastIt = it;
|
|
--lastIt;
|
|
parentOffset = (*lastIt)->Offset;
|
|
}
|
|
|
|
std::list<ReaderLine*>::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);
|
|
}
|
|
|
|
/// <summary>Process root node and start of document.</summary>
|
|
/// <param name="root"></param>
|
|
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));
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>Process sequence node.</summary>
|
|
/// <param name="node"></param>
|
|
/// <param name="it"></param>
|
|
void parseSequence(Node & node, std::list<ReaderLine*>::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));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Process map node.</summary>
|
|
/// <param name="node"></param>
|
|
/// <param name="it"></param>
|
|
void parseMap(Node & node, std::list<ReaderLine*>::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));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Process scalar node.</summary>
|
|
/// <param name="node"></param>
|
|
/// <param name="it"></param>
|
|
void parseScalar(Node & node, std::list<ReaderLine*>::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<bool>(blockFlags & ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::ScalarNewlineFlag)]);
|
|
const bool foldedFlag = static_cast<bool>(blockFlags & ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::FoldedScalarFlag)]);
|
|
const bool literalFlag = static_cast<bool>(blockFlags & ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::LiteralScalarFlag)]);
|
|
size_t parentOffset = 0;
|
|
|
|
// find parent offset
|
|
if (it != m_Lines.begin()) {
|
|
std::list<ReaderLine*>::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;
|
|
}
|
|
|
|
/// <summary>Clear all read lines.</summary
|
|
void clearLines()
|
|
{
|
|
for (auto it = m_Lines.begin(); it != m_Lines.end(); it++) {
|
|
delete* it;
|
|
}
|
|
m_Lines.clear();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="it"></param>
|
|
void clearTrailingEmptyLines(std::list<ReaderLine*>::iterator & it)
|
|
{
|
|
while (it != m_Lines.end()) {
|
|
ReaderLine* pLine = *it;
|
|
if (pLine->Data.size() == 0) {
|
|
delete* it;
|
|
it = m_Lines.erase(it);
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="data"></param>
|
|
/// <param name="line></param>
|
|
/// <param name="flags"></param>
|
|
/// <returns></returns>
|
|
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<size_t>(ReaderLine::ScalarNewlineFlag)];
|
|
}
|
|
flags |= ReaderLine::FlagMask[static_cast<size_t>(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<size_t>(ReaderLine::ScalarNewlineFlag)];
|
|
}
|
|
flags |= ReaderLine::FlagMask[static_cast<size_t>(ReaderLine::FoldedScalarFlag)];
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::list<ReaderLine*> m_Lines; // List of lines.
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Parsing Functions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>Populate given root node with deserialized data.</summary>
|
|
/// <param name="root">Root node to populate.</param>
|
|
/// <param name="filename">Path of input file.</param>
|
|
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<size_t>(f.tellg());
|
|
f.seekg(0, f.beg);
|
|
|
|
std::unique_ptr<char[]> data(new char[fileSize]);
|
|
f.read(data.get(), fileSize);
|
|
f.close();
|
|
|
|
return Parse(root, data.get(), fileSize);
|
|
}
|
|
/// <summary>Populate given root node with deserialized data.</summary>
|
|
/// <param name="root">Root node to populate.</param>
|
|
/// <param name="stream">Input stream.</param>
|
|
bool Parse(Node& root, std::iostream& stream)
|
|
{
|
|
ParseImp* pImp = nullptr;
|
|
|
|
try
|
|
{
|
|
pImp = new ParseImp;
|
|
pImp->parse(root, stream);
|
|
delete pImp;
|
|
return true;
|
|
}
|
|
catch (Exception const& e)
|
|
{
|
|
delete pImp;
|
|
return false;
|
|
}
|
|
}
|
|
/// <summary>Populate given root node with deserialized data.</summary>
|
|
/// <param name="root">Root node to populate.</param>
|
|
/// <param name="string">String of input data.</param>
|
|
bool Parse(Node& root, const std::string& string)
|
|
{
|
|
std::stringstream ss(string);
|
|
return Parse(root, ss);
|
|
}
|
|
/// <summary>Populate given root node with deserialized data.</summary>
|
|
/// <param name="buffer">Character array of input data.</param>
|
|
/// <param name="size">Buffer size.</param>
|
|
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
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the SerializeConfig struct.
|
|
/// </summary>
|
|
/// <param name="spaceIndentation">Number of spaces per indentation.</param>
|
|
/// <param name="scalarMaxLength">Maximum length of scalars. Serialized as folder scalars if exceeded. Ignored if equal to 0.</param>
|
|
/// <param name="sequenceMapNewline">Put maps on a new line if parent node is a sequence.</param>
|
|
/// <param name="mapScalarNewline">Put scalars on a new line if parent node is a map.</param>
|
|
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
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// <summary>Serialize node data.</summary>
|
|
/// <param name="root">Root node to serialize.</param>
|
|
/// <param name="filename">Path of output file.</param>
|
|
/// <param name="config">Serialization configuration.</param>
|
|
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();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="input"></param>
|
|
/// <param name="folded"></param>
|
|
/// <param name="maxLength"></param>
|
|
/// <returns></returns>
|
|
size_t LineFolding(const std::string& input, std::vector<std::string>& 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();
|
|
}
|
|
|
|
/// <summary></summary>
|
|
/// <param name="node"></param>
|
|
/// <param name="stream"></param>
|
|
/// <param name="useLevel"></param>
|
|
/// <param name="level"></param>
|
|
/// <param name="config"></param>
|
|
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<std::string>();
|
|
|
|
// empty scalar
|
|
if (value.size() == 0) {
|
|
stream << "\n";
|
|
break;
|
|
}
|
|
|
|
// get lines of scalar
|
|
std::string line = "";
|
|
std::vector<std::string> 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>Serialize node data.</summary>
|
|
/// <param name="root">Root node to serialize.</param>
|
|
/// <param name="stream">Output stream.</param>
|
|
/// <param name="config">Serialization configuration.</param>
|
|
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);
|
|
}
|
|
|
|
/// <summary>Serialize node data.</summary>
|
|
/// <param name="root">Root node to serialize.</param>
|
|
/// <param name="string">String of output data.</param>
|
|
/// <param name="config">Serialization configuration.</param>
|
|
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<std::pair<size_t, size_t>> 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<size_t, size_t> 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<std::string>();
|
|
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
|