tree data UPDATE refactor default NP container updates

Make sure specific functions are always used so that
it is performed in the same way and always.

Fixes #2315
diff --git a/src/parser_common.c b/src/parser_common.c
index 0d03229..9bd2274 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -420,7 +420,7 @@
             }
 
             /* update dflt flag for all parent NP containers */
-            lyd_cont_set_dflt(lyd_parent(node));
+            lyd_np_cont_dflt_set(lyd_parent(node));
             break;
         }
 
diff --git a/src/tree_data.c b/src/tree_data.c
index 8488401..b53a1a1 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -545,7 +545,6 @@
 void
 lyd_insert_after_node(struct lyd_node **first_sibling_p, struct lyd_node *sibling, struct lyd_node *node)
 {
-    struct lyd_node_inner *par;
     struct lyd_node *first_sibling;
 
     assert(!node->next && (node->prev == node) && (sibling != node));
@@ -571,19 +570,15 @@
     sibling->next = node;
     node->parent = sibling->parent;
 
-    for (par = node->parent; par; par = par->parent) {
-        if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
-            /* remove default flags from NP containers */
-            par->flags &= ~LYD_DEFAULT;
-        }
+    if (!(node->flags & LYD_DEFAULT)) {
+        /* remove default flags from NP containers */
+        lyd_np_cont_dflt_del(lyd_parent(node));
     }
 }
 
 void
 lyd_insert_before_node(struct lyd_node *sibling, struct lyd_node *node)
 {
-    struct lyd_node_inner *par;
-
     assert(!node->next && (node->prev == node) && (sibling != node));
 
     node->next = sibling;
@@ -599,11 +594,9 @@
     }
     node->parent = sibling->parent;
 
-    for (par = node->parent; par; par = par->parent) {
-        if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
-            /* remove default flags from NP containers */
-            par->flags &= ~LYD_DEFAULT;
-        }
+    if (!(node->flags & LYD_DEFAULT)) {
+        /* remove default flags from NP containers */
+        lyd_np_cont_dflt_del(lyd_parent(node));
     }
 }
 
@@ -628,11 +621,9 @@
     par->child = node;
     node->parent = par;
 
-    for ( ; par; par = par->parent) {
-        if ((par->flags & LYD_DEFAULT) && !(node->flags & LYD_DEFAULT)) {
-            /* remove default flags from NP containers */
-            par->flags &= ~LYD_DEFAULT;
-        }
+    if (!(node->flags & LYD_DEFAULT)) {
+        /* remove default flags from NP containers */
+        lyd_np_cont_dflt_del(parent);
     }
 }
 
@@ -1192,7 +1183,7 @@
         }
 
         /* check for NP container whether its last non-default node is not being unlinked */
-        lyd_cont_set_dflt(lyd_parent(node));
+        lyd_np_cont_dflt_set(lyd_parent(node));
 
         node->parent = NULL;
     }
@@ -1285,9 +1276,8 @@
     }
 
     /* remove default flags from NP containers */
-    while (clear_dflt && parent && (parent->schema->nodetype == LYS_CONTAINER) && (parent->flags & LYD_DEFAULT)) {
-        parent->flags &= ~LYD_DEFAULT;
-        parent = lyd_parent(parent);
+    if (clear_dflt) {
+        lyd_np_cont_dflt_del(parent);
     }
 }
 
@@ -2496,7 +2486,6 @@
     const struct lyd_node *child_src, *tmp, *sibling_src;
     struct lyd_node *match_trg, *dup_src, *elem, *leader;
     struct lyd_node_opaq *opaq_trg, *opaq_src;
-    struct lysc_type *type;
     const struct lysc_node *schema;
     struct ly_ht *child_dup_inst = NULL;
     LY_ERR r;
@@ -2546,19 +2535,15 @@
                         &opaq_trg->val_prefix_data);
             }
         } else if ((match_trg->schema->nodetype == LYS_LEAF) &&
-                lyd_compare_single(sibling_src, match_trg, LYD_COMPARE_DEFAULTS)) {
-            /* since they are different, they cannot both be default */
-            assert(!(sibling_src->flags & LYD_DEFAULT) || !(match_trg->flags & LYD_DEFAULT));
+                ((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT))) {
+            /* update value */
+            r = lyd_change_term_val(match_trg, &((struct lyd_node_term *)sibling_src)->value, 0,
+                    sibling_src->flags & LYD_DEFAULT);
+            LY_CHECK_RET(r && (r != LY_EEXIST) && (r != LY_ENOT), r);
 
-            /* update value (or only LYD_DEFAULT flag) only if flag set or the source node is not default */
-            if ((options & LYD_MERGE_DEFAULTS) || !(sibling_src->flags & LYD_DEFAULT)) {
-                type = ((struct lysc_node_leaf *)match_trg->schema)->type;
-                type->plugin->free(LYD_CTX(match_trg), &((struct lyd_node_term *)match_trg)->value);
-                LY_CHECK_RET(type->plugin->duplicate(LYD_CTX(match_trg), &((struct lyd_node_term *)sibling_src)->value,
-                        &((struct lyd_node_term *)match_trg)->value));
-
-                /* copy flags and add LYD_NEW */
-                match_trg->flags = sibling_src->flags | ((options & LYD_MERGE_WITH_FLAGS) ? 0 : LYD_NEW);
+            if (options & LYD_MERGE_WITH_FLAGS) {
+                /* keep the exact same flags */
+                match_trg->flags = sibling_src->flags;
             }
         } else if ((match_trg->schema->nodetype & LYS_ANYDATA) && lyd_compare_single(sibling_src, match_trg, 0)) {
             /* update value */
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
index f480a5a..14b558a 100644
--- a/src/tree_data_common.c
+++ b/src/tree_data_common.c
@@ -4,7 +4,7 @@
  * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief Parsing and validation common functions for data trees
  *
- * Copyright (c) 2015 - 2022 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2024 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -1157,35 +1157,6 @@
     return 0;
 }
 
-void
-lyd_cont_set_dflt(struct lyd_node *node)
-{
-    const struct lyd_node *child;
-
-    while (node) {
-        if (!node->schema || (node->flags & LYD_DEFAULT) || !lysc_is_np_cont(node->schema)) {
-            /* not a non-dflt NP container */
-            break;
-        }
-
-        LY_LIST_FOR(lyd_child(node), child) {
-            if (!(child->flags & LYD_DEFAULT)) {
-                break;
-            }
-        }
-        if (child) {
-            /* explicit child, no dflt change */
-            break;
-        }
-
-        /* set the dflt flag */
-        node->flags |= LYD_DEFAULT;
-
-        /* check all parent containers */
-        node = lyd_parent(node);
-    }
-}
-
 /**
  * @brief Comparison callback to match schema node with a schema of a data node.
  *
@@ -1301,6 +1272,44 @@
     }
 }
 
+void
+lyd_np_cont_dflt_set(struct lyd_node *parent)
+{
+    const struct lyd_node *child;
+
+    while (parent) {
+        if (!parent->schema || (parent->flags & LYD_DEFAULT) || !lysc_is_np_cont(parent->schema)) {
+            /* not a non-dflt NP container */
+            break;
+        }
+
+        LY_LIST_FOR(lyd_child(parent), child) {
+            if (!(child->flags & LYD_DEFAULT)) {
+                break;
+            }
+        }
+        if (child) {
+            /* explicit child, no dflt change */
+            break;
+        }
+
+        /* set the dflt flag */
+        parent->flags |= LYD_DEFAULT;
+
+        /* check all parent containers */
+        parent = lyd_parent(parent);
+    }
+}
+
+void
+lyd_np_cont_dflt_del(struct lyd_node *parent)
+{
+    while (parent && (parent->flags & LYD_DEFAULT)) {
+        parent->flags &= ~LYD_DEFAULT;
+        parent = lyd_parent(parent);
+    }
+}
+
 LY_ERR
 ly_nested_ext_schema(const struct lyd_node *parent, const struct lysc_node *sparent, const char *prefix,
         size_t prefix_len, LY_VALUE_FORMAT format, void *prefix_data, const char *name, size_t name_len,
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 358528c..b4a8d25 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -4,7 +4,7 @@
  * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief internal functions for YANG schema trees.
  *
- * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2024 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -118,13 +118,6 @@
 const struct lys_module *lyd_data_next_module(struct lyd_node **next, struct lyd_node **first);
 
 /**
- * @brief Set dflt flag for a NP container if applicable, recursively for parents.
- *
- * @param[in] node Node whose criteria for the dflt flag has changed.
- */
-void lyd_cont_set_dflt(struct lyd_node *node);
-
-/**
  * @brief Search in the given siblings (NOT recursively) for the first schema node data instance.
  * Uses hashes - should be used whenever possible for best performance.
  *
@@ -148,6 +141,20 @@
 void lyd_del_move_root(struct lyd_node **root, const struct lyd_node *to_del, const struct lys_module *mod);
 
 /**
+ * @brief After adding a default child, check the node and all of its parent NP containers and set their dflt flag.
+ *
+ * @param[in] parent Changed first parent to check.
+ */
+void lyd_np_cont_dflt_set(struct lyd_node *parent);
+
+/**
+ * @brief After adding a non-default child, remove the dflt flag from parent and other parent NP containers.
+ *
+ * @param[in] parent Changed first parent to update.
+ */
+void lyd_np_cont_dflt_del(struct lyd_node *parent);
+
+/**
  * @brief Try to get schema node for data with a parent based on an extension instance.
  *
  * @param[in] parent Parsed parent data node. Set if @p sparent is NULL.
@@ -345,6 +352,23 @@
         LY_VALUE_FORMAT format, void *val_prefix_data, uint32_t hints, struct lyd_node **node);
 
 /**
+ * @brief Change the value of a term (leaf or leaf-list) node.
+ *
+ * Node changed this way is always considered explicitly set, meaning its default flag
+ * is always cleared.
+ *
+ * @param[in] term Term node to change.
+ * @param[in] val New value to use.
+ * @param[in] use_val Whether @p val can be used and spent or should only be duplicated.
+ * @param[in] is_dflt Whether @p val is a default value or not.
+ * @return LY_SUCCESS if value was changed,
+ * @return LY_EEXIST if value was the same and only the default flag was cleared,
+ * @return LY_ENOT if the values were equal and no change occured,
+ * @return LY_ERR value on other errors.
+ */
+LY_ERR lyd_change_term_val(struct lyd_node *term, struct lyd_value *val, ly_bool use_val, ly_bool is_dflt);
+
+/**
  * @brief Check the existence and create any non-existing implicit children.
  *
  * @param[in] parent Parent of the potential default values, NULL for top-level siblings.
diff --git a/src/tree_data_new.c b/src/tree_data_new.c
index cd5d63b..26e408d 100644
--- a/src/tree_data_new.c
+++ b/src/tree_data_new.c
@@ -1243,14 +1243,15 @@
  *
  * Reinserting ensures that the node is in the correct position and the data instances remain properly ordered.
  *
- * @param[in] term Term node to change. If it is a key, the parental list is inserted again.
+ * @param[in] term Term node to change. If it is a key, the parent list is reinserted.
  * @param[in] val New value for @p term.
- * @return LY_SUCCESS on success.
+ * @param[in] use_val Whether @p val can be used and spent or should only be duplicated.
+ * @return LY_ERR value.
  */
 static LY_ERR
-lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val)
+lyd_change_node_value(struct lyd_node_term *term, struct lyd_value *val, ly_bool use_val)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR rc = LY_SUCCESS;
     struct lyd_node *target, *first;
 
     if (term->schema->nodetype == LYS_LEAFLIST) {
@@ -1260,33 +1261,115 @@
     } else {
         /* just change the value */
         term->value.realtype->plugin->free(LYD_CTX(term), &term->value);
-        term->value = *val;
+        if (use_val) {
+            term->value = *val;
+        } else {
+            rc = ((struct lysc_node_leaf *)term->schema)->type->plugin->duplicate(LYD_CTX(term), val, &term->value);
+        }
+
         /* leaf that is not a key, its value is not used for its hash so it does not change */
-        return LY_SUCCESS;
+        return rc;
     }
 
     if (!LYD_NODE_IS_ALONE(target) && lyds_is_supported(target)) {
         /* changing the value may cause a change in the order */
         first = lyd_first_sibling(target);
         first = first == target ? first->next : first;
+
         /* unlink hash and unlink the target node in the lyds tree */
         lyd_unlink_tree(target);
+
         /* change value */
         term->value.realtype->plugin->free(LYD_CTX(term), &term->value);
-        term->value = *val;
+        if (use_val) {
+            term->value = *val;
+        } else {
+            rc = ((struct lysc_node_leaf *)term->schema)->type->plugin->duplicate(LYD_CTX(term), val, &term->value);
+        }
+
         /* reinserting */
         lyd_insert_node(NULL, &first, target, LYD_INSERT_NODE_DEFAULT);
     } else {
         /* unlink hash */
         lyd_unlink_hash(target);
+
         /* change value */
         term->value.realtype->plugin->free(LYD_CTX(term), &term->value);
-        term->value = *val;
+        if (use_val) {
+            term->value = *val;
+        } else {
+            rc = ((struct lysc_node_leaf *)term->schema)->type->plugin->duplicate(LYD_CTX(term), val, &term->value);
+        }
     }
-    lyd_hash(target);
-    ret = lyd_insert_hash(target);
 
-    return ret;
+    lyd_hash(target);
+    rc = lyd_insert_hash(target);
+
+    return rc;
+}
+
+LY_ERR
+lyd_change_term_val(struct lyd_node *term, struct lyd_value *val, ly_bool use_val, ly_bool is_dflt)
+{
+    LY_ERR rc = LY_SUCCESS;
+    struct lysc_type *type;
+    struct lyd_node_term *t;
+    ly_bool dflt_change, val_change;
+
+    t = (struct lyd_node_term *)term;
+    type = ((struct lysc_node_leaf *)term->schema)->type;
+
+    /* compare original and new value */
+    if (type->plugin->compare(LYD_CTX(term), &t->value, val)) {
+        /* since they are different, they cannot both be default */
+        assert(!(term->flags & LYD_DEFAULT) || !is_dflt);
+
+        /* values differ, switch them */
+        LY_CHECK_RET(lyd_change_node_value(t, val, use_val));
+        val_change = 1;
+    } else {
+        /* same values, free the new stored one */
+        if (use_val) {
+            type->plugin->free(LYD_CTX(term), val);
+        }
+        val_change = 0;
+    }
+
+    /* clear links to leafref nodes */
+    if (val_change && (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING)) {
+        lyd_free_leafref_nodes(t);
+    }
+
+    /* update flags */
+    if (val_change) {
+        term->flags |= LYD_NEW;
+    }
+    if ((term->flags & LYD_DEFAULT) && !is_dflt) {
+        /* remove dflt flag */
+        term->flags &= ~LYD_DEFAULT;
+
+        /* remove parent dflt flag */
+        lyd_np_cont_dflt_del(lyd_parent(term));
+
+        dflt_change = 1;
+    } else if (!(term->flags & LYD_DEFAULT) && is_dflt) {
+        /* add dflt flag */
+        term->flags |= LYD_DEFAULT;
+
+        /* add parent dflt flag */
+        lyd_np_cont_dflt_set(lyd_parent(term));
+
+        dflt_change = 1;
+    } else {
+        dflt_change = 0;
+    }
+
+    if (!val_change) {
+        /* only default flag change or no change */
+        rc = dflt_change ? LY_EEXIST : LY_ENOT;
+    } /* else value changed, LY_SUCCESS */
+
+    return rc;
 }
 
 /**
@@ -1307,68 +1390,20 @@
 static LY_ERR
 _lyd_change_term(struct lyd_node *term, const void *value, size_t value_len, LY_VALUE_FORMAT format)
 {
-    LY_ERR ret = LY_SUCCESS;
-    struct lysc_type *type;
-    struct lyd_node_term *t;
-    struct lyd_node *parent;
+    LY_ERR r;
     struct lyd_value val;
-    ly_bool dflt_change, val_change;
 
     assert(term && term->schema && (term->schema->nodetype & LYD_NODE_TERM));
 
-    t = (struct lyd_node_term *)term;
-    type = ((struct lysc_node_leaf *)term->schema)->type;
-
     /* parse the new value */
     LOG_LOCSET(term->schema, term);
-    ret = lyd_value_store(LYD_CTX(term), &val, type, value, value_len, 0, 0, NULL, format, NULL, LYD_HINT_DATA,
-            term->schema, NULL);
+    r = lyd_value_store(LYD_CTX(term), &val, ((struct lysc_node_leaf *)term->schema)->type, value, value_len, 0, 0,
+            NULL, format, NULL, LYD_HINT_DATA, term->schema, NULL);
     LOG_LOCBACK(1, 1);
-    LY_CHECK_GOTO(ret, cleanup);
+    LY_CHECK_RET(r);
 
-    /* compare original and new value */
-    if (type->plugin->compare(LYD_CTX(term), &t->value, &val)) {
-        /* values differ, switch them */
-        lyd_change_node_value(t, &val);
-        /* make the node non-validated */
-        term->flags &= LYD_NEW;
-        val_change = 1;
-    } else {
-        /* same values, free the new stored one */
-        type->plugin->free(LYD_CTX(term), &val);
-        val_change = 0;
-    }
-
-    /* clear links to leafref nodes */
-    if (ly_ctx_get_options(LYD_CTX(term)) & LY_CTX_LEAFREF_LINKING) {
-        lyd_free_leafref_nodes(t);
-    }
-
-    /* always clear the default flag */
-    if (term->flags & LYD_DEFAULT) {
-        for (parent = term; parent; parent = lyd_parent(parent)) {
-            parent->flags &= ~LYD_DEFAULT;
-        }
-        /* make the node non-validated */
-        term->flags &= LYD_NEW;
-        dflt_change = 1;
-    } else {
-        dflt_change = 0;
-    }
-
-    /* return value */
-    if (!val_change) {
-        if (dflt_change) {
-            /* only default flag change */
-            ret = LY_EEXIST;
-        } else {
-            /* no change */
-            ret = LY_ENOT;
-        }
-    } /* else value changed, LY_SUCCESS */
-
-cleanup:
-    return ret;
+    /* change it */
+    return lyd_change_term_val(term, &val, 1, 0);
 }
 
 LIBYANG_API_DEF LY_ERR
diff --git a/src/validation.c b/src/validation.c
index 4f91ab3..0617b5a 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -1640,7 +1640,7 @@
         LY_VAL_ERR_GOTO(r, rc = r, val_opts, cleanup);
 
         /* set default for containers */
-        lyd_cont_set_dflt(node);
+        lyd_np_cont_dflt_set(node);
     }
 
 cleanup: