414 lines
11 KiB
C++
414 lines
11 KiB
C++
/***************************************************************************
|
|
|
|
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 <vector>
|
|
#include <string>
|
|
#include <memory>
|
|
//#include <typeinfo>
|
|
|
|
#include <QString>
|
|
#include <QDebug>
|
|
#include <xqmaptor.h>
|
|
#include <xsingleton.h>
|
|
|
|
#include <znode_iterator.h>
|
|
//#include <znode_vector.h>
|
|
#include <znode_id.h>
|
|
#include <znode_payload.h>
|
|
|
|
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 str_t>
|
|
//class zbasic_node;
|
|
|
|
// ... used here for conveniance typedef
|
|
//template<class str_t>
|
|
//using zshared_node = std::shared_ptr<zbasic_node<str_t>>;
|
|
|
|
|
|
//! einfache tree-klasse, besonderheit: der nutzlast-string-type ist templated.
|
|
template<class str_t>
|
|
class zbasic_node : public zid, public zpayload<str_t>, public std::enable_shared_from_this<zbasic_node<str_t>>
|
|
{
|
|
|
|
public:
|
|
|
|
using str_cref = const str_t&;
|
|
using str_list = std::vector<str_t>;
|
|
|
|
//using zshared_node = std::shared_ptr<zbasic_node>;
|
|
//using znode_list = znode_vector<zbasic_node>;
|
|
//using znode_list = znode_vector<zshared_node<str_t>>;
|
|
//using znode_list = std::vector<zshared_node<str_t>>;
|
|
|
|
using zweak_node = std::weak_ptr<zbasic_node>;
|
|
using zshared_node = std::shared_ptr<zbasic_node>;
|
|
using znode_list = std::vector<zshared_node>;
|
|
using zshared_cref = const std::shared_ptr<zbasic_node>&;
|
|
|
|
using ziterator = znode_iterator<zbasic_node>;
|
|
|
|
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<zbasic_node>( 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<str_t>{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<str_t>{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<zbasic_node>(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<children().size())
|
|
return _children[idx];
|
|
return nullptr;
|
|
}
|
|
|
|
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 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<typename T>
|
|
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<class str_t>
|
|
void for_each_node( zbasic_node<str_t>* node );
|
|
|
|
virtual void end()
|
|
{}
|
|
|
|
};
|
|
|
|
} //namespace znode
|
|
|
|
|
|
#endif // znode_H
|