/*************************************************************************** source::worx xtree Copyright © 2024-2025 c.holzheuer christoph.holzheuer@gmail.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ***************************************************************************/ #ifndef ZNODE_H #define ZNODE_H #include #include #include //#include #include #include #include #include #include //#include #include #include namespace znode { /* - die payload attributes wird 'drangeerbt'. sollte die nicht besser ge-templatet sein? - die finder sollten besser ausgelagert sein */ // forward declaration of base class zbasic_node ... //template //class zbasic_node; // ... used here for conveniance typedef //template //using zshared_node = std::shared_ptr>; //! einfache tree-klasse, besonderheit: der nutzlast-string-type ist templated. template class zbasic_node : public zid, public zpayload, public std::enable_shared_from_this> { public: using str_cref = const str_t&; using str_list = std::vector; //using zshared_node = std::shared_ptr; //using znode_list = znode_vector; //using znode_list = znode_vector>; //using znode_list = std::vector>; using zweak_node = std::weak_ptr; using zshared_node = std::shared_ptr; using znode_list = std::vector; using zshared_cref = const std::shared_ptr&; using ziterator = znode_iterator; protected: zweak_node _parent; znode_list _children; struct match_node { match_node( zbasic_node* match ) : _match{match} {} bool operator()( const zshared_node& node ) { return _match == node.get(); } zbasic_node* _match{}; }; public: //! shortcut auf std::make_shared... static zshared_node make_node( str_cref arg1, str_cref arg2 = "" , zshared_cref parent = nullptr ) { return std::make_shared( arg1, arg2, parent ); } //! leerer konstruktor zbasic_node() = default; //! konstruktor mit tag_name und optionalem elternknoten zbasic_node( str_cref tag_name, zshared_cref parent = nullptr ) : zpayload{tag_name}, _parent{parent} { } //! konstruktor mit tag_name, value und optionalem elternknoten zbasic_node( str_cref tag_name, str_cref value, zshared_cref parent = nullptr ) : zpayload{tag_name,value}, _parent{parent} { } //! konstruktor mit tag_name, attributlist und optionalem elternknoten zbasic_node( str_cref tag_name, const str_list& attributes, zshared_cref parent = nullptr ) : zbasic_node{tag_name, parent} { for( str_cref entry : attributes ) { str_t key_value, data_value; if( xstr_split_by( entry, "=", key_value, data_value) ) set_attribute( key_value, data_value ); } } //! default destruktor virtual ~zbasic_node() = default; //! kopieren ist hier nicht sinnvoll zbasic_node(const zbasic_node&) = delete; zbasic_node& operator=(const zbasic_node&) = delete; // 'move' geht (shared_from_this bleibt gültig) zbasic_node(zbasic_node&&) noexcept = default; zbasic_node& operator=(zbasic_node&&) noexcept = default; //! erzeugt eine deep-copy von 'mir' _mit_ allen kindknoten virtual zshared_node clone(const zshared_node& parent = nullptr ) const { // copy als shared ptr erzeugen zshared_node new_me = std::make_shared(this->_tag_name, this->_value ); // auch die attribute kopieren ... new_me->_attributes = this->_attributes; // ... und, so vorhanden, den parent-node. new_me->_parent = parent; // kinder kopieren for (const zshared_node& child : _children) new_me->_children.push_back( child->clone( new_me ) ); return new_me; } //! stl-ish traverse iterators auto begin() { return ziterator(this); } //! stl-ish traverse iterators auto end() { return ziterator(nullptr); } // ... set_int // ... as_int //! erzeugt einen shared_ptr zshared_node parent() const { return _parent.lock(); } zshared_node sibling() { if( !parent() ) //return zshared_node( make_node("WTF1") ); return zshared_node(); znode_list& childs = _parent->_children; auto it = std::find( childs.begin(), childs.end(), this->shared_from_this() ); if( ++it != childs.end()) return *(it); //return zshared_node( make_node("WTF?") ); return zshared_node(); } inline const znode_list& children() const { return _children; } bool has_children() const { return !children().empty(); } zshared_node first_child() { if(!children().empty()) return children().front(); return nullptr; } zshared_node last_child() { if(!children().empty()) return children().back(); return nullptr; } zshared_node child( int idx ) { if(idx>-1 && idx_parent = this->shared_from_this(); return int(children().size() - 1); } //! fügt einen knoten in meine kinderliste ein und macht mich //! zu dessen elternknoten. int add_child_at( int idx, const zshared_node& node ) { // _fixme! was ist, wenn da schon ein elternknoten ist? _children.insert(children().begin() + idx, node ); node->_parent = this->shared_from_this(); return int(children().size() - 1); } //! fügt einen shard_ptr von 'mir' in die kinderliste meines elternknotens ein. void add_me_at( int offset ) { if( parent() ) parent()->add_child_at( offset, this->shared_from_this() ); else throw std::runtime_error("add_me_at(offset): no parent node"); } //! fügt einen shard_ptr von 'mir' in die kinderliste des übergebenen knotens ein //! und macht diesen zu meinem elternknoten. void add_me_at( int offset, const zshared_node& parent_node ) { if( parent_node ) { _parent = parent_node; parent_node->add_child_at( offset, this->shared_from_this() ); } else { throw std::runtime_error("add_me_at(offset,parent): no parent node"); } } int own_pos() { if( parent()) return parent()->child_pos( this->shared_from_this() ); return -1; } //int child_pos(zbasic_node* child) int child_pos(const zshared_node& child) { //auto pos = std::find_if(children().begin(), children().end(), match_node(child) ); auto pos = std::find(children().begin(), children().end(), child ); if ( pos != children().end() ) return std::distance( children().begin(), pos ); return -1; } //zshared_node unlink_child( zbasic_node* node ) zshared_node unlink_child( const zshared_node& node ) { auto it = std::find(_children.begin(), _children.end(), node); if (it == _children.end()) return nullptr; zshared_node removed = *it; // Parent-Zeiger im Kind zurücksetzen removed->_parent.reset(); _children.erase(it); return removed; } //! entfernt 'mich' aus der kinderliste des elternknotens. void unlink_self() { if(parent()) parent()->unlink_child( this->shared_from_this() ); else throw std::runtime_error("unlink_self(): no parent node"); } //! findet den ersten kind-knoten mit dem attribut 'attrkey' welches den //! wert 'attrvalue' hat. zshared_node find_child_by_attribute(str_cref attrkey, str_cref attrvalue ) { for( auto child : _children ) { qDebug() << " --#- " << child->name() << " : " << child->has_attribute( attrkey, attrvalue ); if( child->has_attribute( attrkey, attrvalue )) return child; } return zshared_node(); } // zshared_node find_child_by_tag_name(str_cref tagname ) { for( auto child : _children ) { if( child->tag_name() == tagname ) return child; } return zshared_node(); } zshared_node find_child_by_id( int id ) { for (auto child : _children ) { if (child->_id == id) return child; } return zshared_node(); } void dump(int indent = 0) const { // fix_me! qDebug() << std::string(indent * 2, ' ').c_str() << this->to_string(); //qDebug() << to_string(); //qDebug() << '\n';// std::endl; if (!children().empty()) { for (auto child : _children) { //qDebug() << " --- type: " << typeid(child).name(); child.get()->dump( indent + 1 ); } } } template bool for_each_x( T func, int depth = 0 ) //bool for_each( auto func ) const { if( !apply( func, depth ) ) return false; if( !children().empty() ) { for( auto child : _children ) { if( !child->for_each( func, depth+1 ) ) return false; } } return true; } // find ... // depth first ... // by attr // by child // by value // by find( auto xxx ) // ... }; class zbasic_node_walker { public: virtual void begin() {} template void for_each_node( zbasic_node* node ); virtual void end() {} }; } //namespace znode #endif // znode_H