tree schema FEATURE lyd_dup() for data node duplication
diff --git a/src/tree_data.c b/src/tree_data.c
index 096b7a3..27742cb 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -633,3 +633,230 @@
LOGINT(node1->schema->module->ctx);
return LY_EINT;
}
+
+/**
+ * @brief Duplicates just a single node and interconnect it into a @p parent (if present) and after the @p prev
+ * sibling (if present).
+ *
+ * Ignores LYD_DUP_WITH_PARENTS and LYD_DUP_WITH_SIBLINGS which are supposed to be handled by lyd_dup().
+ */
+static struct lyd_node *
+lyd_dup_recursive(const struct lyd_node *node, struct lyd_node_inner *parent, struct lyd_node *prev, int options)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *dup = NULL;
+
+ LY_CHECK_ARG_RET(NULL, node, NULL);
+ ctx = node->schema->module->ctx;
+
+ switch (node->schema->nodetype) {
+ case LYS_ACTION:
+ case LYS_NOTIF:
+ case LYS_CONTAINER:
+ case LYS_LIST:
+ dup = calloc(1, sizeof(struct lyd_node_inner));
+ break;
+ case LYS_LEAF:
+ case LYS_LEAFLIST:
+ dup = calloc(1, sizeof(struct lyd_node_term));
+ break;
+ case LYS_ANYDATA:
+ case LYS_ANYXML:
+ dup = calloc(1, sizeof(struct lyd_node_any));
+ break;
+ default:
+ LOGINT(ctx);
+ goto error;
+ }
+
+ /* TODO implement LYD_DUP_WITH_WHEN */
+ dup->flags = node->flags;
+ dup->schema = node->schema;
+
+ /* interconnect the node at the end */
+ dup->parent = parent;
+ if (prev) {
+ dup->prev = prev;
+ prev->next = dup;
+ } else {
+ dup->prev = dup;
+ if (parent) {
+ parent->child = dup;
+ }
+ }
+ if (parent) {
+ parent->child->prev = dup;
+ } else if (prev) {
+ struct lyd_node *first;
+ for (first = prev; first->prev != prev; first = first->prev);
+ first->prev = dup;
+ }
+
+ /* TODO duplicate attributes, implement LYD_DUP_NO_ATTR */
+
+ /* nodetype-specific work */
+ if (dup->schema->nodetype & LYD_NODE_TERM) {
+ struct lyd_node_term *term = (struct lyd_node_term*)dup;
+ struct lyd_node_term *orig = (struct lyd_node_term*)node;
+
+ term->hash = orig->hash;
+ term->value.realtype = orig->value.realtype;
+ LY_CHECK_ERR_GOTO(term->value.realtype->plugin->duplicate(ctx, &orig->value, &term->value),
+ LOGERR(ctx, LY_EINT, "Value duplication failed."), error);
+ } else if (dup->schema->nodetype & LYD_NODE_INNER) {
+ struct lyd_node_inner *inner = (struct lyd_node_inner*)dup;
+ struct lyd_node_inner *orig = (struct lyd_node_inner*)node;
+ struct lyd_node *child, *last = NULL;
+
+ if (options & LYD_DUP_RECURSIVE) {
+ /* duplicate all the children */
+ LY_LIST_FOR(orig->child, child) {
+ last = lyd_dup_recursive(child, inner, last, options);
+ LY_CHECK_GOTO(!last, error);
+ }
+ } else if (dup->schema->nodetype == LYS_LIST && ((struct lysc_node_list*)dup->schema)->keys) {
+ /* always duplicate keys of a list */
+ unsigned int u;
+
+ child = orig->child;
+ LY_ARRAY_FOR(((struct lysc_node_list*)dup->schema)->keys, u) {
+ if (!child) {
+ /* possibly not keys are present in filtered tree */
+ break;
+ }
+ last = lyd_dup_recursive(child, inner, last, options);
+ child = child->next;
+ }
+ }
+ lyd_hash(dup);
+ } else if (dup->schema->nodetype & LYD_NODE_ANY) {
+ struct lyd_node_any *any = (struct lyd_node_any*)dup;
+ struct lyd_node_any *orig = (struct lyd_node_any*)node;
+
+ any->hash = orig->hash;
+ any->value_type = orig->value_type;
+ switch (any->value_type) {
+ case LYD_ANYDATA_DATATREE:
+ if (orig->value.tree) {
+ any->value.tree = lyd_dup(orig->value.tree, NULL, LYD_DUP_RECURSIVE | LYD_DUP_WITH_SIBLINGS);
+ LY_CHECK_GOTO(!any->value.tree, error);
+ }
+ break;
+ case LYD_ANYDATA_STRING:
+ case LYD_ANYDATA_XML:
+ case LYD_ANYDATA_JSON:
+ if (orig->value.str) {
+ any->value.str = lydict_insert(ctx, orig->value.str, strlen(orig->value.str));
+ }
+ break;
+ }
+ }
+
+ lyd_insert_hash(dup);
+ return dup;
+
+error:
+ if (!parent && !prev) {
+ lyd_free_tree(dup);
+ }
+ return NULL;
+}
+
+API struct lyd_node *
+lyd_dup(const struct lyd_node *node, struct lyd_node_inner *parent, int options)
+{
+ struct ly_ctx *ctx;
+ const struct lyd_node *orig; /* original node to be duplicated */
+ struct lyd_node *first = NULL; /* the first duplicated node, this is returned */
+ struct lyd_node *last = NULL; /* the last sibling of the duplicated nodes */
+ struct lyd_node *top = NULL; /* the most higher created node */
+ struct lyd_node_inner *local_parent = NULL; /* the direct parent node for the duplicated node(s) */
+ int keyless_parent_list = 0;
+
+ LY_CHECK_ARG_RET(NULL, node, NULL);
+ ctx = node->schema->module->ctx;
+
+ if (options & LYD_DUP_WITH_PARENTS) {
+ struct lyd_node_inner *orig_parent, *iter;
+ int repeat = 1;
+ for (top = NULL, orig_parent = node->parent; repeat && orig_parent; orig_parent = orig_parent->parent) {
+ if (parent && parent->schema == orig_parent->schema) {
+ /* stop creating parents, connect what we have into the provided parent */
+ iter = parent;
+ repeat = 0;
+ /* get know if there is a keyless list which we will have to rehash */
+ for (struct lyd_node_inner *piter = parent; piter; piter = piter->parent) {
+ if (piter->schema->nodetype == LYS_LIST && !((struct lysc_node_list*)piter->schema)->keys) {
+ keyless_parent_list = 1;
+ break;
+ }
+ }
+ } else {
+ iter = (struct lyd_node_inner*)lyd_dup_recursive((struct lyd_node*)orig_parent, NULL, NULL, 0);
+ LY_CHECK_GOTO(!iter, error);
+ }
+ if (!local_parent) {
+ local_parent = iter;
+ }
+ if (iter->child) {
+ /* 1) list - add after keys
+ * 2) provided parent with some children */
+ iter->child->prev->next = top;
+ if (top) {
+ top->prev = iter->child->prev;
+ iter->child->prev = top;
+ }
+ } else {
+ iter->child = top;
+ if (iter->schema->nodetype == LYS_LIST) {
+ /* keyless list - we will need to rehash it since we are going to add nodes into it */
+ keyless_parent_list = 1;
+ }
+ }
+ if (top) {
+ top->parent = iter;
+ }
+ top = (struct lyd_node*)iter;
+ }
+ if (repeat && parent) {
+ /* given parent and created parents chain actually do not interconnect */
+ LOGERR(ctx, LY_EINVAL, "Invalid argument parent (%s()) - does not interconnect with the created node's parents chain.", __func__);
+ goto error;
+ }
+ } else {
+ local_parent = parent;
+ }
+
+ if (local_parent && local_parent->child) {
+ last = local_parent->child->prev;
+ }
+
+ LY_LIST_FOR(node, orig) {
+ last = lyd_dup_recursive(orig, local_parent, last, options);
+ LY_CHECK_GOTO(!last, error);
+ if (!first) {
+ first = last;
+ }
+
+ if (!(options & LYD_DUP_WITH_SIBLINGS)) {
+ break;
+ }
+ }
+ if (keyless_parent_list) {
+ /* rehash */
+ for (; local_parent; local_parent = local_parent->parent) {
+ if (local_parent->schema->nodetype == LYS_LIST && !((struct lysc_node_list*)local_parent->schema)->keys) {
+ lyd_hash((struct lyd_node*)local_parent);
+ }
+ }
+ }
+ return first;
+
+error:
+ if (top) {
+ lyd_free_tree(top);
+ } else {
+ lyd_free_withsiblings(first);
+ }
+ return NULL;
+}