This commit is contained in:
2021-10-14 13:47:35 +02:00
commit 6625a8dfaa
4026 changed files with 844291 additions and 0 deletions
@@ -0,0 +1,53 @@
PROJECT(openvibe-module-xml)
SET(PROJECT_VERSION_MAJOR ${OV_GLOBAL_VERSION_MAJOR})
SET(PROJECT_VERSION_MINOR ${OV_GLOBAL_VERSION_MINOR})
SET(PROJECT_VERSION_PATCH ${OV_GLOBAL_VERSION_PATCH})
SET(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
FILE(GLOB_RECURSE SRC_FILES src/*.cpp src/*.h src/*.hpp src/*.inl include/*.h)
INCLUDE_DIRECTORIES(include/xml)
INCLUDE("FindSourceRCProperties")
ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DXML_Shared -DXML_Exports")
ADD_LIBRARY(${PROJECT_NAME}-static STATIC ${SRC_FILES})
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
FOLDER ${MODULES_FOLDER}
COMPILE_FLAGS "-DXML_Static -DXML_Exports")
IF(UNIX)
SET_TARGET_PROPERTIES(${PROJECT_NAME}-static PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF(UNIX)
INCLUDE("FindOpenViBECommon")
INCLUDE("FindOpenViBEModuleFS")
INCLUDE("FindThirdPartyBoost")
INCLUDE("FindThirdPartyExpat")
# ---------------------------------
# Target macros
# Defines target operating system, architecture and compiler
# ---------------------------------
SET_BUILD_PLATFORM()
# -----------------------------
# Install files
# -----------------------------
INSTALL(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(TARGETS ${PROJECT_NAME}-static
RUNTIME DESTINATION ${DIST_BINDIR}
LIBRARY DESTINATION ${DIST_LIBDIR}
ARCHIVE DESTINATION ${DIST_LIBDIR})
INSTALL(DIRECTORY include/ DESTINATION ${DIST_INCLUDEDIR} FILES_MATCHING PATTERN "*.h")
@@ -0,0 +1,28 @@
#pragma once
#include "defines.h"
#include <cstdlib> // fix Unix compatibility
namespace XML {
class XML_API IReaderCallback
{
public:
virtual ~IReaderCallback() { }
virtual void openChild(const char* name, const char** attributeName, const char** attributeValue, const size_t nAttribute) = 0;
virtual void processChildData(const char* data) = 0;
virtual void closeChild() = 0;
};
class XML_API IReaderCallBack : public IReaderCallback { };
class XML_API IReader
{
public:
virtual bool processData(const void* buffer, const size_t size) = 0;
virtual void release() = 0;
protected:
virtual ~IReader() { }
};
extern XML_API IReader* createReader(IReaderCallback& callback);
} // namespace XML
@@ -0,0 +1,28 @@
#pragma once
#include "defines.h"
namespace XML {
class XML_API IWriterCallback
{
public:
virtual ~IWriterCallback() { }
virtual void write(const char* sString) = 0;
};
class XML_API IWriterCallBack : public IWriterCallback { };
class XML_API IWriter
{
public:
virtual bool openChild(const char* name) = 0;
virtual bool setAttribute(const char* name, const char* value) = 0;
virtual bool setChildData(const char* data) = 0;
virtual bool closeChild() = 0;
virtual void release() = 0;
protected:
virtual ~IWriter() { }
};
extern XML_API IWriter* createWriter(IWriterCallback& callback);
} // namespace XML
@@ -0,0 +1,60 @@
#pragma once
#include "defines.h"
#include "IXMLNode.h"
#include <string>
#include <cstdlib> // fix Unix compatibility
namespace XML {
/**
* @class IXMLHandler
* @author Serrière Guillaume (INRIA/Loria)
* @brief This class is design to help about XML manipulation.
* @sa XML
*/
class XML_API IXMLHandler
{
public:
/**
* @brief Release the handler.
*/
virtual void release() = 0;
//Parsing
/**
* @brief Parse file points by sPath and return the root name of the document.
* @param path [in] : Path to the File
* @return The root node of the document, or NULL if there is an error.
*/
virtual IXMLNode* parseFile(const char* path) = 0;
/**
* @brief Parse the string sString on uiSize caracters and return the root name of the document.
* @param str [in] : String which contains the XML
* @param size [in] : Size of the part to analyze
* @return The root node of the parse part, or NULL if there is an error.
*/
virtual IXMLNode* parseString(const char* str, const size_t& size) = 0;
//XML extraction
/**
* @brief Write the XML corresponding to the node rNode in the file points by sPath. If the file exists
* it will be erase.
* @param node [in] : The node to write.
* @param path [in] : The path to the file.
* @return True on success, false otherwise.
*/
virtual bool writeXMLInFile(const IXMLNode& node, const char* path) const = 0;
/**
* @brief Get the description of the last error that ocurred
* @return A string object containing the error description
*/
virtual std::string getLastErrorString() const = 0;
protected:
virtual ~IXMLHandler() { }
};
extern XML_API IXMLHandler* createXMLHandler();
} // namespace XML
@@ -0,0 +1,110 @@
#pragma once
#include "defines.h"
#include <string>
#include <cstdlib> // fix Unix compatibility
namespace XML {
/**
* @class IXMLNode
* @author Serrière Guillaume (INRIA/Loria)
* @brief Symbolize a node in a XML tree structure.
* @sa XML
*/
class XML_API IXMLNode
{
public:
virtual void release() = 0;
virtual const char* getName() const = 0;
//Attribute
/**
* @brief Add the attribute name with value
* value to the node.
* @param name [in] : Name of the attribute
* @param value [in] : Value of the attribute
* @return true in success, false otherwise
*/
virtual bool addAttribute(const char* name, const char* value) = 0;
/**
* @brief Indicate if an attribute exists or not.
* @param name [in] : Name of the attribute
* @return true if attribute exists, false otherwise
*/
virtual bool hasAttribute(const char* name) const = 0;
/**
* @brief Return the value of an attribute.
* @param name [in] : Name of the attribute
* @return Value of the attribute
*/
virtual const char* getAttribute(const char* name) const = 0;
//PCDATA
/**
* @brief Set the PCDATA of the node.
* @param data [in] : Value of the PCDATA
*/
virtual void setPCData(const char* data) = 0;
/**
* @brief Apppend a string to the current PCDATA of the node
* @param data [in] : Value of teh PCDATA to append
*/
virtual void appendPCData(const char* data) = 0;
/**
* @brief Return the PCDATA of the node.
* @return Value of PCDATA
*/
virtual const char* getPCData() const = 0;
//Child
/**
* @brief Add a node child of the
* @param node [in] : The Node that will became the new child
*/
virtual void addChild(IXMLNode* node) = 0;
/**
* @brief Return the ith child of the node.
* @param index [in] : index of the child.
* @return The ith child of the node.
*/
virtual IXMLNode* getChild(const size_t index) const = 0;
/**
* @brief Return the first child with the name name.
* @param name [in]] : Name of th child
* @return The first child of the node which name is name.
*/
virtual IXMLNode* getChildByName(const char* name) const = 0;
/**
* @brief Return the amount of child the node has.
* @return Amount of child.
*/
virtual size_t getChildCount() const = 0;
//XML generation
/**
* @brief Return a string which contains the XML of the node. The string is dynamically instantiate so
* it requires to be free.
* @param depth [in] : Amount of indentation
* @return XML string describing the node and its childs.
*/
virtual char* getXML(const size_t depth = 0) const = 0;
protected:
virtual ~IXMLNode() {}
};
/**
* @brief Create a new node with the name name. The node is created dynamically and requires to be free.
* @param name [in] : Name of the node
* @return New node
*/
extern XML_API IXMLNode* createNode(const char* name);
} // namespace XML
@@ -0,0 +1,60 @@
#pragma once
#include "IReader.h"
namespace XML {
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass>
class TReaderCallbackProxy1 final : public IReaderCallback
{
public:
TReaderCallbackProxy1(TOwnerClass& ownerObject,
void (TOwnerClass::*mfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute),
void (TOwnerClass::*mfpProcessChildData)(const char* data), void (TOwnerClass::*mfpCloseChild)())
: m_ownerObject(ownerObject), m_mfpOpenChild(mfpOpenChild), m_mfpProcessChildData(mfpProcessChildData), m_mfpCloseChild(mfpCloseChild) { }
void openChild(const char* name, const char** attributeName, const char** attributeValue, const size_t nAttribute) override
{
if (m_mfpOpenChild) { m_ownerObject.m_mfpOpenChild(name, attributeName, attributeValue, nAttribute); }
}
void processChildData(const char* data) override { if (m_mfpProcessChildData) { m_ownerObject.m_mfpProcessChildData(data); } }
void closeChild() override { if (m_mfpCloseChild) { m_ownerObject.m_mfpCloseChild(); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute);
void (TOwnerClass::*m_mfpProcessChildData)(const char* data);
void (TOwnerClass::*m_mfpCloseChild)();
};
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass,
void (TOwnerClass::*TMfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute),
void (TOwnerClass::*TMfpProcessChildData)(const char* data), void (TOwnerClass::*mfpCloseChild)()>
class TReaderCallbackProxy2 final : public IReaderCallback
{
public:
TReaderCallbackProxy2(TOwnerClass ownerObject)
: m_ownerObject(ownerObject), m_mfpOpenChild(TMfpOpenChild), m_mfpProcessChildData(TMfpProcessChildData), m_mfpCloseChild(mfpCloseChild) { }
void openChild(const char* name, const char** attributeName, const char** attributeValue, const size_t nAttribute) override
{
if (TMfpOpenChild) { m_ownerObject.mfpOpenChild(name, attributeName, attributeValue, nAttribute); }
}
void processChildData(const char* data) override { if (TMfpProcessChildData) { m_ownerObject.mfpProcessChildData(data); } }
void closeChild() override { if (mfpCloseChild) { m_ownerObject.mfpCloseChild(); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpOpenChild)(const char* name, const char** attributeName, const char** attributeValue, size_t nAttribute);
void (TOwnerClass::*m_mfpProcessChildData)(const char* data);
void (TOwnerClass::*m_mfpCloseChild)();
};
} // namespace XML
@@ -0,0 +1,42 @@
#pragma once
#include "IWriter.h"
namespace XML {
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass>
class TWriterCallbackProxy1 final : public IWriterCallback
{
public:
TWriterCallbackProxy1(TOwnerClass& ownerObject, void (TOwnerClass::*mfpWrite)(const char* str))
: m_ownerObject(ownerObject), m_mfpWrite(mfpWrite) { }
void write(const char* str) override { if (m_mfpWrite) { m_ownerObject.m_mfpWrite(str); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpWrite)(const char* str);
};
// ________________________________________________________________________________________________________________
//
template <class TOwnerClass, void (TOwnerClass::*TMfpWrite)(const char* str)>
class TWriterCallbackProxy2 final : public IWriterCallback
{
public:
TWriterCallbackProxy2(TOwnerClass ownerObject) : m_ownerObject(ownerObject), m_mfpWrite(TMfpWrite) { }
void write(const char* str) override { if (TMfpWrite) { m_ownerObject.mfpWrite(str); } }
protected:
TOwnerClass& m_ownerObject;
void (TOwnerClass::*m_mfpWrite)(const char* str);
};
// ________________________________________________________________________________________________________________
//
} // namespace XML
@@ -0,0 +1,25 @@
#pragma once
#include <ov_common_defines.h>
#if defined XML_Shared
# if defined TARGET_OS_Windows
# define XML_API_Export __declspec(dllexport)
# define XML_API_Import __declspec(dllimport)
# elif defined TARGET_OS_Linux
# define XML_API_Export __attribute__((visibility("default")))
# define XML_API_Import __attribute__((visibility("default")))
# else
# define XML_API_Export
# define XML_API_Import
# endif
#else
# define XML_API_Export
# define XML_API_Import
#endif
#if defined XML_Exports
# define XML_API XML_API_Export
#else
# define XML_API XML_API_Import
#endif
@@ -0,0 +1,105 @@
#include "IReader.h"
#include <expat.h>
#include <string>
#include <iostream>
namespace XML {
class CReader final : public IReader
{
public:
explicit CReader(IReaderCallback& callback);
bool processData(const void* buffer, const size_t size) override;
void release() override;
void openChild(const char* name, const char** sAttributeName, const char** sAttributeValue, const size_t nAttribute);
void processChildData(const char* data);
void closeChild();
protected:
IReaderCallback& m_callback;
XML_Parser m_pXMLParser;
std::string m_data;
};
static void XMLCALL ExpatXMLStart(void* data, const char* element, const char** attribute);
static void XMLCALL ExpatXMLEnd(void* data, const char* element);
static void XMLCALL ExpatXMLData(void* data, const char* value, int length);
CReader::CReader(IReaderCallback& callback) : m_callback(callback), m_pXMLParser(nullptr)
{
m_pXMLParser = XML_ParserCreate(nullptr);
XML_SetElementHandler(m_pXMLParser, ExpatXMLStart, ExpatXMLEnd);
XML_SetCharacterDataHandler(m_pXMLParser, ExpatXMLData);
XML_SetUserData(m_pXMLParser, this);
}
bool CReader::processData(const void* buffer, const size_t size)
{
// $$$ TODO take 64bits size into consideration
const XML_Status status = XML_Parse(m_pXMLParser, static_cast<const char*>(buffer), static_cast<const int>(size), false);
if (status != XML_STATUS_OK)
{
const XML_Error error = XML_GetErrorCode(m_pXMLParser);
// Although printf() is not too elegant, this component has no context to use e.g. LogManager -> printf() is better than a silent fail.
std::cout << "processData(): expat error " << error << " on the line " << XML_GetCurrentLineNumber(m_pXMLParser) << " of the input .xml\n";
}
return (status == XML_STATUS_OK);
}
void CReader::release()
{
XML_ParserFree(m_pXMLParser);
delete this;
}
void CReader::openChild(const char* name, const char** sAttributeName, const char** sAttributeValue, const size_t nAttribute)
{
m_callback.openChild(name, sAttributeName, sAttributeValue, nAttribute);
m_data = "";
}
void CReader::processChildData(const char* data) { m_data += data; }
void CReader::closeChild()
{
if (m_data.size() != 0) { m_callback.processChildData(m_data.c_str()); }
m_data = "";
m_callback.closeChild();
}
XML_API IReader* createReader(IReaderCallback& callback) { return new CReader(callback); }
static void XMLCALL ExpatXMLStart(void* data, const char* element, const char** attribute)
{
size_t nAttribute = 0;
while (attribute[nAttribute++]) { }
nAttribute >>= 1;
// $$$ TODO take 64bits size into consideration
const char** name = new const char*[nAttribute];
const char** value = new const char*[nAttribute];
for (size_t i = 0; i < nAttribute; ++i)
{
name[i] = attribute[(i << 1)];
value[i] = attribute[(i << 1) + 1];
}
static_cast<CReader*>(data)->openChild(element, name, value, nAttribute);
delete [] name;
delete [] value;
}
static void XMLCALL ExpatXMLEnd(void* data, const char* /*element*/) { static_cast<CReader*>(data)->closeChild(); }
static void XMLCALL ExpatXMLData(void* data, const char* value, const int length)
{
const std::string str(value, length);
static_cast<CReader*>(data)->processChildData(str.c_str());
}
} // namespace XML
@@ -0,0 +1,129 @@
#include "IWriter.h"
#include <stack>
#include <string>
namespace XML {
class CWriter final : public IWriter
{
public:
explicit CWriter(IWriterCallback& callback) : m_callback(callback) {}
bool openChild(const char* name) override;
bool setChildData(const char* data) override;
bool setAttribute(const char* name, const char* value) override;
bool closeChild() override;
void release() override;
private:
static void sanitize(std::string& str, bool escapeQuotes = true);
protected:
IWriterCallback& m_callback;
std::stack<std::string> m_nodes;
bool m_hasChild = false;
bool m_hasData = false;
bool m_hasClosedOpeningNode = true;
};
bool CWriter::openChild(const char* name)
{
if (name == nullptr) { return false; }
if (m_hasData) { return false; }
if (!m_hasClosedOpeningNode)
{
m_callback.write(">");
m_hasClosedOpeningNode = true;
}
const std::string indent(m_nodes.size(), '\t');
const std::string res = (!m_nodes.empty() ? std::string("\n") : std::string("")) + indent + "<" + name;
m_callback.write(res.c_str());
m_nodes.push(name);
m_hasChild = false;
m_hasData = false;
m_hasClosedOpeningNode = false;
return true;
}
bool CWriter::setChildData(const char* data)
{
if (data == nullptr) { return false; }
if (m_hasChild) { return false; }
if (!m_hasClosedOpeningNode)
{
m_callback.write(">");
m_hasClosedOpeningNode = true;
}
std::string str(data);
this->sanitize(str, false);
m_callback.write(str.c_str());
m_hasChild = false;
m_hasData = true;
return true;
}
bool CWriter::setAttribute(const char* name, const char* value)
{
if (name == nullptr || value == nullptr || m_hasChild || m_hasData || m_hasClosedOpeningNode) { return false; }
std::string str(value);
this->sanitize(str);
const std::string res = std::string(" ") + name + "=\"" + str + "\"";
m_callback.write(res.c_str());
return true;
}
bool CWriter::closeChild()
{
if (m_nodes.empty()) { return false; }
if (!m_hasClosedOpeningNode)
{
m_callback.write(">");
m_hasClosedOpeningNode = true;
}
const std::string indent(m_nodes.size() - 1, '\t');
const std::string res = ((m_hasData || !m_hasChild) ? std::string("") : std::string("\n") + indent) + "</" + m_nodes.top() + ">";
m_callback.write(res.c_str());
m_nodes.pop();
m_hasChild = true;
m_hasData = false;
return true;
}
void CWriter::release()
{
while (!m_nodes.empty()) { closeChild(); }
delete this;
}
void CWriter::sanitize(std::string& str, const bool escapeQuotes)
{
std::string::size_type i;
if (str.length() != 0)
{
// mandatory, this one should be the first because the other ones add & symbols
for (i = str.find("&", 0); i != std::string::npos; i = str.find("&", i + 1)) { str.replace(i, 1, "&amp;"); }
for (i = str.find('<', 0); i != std::string::npos; i = str.find('<', i + 1)) { str.replace(i, 1, "&lt;"); }
for (i = str.find('>', 0); i != std::string::npos; i = str.find('>', i + 1)) { str.replace(i, 1, "&gt;"); }
// Quotes need only be escaped in attributes
if (escapeQuotes)
{
for (i = str.find('\'', 0); i != std::string::npos; i = str.find('\'', i + 1)) { str.replace(i, 1, "&apos;"); }
for (i = str.find('\"', 0); i != std::string::npos; i = str.find('\"', i + 1)) { str.replace(i, 1, "&quot;"); }
}
}
}
XML_API IWriter* createWriter(IWriterCallback& callback) { return new CWriter(callback); }
} // namespace XML
@@ -0,0 +1,205 @@
#include "IXMLHandler.h"
#include <expat.h>
#include <cstring>
#include <stack>
#include <fstream>
#include <sstream>
#include <fs/Files.h>
namespace XML {
class IXMLHandlerImpl final : public IXMLHandler
{
public:
void release() override;
IXMLHandlerImpl();
//Parsing
IXMLNode* parseFile(const char* sPath) override;
IXMLNode* parseString(const char* str, const size_t& size) override;
//XML extraction
bool writeXMLInFile(const IXMLNode& rNode, const char* sPath) const override;
//Error handling
std::string getLastErrorString() const override;
//Internal function for parsing
void openChild(const char* name, const char** sAttributeName, const char** sAttributeValue, const size_t nAttribute);
void processChildData(const char* data);
void closeChild();
std::stringstream& getErrorStringStream() const;
protected:
~IXMLHandlerImpl() override;
private:
XML_Parser m_pXMLParser;
std::stack<IXMLNode*> m_oNodeStack;
IXMLNode* m_pRootNode;
mutable std::stringstream m_ssErrorStringStream;
};
//Callback for expat
static void XMLCALL ExpatXMLStart(void* pData, const char* element, const char** attribute);
static void XMLCALL ExpatXMLEnd(void* data, const char* element);
static void XMLCALL ExpatXMLData(void* data, const char* value, int length);
IXMLHandlerImpl::~IXMLHandlerImpl()
{
XML_ParserFree(m_pXMLParser);
while (!m_oNodeStack.empty())
{
IXMLNode* node = m_oNodeStack.top();
node->release();
m_oNodeStack.pop();
}
}
void IXMLHandlerImpl::release() { delete this; }
IXMLHandlerImpl::IXMLHandlerImpl(): m_pXMLParser(nullptr), m_pRootNode(nullptr)
{
m_pXMLParser = XML_ParserCreate(nullptr);
XML_SetElementHandler(m_pXMLParser, ExpatXMLStart, ExpatXMLEnd);
XML_SetCharacterDataHandler(m_pXMLParser, ExpatXMLData);
XML_SetUserData(m_pXMLParser, this);
}
IXMLNode* IXMLHandlerImpl::parseFile(const char* sPath)
{
std::ifstream file;
FS::Files::openIFStream(file, sPath, std::ios::binary);
if (file.is_open())
{
//Compute size
file.seekg(0, std::ios::end);
const size_t fileLen = size_t(file.tellg());
file.seekg(0, std::ios::beg);
//Read the file
char* buffer = new char[fileLen];
file.read(buffer, fileLen);
file.close();
//Start the parsing with the other function
IXMLNode* res = parseString(buffer, fileLen);
delete[] buffer;
return res;
}
this->getErrorStringStream() << "Error : unable to open the file" << sPath << "." << std::endl;
return nullptr;
}
IXMLNode* IXMLHandlerImpl::parseString(const char* str, const size_t& size)
{
m_pRootNode = nullptr;
const XML_Status status = XML_Parse(m_pXMLParser, str, int(size), false);
//We delete what is still in the stack
while (!m_oNodeStack.empty())
{
// std::cout << "Warning : the file has been parsed but some tags are not closed. The file is probably not well-formed." << std::endl;
IXMLNode* node = m_oNodeStack.top();
node->release();
m_oNodeStack.pop();
}
if (status != XML_STATUS_OK)
{
const XML_Error code = XML_GetErrorCode(m_pXMLParser);
// Although printf() is not too elegant, this component has no context to use e.g. LogManager -> printf() is better than a silent fail.
this->getErrorStringStream() << "processData(): expat error " << code << " on the line " << XML_GetCurrentLineNumber(m_pXMLParser) <<
" of the input .xml\n";
return nullptr;
}
return m_pRootNode;
}
bool IXMLHandlerImpl::writeXMLInFile(const IXMLNode& rNode, const char* sPath) const
{
std::ofstream file;
FS::Files::openOFStream(file, sPath, std::ios::binary);
if (file.is_open())
{
char* xml = rNode.getXML();
file.write(xml, strlen(xml));
file.close();
free(xml);
return true;
}
this->getErrorStringStream() << "Error : unable to open the file " << sPath << "." << std::endl;
return false;
}
void IXMLHandlerImpl::openChild(const char* name, const char** sAttributeName, const char** sAttributeValue, const size_t nAttribute)
{
IXMLNode* node = createNode(name);
for (size_t i = 0; i < nAttribute; ++i) { node->addAttribute(sAttributeName[i], sAttributeValue[i]); }
m_oNodeStack.push(node);
}
void IXMLHandlerImpl::processChildData(const char* data)
{
IXMLNode* node = m_oNodeStack.top();
node->appendPCData(data);
}
void IXMLHandlerImpl::closeChild()
{
IXMLNode* node = m_oNodeStack.top();
m_oNodeStack.pop();
//If the stack is empty this means that node is the root
if (m_oNodeStack.empty()) { m_pRootNode = node; }
else
{//If node, that means that the node if
IXMLNode* parentNode = m_oNodeStack.top();
parentNode->addChild(node);
}
}
std::stringstream& IXMLHandlerImpl::getErrorStringStream() const
{
// empty the string contents and clear the error flags
m_ssErrorStringStream.str("");
m_ssErrorStringStream.clear();
return m_ssErrorStringStream;
}
std::string IXMLHandlerImpl::getLastErrorString() const { return m_ssErrorStringStream.str(); }
static void XMLCALL ExpatXMLStart(void* pData, const char* element, const char** attribute)
{
size_t nAttribute = 0;
while (attribute[nAttribute++]) { }
nAttribute >>= 1;
// $$$ TODO take 64bits size into consideration
const char** name = new const char*[size_t(nAttribute)];
const char** value = new const char*[size_t(nAttribute)];
for (size_t i = 0; i < nAttribute; ++i)
{
name[i] = attribute[(i << 1)];
value[i] = attribute[(i << 1) + 1];
}
static_cast<IXMLHandlerImpl*>(pData)->openChild(element, name, value, nAttribute);
delete [] name;
delete [] value;
}
static void XMLCALL ExpatXMLEnd(void* data, const char* /*element*/) { static_cast<IXMLHandlerImpl*>(data)->closeChild(); }
static void XMLCALL ExpatXMLData(void* data, const char* value, const int length)
{
const std::string str(value, length);
static_cast<IXMLHandlerImpl*>(data)->processChildData(str.c_str());
}
OV_API IXMLHandler* createXMLHandler() { return new IXMLHandlerImpl(); }
} // namespace XML
@@ -0,0 +1,160 @@
#include "IXMLNode.h"
#include <string>
#include <cstring>
#include <stdexcept>
#include <vector>
#include <map>
//This is to remove the warning on windows about strdup
#if defined TARGET_OS_Windows
#define strdup _strdup
#endif
namespace XML {
class IXMLNodeImpl final : public IXMLNode
{
public:
explicit IXMLNodeImpl(const char* name) : m_name(name) {}
const char* getName() const override { return m_name.c_str(); }
void release() override { delete this; }
//Attribute
bool addAttribute(const char* name, const char* value) override;
bool hasAttribute(const char* name) const override { return m_attibutes.count(name) != 0; }
const char* getAttribute(const char* name) const override;
//PCDATA
void setPCData(const char* data) override;
void appendPCData(const char* data) override;
const char* getPCData() const override { return m_pcData.c_str(); }
//Child
void addChild(IXMLNode* node) override { m_nodes.push_back(node); }
IXMLNode* getChild(const size_t index) const override { return m_nodes[index]; }
IXMLNode* getChildByName(const char* name) const override;
size_t getChildCount() const override;
//XMl generation
char* getXML(const size_t depth = 0) const override;
protected:
~IXMLNodeImpl() override { for (size_t i = 0; i < getChildCount(); ++i) { getChild(i)->release(); } }
private:
static std::string sanitize(const std::string& str);
static void applyIndentation(std::string& str, const size_t depth);
std::vector<IXMLNode*> m_nodes;
std::map<std::string, std::string> m_attibutes;
std::string m_name = "";
std::string m_pcData = "";
bool m_hasPCData = false;
};
bool IXMLNodeImpl::addAttribute(const char* name, const char* value)
{
m_attibutes[name] = value;
return true;
}
const char* IXMLNodeImpl::getAttribute(const char* name) const
{
const char* res = nullptr;
const std::string str(name);
const auto it = m_attibutes.find(str);
if (it != m_attibutes.end()) { res = (*it).second.c_str(); }
return res;
}
void IXMLNodeImpl::setPCData(const char* data)
{
m_pcData = data;
m_hasPCData = true;
}
void IXMLNodeImpl::appendPCData(const char* data)
{
m_pcData += data;
m_hasPCData = true;
}
IXMLNode* IXMLNodeImpl::getChildByName(const char* name) const
{
for (auto it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IXMLNode* node = static_cast<IXMLNode*>(*it);
if (strcmp(node->getName(), name) == 0) { return node; }
}
return nullptr;
}
size_t IXMLNodeImpl::getChildCount() const { return m_nodes.size(); }
std::string IXMLNodeImpl::sanitize(const std::string& str)
{
std::string::size_type i;
std::string res(str);
if (res.length() != 0)
{
// mandatory, this one should be the first because the other ones add & symbols
for (i = res.find('&', 0); i != std::string::npos; i = res.find('&', i + 1)) { res.replace(i, 1, "&amp;"); }
// other escape sequences
for (i = res.find('\"', 0); i != std::string::npos; i = res.find('\"', i + 1)) { res.replace(i, 1, "&quot;"); }
for (i = res.find('<', 0); i != std::string::npos; i = res.find('<', i + 1)) { res.replace(i, 1, "&lt;"); }
for (i = res.find('>', 0); i != std::string::npos; i = res.find('>', i + 1)) { res.replace(i, 1, "&gt;"); }
}
return res;
}
void IXMLNodeImpl::applyIndentation(std::string& str, const size_t depth)
{
const std::string indent(depth, '\t');
str.append(indent);
}
char* IXMLNodeImpl::getXML(const size_t depth) const
{
std::string str;
applyIndentation(str, depth);
str += "<" + m_name;
//Add attributes if we have some
if (!m_attibutes.empty())
{
for (auto it = m_attibutes.begin(); it != m_attibutes.end(); ++it) { str += " " + it->first + "=\"" + sanitize(it->second) + "\""; }
}
//If we have nothing else to print let's close the node and return
if (!m_hasPCData && m_nodes.empty())
{
str += "/>";
return ::strdup(str.c_str());
}
str += ">";
if (m_hasPCData) { str = str + sanitize(m_pcData); }
for (auto it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
IXMLNode* node = static_cast<IXMLNode*>(*it);
str += std::string("\n") + node->getXML(depth + 1);
}
if (!m_nodes.empty())
{
str = str + "\n";
applyIndentation(str, depth);
}
str = str + "</" + m_name + ">";
return ::strdup(str.c_str());
}
OV_API IXMLNode* createNode(const char* name) { return new IXMLNodeImpl(name); }
} // namespace XML