1024 lines
28 KiB
Plaintext
1024 lines
28 KiB
Plaintext
// 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 <list>
|
|
#include <ostream>
|
|
|
|
namespace tools {
|
|
namespace xml {
|
|
|
|
// A tree is :
|
|
// <tree atb1="" atb2="" ...>
|
|
// ...
|
|
// <tree...>
|
|
// </tree>
|
|
// ...
|
|
// <element atb1="" atb2="" ...> value </element>
|
|
// ...
|
|
// </tree>
|
|
//
|
|
// tree.attribute_value(<name>,s)
|
|
// retrieve the value of atb <name> of a <tag>
|
|
// tree.attribute_value(<element>,<name>,s)
|
|
// retrieve the value of an atb <name> of a <element> of a <tree>
|
|
//
|
|
|
|
class tree;
|
|
|
|
class factory {
|
|
public:
|
|
virtual ~factory(){}
|
|
public:
|
|
typedef std::pair<std::string,std::string> atb;
|
|
public:
|
|
virtual tree* create(const std::string& a_tag_name,const std::vector<atb>& 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<tree>(this,a_class)) {return p;}
|
|
else return 0;
|
|
}
|
|
public:
|
|
typedef std::pair<std::string,std::string> 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<ielem*>& childs() const {return m_childs;}
|
|
|
|
///////////////////////////////////////////////////////
|
|
/// attributes ////////////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
const std::vector<atb>& 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<number;index++) {
|
|
if(m_atbs[index].first==a_name) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void set_attributes(const std::vector<atb>& 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<linen;count++) {
|
|
if(m_atbs[count].first==a_atb) {
|
|
a_value = m_atbs[count].second;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <class T>
|
|
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<T>(sv,a_value);
|
|
}
|
|
|
|
void remove_attributes(const std::string& a_atb) {
|
|
std::vector<atb>::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<atb>::iterator it = m_atbs.begin();
|
|
size_t linen = m_atbs.size();
|
|
for(size_t count=0;count<linen;count++,++it) {
|
|
if(m_atbs[count].first==a_name) {
|
|
m_atbs.erase(it);
|
|
return true; //Found and removed.
|
|
}
|
|
}
|
|
return false; //Not found.
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
/// elements //////////////////////////////////////////
|
|
///////////////////////////////////////////////////////
|
|
void add_element(const std::string& a_name,const std::vector<atb>& 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<ielem,element>(*(*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;index<atbn;index++) {
|
|
a_out << " attribute : " << sout(m_atbs[index].first) << " "
|
|
<< sout(m_atbs[index].second) << std::endl;
|
|
}}
|
|
|
|
{tools_lforit(element*,m_elems,it) {
|
|
a_out << " element : \"" << (*it)->name() << "\" \""
|
|
<< (*it)->value() << "\"" << std::endl;
|
|
}}
|
|
|
|
{tools_lforit(tree*,m_children,it) {
|
|
(*it)->dump(a_out);
|
|
}}
|
|
}
|
|
|
|
const std::list<element*>& 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<ielem,element>(*(*it))) {
|
|
if(a_elem==_elem->name()) {
|
|
if(_elem->attribute_value(a_atb,a_value)) return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <class T>
|
|
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<T>(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<ielem,element>(*(*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<ielem,element>(*(*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<atb> 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<tree*>(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<ielem,tree>(*(*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<atb>& atbs = a_tree.attributes();
|
|
size_t atbn = atbs.size();
|
|
for(size_t index=0;index<atbn;index++) {
|
|
const std::string& _atb = atbs[index].first;
|
|
const std::string& atbv = atbs[index].second;
|
|
if(atbv.empty()) {
|
|
visitor->f_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<ielem,element>(*(*it))) {
|
|
const std::vector<atb>& atbs = _elem->attributes();
|
|
size_t atbn = atbs.size();
|
|
for(size_t index=0;index<atbn;index++) {
|
|
const std::string& _atb = atbs[index].first;
|
|
const std::string& atbv = atbs[index].second;
|
|
if(atbv.empty()) {
|
|
visitor->f_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<tree*>& 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<ielem,tree>(*(*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<tree*>(this);
|
|
// Look children :
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*it))) {
|
|
tree* itemML = _tree->find_by_tag(a_tag);
|
|
if(itemML) return itemML;
|
|
}}
|
|
return 0;
|
|
}
|
|
|
|
static void unique(std::vector<tree*>& a_items) {
|
|
std::vector<tree*> 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<linen;count++) {
|
|
if(m_atbs[count].first=="name") {
|
|
if(m_atbs[count].second==a_name) return const_cast<tree*>(this);
|
|
break;
|
|
}
|
|
}}
|
|
|
|
// Look children :
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*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<tree*>(this);
|
|
}
|
|
|
|
// Look children :
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*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<ielem*>::iterator it;
|
|
for(it=m_childs.begin();it!=m_childs.end();) {
|
|
if(element* _elem = id_cast<ielem,element>(*(*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<ielem,element>(*(*it))) {
|
|
itemML->m_childs.push_back(new element(*_elem));
|
|
}}}
|
|
|
|
{tools_lforcit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*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<ielem,element>(*(*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<ielem,tree>(*(*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<atbn;index++) {
|
|
std::string& value = m_atbs[index].second;
|
|
replace_(value,a_old,a_new);
|
|
}}
|
|
|
|
tools_lforit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*it))) {
|
|
_tree->replace(a_old,a_new);
|
|
} else if(element* _elem = id_cast<ielem,element>(*(*it))) {
|
|
_elem->replace(a_old,a_new);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void collect_by_attribute(tree& a_tree,
|
|
const std::string& a_tag,
|
|
std::vector<tree*>& 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<ielem,tree>(*(*it))) {
|
|
collect_by_attribute(*_tree,a_tag,a_items);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool element_values(const std::string& a_name,
|
|
std::vector<std::string>& a_values) const {
|
|
a_values.clear();
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(element* _elem = id_cast<ielem,element>(*(*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<ielem,tree>(*(*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<tree*>(this);
|
|
|
|
if(a_left_right) {
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*it))) {
|
|
tree* itemML =
|
|
_tree->find_by_attribute(a_atb,a_value,a_up_down,a_left_right);
|
|
if(itemML) return itemML;
|
|
}
|
|
}
|
|
} else {
|
|
std::list<ielem*>::const_reverse_iterator it;
|
|
for(it=m_childs.rbegin();it!=m_childs.rend();++it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*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<ielem,tree>(*(*it))) {
|
|
tree* itemML =
|
|
_tree->find_by_attribute(a_atb,a_value,a_up_down,a_left_right);
|
|
if(itemML) return itemML;
|
|
}}
|
|
} else {
|
|
std::list<ielem*>::const_reverse_iterator it;
|
|
for(it=m_childs.rbegin();it!=m_childs.rend();++it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*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<tree*>(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<atbn;index++) {
|
|
a_out << " " << m_atbs[index].first << "="
|
|
<< sout(m_atbs[index].second);
|
|
}}
|
|
a_out << ">" << std::endl;
|
|
|
|
{tools_lforcit(ielem*,m_childs,it) {
|
|
if(element* _elem = id_cast<ielem,element>(*(*it))) {
|
|
|
|
const std::vector<atb>& atbs = _elem->attributes();
|
|
|
|
bool isCallback = false;
|
|
|
|
a_out << a_spaces << " <" << _elem->name();
|
|
size_t atbn = atbs.size();
|
|
for(size_t index=0;index<atbn;index++) {
|
|
a_out << " " << atbs[index].first << "="
|
|
<< sout(atbs[index].second);
|
|
if(atbs[index].first=="exec") isCallback = true;
|
|
}
|
|
if(_elem->value().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 << "</" << _elem->name() << ">" << std::endl;
|
|
}
|
|
}}}
|
|
|
|
// End tag :
|
|
a_out << a_spaces << "</" << m_tag_name << ">" << 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<linen;count++) {
|
|
if(m_atbs[count].first==a_atb) {
|
|
m_atbs[count].second = a_value;
|
|
return true;
|
|
}
|
|
}
|
|
// Not found, add it :
|
|
m_atbs.push_back(atb(a_atb,a_value));
|
|
return true;
|
|
} else {
|
|
int index = 0;
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(element* _elem = id_cast<ielem,element>(*(*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<tree*>& a_list) const {
|
|
a_list.clear();
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*it))) {
|
|
a_list.push_back(_tree);
|
|
}}
|
|
}
|
|
void sub_elems(std::list<element*>& a_list) const {
|
|
a_list.clear();
|
|
tools_lforcit(ielem*,m_childs,it) {
|
|
if(element* _elem = id_cast<ielem,element>(*(*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<ielem,tree>(*(*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<tree>(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<ielem,element>(*(*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<ielem,element>(*(*it))) {
|
|
if(_elem==a_element) {
|
|
m_childs.erase(it);
|
|
delete _elem;
|
|
break;
|
|
}
|
|
}}
|
|
}
|
|
|
|
void delete_sub_trees(){
|
|
std::list<ielem*>::iterator it;
|
|
for(it=m_childs.begin();it!=m_childs.end();) {
|
|
if(tree* _tree = id_cast<ielem,tree>(*(*it))) {
|
|
it = m_childs.erase(it);
|
|
delete _tree;
|
|
} else {
|
|
it++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void delete_sub_elems(){
|
|
std::list<ielem*>::iterator it;
|
|
for(it=m_childs.begin();it!=m_childs.end();) {
|
|
if(element* _elem = id_cast<ielem,element>(*(*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<ielem,tree>(*(*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<ielem,tree>(*(*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<ielem,tree>(*(*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<tree*>::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<tree*>::iterator it;
|
|
// for(it=m_children.begin();
|
|
// it!=m_children.end();
|
|
// it = m_children.erase(it))
|
|
// delete (*it);}
|
|
//
|
|
//{std::list<element*>::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<tree*> m_children;
|
|
//std::list<element*> m_elems;
|
|
std::list<ielem*> m_childs;
|
|
std::vector<atb> 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<ielem,tree>(*(*m_it));
|
|
if(_tree) {m_it++;return _tree;}
|
|
}
|
|
return 0;
|
|
}
|
|
element* next_element() {
|
|
for(;m_it!=m_end;++m_it) {
|
|
element* _elem = id_cast<ielem,element>(*(*m_it));
|
|
if(_elem) {m_it++;return _elem;}
|
|
}
|
|
return 0;
|
|
}
|
|
protected:
|
|
std::list<ielem*>::const_iterator m_it;
|
|
std::list<ielem*>::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<tree::atb>& 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
|