// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_xml_tree #define tools_xml_tree #include "element" #include "../sout" #include "../strip" #include "../S_STRING" #include "../forit" #include #include namespace tools { namespace xml { // A tree is : // // ... // // // ... // value // ... // // // tree.attribute_value(,s) // retrieve the value of atb of a // tree.attribute_value(,,s) // retrieve the value of an atb of a of a // class tree; class factory { public: virtual ~factory(){} public: typedef std::pair atb; public: virtual tree* create(const std::string& a_tag_name,const std::vector& a_atbs,tree* a_parent) = 0; }; class tree : public virtual ielem { public: TOOLS_SCLASS(tools::xml::tree) public: static cid id_class() {return 1;} virtual void* cast(cid a_class) const { if(void* p = cmp_cast(this,a_class)) {return p;} else return 0; } public: typedef std::pair atb; typedef bool (*exec_func)(tree&,void*); enum copy_what { copy_attributes, copy_elements, copy_children, copy_all }; public: tree(const std::string& a_tag_name,factory& a_factory,tree* a_parent) :m_tag_name(a_tag_name) ,m_factory(a_factory) ,m_parent(a_parent) ,m_save(true) ,m_data_1(0) ,m_data_2(0) ,m_data_int(0) ,m_depth(0) { #ifdef TOOLS_MEM mem::increment(s_class().c_str()); #endif } virtual ~tree(){ clear(); #ifdef TOOLS_MEM mem::decrement(s_class().c_str()); #endif } protected: tree(const tree& a_from) :ielem(a_from) ,m_tag_name(a_from.m_tag_name) ,m_factory(a_from.m_factory) ,m_parent(0) ,m_save(0) ,m_data_1(0) ,m_data_2(0) ,m_data_int(0) ,m_depth(0) { #ifdef TOOLS_MEM mem::increment(s_class().c_str()); #endif } tree& operator=(const tree&){return *this;} public: virtual bool invalidate() {return true;} const std::list& childs() const {return m_childs;} /////////////////////////////////////////////////////// /// attributes //////////////////////////////////////// /////////////////////////////////////////////////////// const std::vector& attributes() const {return m_atbs;} void add_attribute(const std::string& a_name,const std::string& a_value){ // No check is done about an existing a_name. m_atbs.push_back(atb(a_name,a_value)); } bool is_attribute(const std::string& a_name) const { size_t number = m_atbs.size(); for(size_t index=0;index& a_atbs) { m_atbs = a_atbs; } const std::string& tag_name() const {return m_tag_name;} bool attribute_value(const std::string& a_atb,std::string& a_value) const { a_value.clear(); size_t linen = m_atbs.size(); for(size_t count=0;count bool attribute_value(const std::string& a_atb,T& a_value) const { std::string sv; if(!attribute_value(a_atb,sv)) {a_value=T();return false;} return to(sv,a_value); } void remove_attributes(const std::string& a_atb) { std::vector::iterator it; for(it=m_atbs.begin();it!=m_atbs.end();) { if((*it).first==a_atb) { it = m_atbs.erase(it); } else { ++it; } } } bool remove_attribute(const std::string& a_name){ //TOOLS_STL : to be checked : std::vector::iterator it = m_atbs.begin(); size_t linen = m_atbs.size(); for(size_t count=0;count& a_atbs, const std::string& a_value){ m_childs.push_back(new element(a_name,a_atbs,a_value)); } bool element_value(const std::string& a_name,std::string& a_value) const { tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(a_name==_elem->name()) { a_value = _elem->value(); return true; } } } a_value.clear(); return false; } /* //NOTE : print is a Python keyword. void dump(std::ostream& a_out){ a_out << "dump :" << " -----> " << this << " parent : " << m_parent << std::endl; a_out << " data1 : " << m_data_1 << " data2 : " << m_data_2 << " dataInt : " << m_data_int << std::endl; {size_t atbn = m_atbs.size(); for(size_t index=0;indexname() << "\" \"" << (*it)->value() << "\"" << std::endl; }} {tools_lforit(tree*,m_children,it) { (*it)->dump(a_out); }} } const std::list& elements() const {return m_elems;} */ bool element_atb_value(const std::string& a_elem, const std::string& a_atb, std::string& a_value) const { a_value.clear(); tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(a_elem==_elem->name()) { if(_elem->attribute_value(a_atb,a_value)) return true; } } } return false; } template bool element_atb_value(const std::string& a_elem,const std::string& a_atb,T& a_value) const { std::string sv; if(!element_atb_value(a_elem,a_atb,sv)) {a_value=T();return false;} return to(sv,a_value); } ////////////////////////////////////////////////// /// for osc ////////////////////////////////////// ////////////////////////////////////////////////// bool is_element(const std::string& a_name) const { tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(a_name==_elem->name()) return true; } } return false; } bool set_element_value(const std::string& a_name, const std::string& a_value,int a_index = 0){ int index = 0; tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(a_name==_elem->name()) { if(index==a_index) { _elem->set_value(a_value); return true; } else { index ++; } } } } // Not found, add one : std::vector atts; add_element(a_name,atts,a_value); return true; } bool set_attribute_value(const std::string& a_atb, const std::string& a_value) { tools_vforit(atb,m_atbs,it) { if((*it).first==a_atb) { (*it).second = a_value; return true; } } // Not found, add one : m_atbs.push_back(atb(a_atb,a_value)); return true; } bool has_empty_attribute_value(std::ostream& a_out) const{ empty_visitor visitor(a_out); visitor.m_status = true; const_cast(this)->post_execute(check_item,&visitor); return visitor.m_status; } class empty_visitor { public: empty_visitor(std::ostream& a_out) :f_out(a_out),m_status(true){} public: std::ostream& f_out; bool m_status; }; void post_execute(exec_func a_function,void* a_tag) { if(!a_function) return; if(!a_function(*this,a_tag)) return; tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { _tree->post_execute(a_function,a_tag); } } } static bool check_item(tree& a_tree,void* a_tag){ empty_visitor* visitor = (empty_visitor*)a_tag; {const std::vector& atbs = a_tree.attributes(); size_t atbn = atbs.size(); for(size_t index=0;indexf_out << "check_item :" << " for XML item " << sout(a_tree.tag_name()) << ", attribute " << sout(_atb) << " has an empty value." << std::endl; visitor->m_status = false; } }} {tools_lforcit(ielem*,a_tree.m_childs,it) { if(element* _elem = id_cast(*(*it))) { const std::vector& atbs = _elem->attributes(); size_t atbn = atbs.size(); for(size_t index=0;indexf_out << "ItemM::check_item :" << " for XML item " << sout(a_tree.tag_name()) << ", attribute " << sout(_atb) << " has an empty value." << std::endl; visitor->m_status = false; } } }}} return true; } static void collect_by_tag(tree& a_tree,const std::string& a_tag, std::vector& a_items) { if(a_tree.tag_name()==a_tag) a_items.push_back(&a_tree); tools_lforcit(ielem*,a_tree.m_childs,it) { if(tree* _tree = id_cast(*(*it))) { collect_by_tag(*_tree,a_tag,a_items); } } } tree* find_by_tag(const std::string& a_tag) const { if(tag_name()==a_tag) return const_cast(this); // Look children : tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { tree* itemML = _tree->find_by_tag(a_tag); if(itemML) return itemML; }} return 0; } static void unique(std::vector& a_items) { std::vector items2; tools_vforcit(tree*,a_items,it) { std::string name; if(!(*it)->attribute_value("name",name)) continue; if(name.empty()) continue; bool found = false; {tools_vforit(tree*,items2,it2) { std::string name2; (*it2)->attribute_value("name",name2); if(name2==name) { found = true; break; } }} if(!found) { items2.push_back(*it); } } a_items = items2; } tree* find_item(const std::string& a_name) const { {size_t linen = m_atbs.size(); for(unsigned int count=0;count(this); break; } }} // Look children : tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { tree* item = _tree->find_item(a_name); if(item) return item; }} return 0; } tree* find_item_with_tag(const std::string& a_tag, const std::string& a_name) const { if(a_tag==tag_name()) { std::string s; attribute_value("name",s); if(a_name==s) return const_cast(this); } // Look children : tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { tree* item = _tree->find_item_with_tag(a_tag,a_name); if(item) return item; }} return 0; } void remove_elements(const std::string& a_name){ // TOOLS_STL : to be checked : std::list::iterator it; for(it=m_childs.begin();it!=m_childs.end();) { if(element* _elem = id_cast(*(*it))) { if(a_name==_elem->name()) { it = m_childs.erase(it); delete _elem; } else { ++it; } } else { ++it; } } } void add_child(tree* a_tree) {m_childs.push_back(a_tree);} tree* create_copy(tree* a_parent) { tree* itemML = m_factory.create(m_tag_name,m_atbs,a_parent); if(!itemML) return 0; itemML->m_atbs = m_atbs; //FIXME : m_save {tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { itemML->m_childs.push_back(new element(*_elem)); }}} {tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { //FIXME : could we have mismatch parent/child ? tree* obj = _tree->create_copy(itemML); if(!obj) { delete itemML; return 0; } itemML->add_child(obj); }}} return itemML; } bool copy(const tree& a_from, copy_what a_what = copy_all, bool a_clear = true){ // Copy data (atbs, propertis, children) of a_from onto this. if((a_what==copy_all)||(a_what==copy_attributes)) { if(a_clear) m_atbs.clear(); tools_vforcit(atb,a_from.m_atbs,it) m_atbs.push_back(*it); } if((a_what==copy_all)||(a_what==copy_elements)) { if(a_clear) delete_sub_elems(); tools_lforcit(ielem*,a_from.m_childs,it) { if(element* _elem = id_cast(*(*it))) { m_childs.push_back(new element(*_elem)); } } } if((a_what==copy_all)||(a_what==copy_children)) { if(a_clear) delete_sub_trees(); tools_lforcit(ielem*,a_from.m_childs,it) { if(tree* _tree = id_cast(*(*it))) { //FIXME : could we have mismatch parent/child ? tree* obj = _tree->create_copy(this); if(!obj) { // Something wrong, cleanup this. clear(); return false; } add_child(obj); } } } //FIXME : m_save return true; } void replace(const std::string& a_old,const std::string& a_new) { // Used by the obuild template system. {size_t atbn = m_atbs.size(); for(size_t index=0;index(*(*it))) { _tree->replace(a_old,a_new); } else if(element* _elem = id_cast(*(*it))) { _elem->replace(a_old,a_new); } } } static void collect_by_attribute(tree& a_tree, const std::string& a_tag, std::vector& a_items) { std::string value; if(a_tree.attribute_value(a_tag,value)) a_items.push_back(&a_tree); tools_lforcit(ielem*,a_tree.m_childs,it) { if(tree* _tree = id_cast(*(*it))) { collect_by_attribute(*_tree,a_tag,a_items); } } } bool element_values(const std::string& a_name, std::vector& a_values) const { a_values.clear(); tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(a_name==_elem->name()) { a_values.push_back(_elem->value()); } } } return true; } void post_execute_backward(exec_func a_function,void* a_tag){ if(!a_function) return; tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { _tree->post_execute_backward(a_function,a_tag); } } if(!a_function(*this,a_tag)) return; } tree* find_by_attribute(const std::string& a_atb,const std::string& a_value, bool a_up_down = true,bool a_left_right = true) const { if(a_up_down) { std::string s; attribute_value(a_atb,s); if(s==a_value) return const_cast(this); if(a_left_right) { tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { tree* itemML = _tree->find_by_attribute(a_atb,a_value,a_up_down,a_left_right); if(itemML) return itemML; } } } else { std::list::const_reverse_iterator it; for(it=m_childs.rbegin();it!=m_childs.rend();++it) { if(tree* _tree = id_cast(*(*it))) { tree* itemML = _tree->find_by_attribute(a_atb,a_value,a_up_down,a_left_right); if(itemML) return itemML; }} } } else { if(a_left_right) { tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { tree* itemML = _tree->find_by_attribute(a_atb,a_value,a_up_down,a_left_right); if(itemML) return itemML; }} } else { std::list::const_reverse_iterator it; for(it=m_childs.rbegin();it!=m_childs.rend();++it) { if(tree* _tree = id_cast(*(*it))) { tree* itemML = _tree->find_by_attribute(a_atb,a_value,a_up_down,a_left_right); if(itemML) return itemML; }} } std::string s; attribute_value(a_atb,s); if(s==a_value) return const_cast(this); } return 0; } void dump_xml(std::ostream& a_out,const std::string& a_spaces = "") { // begin tag : a_out << a_spaces << "<" << m_tag_name; {size_t atbn = m_atbs.size(); for(size_t index=0;index" << std::endl; {tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { const std::vector& atbs = _elem->attributes(); bool isCallback = false; a_out << a_spaces << " <" << _elem->name(); size_t atbn = atbs.size(); for(size_t index=0;indexvalue().empty()) { a_out << "/>" << std::endl; } else { a_out << ">"; std::string value = to_xml(_elem->value()); if(isCallback) { if(value.find("\\n\\")==std::string::npos) { a_out << value; } else { a_out << std::endl; replace_(value,"\\n\\","@OnX@"); replace_(value,"@OnX@","\\n\\\n"); strip(value,trailing,' '); a_out << value; size_t l = value.size(); if(l && value[l-1]!='\n') a_out << std::endl; a_out << a_spaces << " "; } } else { a_out << value; } a_out << "name() << ">" << std::endl; } }}} // End tag : a_out << a_spaces << "" << std::endl; } bool set_element_atb_value(const std::string& a_elem, const std::string& a_atb, const std::string& a_value,int a_index = 0){ if(a_elem.empty()) { size_t linen = m_atbs.size(); for(size_t count=0;count(*(*it))) { if(a_elem==_elem->name()) { if(index==a_index) { _elem->set_attribute_value(a_atb,a_value); return true; } else { index++; } } }} return false; } } // NOTE : used in osc. Should be removed. void sub_trees(std::list& a_list) const { a_list.clear(); tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { a_list.push_back(_tree); }} } void sub_elems(std::list& a_list) const { a_list.clear(); tools_lforcit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { a_list.push_back(_elem); }} } bool replace_child(tree* a_old,tree* a_new) { tools_lforit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { if(_tree==a_old) { (*it) = a_new; return true; //replacement done. } }} return false; //no replacement done. } // used in OnX/Widget. virtual void* cast(const std::string& a_class) const { if(void* p = cmp_cast(this,a_class)) {return p;} else return 0; } void delete_element(const std::string& a_name) { tools_lforit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(a_name==_elem->name()) { m_childs.erase(it); delete _elem; break; } }} } void delete_element(element* a_element) { tools_lforit(ielem*,m_childs,it) { if(element* _elem = id_cast(*(*it))) { if(_elem==a_element) { m_childs.erase(it); delete _elem; break; } }} } void delete_sub_trees(){ std::list::iterator it; for(it=m_childs.begin();it!=m_childs.end();) { if(tree* _tree = id_cast(*(*it))) { it = m_childs.erase(it); delete _tree; } else { it++; } } } void delete_sub_elems(){ std::list::iterator it; for(it=m_childs.begin();it!=m_childs.end();) { if(element* _elem = id_cast(*(*it))) { it = m_childs.erase(it); delete _elem; } else { it++; } } } const tree* find_by_element_in_same_level(const std::string& a_name, const std::string& a_value) const { // Look children : tools_lforcit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { std::string s; if(_tree->element_value(a_name,s) && (a_value==s)) return _tree; }} return 0; } bool element_value_boolean(const std::string& a_name,bool& a_value) const { std::string value; if(!element_value(a_name,value)) return false; return to(value,a_value); } void update_tree(const tree& a_old) { // We map the opened elements in the old tree within this tree. // Algorithm based on the existence of a "label" element. tools_lforit(ielem*,m_childs,it) { if(tree* _tree = id_cast(*(*it))) { std::string slabel; if(_tree->element_value("label",slabel)) { // Try to find a same label in the same level : const tree* item = a_old.find_by_element_in_same_level("label",slabel); if(item) { bool sopened; if(item->element_value_boolean("opened",sopened) && sopened) { _tree->set_element_value("opened","true"); } if(a_old.number_of_trees()) _tree->update_tree(*item); } } }} } ////////////////////////////////////////////////// /// end osc ////////////////////////////////////// ////////////////////////////////////////////////// /////////////////////////////////////////////////////// /// sub trees ///////////////////////////////////////// /////////////////////////////////////////////////////// tree* parent() const {return m_parent;} void set_parent(tree* a_parent) {m_parent = a_parent;} unsigned int number_of_trees() const { unsigned int number = 0; tools_lforcit(ielem*,m_childs,it) { if(id_cast(*(*it))) number++; } return number; } void remove_child(tree*& a_tree,bool a_delete = true){ m_childs.remove(a_tree); if(a_delete) { delete a_tree; a_tree = 0; } } /* bool is_first_child(const tree& a_tree) { if(m_children.empty()) return false; return ( (*(m_children.begin()))==&a_tree) ? true : false; } bool is_last_child(const tree& a_tree) { if(m_children.empty()) return false; return ( m_children.back()==&a_tree) ? true : false; } */ /* TOOLS_STL : no std::list::insert. void add_after(tree* a_tree,tree* a_entry){ tools_lforit(tree*,m_children,it) { if((*it)==a_tree) { m_children.insert(it,a_entry); return; } } } //TOOLS_STL : no std::list::iterator::operator--. tree* previous_child(const tree& a_tree){ tools_lforit(tree*,m_children,it) { if((*it)==&a_tree) { if(it==m_children.begin()) return 0; --it; return *it; } } return 0; } tree* next_child(const tree& a_tree){ tools_lforit(tree*,m_children,it) { if((*it)==&a_tree) { std::list::iterator last = m_children.end(); --last; if(it==last) return 0; ++it; return *it; } } return 0; } */ void set_data(void* a_data_1,void* a_data_2,int a_data_int) { m_data_1 = a_data_1; m_data_2 = a_data_2; m_data_int = a_data_int; } void get_data(void*& a_data_1,void*& a_data_2,int& a_data_int) const { a_data_1 = m_data_1; a_data_2 = m_data_2; a_data_int = m_data_int; } void* get_data1() const {return m_data_1;} void* get_data2() const {return m_data_2;} int get_data_int() const {return m_data_int;} void set_depth(unsigned int a_depth) {m_depth = a_depth;} unsigned int depth() const {return m_depth;} void set_save_flag(bool a_value) {m_save = a_value;} bool save_flag() const {return m_save;} void set_file(const std::string& a_file) {m_file = a_file;} const std::string& file() const {return m_file;} protected: void clear(){ m_atbs.clear(); // TOOLS_STL : no std::list::erase. //{std::list::iterator it; // for(it=m_children.begin(); // it!=m_children.end(); // it = m_children.erase(it)) // delete (*it);} // //{std::list::iterator it; // for(it=m_elems.begin();it!=m_elems.end();it = m_elems.erase(it)) // delete (*it);} while(!m_childs.empty()) { ielem* item = m_childs.front(); m_childs.remove(item); delete item; } } protected: std::string m_tag_name; factory& m_factory; tree* m_parent; //std::list m_children; //std::list m_elems; std::list m_childs; std::vector m_atbs; std::string m_file; bool m_save; void* m_data_1; void* m_data_2; int m_data_int; int m_depth; }; class looper { public: looper(const tree& a_tree) :m_it(a_tree.childs().begin()) ,m_end(a_tree.childs().end()) {} virtual ~looper(){} protected: looper(const looper& a_from) :m_it(a_from.m_it) ,m_end(a_from.m_end) {} looper& operator=(const looper&){return *this;} public: tree* next_tree() { for(;m_it!=m_end;++m_it) { tree* _tree = id_cast(*(*m_it)); if(_tree) {m_it++;return _tree;} } return 0; } element* next_element() { for(;m_it!=m_end;++m_it) { element* _elem = id_cast(*(*m_it)); if(_elem) {m_it++;return _elem;} } return 0; } protected: std::list::const_iterator m_it; std::list::const_iterator m_end; }; class default_factory : public virtual factory { #ifdef TOOLS_MEM TOOLS_SCLASS(tools::xml::default_factory) #endif public: virtual tree* create(const std::string& a_tag_name,const std::vector& a_atbs,tree* a_parent) { // It does not add the new tree in parent. tree* itemML = new tree(a_tag_name,*this,a_parent); itemML->set_attributes(a_atbs); return itemML; } public: default_factory(){ #ifdef TOOLS_MEM mem::increment(s_class().c_str()); #endif } virtual ~default_factory(){ #ifdef TOOLS_MEM mem::decrement(s_class().c_str()); #endif } public: default_factory(const default_factory& a_from) :factory(a_from){ #ifdef TOOLS_MEM mem::increment(s_class().c_str()); #endif } default_factory& operator=(const default_factory&){return *this;} }; }} #endif