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,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