354 lines
8.9 KiB
C++
354 lines
8.9 KiB
C++
#pragma once
|
|
|
|
#include "precomp.h"
|
|
#include "uniconv.h"
|
|
#include "tinyxml2.h"
|
|
#include "picojson.h"
|
|
#include <map>
|
|
#include <list>
|
|
|
|
#ifndef MAX_XML_BUFFER_SIZE
|
|
#define MAX_XML_BUFFER_SIZE 4096
|
|
#endif
|
|
|
|
class PathEntry
|
|
{
|
|
friend class PropertyCollection;
|
|
|
|
public:
|
|
PathEntry()
|
|
{
|
|
processed = false;
|
|
}
|
|
|
|
PathEntry(const PathEntry & copy)
|
|
{
|
|
processed = false;
|
|
path = copy.path;
|
|
nameValueCollection = copy.nameValueCollection;
|
|
}
|
|
|
|
PathEntry(const PathEntry * copy)
|
|
{
|
|
processed = false;
|
|
path = copy->path;
|
|
nameValueCollection = copy->nameValueCollection;
|
|
}
|
|
|
|
private:
|
|
string path;
|
|
std::map<string, string> nameValueCollection;
|
|
bool processed;
|
|
};
|
|
|
|
class PropertyCollection
|
|
{
|
|
public:
|
|
PropertyCollection()
|
|
{
|
|
|
|
}
|
|
|
|
void SetProperty(const char * path, const char * name, const char * value)
|
|
{
|
|
string propName = (name != NULL) ? name : "";
|
|
std::map<string, PathEntry>::iterator entry;
|
|
|
|
if ((entry = m_properties.find(path)) == m_properties.end())
|
|
{
|
|
PathEntry entry;
|
|
entry.path = path;
|
|
entry.nameValueCollection.insert(std::map<string, string>::value_type(propName, value));
|
|
m_properties.insert(std::map<string, PathEntry>::value_type(path, entry));
|
|
} else
|
|
entry->second.nameValueCollection.insert(std::map<string, string>::value_type(propName, value));
|
|
}
|
|
|
|
const char * GetProperty(const char * path, const char * name) const
|
|
{
|
|
std::map<string, PathEntry>::const_iterator entry = m_properties.find(path);
|
|
|
|
if (entry == m_properties.end())
|
|
return NULL;
|
|
|
|
std::map<string, string>::const_iterator nv = entry->second.nameValueCollection.find((name != NULL) ? name : "");
|
|
|
|
if (nv == entry->second.nameValueCollection.end())
|
|
return NULL;
|
|
|
|
return nv->second.c_str();
|
|
}
|
|
|
|
void LoadXml(tinyxml2::XMLElement * src)
|
|
{
|
|
XmlToPathList("", src);
|
|
}
|
|
|
|
void SaveXml(std::string & xml)
|
|
{
|
|
PathListToXml("/", &m_properties, xml, 1);
|
|
}
|
|
|
|
void LoadJson(picojson::object & json)
|
|
{
|
|
|
|
string path;
|
|
size_t pos;
|
|
|
|
for (picojson::object::iterator iter = json.begin(); iter != json.end(); iter++)
|
|
{
|
|
path = iter->first.c_str();
|
|
|
|
if ((pos = path.find_last_of('#')) == string::npos)
|
|
SetProperty(path.c_str(), NULL, iter->second.get<std::string>().c_str());
|
|
else
|
|
{
|
|
SetProperty(path.substr(0, pos).c_str(), path.substr(pos + 1).c_str(), iter->second.get<std::string>().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void SaveJson(std::string & json, const char * indent = "")
|
|
{
|
|
|
|
bool first = true;
|
|
json.append("{");
|
|
|
|
for (std::map<string, PathEntry>::const_iterator item = m_properties.begin(); item != m_properties.end(); item++)
|
|
{
|
|
for (std::map<string, string>::const_iterator innerItem = item->second.nameValueCollection.begin(); innerItem != item->second.nameValueCollection.end(); innerItem++)
|
|
{
|
|
if (!first) json.append(",");
|
|
json.append("\n");
|
|
json.append(indent);
|
|
json.append("\t");
|
|
json.append("\"");
|
|
json.append(item->first.c_str());
|
|
if (innerItem->first.length() > 0)
|
|
{
|
|
json.append("#"); json.append(innerItem->first.c_str());
|
|
}
|
|
json.append("\":\""); json.append(innerItem->second.c_str()); json.append("\"");
|
|
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
json.append("\n"); json.append(indent); json.append("}");
|
|
}
|
|
|
|
size_t GetSize() const
|
|
{
|
|
return m_properties.size();
|
|
}
|
|
|
|
private:
|
|
std::map<string, PathEntry> m_properties;
|
|
|
|
void XmlToPathList(const std::string prefix, const tinyxml2::XMLElement * node)
|
|
{
|
|
const char * text = node->GetText();
|
|
|
|
if (text != NULL)
|
|
SetProperty(prefix.c_str(), "", text);
|
|
|
|
const tinyxml2::XMLAttribute * attr = node->FirstAttribute();
|
|
|
|
// risk of stack overflow because _alloca is called in loop. However the current number of properties
|
|
// is very low so it is acceptable
|
|
while (attr)
|
|
{
|
|
SetProperty(prefix.c_str(), attr->Name(), attr->Value());
|
|
|
|
attr = attr->Next();
|
|
}
|
|
|
|
tinyxml2::XMLElement * child;
|
|
|
|
for (child = (tinyxml2::XMLElement *)node->FirstChildElement(); child != NULL; child = child->NextSiblingElement())
|
|
{
|
|
XmlToPathList(prefix + "/" + child->Name(), child);
|
|
}
|
|
}
|
|
|
|
void PathListToXml(const std::string pathPrefix, std::map<string, PathEntry> * sortedPathList, std::string & xml, int indent)
|
|
{
|
|
std::string elementValue;
|
|
bool startElementClosed = false;
|
|
|
|
if (pathPrefix != "/")
|
|
{
|
|
if (indent > 0)
|
|
xml.append(indent, '\t');
|
|
|
|
xml.append("<"); xml.append(1 + strrchr(pathPrefix.c_str(), '/'));
|
|
}
|
|
|
|
for (std::map<string, PathEntry>::const_iterator item = sortedPathList->begin(); item != sortedPathList->end(); item++)
|
|
{
|
|
if (strcmp(item->second.path.c_str(), pathPrefix.c_str()) == 0)
|
|
{
|
|
for (std::map<string, string>::const_iterator nvItem = item->second.nameValueCollection.begin();
|
|
nvItem != item->second.nameValueCollection.end();
|
|
nvItem++)
|
|
{
|
|
if (nvItem->first.length() == 0)
|
|
{
|
|
elementValue = nvItem->second.c_str();
|
|
} else
|
|
{
|
|
xml.append(" ");
|
|
xml.append(nvItem->first.c_str());
|
|
xml.append("=\"");
|
|
xml.append(nvItem->second.c_str());
|
|
xml.append("\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pathPrefix != "/")
|
|
{
|
|
xml.append(">");
|
|
|
|
if (elementValue.length() > 0)
|
|
{
|
|
xml.append(elementValue);
|
|
} else
|
|
if (indent >= 0)
|
|
xml.append("\n");
|
|
}
|
|
|
|
bool finished = false;
|
|
|
|
while (!finished)
|
|
{
|
|
finished = true;
|
|
std::string firstUnprocessedPrefix;
|
|
|
|
std::map<string, PathEntry> innerPathList;
|
|
|
|
for (std::map<string, PathEntry>::iterator item = sortedPathList->begin(); item != sortedPathList->end(); item++)
|
|
{
|
|
if (!item->second.processed && item->second.path.length() > pathPrefix.length() && (firstUnprocessedPrefix.empty() || strncmp(item->second.path.c_str(), firstUnprocessedPrefix.c_str(), firstUnprocessedPrefix.length()) == 0))
|
|
{
|
|
if (firstUnprocessedPrefix.empty())
|
|
{
|
|
int nextSep = item->second.path.find('/', pathPrefix.length() + 1);
|
|
firstUnprocessedPrefix = item->second.path.substr(0, (nextSep >= 0) ? nextSep : item->second.path.length()).c_str();
|
|
}
|
|
|
|
innerPathList.insert(std::map<string, PathEntry>::value_type(item->first, item->second));
|
|
|
|
item->second.processed = true;
|
|
finished = false;
|
|
}
|
|
}
|
|
|
|
if (!finished)
|
|
PathListToXml(firstUnprocessedPrefix, &innerPathList, xml, (indent >= 0) ? indent + 1 : indent);
|
|
}
|
|
|
|
if (pathPrefix != "/")
|
|
{
|
|
if (indent > 0 && elementValue.length() == 0)
|
|
xml.append(indent, '\t');
|
|
|
|
xml.append("</"); xml.append(1 + strrchr(pathPrefix.c_str(), '/')); xml.append(">");
|
|
|
|
if (indent >= 0) xml.append("\n");
|
|
}
|
|
}
|
|
|
|
void PathListToJson(const std::string pathPrefix, std::map<string, PathEntry> * sortedPathList, std::string & xml, int indent)
|
|
{
|
|
std::string elementValue;
|
|
bool startElementClosed = false;
|
|
|
|
if (pathPrefix != "/")
|
|
{
|
|
if (indent > 0)
|
|
xml.append(indent, '\t');
|
|
|
|
xml.append("\""); xml.append(1 + strrchr(pathPrefix.c_str(), '/')); xml.append("\":");
|
|
}
|
|
|
|
for (std::map<string, PathEntry>::const_iterator item = sortedPathList->begin(); item != sortedPathList->end(); item++)
|
|
{
|
|
if (strcmp(item->second.path.c_str(), pathPrefix.c_str()) == 0)
|
|
{
|
|
for (std::map<string, string>::const_iterator nvItem = item->second.nameValueCollection.begin();
|
|
nvItem != item->second.nameValueCollection.end();
|
|
nvItem++)
|
|
{
|
|
if (nvItem->first.length() == 0)
|
|
{
|
|
elementValue = nvItem->second.c_str();
|
|
}
|
|
else
|
|
{
|
|
xml.append(" ");
|
|
xml.append(nvItem->first.c_str());
|
|
xml.append("=\"");
|
|
xml.append(nvItem->second.c_str());
|
|
xml.append("\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pathPrefix != "/")
|
|
{
|
|
xml.append(">");
|
|
|
|
if (elementValue.length() > 0)
|
|
{
|
|
xml.append(elementValue);
|
|
}
|
|
else
|
|
if (indent >= 0)
|
|
xml.append("\n");
|
|
}
|
|
|
|
bool finished = false;
|
|
|
|
while (!finished)
|
|
{
|
|
finished = true;
|
|
std::string firstUnprocessedPrefix;
|
|
|
|
std::map<string, PathEntry> innerPathList;
|
|
|
|
for (std::map<string, PathEntry>::iterator item = sortedPathList->begin(); item != sortedPathList->end(); item++)
|
|
{
|
|
if (!item->second.processed && item->second.path.length() > pathPrefix.length() && (firstUnprocessedPrefix.empty() || strncmp(item->second.path.c_str(), firstUnprocessedPrefix.c_str(), firstUnprocessedPrefix.length()) == 0))
|
|
{
|
|
if (firstUnprocessedPrefix.empty())
|
|
{
|
|
int nextSep = item->second.path.find('/', pathPrefix.length() + 1);
|
|
firstUnprocessedPrefix = item->second.path.substr(0, (nextSep >= 0) ? nextSep : item->second.path.length()).c_str();
|
|
}
|
|
|
|
innerPathList.insert(std::map<string, PathEntry>::value_type(item->first, item->second));
|
|
|
|
item->second.processed = true;
|
|
finished = false;
|
|
}
|
|
}
|
|
|
|
if (!finished)
|
|
PathListToXml(firstUnprocessedPrefix, &innerPathList, xml, (indent >= 0) ? indent + 1 : indent);
|
|
}
|
|
|
|
if (pathPrefix != "/")
|
|
{
|
|
if (indent > 0 && elementValue.length() == 0)
|
|
xml.append(indent, '\t');
|
|
|
|
xml.append("}");
|
|
|
|
if (indent >= 0) xml.append("\n");
|
|
}
|
|
}
|
|
};
|