#pragma once #include "precomp.h" #include "uniconv.h" #include "tinyxml2.h" #include "picojson.h" #include #include #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 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::iterator entry; if ((entry = m_properties.find(path)) == m_properties.end()) { PathEntry entry; entry.path = path; entry.nameValueCollection.insert(std::map::value_type(propName, value)); m_properties.insert(std::map::value_type(path, entry)); } else entry->second.nameValueCollection.insert(std::map::value_type(propName, value)); } const char * GetProperty(const char * path, const char * name) const { std::map::const_iterator entry = m_properties.find(path); if (entry == m_properties.end()) return NULL; std::map::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().c_str()); else { SetProperty(path.substr(0, pos).c_str(), path.substr(pos + 1).c_str(), iter->second.get().c_str()); } } } void SaveJson(std::string & json, const char * indent = "") { bool first = true; json.append("{"); for (std::map::const_iterator item = m_properties.begin(); item != m_properties.end(); item++) { for (std::map::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 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 * 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::const_iterator item = sortedPathList->begin(); item != sortedPathList->end(); item++) { if (strcmp(item->second.path.c_str(), pathPrefix.c_str()) == 0) { for (std::map::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 innerPathList; for (std::map::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::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"); } } void PathListToJson(const std::string pathPrefix, std::map * 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::const_iterator item = sortedPathList->begin(); item != sortedPathList->end(); item++) { if (strcmp(item->second.path.c_str(), pathPrefix.c_str()) == 0) { for (std::map::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 innerPathList; for (std::map::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::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"); } } };