data types FEATURE add printer callback to the type plugins

Direct using of lyd_value's canonized member is dangerous since some
of the data may not have canonical format and plugin doesn't store it.
Therefore there is a need of a generic printer plugins to provide
printable representation of the value for the specific format (XML,
JSON).
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 62e3498..7d2b33b 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -45,6 +45,22 @@
 }
 
 /**
+ * @brief Generic printer callback of the canonized value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_canonical(const struct lyd_value *value, LYD_FORMAT UNUSED(format), int prefixes, int *dynamic)
+{
+    *dynamic = 0;
+    if (prefixes) {
+        return "";
+    } else {
+        return (char*)value->canonized;
+    }
+}
+
+/**
  * @brief Free canonized value in lyd_value.
  *
  * Implementation of the ly_type_free_clb.
@@ -1435,8 +1451,12 @@
 
         if (!lyd_target(storage->target, trees)) {
             /* TODO print instance-identifier */
-            asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.",
-                     "TODO");
+            int dynamic = 0;
+            const char *id = storage->realtype->plugin->print(storage, format, 0, &dynamic);
+            asprintf(&errmsg, "Invalid instance-identifier \"%s\" value - required instance not found.", id);
+            if (dynamic) {
+                free((char*)id);
+            }
             /* we have to clean up the storage */
             type->plugin->free(ctx, storage);
 
@@ -1805,6 +1825,141 @@
     return LY_SUCCESS;
 }
 
+static void
+ly_type_print_prefixes_mod(char **str, struct ly_set *printed, struct lys_module *mod)
+{
+    unsigned int u = printed->count;
+    if (ly_set_add(printed, mod, 0) == (int)u) {
+        /* newly added module - print it */
+        asprintf(str, "%s%sxmlns:%s=\"%s\"", *str ? *str : "", *str ? " " : "", mod->prefix, mod->ns);
+    }
+}
+
+/**
+ * @brief Printer callback printing the instance-identifier value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_instanceid(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic)
+{
+    unsigned int u, v;
+    char *result = NULL;
+    struct ly_set printed_prefixes = {0};
+
+    if (prefixes) {
+        /* print namespace definition -only for XML, it does not make sense in JSON */
+        if (format == LYD_XML) {
+            LY_ARRAY_FOR(value->target, u) {
+                ly_type_print_prefixes_mod(&result, &printed_prefixes, value->target[u].node->module);
+                LY_ARRAY_FOR(value->target[u].predicates, v) {
+                    struct lyd_value_path_predicate *pred = &value->target[u].predicates[v];
+                    if (pred->type) {
+                        /* non-position predicate with a value
+                         * (key is of the same namespace as the list, so we don't need to handle its prefix) */
+                        int d = 0;
+                        char *token, *s = (char*)pred->value->realtype->plugin->print(pred->value, LYD_XML, 1, &d);
+                        if (s && s[0]) {
+                            char *start = s;
+                            if (!d) {
+                                start = s = strdup(s);
+                            }
+                            while ((token = strsep(&s, " "))) {
+                                unsigned int i;
+                                char *equal;
+                                equal = strchr(token, '=');
+                                /* check if we already have such a prefix */
+                                for (i = 0; i < printed_prefixes.count; i++) {
+                                    if (!strcmp(((struct lys_module *)printed_prefixes.objs[i])->ns, equal + 1)) {
+                                        /* match, do not print this namespace definition */
+                                        break;
+                                    }
+                                }
+                                if (i == printed_prefixes.count) {
+                                    /* not yet printed namespace */
+                                    ly_strcat(&result, "%s%s", result ? " " : "", token);
+                                }
+                            }
+                            free(start);
+                        }
+                    }
+                }
+            }
+        } else { /* JSON */
+            *dynamic = 0;
+            return "";
+        }
+    } else {
+        /* print value */
+        if (format == LYD_XML) {
+            /* everything is prefixed */
+            LY_ARRAY_FOR(value->target, u) {
+                 ly_strcat(&result, "/%s:%s", value->target[u].node->module->prefix, value->target[u].node->name);
+                LY_ARRAY_FOR(value->target[u].predicates, v) {
+                    struct lyd_value_path_predicate *pred = &value->target[u].predicates[v];
+                    if (pred->type == 0) {
+                        /* position predicate */
+                        ly_strcat(&result, "[%"PRIu64"]", pred->position);
+                    } else if (pred->type == 1) {
+                        /* key-predicate */
+                        int d = 0;
+                        const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+                        ly_strcat(&result, "[%s:%s='%s']", pred->key->module->prefix, pred->key->name, value);
+                        if (d) {
+                            free((char*)value);
+                        }
+                    } else if (pred->type == 2) {
+                        /* leaf-list-predicate */
+                        int d = 0;
+                        const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+                        ly_strcat(&result, "[.='%s']", value);
+                        if (d) {
+                            free((char*)value);
+                        }
+                    }
+                }
+            }
+        } else { /* LYD_JSON */
+            /* only the first node or the node changing module is prefixed */
+            struct lys_module *mod = NULL;
+            LY_ARRAY_FOR(value->target, u) {
+                if (mod != value->target[u].node->module) {
+                    mod = value->target[u].node->module;
+                    ly_strcat(&result, "/%s:%s", mod->name, value->target[u].node->name);
+                } else {
+                    ly_strcat(&result, "/%s", value->target[u].node->name);
+                }
+                LY_ARRAY_FOR(value->target[u].predicates, v) {
+                    struct lyd_value_path_predicate *pred = &value->target[u].predicates[v];
+                    if (pred->type == 0) {
+                        /* position predicate */
+                        ly_strcat(&result, "[%"PRIu64"]", pred->position);
+                    } else if (pred->type == 1) {
+                        /* key-predicate */
+                        int d = 0;
+                        const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+                        ly_strcat(&result, "[%s='%s']", pred->key->name, value);
+                        if (d) {
+                            free((char*)value);
+                        }
+                    } else if (pred->type == 2) {
+                        /* leaf-list-predicate */
+                        int d = 0;
+                        const char *value = pred->value->realtype->plugin->print(pred->value, format, 0, &d);
+                        ly_strcat(&result, "[.='%s']", value);
+                        if (d) {
+                            free((char*)value);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    *dynamic = 1;
+    return result;
+}
+
 /**
  * @brief Free value of the YANG built-in instance-identifier types.
  *
@@ -2077,6 +2232,17 @@
 }
 
 /**
+ * @brief Printer callback printing the leafref value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_leafref(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic)
+{
+    return value->realtype->plugin->print(value, format, prefixes, dynamic);
+}
+
+/**
  * @brief Free value of the YANG built-in leafref type.
  *
  * Implementation of the ly_type_free_clb.
@@ -2221,6 +2387,34 @@
 }
 
 /**
+ * @brief Printer callback printing the union value.
+ *
+ * Implementation of the ly_type_print_clb.
+ */
+static const char *
+ly_type_print_union(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic)
+{
+    unsigned int u;
+    char *result = NULL;
+
+    if (prefixes) {
+        if (format == LYD_XML) {
+            LY_ARRAY_FOR(value->subvalue->prefixes, u) {
+                ly_strcat(&result, "%sxmlns:%s=\"%s\"", result ? " " : "",
+                          value->subvalue->prefixes[u].prefix, value->subvalue->prefixes[u].mod->ns);
+            }
+            *dynamic = 1;
+            return result;
+        } else {
+            *dynamic = 0;
+            return "";
+        }
+    } else {
+        return ly_type_print_canonical(value, format, 0, dynamic);
+    }
+}
+
+/**
  * @brief Free value of the YANG built-in union type.
  *
  * Implementation of the ly_type_free_clb.
@@ -2250,23 +2444,23 @@
  */
 struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
     {0}, /* LY_TYPE_UNKNOWN */
-    {.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_STRING, .store = ly_type_store_string, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_BITS, .store = ly_type_store_bits, .compare = ly_type_compare_canonical, .free = ly_type_free_bits},
-    {.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .compare = ly_type_compare_empty, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .free = ly_type_free_instanceid},
-    {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref, .free = ly_type_free_leafref},
-    {.type = LY_TYPE_UNION, .store = ly_type_store_union, .compare = ly_type_compare_union, .free = ly_type_free_union},
-    {.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_INT32, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
-    {.type = LY_TYPE_INT64, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_STRING, .store = ly_type_store_string, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_BITS, .store = ly_type_store_bits, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_bits},
+    {.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .compare = ly_type_compare_empty, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .print = ly_type_print_instanceid, .free = ly_type_free_instanceid},
+    {.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref, .print = ly_type_print_leafref, .free = ly_type_free_leafref},
+    {.type = LY_TYPE_UNION, .store = ly_type_store_union, .compare = ly_type_compare_union, .print = ly_type_print_union, .free = ly_type_free_union},
+    {.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_INT32, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
+    {.type = LY_TYPE_INT64, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical, .free = ly_type_free_canonical},
 };
diff --git a/src/plugins_types.h b/src/plugins_types.h
index a4b243f..3340832 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -132,6 +132,22 @@
 typedef LY_ERR (*ly_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
+ * @brief Callback to receive printed (canoncal) value of the data stored in @p value.
+ *
+ * @param[in] value Value to print.
+ * @param[in] format Format in which the data are supposed to be printed.
+ *            Only 2 formats are currently implemented: LYD_XML and LYD_JSON.
+ * @param[in] prefixes Flag to get printed prefix/namespace string instead of the value itself.
+ *            Since JSON uses YANG module names as prefixes, this option make sense only with LYD_XML @p format.
+ * @param[out] dynamic Flag if the returned string is dynamically allocated. In such a case the caller is responsible
+ *            for freeing it.
+ * @return String with the value of @p value in specified @p format. According to the returned @p dynamic flag, caller
+ *         can be responsible for freeing allocated memory.
+ * @return NULL in case of error.
+ */
+typedef const char *(*ly_type_print_clb)(const struct lyd_value *value, LYD_FORMAT format, int prefixes, int *dynamic);
+
+/**
  * @brief Callback for freeing the user type values stored by ly_type_store_clb().
  *
  * Note that this callback is responsible also for freeing the canonized member in the @p value.
@@ -152,6 +168,7 @@
     LY_DATA_TYPE type;               /**< implemented type, use LY_TYPE_UNKNOWN for derived data types */
     ly_type_store_clb store;         /**< function to validate, canonize and store (according to the options) the value in the type-specific way */
     ly_type_compare_clb compare;     /**< comparison callback to compare 2 values of the same type */
+    ly_type_print_clb print;         /**< printer callback to get string representing the value */
     ly_type_free_clb free;           /**< optional function to free the type-spceific way stored value */
     uint32_t flags;                  /**< [type flags ](@ref plugintypeflags). */
 };
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index 6d057d0..5075294 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -816,11 +816,11 @@
 
     data =  "<cont xmlns=\"urn:tests:types\"/><t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaftarget</t:inst>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
-    logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+    logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaftarget\" value - required instance not found. /");
 
     data =  "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaftarget</t:inst>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
-    logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+    logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaftarget\" value - required instance not found. /");
 
     data =  "<leaflisttarget xmlns=\"urn:tests:types\">x</leaflisttarget><t:inst xmlns:t=\"urn:tests:types\">/t:leaflisttarget[1</t:inst>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
@@ -845,7 +845,7 @@
     data =  "<cont xmlns=\"urn:tests:types\"><leaflisttarget>1</leaflisttarget><leaflisttarget>2</leaflisttarget></cont>"
             "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[4]</t:inst>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
-    logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+    logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaflisttarget[4]\" value - required instance not found. /");
 
     data =  "<t:inst-noreq xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[6]</t:inst-noreq>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
@@ -876,7 +876,7 @@
     data =  "<cont xmlns=\"urn:tests:types\"><leaflisttarget>1</leaflisttarget></cont>"
             "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[.='2']</t:inst>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
-    logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+    logbuf_assert("Invalid instance-identifier \"/t:cont/t:leaflisttarget[.='2']\" value - required instance not found. /");
 
     data =  "<cont xmlns=\"urn:tests:types\"><leaflisttarget>1</leaflisttarget></cont>"
             "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:leaflisttarget[.='x']</t:inst>";
@@ -893,7 +893,7 @@
     data =  "<cont xmlns=\"urn:tests:types\"><listtarget><id>1</id><value>x</value></listtarget></cont>"
             "<t:inst xmlns:t=\"urn:tests:types\">/t:cont/t:listtarget[t:id='2']</t:inst>";
     assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
-    logbuf_assert("Invalid instance-identifier \"TODO\" value - required instance not found. /");
+    logbuf_assert("Invalid instance-identifier \"/t:cont/t:listtarget[t:id='2']\" value - required instance not found. /");
 
     data = "<leaflisttarget xmlns=\"urn:tests:types\">a</leaflisttarget>"
            "<leaflisttarget xmlns=\"urn:tests:types\">b</leaflisttarget>"