1089 lines
31 KiB
C++
Executable File
1089 lines
31 KiB
C++
Executable File
/*
|
|
* DataElement/DataNode/DataTree -- structured serialization/unserialization system
|
|
* designed for the CoolMule project :)
|
|
*
|
|
Copyright (C) 2003 by Charles J. Cliffe
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
|
|
#include "DataTree.h"
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <cstdlib>
|
|
#include <algorithm>
|
|
|
|
|
|
/* DataElement class */
|
|
|
|
using namespace std;
|
|
|
|
#define STRINGIFY(A) #A
|
|
|
|
#define MAX_STR_SIZE (1024)
|
|
|
|
DataElement::DataElement() : data_type(DataElement::Type::DATA_NULL) {
|
|
//
|
|
}
|
|
|
|
DataElement::DataElement(DataElement &cloneFrom) : data_type(cloneFrom.getDataType()), data_val(cloneFrom.data_val), data_val_vector(cloneFrom.data_val_vector) {
|
|
//
|
|
}
|
|
|
|
DataElement::~DataElement() = default;
|
|
|
|
|
|
char * DataElement::getDataPointer() {
|
|
|
|
if (!data_val.empty()) {
|
|
return (char*)&data_val[0];
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
DataElement::Type DataElement::getDataType() {
|
|
return data_type;
|
|
}
|
|
|
|
size_t DataElement::getDataSize() {
|
|
return data_val.size();
|
|
}
|
|
|
|
|
|
void DataElement::set(const char *data_in, long size_in) {
|
|
data_type = DataElement::Type::DATA_VOID;
|
|
|
|
data_val.assign(data_in, data_in + size_in);
|
|
}
|
|
|
|
void DataElement::set(const char *data_in) {
|
|
data_type = DataElement::Type::DATA_STRING;
|
|
|
|
size_t clamped_size = ::strnlen(data_in, MAX_STR_SIZE);
|
|
|
|
data_val.assign(data_in, data_in + clamped_size);
|
|
}
|
|
|
|
|
|
void DataElement::set(const std::set<string> &strset_in) {
|
|
|
|
vector<string> tmp_vect;
|
|
|
|
for (const auto& single_string : strset_in) {
|
|
tmp_vect.push_back(single_string);
|
|
}
|
|
|
|
set(tmp_vect);
|
|
}
|
|
|
|
|
|
void DataElement::get(DataElement::DataElementBuffer& data_in) {
|
|
|
|
if (data_type != DataElement::Type::DATA_VOID) {
|
|
throw(DataTypeMismatchException("Type mismatch, not a VOID*"));
|
|
}
|
|
|
|
data_in = data_val;
|
|
}
|
|
|
|
|
|
void DataElement::get(std::set<string> &strset_out) {
|
|
|
|
if (data_type != DataElement::Type::DATA_STR_VECTOR)
|
|
throw(DataTypeMismatchException("Type mismatch, not a STRING VECTOR/SET"));
|
|
|
|
std::vector<string> tmp_vect;
|
|
|
|
get(tmp_vect);
|
|
|
|
strset_out.clear();
|
|
|
|
for (const auto& single_string : tmp_vect) {
|
|
strset_out.insert(single_string);
|
|
}
|
|
}
|
|
|
|
std::string DataElement::toString() {
|
|
DataElement::Type dataType = getDataType();
|
|
std::string strValue;
|
|
|
|
try {
|
|
if (dataType == DataElement::Type::DATA_STRING) {
|
|
get(strValue);
|
|
} else if (dataType == DataElement::Type::DATA_INT || dataType == DataElement::Type::DATA_LONG || dataType == DataElement::Type::DATA_LONGLONG) {
|
|
long long intSettingValue;
|
|
get(intSettingValue);
|
|
strValue = std::to_string(intSettingValue);
|
|
} else if (dataType == DataElement::Type::DATA_FLOAT || dataType == DataElement::Type::DATA_DOUBLE) {
|
|
double floatSettingValue;
|
|
get(floatSettingValue);
|
|
strValue = std::to_string(floatSettingValue);
|
|
} else if (dataType == DataElement::Type::DATA_NULL) {
|
|
strValue = "";
|
|
} else if (dataType == DataElement::Type::DATA_WSTRING) {
|
|
std::wstring wstr;
|
|
get(wstr);
|
|
//TODO: code below returns a forced cast in (char*) beware...
|
|
strValue = *wstr.c_str();
|
|
}
|
|
else {
|
|
std::cout << "Unhandled DataElement toString for type: " << (int)dataType << std::endl;
|
|
}
|
|
} catch (const DataTypeMismatchException &) {
|
|
std::cout << "toString() DataTypeMismatch: " << (int)dataType << std::endl;
|
|
}
|
|
|
|
return strValue;
|
|
}
|
|
|
|
/* DataNode class */
|
|
|
|
DataNode::DataNode(): parentNode(nullptr), ptr(0) {
|
|
data_elem = new DataElement();
|
|
}
|
|
|
|
DataNode::DataNode(const char *name_in): parentNode(nullptr), ptr(0) {
|
|
node_name = name_in;
|
|
data_elem = new DataElement();
|
|
}
|
|
|
|
DataNode::DataNode(const char *name_in, DataNode &cloneFrom): parentNode(nullptr), ptr(0) {
|
|
node_name = name_in;
|
|
data_elem = new DataElement(*cloneFrom.element());
|
|
|
|
// TODO: stack recursion optimization
|
|
while (cloneFrom.hasAnother()) {
|
|
DataNode *cNode = cloneFrom.getNext();
|
|
newChildCloneFrom(cNode->getName().c_str(), cNode);
|
|
}
|
|
}
|
|
|
|
DataNode::DataNode(const char *name_in, DataElement &cloneFrom): parentNode(nullptr), ptr(0) {
|
|
node_name = name_in;
|
|
data_elem = new DataElement(cloneFrom);
|
|
}
|
|
|
|
DataNode::~DataNode() {
|
|
while (!children.empty()) {
|
|
DataNode *del = children.back();
|
|
children.pop_back();
|
|
delete del;
|
|
}
|
|
delete data_elem;
|
|
}
|
|
|
|
void DataNode::setName(const char *name_in) {
|
|
node_name = name_in;
|
|
}
|
|
|
|
DataElement *DataNode::element() {
|
|
return data_elem;
|
|
}
|
|
|
|
DataNode *DataNode::newChild(const char *name_in) {
|
|
children.push_back(new DataNode(name_in));
|
|
childmap[name_in].push_back(children.back());
|
|
|
|
children.back()->setParentNode(*this);
|
|
|
|
return children.back();
|
|
}
|
|
|
|
DataNode *DataNode::newChild(const char *name_in, DataNode *otherNode) {
|
|
children.push_back(otherNode);
|
|
childmap[name_in].push_back(children.back());
|
|
|
|
children.back()->setParentNode(*this);
|
|
|
|
return children.back();
|
|
}
|
|
|
|
DataNode *DataNode::newChildCloneFrom(const char *name_in, DataNode *cloneFrom) {
|
|
auto *cloneNode = new DataNode(name_in, *cloneFrom->element());
|
|
|
|
children.push_back(cloneNode);
|
|
childmap[name_in].push_back(children.back());
|
|
children.back()->setParentNode(*this);
|
|
|
|
// TODO: stack recursion optimization
|
|
while (cloneFrom->hasAnother()) {
|
|
DataNode *cNode = cloneFrom->getNext();
|
|
cloneNode->newChildCloneFrom(cNode->getName().c_str(), cNode);
|
|
}
|
|
|
|
cloneFrom->rewind();
|
|
|
|
return children.back();
|
|
}
|
|
|
|
|
|
DataNode *DataNode::child(const char *name_in, int index) {
|
|
DataNode *child_ret;
|
|
|
|
child_ret = childmap[name_in][index];
|
|
|
|
if (!child_ret) {
|
|
stringstream error_str;
|
|
error_str << "no child '" << index << "' in DataNode '" << node_name << "'";
|
|
throw(DataInvalidChildException(error_str.str().c_str()));
|
|
}
|
|
|
|
return child_ret;
|
|
}
|
|
|
|
DataNode *DataNode::child(int index) {
|
|
|
|
DataNode *child_ret;
|
|
|
|
child_ret = children[index];
|
|
|
|
if (!child_ret) {
|
|
stringstream error_str;
|
|
error_str << "no child '" << index << "' in DataNode '" << node_name << "'";
|
|
throw(DataInvalidChildException(error_str.str().c_str()));
|
|
}
|
|
|
|
return child_ret;
|
|
}
|
|
|
|
size_t DataNode::numChildren() {
|
|
return children.size();
|
|
}
|
|
|
|
size_t DataNode::numChildren(const char *name_in) {
|
|
return childmap[name_in].size();
|
|
}
|
|
|
|
bool DataNode::hasAnother() {
|
|
return children.size() != ptr;
|
|
}
|
|
|
|
bool DataNode::hasAnother(const char *name_in) {
|
|
return childmap[name_in].size() != childmap_ptr[name_in];
|
|
}
|
|
|
|
DataNode *DataNode::getNext() {
|
|
return child(ptr++);
|
|
}
|
|
|
|
DataNode *DataNode::getNext(const char *name_in) {
|
|
return child(name_in, childmap_ptr[name_in]++);
|
|
}
|
|
|
|
void DataNode::rewind() {
|
|
ptr = 0;
|
|
childmap_ptr.erase(childmap_ptr.begin(),childmap_ptr.end());
|
|
}
|
|
|
|
void DataNode::rewind(const char *name_in) {
|
|
childmap_ptr[name_in] = 0;
|
|
}
|
|
|
|
/* DataTree class */
|
|
|
|
DataTree::DataTree(const char *name_in) {
|
|
dn_root.setName(name_in);
|
|
}
|
|
|
|
DataTree::DataTree() = default;
|
|
|
|
DataTree::~DataTree() = default;
|
|
|
|
DataNode *DataTree::rootNode() {
|
|
return &dn_root;
|
|
}
|
|
|
|
std::string trim(std::string& s, const std::string& drop = " ") {
|
|
std::string r = s.erase(s.find_last_not_of(drop) + 1);
|
|
return r.erase(0, r.find_first_not_of(drop));
|
|
}
|
|
|
|
string DataTree::wsEncode(const wstring& wstr) {
|
|
stringstream encStream;
|
|
|
|
//wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere.
|
|
size_t bufSizeBytes = (wstr.length()+1) * sizeof(wchar_t);
|
|
|
|
char *data_str = (char *)calloc(bufSizeBytes, sizeof(char));
|
|
|
|
wcstombs(data_str, wstr.c_str(), bufSizeBytes - sizeof(wchar_t));
|
|
|
|
std::string byte_str(data_str);
|
|
|
|
free(data_str);
|
|
|
|
encStream << std::hex;
|
|
|
|
for(char & i : byte_str) {
|
|
encStream << '%' << setfill('0') << (unsigned int)((unsigned char)i);
|
|
}
|
|
|
|
return encStream.str();
|
|
}
|
|
|
|
wstring DataTree::wsDecode(const string& str) {
|
|
|
|
std::stringstream decStream;
|
|
std::stringstream mbstr;
|
|
unsigned int x;
|
|
|
|
string decStr = str;
|
|
std::replace( decStr.begin(), decStr.end(), '%', ' ');
|
|
decStream << trim(decStr);
|
|
|
|
string sResult;
|
|
|
|
//this actually assume we will get as many char as wchar_t from the decodes string,
|
|
//who cares ?
|
|
size_t maxLen = decStr.length();
|
|
|
|
//wchar_t is typically 16 bits on windows, and 32 bits on Unix, so use sizeof(wchar_t) everywhere.
|
|
auto *wc_str = (wchar_t *) ::calloc(maxLen + 1, sizeof(wchar_t));
|
|
|
|
while (!decStream.eof()) {
|
|
decStream >> std::hex >> x;
|
|
//extract actually 2 hex-chars by 2 hex-chars to form a char value.
|
|
mbstr << (unsigned char) x;
|
|
}
|
|
|
|
::mbstowcs(wc_str, mbstr.str().c_str(), maxLen);
|
|
|
|
wstring result(wc_str);
|
|
|
|
::free(wc_str);
|
|
|
|
return result;
|
|
}
|
|
|
|
void DataTree::decodeXMLText(DataNode *elem, const char *src_text, DT_FloatingPointPolicy fpp) {
|
|
|
|
int tmp_char;
|
|
int tmp_int;
|
|
long tmp_long;
|
|
long long tmp_llong;
|
|
double tmp_double;
|
|
float tmp_float;
|
|
string tmp_str;
|
|
string tmp_str2;
|
|
std::stringstream tmp_stream;
|
|
std::stringstream tmp_stream2;
|
|
|
|
vector<char> tmp_charvect;
|
|
vector<int> tmp_intvect;
|
|
vector<long> tmp_longvect;
|
|
vector<long> tmp_llongvect;
|
|
|
|
vector<double> tmp_doublevect;
|
|
|
|
vector<float> tmp_floatvect;
|
|
|
|
bool vChars = false;
|
|
bool vInts = false;
|
|
bool vLongs = false;
|
|
|
|
string in_text = src_text;
|
|
|
|
trim(in_text);
|
|
trim(in_text, "\r\n");
|
|
tmp_stream.str("");
|
|
tmp_stream2.str("");
|
|
|
|
if (in_text.find_first_not_of("0123456789-") == string::npos) {
|
|
tmp_stream << in_text;
|
|
tmp_stream >> tmp_llong;
|
|
|
|
tmp_int = (int)tmp_llong;
|
|
tmp_long = (long)tmp_llong;
|
|
|
|
if (tmp_int == tmp_llong) {
|
|
elem->element()->set(tmp_int);
|
|
} else if (tmp_long == tmp_llong) {
|
|
elem->element()->set(tmp_long);
|
|
} else {
|
|
elem->element()->set(tmp_llong);
|
|
}
|
|
} else if (in_text.find_first_not_of("0123456789.e+-") == string::npos) {
|
|
tmp_stream << in_text;
|
|
|
|
if (fpp == USE_FLOAT) {
|
|
tmp_stream >> tmp_float;
|
|
|
|
elem->element()->set(tmp_float);
|
|
} else {
|
|
tmp_stream >> tmp_double;
|
|
|
|
elem->element()->set(tmp_double);
|
|
}
|
|
} else if (in_text.find_first_not_of("0123456789- ") == string::npos) {
|
|
tmp_stream << in_text;
|
|
|
|
vChars = true;
|
|
vInts = true;
|
|
vLongs = true;
|
|
|
|
while (!tmp_stream.eof()) {
|
|
tmp_stream >> tmp_llong;
|
|
tmp_char = (char)tmp_llong;
|
|
tmp_int = (int)tmp_llong;
|
|
tmp_long = (long)tmp_llong;
|
|
|
|
if ((long long)tmp_char != tmp_llong) {
|
|
vChars = false;
|
|
}
|
|
if (tmp_int != tmp_llong) {
|
|
vInts = false;
|
|
}
|
|
if (tmp_long != tmp_llong) {
|
|
vLongs = false;
|
|
}
|
|
tmp_llongvect.push_back((long) tmp_long);
|
|
}
|
|
|
|
if (vChars) {
|
|
for (auto single_long : tmp_llongvect) {
|
|
tmp_charvect.push_back((char)single_long);
|
|
}
|
|
tmp_llongvect.clear();
|
|
elem->element()->set(tmp_charvect);
|
|
tmp_charvect.clear();
|
|
|
|
} else if (vInts) {
|
|
for (auto single_long : tmp_llongvect) {
|
|
tmp_intvect.push_back((int)single_long);
|
|
}
|
|
tmp_llongvect.clear();
|
|
elem->element()->set(tmp_intvect);
|
|
tmp_intvect.clear();
|
|
} else if (vLongs) {
|
|
for (auto single_long : tmp_llongvect) {
|
|
tmp_longvect.push_back(single_long);
|
|
}
|
|
tmp_llongvect.clear();
|
|
elem->element()->set(tmp_longvect);
|
|
tmp_longvect.clear();
|
|
} else {
|
|
elem->element()->set(tmp_llongvect);
|
|
}
|
|
} else if (in_text.find_first_not_of("0123456789.e-+ ") == string::npos) {
|
|
tmp_stream << in_text;
|
|
|
|
if (fpp == USE_FLOAT) {
|
|
tmp_floatvect.clear();
|
|
} else {
|
|
tmp_doublevect.clear();
|
|
}
|
|
|
|
while (!tmp_stream.eof()) {
|
|
|
|
if (fpp == USE_FLOAT) {
|
|
tmp_stream >> tmp_float;
|
|
tmp_floatvect.push_back(tmp_float);
|
|
} else {
|
|
tmp_stream >> tmp_double;
|
|
tmp_doublevect.push_back(tmp_double);
|
|
}
|
|
}
|
|
|
|
if (fpp == USE_FLOAT) {
|
|
elem->element()->set(tmp_floatvect);
|
|
} else {
|
|
elem->element()->set(tmp_doublevect);
|
|
}
|
|
} else if (in_text.find_first_not_of("0123456789abcdef%") == string::npos) {
|
|
elem->element()->set(wsDecode(src_text));
|
|
} else {
|
|
elem->element()->set(src_text);
|
|
// printf( "Unhandled DataTree XML Field: [%s]", tmp_str.c_str() );
|
|
}
|
|
}
|
|
|
|
void DataTree::setFromXML(DataNode *elem, TiXmlNode *elxml, bool root_node, DT_FloatingPointPolicy fpp) {
|
|
TiXmlText *pText;
|
|
int t = elxml->Type();
|
|
string tmp_str;
|
|
|
|
switch (t) {
|
|
case TiXmlNode::TINYXML_DOCUMENT:
|
|
// printf( "Document" );
|
|
break;
|
|
|
|
case TiXmlNode::TINYXML_ELEMENT:
|
|
if (!root_node)
|
|
elem = elem->newChild(elxml->Value());
|
|
|
|
const TiXmlAttribute *attribs;
|
|
attribs = elxml->ToElement()->FirstAttribute();
|
|
|
|
while (attribs) {
|
|
|
|
// following badgerfish xml->json and xml->ruby convention for attributes..
|
|
string attrName("@");
|
|
attrName.append(attribs->Name());
|
|
|
|
decodeXMLText(elem->newChild(attrName.c_str()), attribs->Value(), fpp);
|
|
|
|
attribs = attribs->Next();
|
|
}
|
|
|
|
// printf( "Element \"%s\"", elxml->Value());
|
|
break;
|
|
|
|
case TiXmlNode::TINYXML_COMMENT:
|
|
// printf( "Comment: \"%s\"", elxml->Value());
|
|
break;
|
|
|
|
case TiXmlNode::TINYXML_UNKNOWN:
|
|
// printf( "Unknown" );
|
|
break;
|
|
|
|
case TiXmlNode::TINYXML_TEXT:
|
|
pText = elxml->ToText();
|
|
|
|
decodeXMLText(elem, pText->Value(), fpp);
|
|
|
|
// pText = elxml->ToText();
|
|
// printf( "Text: [%s]", pText->Value() );
|
|
break;
|
|
|
|
case TiXmlNode::TINYXML_DECLARATION:
|
|
// printf( "Declaration" );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// printf( "\n" );
|
|
|
|
TiXmlNode * pChild;
|
|
|
|
if (!elxml->NoChildren()) {
|
|
if (elxml->FirstChild()->Type() == TiXmlNode::TINYXML_ELEMENT) {
|
|
if (elxml->FirstChild()->Value() == TIXML_STRING("str")) {
|
|
std::vector<std::string> tmp_strvect;
|
|
|
|
for (pChild = elxml->FirstChild(); pChild != nullptr; pChild = pChild->NextSibling()) {
|
|
if (pChild->Value() == TIXML_STRING("str")) {
|
|
if (!pChild->FirstChild()) {
|
|
tmp_strvect.push_back("");
|
|
continue;
|
|
}
|
|
|
|
pText = pChild->FirstChild()->ToText();
|
|
|
|
if (pText) {
|
|
tmp_str = pText->Value();
|
|
tmp_strvect.push_back(tmp_str);
|
|
}
|
|
}
|
|
}
|
|
|
|
elem->element()->set(tmp_strvect);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (pChild = elxml->FirstChild(); pChild != nullptr; pChild = pChild->NextSibling()) {
|
|
setFromXML(elem, pChild, false, fpp);
|
|
}
|
|
|
|
}
|
|
|
|
void DataTree::nodeToXML(DataNode *elem, TiXmlElement *elxml) {
|
|
DataNode *child;
|
|
|
|
elem->rewind();
|
|
|
|
while (elem->hasAnother()) {
|
|
child = elem->getNext();
|
|
|
|
std::string nodeName = child->getName();
|
|
|
|
TiXmlElement *element;
|
|
|
|
element = new TiXmlElement(nodeName.length() ? nodeName.c_str() : "node");
|
|
std::string tmp;
|
|
std::wstring wtmp;
|
|
std::stringstream tmp_stream;
|
|
TiXmlText *text;
|
|
std::vector<float> tmp_floatvect;
|
|
std::vector<float>::iterator tmp_floatvect_i;
|
|
std::vector<double> tmp_doublevect;
|
|
std::vector<double>::iterator tmp_doublevect_i;
|
|
std::vector<int> tmp_intvect;
|
|
std::vector<int>::iterator tmp_intvect_i;
|
|
std::vector<char> tmp_charvect;
|
|
std::vector<char>::iterator tmp_charvect_i;
|
|
std::vector<unsigned char> tmp_ucharvect;
|
|
std::vector<unsigned char>::iterator tmp_ucharvect_i;
|
|
std::vector<unsigned int> tmp_uintvect;
|
|
std::vector<unsigned int>::iterator tmp_uintvect_i;
|
|
std::vector<long> tmp_longvect;
|
|
std::vector<long>::iterator tmp_longvect_i;
|
|
std::vector<unsigned long> tmp_ulongvect;
|
|
std::vector<unsigned long>::iterator tmp_ulongvect_i;
|
|
std::vector<long long> tmp_llongvect;
|
|
std::vector<long long>::iterator tmp_llongvect_i;
|
|
|
|
std::vector<string> tmp_stringvect;
|
|
|
|
TiXmlElement *tmp_node;
|
|
std::string tmp_pstr_as_string;
|
|
double tmp_double;
|
|
|
|
float tmp_float;
|
|
char tmp_char;
|
|
unsigned char tmp_uchar;
|
|
int tmp_int;
|
|
unsigned int tmp_uint;
|
|
long tmp_long;
|
|
unsigned long tmp_ulong;
|
|
long long tmp_llong;
|
|
|
|
switch (child->element()->getDataType()) {
|
|
case DataElement::Type::DATA_NULL:
|
|
break;
|
|
case DataElement::Type::DATA_VOID:
|
|
child->element()->get(tmp_pstr_as_string); // returned VOID as string
|
|
// following badgerfish xml->json and xml->ruby convention for attributes..
|
|
if (nodeName.substr(0, 1) == string("@")) {
|
|
elxml->SetAttribute(nodeName.substr(1).c_str(), tmp_pstr_as_string.c_str()); //the c_str take care of adding a null erminated character...
|
|
delete element;
|
|
element = nullptr;
|
|
} else {
|
|
text = new TiXmlText(tmp_pstr_as_string.c_str());
|
|
element->LinkEndChild(text);
|
|
}
|
|
break;
|
|
case DataElement::Type::DATA_CHAR:
|
|
child->element()->get(tmp_char);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_char;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_UCHAR:
|
|
child->element()->get(tmp_uchar);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_uchar;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_INT:
|
|
child->element()->get(tmp_int);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_int;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_UINT:
|
|
child->element()->get(tmp_uint);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_uint;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_LONG:
|
|
child->element()->get(tmp_long);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_long;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_ULONG:
|
|
child->element()->get(tmp_ulong);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_ulong;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_LONGLONG:
|
|
child->element()->get(tmp_llong);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_llong;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_FLOAT:
|
|
child->element()->get(tmp_float);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_float;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_DOUBLE:
|
|
child->element()->get(tmp_double);
|
|
|
|
tmp_stream.str("");
|
|
|
|
tmp_stream << tmp_double;
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
break;
|
|
case DataElement::Type::DATA_STRING:
|
|
child->element()->get(tmp);
|
|
if (nodeName.substr(0, 1) == string("@")) {
|
|
elxml->SetAttribute(nodeName.substr(1).c_str(), tmp.c_str());
|
|
delete element;
|
|
element = nullptr;
|
|
} else {
|
|
text = new TiXmlText(tmp.c_str());
|
|
element->LinkEndChild(text);
|
|
}
|
|
break;
|
|
case DataElement::Type::DATA_WSTRING:
|
|
child->element()->get(wtmp);
|
|
tmp = wsEncode(wtmp);
|
|
if (nodeName.substr(0, 1) == string("@")) {
|
|
elxml->SetAttribute(nodeName.substr(1).c_str(), tmp.c_str());
|
|
delete element;
|
|
element = nullptr;
|
|
} else {
|
|
text = new TiXmlText(tmp.c_str());
|
|
element->LinkEndChild(text);
|
|
}
|
|
break;
|
|
case DataElement::Type::DATA_STR_VECTOR:
|
|
child->element()->get(tmp_stringvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (const auto& single_string : tmp_stringvect) {
|
|
tmp_node = new TiXmlElement("str");
|
|
text = new TiXmlText(single_string.c_str());
|
|
tmp_node->LinkEndChild(text);
|
|
element->LinkEndChild(tmp_node);
|
|
}
|
|
|
|
tmp_stringvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_CHAR_VECTOR:
|
|
child->element()->get(tmp_charvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_charvect_i = tmp_charvect.begin(); tmp_charvect_i != tmp_charvect.end(); tmp_charvect_i++) {
|
|
tmp_stream << (*tmp_charvect_i);
|
|
if (tmp_charvect_i != tmp_charvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_charvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_UCHAR_VECTOR:
|
|
child->element()->get(tmp_ucharvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_ucharvect_i = tmp_ucharvect.begin(); tmp_ucharvect_i != tmp_ucharvect.end(); tmp_ucharvect_i++) {
|
|
tmp_stream << (*tmp_ucharvect_i);
|
|
if (tmp_ucharvect_i != tmp_ucharvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_ucharvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_INT_VECTOR:
|
|
child->element()->get(tmp_intvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_intvect_i = tmp_intvect.begin(); tmp_intvect_i != tmp_intvect.end(); tmp_intvect_i++) {
|
|
tmp_stream << (*tmp_intvect_i);
|
|
if (tmp_intvect_i != tmp_intvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_intvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_UINT_VECTOR:
|
|
child->element()->get(tmp_uintvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_uintvect_i = tmp_uintvect.begin(); tmp_uintvect_i != tmp_uintvect.end(); tmp_uintvect_i++) {
|
|
tmp_stream << (*tmp_intvect_i);
|
|
if (tmp_uintvect_i != tmp_uintvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_uintvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_LONG_VECTOR:
|
|
child->element()->get(tmp_longvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_longvect_i = tmp_longvect.begin(); tmp_longvect_i != tmp_longvect.end(); tmp_longvect_i++) {
|
|
tmp_stream << (*tmp_longvect_i);
|
|
if (tmp_longvect_i != tmp_longvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_longvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_ULONG_VECTOR:
|
|
child->element()->get(tmp_ulongvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_ulongvect_i = tmp_ulongvect.begin(); tmp_ulongvect_i != tmp_ulongvect.end(); tmp_ulongvect_i++) {
|
|
tmp_stream << (*tmp_ulongvect_i);
|
|
if (tmp_ulongvect_i != tmp_ulongvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_ulongvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_LONGLONG_VECTOR:
|
|
child->element()->get(tmp_llongvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_llongvect_i = tmp_llongvect.begin(); tmp_llongvect_i != tmp_llongvect.end(); tmp_llongvect_i++) {
|
|
tmp_stream << (*tmp_llongvect_i);
|
|
if (tmp_llongvect_i != tmp_llongvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_llongvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_FLOAT_VECTOR:
|
|
child->element()->get(tmp_floatvect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_floatvect_i = tmp_floatvect.begin(); tmp_floatvect_i != tmp_floatvect.end(); tmp_floatvect_i++) {
|
|
tmp_stream << (*tmp_floatvect_i);
|
|
if (tmp_floatvect_i != tmp_floatvect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_floatvect.clear();
|
|
break;
|
|
case DataElement::Type::DATA_DOUBLE_VECTOR:
|
|
child->element()->get(tmp_doublevect);
|
|
|
|
tmp_stream.str("");
|
|
|
|
for (tmp_doublevect_i = tmp_doublevect.begin(); tmp_doublevect_i != tmp_doublevect.end(); tmp_doublevect_i++) {
|
|
tmp_stream << (*tmp_doublevect_i);
|
|
if (tmp_doublevect_i != tmp_doublevect.end() - 1)
|
|
tmp_stream << " ";
|
|
}
|
|
|
|
text = new TiXmlText(tmp_stream.str().c_str());
|
|
element->LinkEndChild(text);
|
|
tmp_doublevect.clear();
|
|
break;
|
|
} //en switch
|
|
|
|
|
|
if (element) {
|
|
elxml->LinkEndChild(element);
|
|
|
|
if (child->numChildren()) {
|
|
nodeToXML(child, element);
|
|
}
|
|
}
|
|
} // end while
|
|
|
|
elem->rewind();
|
|
}
|
|
|
|
void DataTree::printXML() /* get serialized size + return node names header */
|
|
{
|
|
TiXmlDocument doc;
|
|
auto * decl = new TiXmlDeclaration("1.0", "", "");
|
|
doc.LinkEndChild(decl);
|
|
|
|
DataNode *root = rootNode();
|
|
|
|
string rootName = root->getName();
|
|
|
|
auto *element = new TiXmlElement(rootName.empty() ? "root" : rootName.c_str());
|
|
doc.LinkEndChild(element);
|
|
|
|
if (!root->numChildren())
|
|
doc.Print();
|
|
|
|
nodeToXML(root, element);
|
|
|
|
root->rewind();
|
|
|
|
doc.Print();
|
|
}
|
|
|
|
void DataNode::rewindAll() {
|
|
stack<DataNode *> dn_stack;
|
|
|
|
/* start at the root */
|
|
dn_stack.push(this);
|
|
|
|
while (!dn_stack.empty()) {
|
|
dn_stack.top()->rewind();
|
|
|
|
/* if it has children, traverse into them */
|
|
if (dn_stack.top()->hasAnother()) {
|
|
dn_stack.push(dn_stack.top()->getNext());
|
|
dn_stack.top()->rewind();
|
|
} else {
|
|
/* no more children, back out until we have children, then add next child to the top */
|
|
while (!dn_stack.empty()) {
|
|
if (!dn_stack.top()->hasAnother()) {
|
|
dn_stack.top()->rewind();
|
|
dn_stack.pop();
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (!dn_stack.empty()) {
|
|
dn_stack.push(dn_stack.top()->getNext());
|
|
dn_stack.top()->rewind();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void DataNode::findAll(const char *name_in, vector<DataNode *> &node_list_out) {
|
|
stack<DataNode *> dn_stack;
|
|
|
|
/* start at the root */
|
|
dn_stack.push(this);
|
|
|
|
if (string(getName()) == string(name_in))
|
|
node_list_out.push_back(this);
|
|
|
|
while (!dn_stack.empty()) {
|
|
while (dn_stack.top()->hasAnother(name_in)) {
|
|
node_list_out.push_back(dn_stack.top()->getNext(name_in));
|
|
}
|
|
|
|
/* if it has children, traverse into them */
|
|
if (dn_stack.top()->hasAnother()) {
|
|
dn_stack.push(dn_stack.top()->getNext());
|
|
dn_stack.top()->rewind();
|
|
} else {
|
|
/* no more children, back out until we have children, then add next child to the top */
|
|
while (!dn_stack.empty()) {
|
|
if (!dn_stack.top()->hasAnother()) {
|
|
dn_stack.top()->rewind();
|
|
dn_stack.pop();
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (!dn_stack.empty()) {
|
|
dn_stack.push(dn_stack.top()->getNext());
|
|
dn_stack.top()->rewind();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool DataTree::LoadFromFileXML(const std::string& filename, DT_FloatingPointPolicy fpp) {
|
|
TiXmlDocument doc(filename.c_str());
|
|
|
|
bool loadOkay = doc.LoadFile();
|
|
|
|
if (!loadOkay) {
|
|
std::cout << "LoadFromFileXML[error loading]: " << filename << std::endl;
|
|
return false;
|
|
}
|
|
|
|
TiXmlNode *xml_root_node = doc.RootElement();
|
|
|
|
if (!xml_root_node) {
|
|
std::cout << "LoadFromFileXML[error no root]: " << filename << std::endl;
|
|
return false;
|
|
}
|
|
|
|
rootNode()->setName(xml_root_node->ToElement()->Value());
|
|
|
|
setFromXML(rootNode(), xml_root_node, true, fpp);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DataTree::SaveToFileXML(const std::string& filename) {
|
|
TiXmlDocument doc;
|
|
auto * decl = new TiXmlDeclaration("1.0", "", "");
|
|
doc.LinkEndChild(decl);
|
|
|
|
string rootName = rootNode()->getName();
|
|
|
|
auto *element = new TiXmlElement(rootName.empty() ? "root" : rootName.c_str());
|
|
|
|
doc.LinkEndChild(element);
|
|
|
|
nodeToXML(rootNode(), element);
|
|
|
|
doc.SaveFile(filename.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|