diff --git a/src/c4/yml/tree.cpp b/src/c4/yml/tree.cpp index 7b7eaa582..12357d476 100644 --- a/src/c4/yml/tree.cpp +++ b/src/c4/yml/tree.cpp @@ -1280,6 +1280,101 @@ void Tree::merge_with(Tree const *src, size_t src_node, size_t dst_node) } +//----------------------------------------------------------------------------- + +bool Tree::has_all(Tree const* reftree, size_t refnode, size_t subject_node) const +{ + _RYML_CB_ASSERT(m_callbacks, reftree != nullptr); + if (subject_node == NONE) + subject_node = root_id(); + if (refnode == NONE) + refnode = reftree->root_id(); + + return _has_all_recursive(reftree, refnode, subject_node); +} + +bool Tree::_has_all_recursive(Tree const* reftree, size_t refnode, size_t subject_node) const +{ + if (is_val(subject_node)) + { + if( ! reftree->is_val(refnode)) + return false; + // skip value comparison + return true; + } + else if (is_keyval(subject_node)) + { + if( ! reftree->is_keyval(refnode)) + return false; + if (key(subject_node) == reftree->key(refnode)) + return true; + return false; + } + else if (is_map(subject_node)) + { + if( ! reftree->is_map(refnode)) + return false; + + if (num_children(subject_node) > reftree->num_children(refnode)) + return false; + + for (size_t sch = first_child(subject_node); sch != NONE; sch = next_sibling(sch)) + { + size_t rch = reftree->find_child(refnode, key(sch)); + if (rch == NONE) + return false; + if ( ! _has_all_recursive(reftree, rch, sch)) + return false; + } + return true; + } + else if (is_seq(subject_node)) + { + if( ! reftree->is_seq(refnode)) + return false; + + if (num_children(subject_node) > reftree->num_children(refnode)) + return false; + + size_t rch = reftree->first_child(refnode); + for (size_t sch = first_child(subject_node); sch != NONE; sch = next_sibling(sch)) + { + if ( ! _has_all_recursive(reftree, rch, sch)) + return false; + rch = reftree->next_sibling(rch); + } + return true; + } + else if (is_stream(subject_node)) + { + if( ! reftree->is_stream(refnode)) + return false; + + if (num_children(subject_node) > reftree->num_children(refnode)) + return false; + + size_t rch = reftree->first_child(refnode); + for (size_t sch = first_child(subject_node); sch != NONE;sch = next_sibling(sch)) + { + if ( ! _has_all_recursive(reftree, rch, sch)) + return false; + rch = reftree->next_sibling(rch); + } + return true; + } + else if(type(subject_node) == NOTYPE) + { + if(reftree->type(refnode) != NOTYPE) + return false; + return true; + } + else + { + C4_NEVER_REACH(); + } +} + + //----------------------------------------------------------------------------- namespace detail { diff --git a/src/c4/yml/tree.hpp b/src/c4/yml/tree.hpp index b54a48e4a..df0aeb5a9 100644 --- a/src/c4/yml/tree.hpp +++ b/src/c4/yml/tree.hpp @@ -927,6 +927,20 @@ class RYML_EXPORT Tree void merge_with(Tree const* src, size_t src_node=NONE, size_t dst_root=NONE); +public: + + /** non-recursive predicates: */ + + /** return true if subject_node has all the keys or indices in refnode from a reftree + * @note does not check values, only keys (for maps) or indices (for seqs) */ + bool has_all(Tree const* reftree, size_t refnode = NONE, size_t subject_node = NONE ) const; + +private: + + /** and helper functions to drive the recursive descent: */ + + bool _has_all_recursive(Tree const* reftree, size_t refnode, size_t subject_node) const; + /** @} */ public: