/*************************************************************************** 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> 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; // functor, der auf pointer gleichheit prüft. 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... //! beachte: der eltern-knoten wird hier nicht gesetzt, der neue knoten //! wird nirgends eingefügt. static zshared_node make_node( str_cref arg1, str_cref arg2 = "" ) { return std::make_shared( arg1, arg2 ); } //! 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(); } //! gibt den nachfolge-knoten oder 'end()' zurück. zshared_node sibling() { if( parent() ) { znode_list& childs = _parent->_children; auto it = std::find( childs.begin(), childs.end(), this->shared_from_this() ); return *(it); } throw std::runtime_error("sibling(): no parent node"); } //! gibt den vector mit kind-knoten zurück inline const znode_list& children() const { return _children; } //! testet, ob kinder vorhanden sind. bool has_children() const { return !children().empty(); } bool has_child(int idx) const { return (idx>-1 && idx-1 && idxtag_name() == tagname ) return child; } return nullptr; } //! hängt einen knoten an meine kinderliste an. int add_child( const zshared_node& node ) { _children.push_back( node ); node->_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 shared_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"); } } //! findet die eigene position im eltern-knoten int own_pos() { if( parent()) return parent()->child_pos( this->shared_from_this() ); return -1; } //! findet die postion eines kind-knotens 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; } //! findet die postion eines kind-knotens 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 ) const { for( auto child : _children ) { if( child->has_attribute( attrkey, attrvalue )) return child; } return nullptr; } zshared_node find_child_by_id( int id ) const { for (auto child : _children ) { if (child->_id == id) return child; } return nullptr; } // find ... // depth first ... // by attr // by child // by value // by find( auto xxx ) // ... }; } //namespace znode #endif // znode_H