init
This commit is contained in:
@@ -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