data tree NEW support for with-defaults attribute
Tests included.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index da7ba5d..b23b5bf 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -133,7 +133,7 @@
}
static LY_ERR
-lydxml_attributes(struct lyd_xml_ctx *ctx, struct ly_set *attrs_data, struct lyd_attr **attr)
+lydxml_attributes(struct lyd_xml_ctx *ctx, struct ly_set *attrs_data, const struct lysc_node *sparent, struct lyd_attr **attr)
{
LY_ERR ret = LY_EVALID, rc;
const struct lyxml_ns *ns;
@@ -177,7 +177,7 @@
}
rc = lyd_create_attr(NULL, attr, mod, attr_data->name, attr_data->name_len, attr_data->value,
- attr_data->value_len, &attr_data->dynamic, lydxml_resolve_prefix, ctx, LYD_XML);
+ attr_data->value_len, &attr_data->dynamic, lydxml_resolve_prefix, ctx, LYD_XML, sparent);
if (rc == LY_EINCOMPLETE) {
ly_set_add(&ctx->incomplete_type_validation_attrs, attr, LY_SET_OPT_USEASLIST);
} else if (rc) {
@@ -216,7 +216,7 @@
size_t prefix_len, name_len;
struct ly_set attrs_data = {0};
const struct lyxml_ns *ns;
- struct lyd_attr *attr;
+ struct lyd_attr *attr = NULL, *attr2;
const struct lysc_node *snode;
struct lys_module *mod;
unsigned int parents_count = ctx->elements.count;
@@ -238,16 +238,12 @@
}
}
- attr = NULL;
if (ctx->status == LYXML_ATTRIBUTE) {
/* first parse all attributes so we have all the namespaces available */
if (lydxml_attributes_parse(ctx, data, &attrs_data) != LY_SUCCESS) {
ret = LY_EVALID;
goto cleanup;
}
-
- /* create actual attributes so that prefixes are available in the context */
- LY_CHECK_GOTO(ret = lydxml_attributes(ctx, &attrs_data, &attr), cleanup);
}
ns = lyxml_ns_get((struct lyxml_context *)ctx, prefix, prefix_len);
@@ -270,6 +266,11 @@
goto cleanup;
}
+ /* create actual attributes so that prefixes are available in the context */
+ if (attrs_data.count) {
+ LY_CHECK_GOTO(ret = lydxml_attributes(ctx, &attrs_data, snode, &attr), cleanup);
+ }
+
if (snode->nodetype & (LYS_ACTION | LYS_NOTIF)) {
LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_DATA, "Unexpected %s element \"%.*s\".",
snode->nodetype == LYS_ACTION ? "RPC/action" : "notification", name_len, name);
@@ -402,11 +403,6 @@
}
}
- /* add attributes */
- assert(!cur->attr);
- cur->attr = attr;
- attr = NULL;
-
/* correct flags */
if (!(snode->nodetype & (LYS_ACTION | LYS_NOTIF)) && snode->when) {
if (ctx->options & LYD_OPT_TRUSTED) {
@@ -421,6 +417,18 @@
/* node is valid */
cur->flags &= ~LYD_NEW;
}
+ LY_LIST_FOR(attr, attr2) {
+ if (!strcmp(attr2->name, "default") && !strcmp(attr2->annotation->module->name, "ietf-netconf-with-defaults")
+ && attr2->value.boolean) {
+ /* node is default according to the metadata */
+ cur->flags |= LYD_DEFAULT;
+ }
+ }
+
+ /* add attributes */
+ assert(!cur->attr);
+ cur->attr = attr;
+ attr = NULL;
/* insert */
lyd_insert_node((struct lyd_node *)parent, node, cur);
diff --git a/src/tree_data.c b/src/tree_data.c
index f3c049f..f6cd61f 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -185,26 +185,25 @@
}
LY_ERR
-lyd_value_parse_attr(struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic, int second,
- ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node **trees)
+lyd_value_parse_attr(struct ly_ctx *ctx, struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic,
+ int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+ const struct lysc_node *ctx_snode, const struct lyd_node **trees)
{
LY_ERR ret = LY_SUCCESS;
struct ly_err_item *err = NULL;
- struct ly_ctx *ctx;
struct lyext_metadata *ant;
int options = LY_TYPE_OPTS_STORE | (second ? LY_TYPE_OPTS_SECOND_CALL : 0) |
(dynamic && *dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | (trees ? 0 : LY_TYPE_OPTS_INCOMPLETE_DATA);
- assert(attr);
- ctx = attr->parent->schema->module->ctx;
+ assert(ctx && attr && ((trees && attr->parent) || ctx_snode));
+
ant = attr->annotation->data;
if (!second) {
attr->value.realtype = ant->type;
}
ret = ant->type->plugin->store(ctx, ant->type, value, value_len, options, get_prefix, parser, format,
- trees ? (void*)attr->parent : (void*)attr->parent->schema, trees,
- &attr->value, NULL, &err);
+ trees ? (void *)attr->parent : (void *)ctx_snode, trees, &attr->value, NULL, &err);
if (ret && (ret != LY_EINCOMPLETE)) {
if (err) {
ly_err_print(err);
@@ -879,14 +878,14 @@
LY_ERR
lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct lys_module *mod, const char *name,
size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix get_prefix,
- void *prefix_data, LYD_FORMAT format)
+ void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode)
{
LY_ERR ret;
struct lysc_ext_instance *ant = NULL;
struct lyd_attr *at, *last;
uint32_t v;
- assert(parent || attr);
+ assert((parent || attr) && mod);
LY_ARRAY_FOR(mod->compiled->exts, v) {
if (mod->compiled->exts[v].def->plugin == lyext_plugins_internal[LYEXT_PLUGIN_INTERNAL_ANNOTATION].plugin &&
@@ -907,7 +906,7 @@
LY_CHECK_ERR_RET(!at, LOGMEM(mod->ctx), LY_EMEM);
at->parent = parent;
at->annotation = ant;
- ret = lyd_value_parse_attr(at, value, value_len, dynamic, 0, get_prefix, prefix_data, format, NULL);
+ ret = lyd_value_parse_attr(mod->ctx, at, value, value_len, dynamic, 0, get_prefix, prefix_data, format, ctx_snode, NULL);
if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
free(at);
return ret;
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 3e6b1de..7cd5d34 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -139,13 +139,14 @@
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] prefix_data User data for @p get_prefix.
* @param[in] format Input format of @p value.
+ * @param[in] ctx_snode Context node for value resolution in schema.
* @return LY_SUCCESS on success.
* @return LY_EINCOMPLETE in case data tree is needed to finish the validation.
* @return LY_ERR value if an error occurred.
*/
LY_ERR lyd_create_attr(struct lyd_node *parent, struct lyd_attr **attr, const struct lys_module *mod, const char *name,
- size_t name_len, const char *value, size_t value_len, int *dynamic,
- ly_clb_resolve_prefix get_prefix, void *prefix_data, LYD_FORMAT format);
+ size_t name_len, const char *value, size_t value_len, int *dynamic, ly_clb_resolve_prefix get_prefix,
+ void *prefix_data, LYD_FORMAT format, const struct lysc_node *ctx_snode);
/**
* @brief Validate, canonize and store the given @p value into the node according to the node's type's rules.
@@ -171,6 +172,7 @@
/**
* @brief Validate, canonize and store the given @p value into the attribute according to the metadata annotation type's rules.
*
+ * @param[in] ctx libyang context.
* @param[in] attr Data attribute for the @p value.
* @param[in] value String value to be parsed, must not be NULL.
* @param[in] value_len Length of the give @p value (mandatory).
@@ -179,6 +181,7 @@
* @param[in] get_prefix Parser-specific getter to resolve prefixes used in the @p value string.
* @param[in] parser Parser's data for @p get_prefix
* @param[in] format Input format of the data.
+ * @param[in] ctx_snode Context node for value resolution in schema.
* @param[in] trees ([Sized array](@ref sizedarrays)) of data trees (e.g. when validating RPC/Notification) where the required
* data instance (leafref target, instance-identifier) can be placed. NULL in case the data tree are not yet complete,
* then LY_EINCOMPLETE can be returned.
@@ -186,8 +189,9 @@
* @return LY_EINCOMPLETE in case the @p trees is not provided and it was needed to finish the validation.
* @return LY_ERR value if an error occurred.
*/
-LY_ERR lyd_value_parse_attr(struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic, int second,
- ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format, const struct lyd_node **trees);
+LY_ERR lyd_value_parse_attr(struct ly_ctx *ctx, struct lyd_attr *attr, const char *value, size_t value_len, int *dynamic,
+ int second, ly_clb_resolve_prefix get_prefix, void *parser, LYD_FORMAT format,
+ const struct lysc_node *ctx_snode, const struct lyd_node **trees);
/**
* @brief Parse XML string as YANG data tree.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 80633a3..b1a662e 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -801,20 +801,6 @@
}
}
-#if 0
- /* hack for NETCONF's edit-config's operation attribute. It is not defined in the schema, but since libyang
- * implements YANG metadata (annotations), we need its definition. Because the ietf-netconf schema is not the
- * internal part of libyang, we cannot add the annotation into the schema source, but we do it here to have
- * the anotation definitions available in the internal schema structure. There is another hack in schema
- * printers to do not print this internally added annotation. */
- if (ly_strequal(mod->name, "ietf-netconf", 0)) {
- if (lyp_add_ietf_netconf_annotations(mod)) {
- lys_free(mod, NULL, 1, 1);
- return NULL;
- }
- }
-#endif
-
if (!mod->implemented) {
/* pre-compile features and extension definitions of the module */
LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->off_features), error);
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 064e6cd..61f4a19 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -68,7 +68,7 @@
LY_ARRAY_CREATE_GOTO((CTX)->ctx, EXT_C, LY_ARRAY_SIZE(EXTS_P), RET, GOTO); \
for (uint32_t __exts_iter = 0, __array_offset = LY_ARRAY_SIZE(EXT_C); __exts_iter < LY_ARRAY_SIZE(EXTS_P); ++__exts_iter) { \
LY_ARRAY_INCREMENT(EXT_C); \
- RET = lys_compile_ext(CTX, &(EXTS_P)[__exts_iter], &(EXT_C)[__exts_iter + __array_offset], PARENT, PARENT_TYPE); \
+ RET = lys_compile_ext(CTX, &(EXTS_P)[__exts_iter], &(EXT_C)[__exts_iter + __array_offset], PARENT, PARENT_TYPE, NULL); \
LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
} \
}
@@ -441,13 +441,23 @@
return NULL;
}
+/**
+ * @brief Fill in the prepared compiled extension instance structure according to the parsed extension instance.
+ *
+ * @param[in] ctx Compilation context.
+ * @param[in] ext_p Parsed extension instance.
+ * @param[in,out] ext Prepared compiled extension instance.
+ * @param[in] parent Extension instance parent.
+ * @param[in] parent_type Extension instance parent type.
+ * @param[in] ext_mod Optional module with the extension instance extension definition, set only for internal annotations.
+ */
static LY_ERR
-lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext, void *parent, LYEXT_PARENT parent_type)
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext, void *parent,
+ LYEXT_PARENT parent_type, const struct lys_module *ext_mod)
{
LY_ERR ret = LY_EVALID;
const char *name;
unsigned int u;
- const struct lys_module *mod;
struct lysc_ext **elist = NULL;
const char *prefixed_name = NULL;
@@ -491,24 +501,26 @@
}
lysc_update_path(ctx, NULL, prefixed_name);
- mod = lys_module_find_prefix(ctx->mod_def, prefixed_name, u - 1);
- if (!mod) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Invalid prefix \"%.*s\" used for extension instance identifier.", u, prefixed_name);
- goto cleanup;
- } else if (!mod->parsed->extensions) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Extension instance \"%s\" refers \"%s\" module that does not contain extension definitions.",
- prefixed_name, mod->name);
- goto cleanup;
+ if (!ext_mod) {
+ ext_mod = lys_module_find_prefix(ctx->mod_def, prefixed_name, u - 1);
+ if (!ext_mod) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid prefix \"%.*s\" used for extension instance identifier.", u, prefixed_name);
+ goto cleanup;
+ } else if (!ext_mod->parsed->extensions) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Extension instance \"%s\" refers \"%s\" module that does not contain extension definitions.",
+ prefixed_name, ext_mod->name);
+ goto cleanup;
+ }
}
name = &prefixed_name[u];
/* find the extension definition there */
- if (mod->off_extensions) {
- elist = mod->off_extensions;
+ if (ext_mod->off_extensions) {
+ elist = ext_mod->off_extensions;
} else {
- elist = mod->compiled->extensions;
+ elist = ext_mod->compiled->extensions;
}
LY_ARRAY_FOR(elist, u) {
if (!strcmp(name, elist[u]->name)) {
@@ -7209,6 +7221,44 @@
return ret;
}
+static LY_ERR
+lys_compile_ietf_netconf_wd_annotation(struct lysc_ctx *ctx, struct lys_module *mod)
+{
+ struct lysc_ext_instance *ext;
+ struct lysp_ext_instance *ext_p = NULL;
+ struct lysp_stmt *stmt;
+ const struct lys_module *ext_mod;
+ LY_ERR ret = LY_SUCCESS;
+
+ /* create the parsed extension instance manually */
+ ext_p = calloc(1, sizeof *ext_p);
+ LY_CHECK_ERR_GOTO(!ext_p, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
+ ext_p->name = lydict_insert(ctx->ctx, "md:annotation", 0);
+ ext_p->argument = lydict_insert(ctx->ctx, "default", 0);
+ ext_p->insubstmt = LYEXT_SUBSTMT_SELF;
+ ext_p->insubstmt_index = 0;
+
+ stmt = calloc(1, sizeof *ext_p->child);
+ stmt->stmt = lydict_insert(ctx->ctx, "type", 0);
+ stmt->arg = lydict_insert(ctx->ctx, "boolean", 0);
+ stmt->kw = LY_STMT_TYPE;
+ ext_p->child = stmt;
+
+ /* allocate new extension instance */
+ LY_ARRAY_NEW_GOTO(mod->ctx, mod->compiled->exts, ext, ret, cleanup);
+
+ /* manually get extension definition module */
+ ext_mod = ly_ctx_get_module_latest(ctx->ctx, "ietf-yang-metadata");
+
+ /* compile the extension instance */
+ LY_CHECK_GOTO(ret = lys_compile_ext(ctx, ext_p, ext, mod->compiled, LYEXT_PAR_MODULE, ext_mod), cleanup);
+
+cleanup:
+ lysp_ext_instance_free(ctx->ctx, ext_p);
+ free(ext_p);
+ return ret;
+}
+
LY_ERR
lys_compile(struct lys_module *mod, int options)
{
@@ -7408,6 +7458,24 @@
leafref paths, default values and must/when expressions in all schemas of the context to check that they are still valid */
}
+#if 0
+ /* hack for NETCONF's edit-config's operation attribute. It is not defined in the schema, but since libyang
+ * implements YANG metadata (annotations), we need its definition. Because the ietf-netconf schema is not the
+ * internal part of libyang, we cannot add the annotation into the schema source, but we do it here to have
+ * the anotation definitions available in the internal schema structure. */
+ if (ly_strequal(mod->name, "ietf-netconf", 0)) {
+ if (lyp_add_ietf_netconf_annotations(mod)) {
+ lys_free(mod, NULL, 1, 1);
+ return NULL;
+ }
+ }
+#endif
+
+ /* add ietf-netconf-with-defaults "default" metadata to the compiled module */
+ if (!strcmp(mod->name, "ietf-netconf-with-defaults")) {
+ LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, mod), error);
+ }
+
ly_set_erase(&ctx.dflts, free);
ly_set_erase(&ctx.unres, NULL);
ly_set_erase(&ctx.groupings, NULL);
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 736629d..5efbb1e 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -715,11 +715,18 @@
/**
* @brief Free the parsed type structure.
* @param[in] ctx libyang context where the string data resides in a dictionary.
- * @param[in] type Parsed schema type structure to free. Note that since the type itself is not freed.
+ * @param[in] type Parsed schema type structure to free. Note that the type itself is not freed.
*/
void lysp_type_free(struct ly_ctx *ctx, struct lysp_type *type);
/**
+ * @brief Free the parsed extension instance structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in] type Parsed extension instance structure to free. Note that the instance itself is not freed.
+ */
+void lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext);
+
+/**
* @param[in,out] exts [sized array](@ref sizedarrays) For extension instances in case of statements that do not store extension instances in their own list.
*/
LY_ERR lysp_stmt_parse(struct lysc_ctx *ctx, const struct lysp_stmt *stmt, enum ly_stmt kw, void **result, struct lysp_ext_instance **exts);
diff --git a/src/validation.c b/src/validation.c
index a32a11e..97f02d6 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -92,8 +92,8 @@
struct lyd_attr *attr = (struct lyd_attr *)attr_types->objs[u];
/* validate and store the value of the node */
- ret = lyd_value_parse_attr(attr, attr->value.original, strlen(attr->value.original), 0, 1, get_prefix_clb,
- parser_data, format, trees);
+ ret = lyd_value_parse_attr(attr->parent->schema->module->ctx, attr, attr->value.original,
+ strlen(attr->value.original), 0, 1, get_prefix_clb, parser_data, format, NULL, trees);
LY_CHECK_RET(ret);
}