init
This commit is contained in:
@@ -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, "&"); }
|
||||
for (i = str.find('<', 0); i != std::string::npos; i = str.find('<', i + 1)) { str.replace(i, 1, "<"); }
|
||||
for (i = str.find('>', 0); i != std::string::npos; i = str.find('>', i + 1)) { str.replace(i, 1, ">"); }
|
||||
|
||||
// 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, "'"); }
|
||||
for (i = str.find('\"', 0); i != std::string::npos; i = str.find('\"', i + 1)) { str.replace(i, 1, """); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, "&"); }
|
||||
// other escape sequences
|
||||
for (i = res.find('\"', 0); i != std::string::npos; i = res.find('\"', i + 1)) { res.replace(i, 1, """); }
|
||||
for (i = res.find('<', 0); i != std::string::npos; i = res.find('<', i + 1)) { res.replace(i, 1, "<"); }
|
||||
for (i = res.find('>', 0); i != std::string::npos; i = res.find('>', i + 1)) { res.replace(i, 1, ">"); }
|
||||
}
|
||||
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
|
||||
Reference in New Issue
Block a user