Files
ANSCORE/anslicensing/propertycollection.h

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");
}
}
};