#include "ofXml.h"
#include "ofUtils.h"
using namespace std;
ofXml::ofXml()
:doc(new pugi::xml_document){
xml = doc->root();
}
ofXml::ofXml(std::shared_ptr<pugi::xml_document> doc, const pugi::xml_node & xml)
:doc(doc)
,xml(xml){
}
bool ofXml::load(const std::filesystem::path & file){
auto auxDoc = std::make_shared<pugi::xml_document>();
if(auxDoc->load_file(ofToDataPath(file).c_str())){
doc = auxDoc;
xml = doc->root();
return true;
}else{
return false;
}
}
bool ofXml::load(const ofBuffer & buffer){
return parse(buffer.getText());
}
bool ofXml::parse(const std::string & xmlStr){
auto auxDoc = std::make_shared<pugi::xml_document>();
#if ( defined(PUGIXML_VERSION) && PUGIXML_VERSION >= 150 )
if(auxDoc->load_string(xmlStr.c_str())){
#else
if(auxDoc->load(xmlStr.c_str())){
#endif
doc = auxDoc;
xml = doc->root();
return true;
}else{
return false;
}
}
bool ofXml::save(const std::filesystem::path & file) const{
if(xml == doc->root()){
return doc->save_file(ofToDataPath(file).c_str());
}else{
pugi::xml_document doc;
if(doc.append_copy(xml.root())){
return doc.save_file(ofToDataPath(file).c_str());
}
}
return false;
}
void ofXml::clear(){
doc.reset(new pugi::xml_document);
xml = doc->root();
}
std::string ofXml::toString(const std::string & indent) const{
ostringstream stream;
if(xml == doc->root()){
doc->print(stream, indent.c_str());
}else{
pugi::xml_document doc;
doc.append_copy(xml.root());
}
return stream.str();
}
ofXml ofXml::getChild(const std::string & name) const{
return ofXml(doc, xml.child(name.c_str()));
}
ofXml::Range<ofXmlIterator<pugi::xml_node_iterator>> ofXml::getChildren() const{
return ofXml::Range<ofXmlIterator<pugi::xml_node_iterator>>(doc, xml.children());
}
ofXml::Range<ofXmlIterator<pugi::xml_named_node_iterator>> ofXml::getChildren(const std::string & name) const{
return ofXml::Range<ofXmlIterator<pugi::xml_named_node_iterator>>(doc, xml.children(name.c_str()));
}
ofXml ofXml::appendChild(const ofXml & xml){
return ofXml(doc, this->xml.append_copy(xml.xml));
}
ofXml ofXml::prependChild(const ofXml & xml){
return ofXml(doc, this->xml.prepend_copy(xml.xml));
}
#if PUGIXML_VERSION>=170
ofXml ofXml::appendChild(ofXml && xml){
return ofXml(doc, this->xml.append_move(xml.xml));
}
ofXml ofXml::prependChild(ofXml && xml){
return ofXml(doc, this->xml.prepend_move(xml.xml));
}
bool ofXml::removeChild(ofXml && node){
return xml.remove_child(node.xml);
}
#endif
ofXml ofXml::appendChild(const std::string & name){
return ofXml(doc, this->xml.append_child(name.c_str()));
}
ofXml ofXml::prependChild(const std::string & name){
return ofXml(doc, this->xml.prepend_child(name.c_str()));
}
ofXml ofXml::insertChildAfter(const std::string & name, const ofXml & after){
return ofXml(doc, this->xml.insert_child_after(name.c_str(), after.xml));
}
ofXml ofXml::insertChildBefore(const std::string & name, const ofXml & before){
return ofXml(doc, this->xml.insert_child_before(name.c_str(), before.xml));
}
bool ofXml::removeChild(const std::string & name){
return xml.remove_child(name.c_str());
}
bool ofXml::removeChild(const ofXml & node){
return xml.remove_child(node.xml);
}
ofXml ofXml::getNextSibling() const{
return ofXml(doc, this->xml.next_sibling());
}
ofXml ofXml::getPreviousSibling() const{
return ofXml(doc, this->xml.previous_sibling());
}
ofXml ofXml::getNextSibling(const std::string & name) const{
return ofXml(doc, this->xml.next_sibling(name.c_str()));
}
ofXml ofXml::getPreviousSibling(const std::string & name) const{
return ofXml(doc, this->xml.previous_sibling(name.c_str()));
}
ofXml ofXml::getFirstChild() const{
return ofXml(doc, this->xml.first_child());
}
ofXml ofXml::getLastChild() const{
return ofXml(doc, this->xml.last_child());
}
ofXml ofXml::getParent() const {
return ofXml(doc, this->xml.parent());
}
ofXml::Attribute ofXml::getAttribute(const std::string & name) const{
return this->xml.attribute(name.c_str());
}
ofXml::Range<ofXmlAttributeIterator> ofXml::getAttributes() const{
return ofXml::Range<ofXmlAttributeIterator>(doc, this->xml.attributes());
}
ofXml::Attribute ofXml::getFirstAttribute() const{
return this->xml.first_attribute();
}
ofXml::Attribute ofXml::getLastAttribute() const{
return this->xml.last_attribute();
}
ofXml::Attribute ofXml::appendAttribute(const std::string & name){
return this->xml.append_attribute(name.c_str());
}
ofXml::Attribute ofXml::prependAttribute(const std::string & name){
return this->xml.prepend_attribute(name.c_str());
}
bool ofXml::removeAttribute(const std::string & name){
auto attr = getAttribute(name);
if(attr){
return xml.remove_attribute(attr.attr);
}else{
return false;
}
}
bool ofXml::removeAttribute(const ofXml::Attribute & attr){
return xml.remove_attribute(attr.attr);
}
bool ofXml::removeAttribute(ofXml::Attribute && attr){
return xml.remove_attribute(attr.attr);
}
ofXml ofXml::findFirst(const std::string & path) const{
try{
return ofXml(doc, this->xml.select_single_node(path.c_str()).node());
}catch(pugi::xpath_exception & e){
return ofXml();
}
}
ofXml::Search ofXml::find(const std::string & path) const{
try{
return ofXml::Search(doc, this->xml.select_nodes(path.c_str()));
}catch(pugi::xpath_exception & e){
ofLogError() << e.what();
return ofXml::Search();
}
}
std::string ofXml::getValue() const{
return this->xml.text().as_string();
}
std::string ofXml::getName() const{
return this->xml.name();
}
void ofXml::setName(const std::string & name){
if(xml==doc->document_element()){
xml = doc->append_child(pugi::node_element);
}
this->xml.set_name(name.c_str());
}
int ofXml::getIntValue() const{
return this->xml.text().as_int();
}
unsigned int ofXml::getUintValue() const{
return this->xml.text().as_uint();
}
float ofXml::getFloatValue() const{
return this->xml.text().as_float();
}
double ofXml::getDoubleValue() const{
return this->xml.text().as_double();
}
bool ofXml::getBoolValue() const{
return this->xml.text().as_bool();
}
ofXml::operator bool() const{
return this->xml;
}
ofXml::Attribute::Attribute(const pugi::xml_attribute & attr)
:attr(attr){}
std::string ofXml::Attribute::getValue() const{
return this->attr.as_string();
}
void ofXml::Attribute::setName(const std::string & name){
this->attr.set_name(name.c_str());
}
std::string ofXml::Attribute::getName() const{
return this->attr.name();
}
int ofXml::Attribute::getIntValue() const{
return this->attr.as_int();
}
unsigned int ofXml::Attribute::getUintValue() const{
return this->attr.as_uint();
}
float ofXml::Attribute::getFloatValue() const{
return this->attr.as_float();
}
double ofXml::Attribute::getDoubleValue() const{
return this->attr.as_double();
}
bool ofXml::Attribute::getBoolValue() const{
return this->attr.as_bool();
}
ofXml::Attribute::operator bool() const{
return this->attr;
}
ofXml::Attribute ofXml::Attribute::getNextAttribute() const{
return this->attr.next_attribute();
}
ofXml::Attribute ofXml::Attribute::getPreviousAttribute() const{
return this->attr.previous_attribute();
}
pugi::xpath_node_set::type_t ofXml::Search::type() const{
return search.type();
}
size_t ofXml::Search::size() const{
return search.size();
}
ofXml ofXml::Search::operator[](size_t index) const{
return ofXml(doc, search[index].node());
}
ofXmlSearchIterator ofXml::Search::begin() const{
return ofXmlSearchIterator(doc, search.begin());
}
ofXmlSearchIterator ofXml::Search::end() const{
return ofXmlSearchIterator(doc, search.end());
}
void ofXml::Search::sort(bool reverse){
search.sort(reverse);
}
ofXml ofXml::Search::getFirst() const{
return ofXml(doc, search.first().node());
}
bool ofXml::Search::empty() const{
return search.empty();
}
ofXml::Search::Search(std::shared_ptr<pugi::xml_document> doc, pugi::xpath_node_set set)
:doc(doc)
,search(set){}
ofXmlSearchIterator::ofXmlSearchIterator(){}
bool ofXmlSearchIterator::operator==(const ofXmlSearchIterator& rhs) const{
return this->node == rhs.node;
}
bool ofXmlSearchIterator::operator!=(const ofXmlSearchIterator& rhs) const{
return this->node != rhs.node;
}
ofXml & ofXmlSearchIterator::operator*() const{
return xml;
}
ofXml * ofXmlSearchIterator::operator->() const{
return &xml;
}
const ofXmlSearchIterator& ofXmlSearchIterator::operator++(){
if(node){
node = node + 1;
if(node){
xml.xml = node->node();
}
}
return *this;
}
ofXmlSearchIterator ofXmlSearchIterator::operator++(int){
if(node){
auto now = *this;
++(*this);
return now;
}else{
return *this;
}
}
const ofXmlSearchIterator& ofXmlSearchIterator::operator--(){
if(node){
node = node - 1;
if(node){
xml.xml = node->node();
}
}
return *this;
}
ofXmlSearchIterator ofXmlSearchIterator::operator--(int){
if(node){
auto now = *this;
--(*this);
return now;
}else{
return *this;
}
}
void ofSerialize(ofXml & xml, const ofAbstractParameter & parameter){
if(!parameter.isSerializable()){
return;
}
string name = parameter.getEscapedName();
if(name == ""){
name = "UnknownName";
}
ofXml child = xml.findFirst(name);
if(!child){
child = xml.appendChild(name);
ofLogVerbose("ofXml") << "creating group " << name;
}
if(parameter.type() == typeid(ofParameterGroup).name()){
const ofParameterGroup & group = static_cast <const ofParameterGroup &>(parameter);
ofLogVerbose("ofXml") << "group " << name;
for(auto & p: group){
ofSerialize(child, *p);
}
ofLogVerbose("ofXml") << "end group " << name;
}else{
string value = parameter.toString();
child.set(value);
}
}
void ofDeserialize(const ofXml & xml, ofAbstractParameter & parameter){
if(!parameter.isSerializable()){
return;
}
string name = parameter.getEscapedName();
ofXml child = xml.findFirst(name);
if(child){
if(parameter.type() == typeid(ofParameterGroup).name()){
ofParameterGroup & group = static_cast <ofParameterGroup &>(parameter);
for(auto & p: group){
ofDeserialize(child, *p);
}
}else{
if(parameter.type() == typeid(ofParameter <int> ).name()){
parameter.cast <int>() = child.getIntValue();
}else if(parameter.type() == typeid(ofParameter <float> ).name()){
parameter.cast <float>() = child.getFloatValue();
}else if(parameter.type() == typeid(ofParameter <bool> ).name()){
parameter.cast <bool>() = child.getBoolValue();
}else if(parameter.type() == typeid(ofParameter <string> ).name()){
parameter.cast <string>() = child.getValue();
}else{
parameter.fromString(child.getValue());
}
}
}
}
Comments