XML data FEATURE parse/print XML attributes mapped to Metadata annotations
diff --git a/src/log.c b/src/log.c
index 8efac1a..c3a6c13 100644
--- a/src/log.c
+++ b/src/log.c
@@ -476,12 +476,12 @@
     }
     ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
     if (ret == -1) {
-        LOGMEM(ext->def->module->ctx);
+        LOGMEM(ext->module->ctx);
         return;
     }
 
     va_start(ap, format);
-    log_vprintf(ext->def->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), err_no, path ? strdup(path) : NULL, plugin_msg, ap);
+    log_vprintf(ext->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), err_no, path ? strdup(path) : NULL, plugin_msg, ap);
     va_end(ap);
 
     free(plugin_msg);
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 6685e8b..d967fe9 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -67,6 +67,16 @@
     return ly_ctx_get_module_implemented_ns(ctx, ns->uri);
 }
 
+struct attr_data_s {
+    const char *prefix;
+    const char *name;
+    char *value;
+    size_t prefix_len;
+    size_t name_len;
+    size_t value_len;
+    int dynamic;
+};
+
 /**
  * @brief Parse XML attributes of the XML element of YANG data.
  *
@@ -77,23 +87,13 @@
  * @reutn LY_ERR value.
  */
 static LY_ERR
-lydxml_attributes(struct lyd_xml_ctx *ctx, const char **data, struct lyd_attr **attributes)
+lydxml_attributes_parse(struct lyd_xml_ctx *ctx, const char **data, struct ly_set *attrs_data)
 {
     LY_ERR ret = LY_SUCCESS;
-    unsigned int u, v;
+    unsigned int u;
     const char *prefix, *name;
     size_t prefix_len, name_len;
-    struct lyd_attr *attr = NULL, *last = NULL;
-    const struct lyxml_ns *ns;
-    struct ly_set attr_datas = {0};
-    struct attr_data_s {
-        const char *prefix;
-        char *value;
-        size_t prefix_len;
-        size_t value_len;
-        int dynamic;
-    } *attr_data;
-    struct lys_module *mod;
+    struct attr_data_s *attr_data;
 
     while(ctx->status == LYXML_ATTRIBUTE &&
             lyxml_get_attribute((struct lyxml_context*)ctx, data, &prefix, &prefix_len, &name, &name_len) == LY_SUCCESS) {
@@ -110,60 +110,122 @@
          * annotation definition for the attribute and correctly process the value */
         attr_data = malloc(sizeof *attr_data);
         attr_data->prefix = prefix;
+        attr_data->name = name;
         attr_data->prefix_len = prefix_len;
+        attr_data->name_len = name_len;
         ret = lyxml_get_string((struct lyxml_context *)ctx, data, &buffer, &buffer_size, &attr_data->value, &attr_data->value_len, &attr_data->dynamic);
-        LY_CHECK_ERR_GOTO(ret, free(attr_data), cleanup);
-        ly_set_add(&attr_datas, attr_data, LY_SET_OPT_USEASLIST);
+        LY_CHECK_ERR_GOTO(ret, free(attr_data), error);
+        ly_set_add(attrs_data, attr_data, LY_SET_OPT_USEASLIST);
+    }
+
+    return LY_SUCCESS;
+
+error:
+    for (u = 0; u < attrs_data->count; ++u) {
+        if (((struct attr_data_s*)attrs_data->objs[u])->dynamic) {
+            free(((struct attr_data_s*)attrs_data->objs[u])->value);
+        }
+    }
+    ly_set_erase(attrs_data, free);
+    return ret;
+}
+
+static LY_ERR
+lydxml_attributes(struct lyd_xml_ctx *ctx, struct ly_set *attrs_data, struct lyd_node *parent)
+{
+    LY_ERR ret = LY_EVALID, rc;
+    struct lyd_attr *attr = NULL, *last = NULL;
+    const struct lyxml_ns *ns;
+    struct lys_module *mod;
+
+    for (unsigned int u = 0; u < attrs_data->count; ++u) {
+        unsigned int v;
+        struct lysc_ext_instance *ant = NULL;
+        struct attr_data_s *attr_data = (struct attr_data_s*)attrs_data->objs[u];
+
+        if (!attr_data->prefix_len) {
+            /* in XML, all attributes must be prefixed
+             * TODO exception for NETCONF filters which are supposed to map to the ietf-netconf without prefix */
+            if (ctx->options & LYD_OPT_STRICT) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Missing mandatory prefix for XML attribute \"%.*s\".",
+                       attr_data->name_len, attr_data->name);
+            }
+skip_attr:
+            if (attr_data->dynamic) {
+                free(attr_data->value);
+                attr_data->dynamic = 0;
+            }
+            continue;
+        }
+
+        /* get namespace of the attribute to find its annotation definition */
+        ns = lyxml_ns_get((struct lyxml_context *)ctx, attr_data->prefix, attr_data->prefix_len);
+        if (!ns) {
+            /* unknown namespace, ignore the attribute */
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", attr_data->prefix_len, attr_data->prefix);
+            goto cleanup;
+        }
+        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
+        if (!mod) {
+            /* module is not implemented or not present in the schema */
+            if (ctx->options & LYD_OPT_STRICT) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE,
+                       "Unknown (or not implemented) YANG module with namespace \"%s\" for attribute \"%.*s%s%.*s\".",
+                       ns, attr_data->prefix_len, attr_data->prefix, attr_data->prefix_len ? ":" : "", attr_data->name_len, attr_data->name);
+            }
+            goto skip_attr;
+        }
+
+        LY_ARRAY_FOR(mod->compiled->exts, v) {
+            if (mod->compiled->exts[v].def->plugin == lyext_plugins_internal[LYEXT_PLUGIN_INTERNAL_ANNOTATION].plugin &&
+                    !strncmp(mod->compiled->exts[v].argument, attr_data->name, attr_data->name_len) && !mod->compiled->exts[v].argument[attr_data->name_len]) {
+                /* we have the annotation definition */
+                ant = &mod->compiled->exts[v];
+                break;
+            }
+        }
+        if (!ant) {
+            /* attribute is not defined as a metadata annotation (RFC 7952) */
+            if (ctx->options & LYD_OPT_STRICT) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Annotation definition for attribute \"%s:%.*s\" not found.",
+                       mod->name, attr_data->name_len, attr_data->name);
+            }
+            goto skip_attr;
+        }
 
         attr = calloc(1, sizeof *attr);
         LY_CHECK_ERR_GOTO(!attr, LOGMEM(ctx->ctx); ret = LY_EMEM, cleanup);
-        attr->name = lydict_insert(ctx->ctx, name, name_len);
+        attr->parent = parent;
+        attr->annotation = ant;
+        rc = lyd_value_parse_attr(attr, attr_data->value, attr_data->value_len, attr_data->dynamic, 0, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
+        if (rc == LY_EINCOMPLETE) {
+            ly_set_add(&ctx->incomplete_type_validation_attrs, attr, LY_SET_OPT_USEASLIST);
+        } else if (rc) {
+            ret = rc;
+            free(attr);
+            goto cleanup;
+        }
+        attr_data->dynamic = 0; /* value eaten by lyd_value_parse_attr() */
+        attr->name = lydict_insert(ctx->ctx, attr_data->name, attr_data->name_len);
 
         if (last) {
             last->next = attr;
         } else {
-            (*attributes) = attr;
+            parent->attr = attr;
         }
         last = attr;
     }
-
-    /* resolve annotation pointers in all the attributes and process the attribute's values */
-    for (last = *attributes, u = 0; u < attr_datas.count && last; u++, last = last->next) {
-        attr_data = (struct attr_data_s*)attr_datas.objs[u];
-        ns = lyxml_ns_get((struct lyxml_context *)ctx, attr_data->prefix, attr_data->prefix_len);
-        mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
-
-        LY_ARRAY_FOR(mod->compiled->exts, v) {
-            if (mod->compiled->exts[v].def->plugin == lyext_plugins_internal[LYEXT_PLUGIN_INTERNAL_ANNOTATION].plugin &&
-                    !strcmp(mod->compiled->exts[v].argument, last->name)) {
-                /* we have the annotation definition */
-                last->annotation = &mod->compiled->exts[v];
-                break;
-            }
-        }
-
-        if (!last->annotation) {
-            /* attribute is not defined as a metadata annotation (RFC 7952) */
-
-        }
-
-        ret = lyd_value_parse_attr(attr, attr_data->value, attr_data->value_len, attr_data->dynamic, 0, lydxml_resolve_prefix, ctx, LYD_XML, NULL);
-        if (ret == LY_EINCOMPLETE) {
-            ly_set_add(&ctx->incomplete_type_validation_attrs, attr, LY_SET_OPT_USEASLIST);
-        } else if (ret) {
-            goto cleanup;
-        }
-        attr_data->dynamic = 0; /* value eaten by lyd_value_parse_attr() */
-    }
+    ret = LY_SUCCESS;
 
 cleanup:
 
-    for (u = 0; u < attr_datas.count; ++u) {
-        if (((struct attr_data_s*)attr_datas.objs[u])->dynamic) {
-            free(((struct attr_data_s*)attr_datas.objs[u])->value);
+    for (unsigned int u = 0; u < attrs_data->count; ++u) {
+        if (((struct attr_data_s*)attrs_data->objs[u])->dynamic) {
+            free(((struct attr_data_s*)attrs_data->objs[u])->value);
         }
     }
-    ly_set_erase(&attr_datas, free);
+    ly_set_erase(attrs_data, free);
+
     return ret;
 }
 
@@ -182,7 +244,7 @@
     LY_ERR ret = LY_SUCCESS;
     const char *prefix, *name;
     size_t prefix_len, name_len;
-    struct lyd_attr *attributes = NULL;
+    struct ly_set attrs_data = {0};
     const struct lyxml_ns *ns;
     const struct lysc_node *snode;
     struct lys_module *mod;
@@ -203,14 +265,13 @@
                 continue;
             }
         }
-        attributes = NULL;
         if (ctx->status == LYXML_ATTRIBUTE) {
-            LY_CHECK_GOTO(lydxml_attributes(ctx, data, &attributes), error);
+            LY_CHECK_GOTO(lydxml_attributes_parse(ctx, data, &attrs_data), error);
         }
 
         ns = lyxml_ns_get((struct lyxml_context *)ctx, prefix, prefix_len);
         if (!ns) {
-            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%*.s\".", prefix_len, prefix);
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE, "Unknown XML prefix \"%.*s\".", prefix_len, prefix);
             goto error;
         }
         mod = ly_ctx_get_module_implemented_ns(ctx->ctx, ns->uri);
@@ -338,8 +399,7 @@
             } /* first top level node - nothing more to do */
         }
         prev = last;
-        cur->attr = attributes;
-        attributes = NULL;
+        LY_CHECK_GOTO(ret = lydxml_attributes(ctx, &attrs_data, cur), cleanup);
 
         if (snode->nodetype & LYD_NODE_TERM) {
             int dynamic = 0;
@@ -442,7 +502,12 @@
     /* TODO add missing siblings default elements */
 
 cleanup:
-    lyd_free_attr(ctx->ctx, attributes, 1);
+    for (unsigned int u = 0; u < attrs_data.count; ++u) {
+        if (((struct attr_data_s*)attrs_data.objs[u])->dynamic) {
+            free(((struct attr_data_s*)attrs_data.objs[u])->value);
+        }
+    }
+    ly_set_erase(&attrs_data, free);
     return ret;
 
 error:
diff --git a/src/plugins_exts_internal.h b/src/plugins_exts_internal.h
index 9b25a27..18fb33a 100644
--- a/src/plugins_exts_internal.h
+++ b/src/plugins_exts_internal.h
@@ -23,7 +23,7 @@
 /**
  * @brief List of internally implemented extension plugins.
  */
-extern struct lyext_plugins_list *lyext_plugins_internal;
+extern struct lyext_plugins_list lyext_plugins_internal[];
 
 /**
  * @brief Index of Metadata's annotation extension plugin in lyext_plugins_internal
diff --git a/src/printer_xml.c b/src/printer_xml.c
index ecc6e75..4ce56b8 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -50,7 +50,6 @@
     struct lys_module *module;
 } *mlist = NULL, *mlist_new;
 
-#if 0
 static LY_ERR
 modlist_add(struct mlist **mlist, const struct lys_module *mod)
 {
@@ -72,7 +71,6 @@
 
     return LY_SUCCESS;
 }
-#endif
 
 /**
  * TODO
@@ -83,11 +81,10 @@
     struct lyd_node *next, *cur, *child;
     struct lyd_attr *attr;
 
-#if 0
     struct mlist *mlist = NULL, *miter;
-
+#if 0
     const struct lys_module *wdmod = NULL;
-
+#endif
     /* add node attribute modules */
     for (attr = node->attr; attr; attr = attr->next) {
         if (!strcmp(node->schema->name, "filter") &&
@@ -95,14 +92,10 @@
                  !strcmp(node->schema->module->name, "notifications"))) {
             /* exception for NETCONF's filter attributes */
             continue;
-        } else {
-            r = modlist_add(&mlist, lys_main_module(attr->annotation->module));
-        }
-        if (r) {
+        } else if (modlist_add(&mlist, attr->annotation->module)) {
             goto print;
         }
     }
-#endif
 
     /* add node children nodes and attribute modules */
     switch (node->schema->nodetype) {
@@ -150,7 +143,7 @@
     default:
         break;
     }
-#if 0
+
 print:
     /* print used namespaces */
     while (mlist) {
@@ -160,7 +153,20 @@
         ly_print(ctx->out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
         free(miter);
     }
-#endif
+}
+
+/**
+ * @brief XML mapping of YANG modules to prefixes in values.
+ *
+ * Implementation of ly_clb_get_prefix
+ */
+static const char *
+xml_print_get_prefix(const struct lys_module *mod, void *private)
+{
+    struct ly_set *ns_list = (struct ly_set*)private;
+
+    ly_set_add(ns_list, (void*)mod, 0);
+    return mod->prefix;
 }
 
 /**
@@ -169,11 +175,8 @@
 static LY_ERR
 xml_print_attrs(struct xmlpr_ctx *ctx, const struct lyd_node *node)
 {
-    (void) ctx;
-    (void) node;
-
-#if 0
     struct lyd_attr *attr;
+#if 0
     const char **prefs, **nss;
     const char *xml_expr = NULL, *mod_name;
     uint32_t ns_count, i;
@@ -181,9 +184,12 @@
     const struct lys_module *wdmod = NULL;
     char *p;
     size_t len;
+#endif
+    struct ly_set ns_list = {0};
+    int dynamic;
+    unsigned int u;
 
-    LY_PRINT_SET;
-
+#if 0
     /* with-defaults */
     if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
         if ((node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) ||
@@ -202,8 +208,18 @@
             && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
         rpc_filter = 1;
     }
-
+#endif
     for (attr = node->attr; attr; attr = attr->next) {
+        const char *value = attr->value.realtype->plugin->print(&attr->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
+
+        /* print namespaces connected with the values's prefixes */
+        for (u = 0; u < ns_list.count; ++u) {
+            const struct lys_module *mod = (const struct lys_module*)ns_list.objs[u];
+            ly_print(ctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
+        }
+        ly_set_erase(&ns_list, NULL);
+
+#if 0
         if (rpc_filter) {
             /* exception for NETCONF's filter's attributes */
             if (!strcmp(attr->name, "select")) {
@@ -222,81 +238,20 @@
             }
             ly_print(out, " %s=\"", attr->name);
         } else {
-            ly_print(out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
+#endif
+            ly_print(ctx->out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
+#if 0
         }
+#endif
 
-        switch (attr->value_type) {
-        case LY_TYPE_BINARY:
-        case LY_TYPE_STRING:
-        case LY_TYPE_BITS:
-        case LY_TYPE_ENUM:
-        case LY_TYPE_BOOL:
-        case LY_TYPE_DEC64:
-        case LY_TYPE_INT8:
-        case LY_TYPE_INT16:
-        case LY_TYPE_INT32:
-        case LY_TYPE_INT64:
-        case LY_TYPE_UINT8:
-        case LY_TYPE_UINT16:
-        case LY_TYPE_UINT32:
-        case LY_TYPE_UINT64:
-            if (attr->value_str) {
-                /* xml_expr can contain transformed xpath */
-                lyxml_dump_text(out, xml_expr ? xml_expr : attr->value_str, LYXML_DATA_ATTR);
-            }
-            break;
-
-        case LY_TYPE_IDENT:
-            if (!attr->value_str) {
-                break;
-            }
-            p = strchr(attr->value_str, ':');
-            assert(p);
-            len = p - attr->value_str;
-            mod_name = attr->annotation->module->name;
-            if (!strncmp(attr->value_str, mod_name, len) && !mod_name[len]) {
-                lyxml_dump_text(out, ++p, LYXML_DATA_ATTR);
-            } else {
-                /* avoid code duplication - use instance-identifier printer which gets necessary namespaces to print */
-                goto printinst;
-            }
-            break;
-        case LY_TYPE_INST:
-printinst:
-            xml_expr = transform_json2xml(node->schema->module, ((struct lyd_node_leaf_list *)node)->value_str, 1,
-                                          &prefs, &nss, &ns_count);
-            if (!xml_expr) {
-                /* error */
-                return EXIT_FAILURE;
-            }
-
-            for (i = 0; i < ns_count; ++i) {
-                ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
-            }
-            free(prefs);
-            free(nss);
-
-            lyxml_dump_text(out, xml_expr, LYXML_DATA_ATTR);
-            lydict_remove(node->schema->module->ctx, xml_expr);
-            break;
-
-        /* LY_TYPE_LEAFREF not allowed */
-        case LY_TYPE_EMPTY:
-            break;
-
-        default:
-            /* error */
-            LOGINT(node->schema->module->ctx);
-            return EXIT_FAILURE;
+        if (value && value[0]) {
+            lyxml_dump_text(ctx->out, value, 1);
         }
-
-        ly_print(out, "\"");
-
-        if (xml_expr) {
-            lydict_remove(node->schema->module->ctx, xml_expr);
+        ly_print(ctx->out, "\"");
+        if (dynamic) {
+            free((void*)value);
         }
     }
-#endif
 
     return LY_SUCCESS;
 }
@@ -333,20 +288,6 @@
 static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
 
 /**
- * @brief XML mapping of YANG modules to prefixes in values.
- *
- * Implementation of ly_clb_get_prefix
- */
-static const char *
-xml_print_get_prefix(const struct lys_module *mod, void *private)
-{
-    struct ly_set *ns_list = (struct ly_set*)private;
-
-    ly_set_add(ns_list, (void*)mod, 0);
-    return mod->prefix;
-}
-
-/**
  * @brief Print XML element representing lyd_node_term.
  *
  * @param[in] ctx XML printer context.
@@ -356,11 +297,13 @@
 static LY_ERR
 xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
 {
-    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
     struct ly_set ns_list = {0};
     unsigned int u;
     int dynamic;
-    const char *value = node->value.realtype->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
+    const char *value;
+
+    LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
+    value = node->value.realtype->plugin->print(&node->value, LYD_XML, xml_print_get_prefix, &ns_list, &dynamic);
 
     /* print namespaces connected with the values's prefixes */
     for (u = 0; u < ns_list.count; ++u) {
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index b8c5329..9ff54c4 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -128,7 +128,7 @@
         iter = iter->next;
 
         FREE_STRING(ctx, attr->name);
-        /* TODO type->plugin->free(ctx, type, &attr->value); */
+        attr->value.realtype->plugin->free(ctx, &attr->value);
         free(attr);
     }
 }
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 8346592..abe4908 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1115,6 +1115,7 @@
  */
 struct lysc_ext_instance {
     struct lysc_ext *def;            /**< pointer to the extension definition */
+    struct lys_module *module;       /**< module where the extension instantiated is defined */
     void *parent;                    /**< pointer to the parent element holding the extension instance(s), use
                                           ::lysc_ext_instance#parent_type to access the schema element */
     const char *argument;            /**< optional value of the extension's argument */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 25b16e4..25e9ffa 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -435,6 +435,7 @@
     DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
     ext->insubstmt = ext_p->insubstmt;
     ext->insubstmt_index = ext_p->insubstmt_index;
+    ext->module = ctx->mod_def;
     ext->parent = parent;
     ext->parent_type = parent_type;