validation NEW when validation

Integrated only into XML parser for now.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e48cb18..8023c2b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -229,7 +229,8 @@
     src/plugins_types.c
     src/plugins_exts.c
     src/xml.c
-    src/xpath.c)
+    src/xpath.c
+    src/validation.c)
 
 set(lintsrc
     tools/lint/main.c
diff --git a/src/parser_xml.c b/src/parser_xml.c
index bf7c6ab..31fac0b 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -28,6 +28,7 @@
 #include "tree_schema.h"
 #include "xml.h"
 #include "plugins_exts_internal.h"
+#include "validation.h"
 
 /**
  * @brief internal context for XML YANG data parser.
@@ -487,6 +488,11 @@
             }
         }
 
+        /* remember we need to evaluate this node's when */
+        if (!(snode->nodetype & (LYS_ACTION | LYS_NOTIF)) && snode->when) {
+            ly_set_add(&ctx->when_check, cur, LY_SET_OPT_USEASLIST);
+        }
+
         /* calculate the hash and insert it into parent (list with keys is handled when its keys are inserted) */
         lyd_hash(cur);
         lyd_insert_hash(cur);
@@ -496,8 +502,6 @@
                 !cur->attr && !(((struct lysc_node_container*)cur->schema)->flags & LYS_PRESENCE)) {
             cur->flags |= LYD_DEFAULT;
         }
-
-        /* TODO context validation */
     }
 
     /* TODO add missing siblings default elements */
@@ -521,6 +525,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyd_node_inner *parent = NULL;
+    const struct lyd_node **result_trees = NULL;
     struct lyd_xml_ctx xmlctx = {0};
 
     xmlctx.options = options;
@@ -553,70 +558,45 @@
     }
 
     if (!data || !data[0]) {
-        goto no_data;
+        /* no data - just check for missing mandatory nodes */
+        goto validation;
     }
 
     ret = lydxml_nodes(&xmlctx, parent, &data, *result ? &parent->child : result);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* prepare sized array for validator */
+    if (*result) {
+        result_trees = lyd_trees_new(1, *result);
+    }
+
+    /* finish incompletely validated terminal values/attributes and when conditions */
+    ret = lyd_validate_unres(&xmlctx.incomplete_type_validation, &xmlctx.incomplete_type_validation_attrs,
+                             &xmlctx.when_check, LYD_XML, lydxml_resolve_prefix, ctx, result_trees);
+    LY_CHECK_GOTO(ret, cleanup);
+
+validation:
+    if ((!(*result) || (parent && !parent->child)) && (options & (LYD_OPT_RPC | LYD_OPT_NOTIF))) {
+        /* error, missing top level node identify RPC and Notification */
+        LOGERR(ctx, LY_EINVAL, "Invalid input data of data parser - expected %s which cannot be empty.",
+               lyd_parse_options_type2str(options));
+        ret = LY_EINVAL;
+        goto cleanup;
+    }
+
+    /* context node and other validation tasks that depend on other data nodes */
+    ret = lyd_validate_modules(result_trees, NULL, 0, ctx, options);
+    LY_CHECK_GOTO(result, cleanup);
+
+cleanup:
+    ly_set_erase(&xmlctx.incomplete_type_validation, NULL);
+    ly_set_erase(&xmlctx.incomplete_type_validation_attrs, NULL);
+    ly_set_erase(&xmlctx.when_check, NULL);
+    lyxml_context_clear((struct lyxml_context*)&xmlctx);
+    lyd_trees_free(result_trees, 0);
     if (ret) {
         lyd_free_all(*result);
         *result = NULL;
-    } else {
-        /* finish incompletely validated terminal values */
-        for (unsigned int u = 0; u < xmlctx.incomplete_type_validation.count; u++) {
-            struct lyd_node_term *node = (struct lyd_node_term*)xmlctx.incomplete_type_validation.objs[u];
-            const struct lyd_node **result_trees = NULL;
-
-            /* prepare sized array for validator */
-            if (*result) {
-                result_trees = lyd_trees_new(1, *result);
-            }
-            /* validate and store the value of the node */
-            ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1,
-                                  lydxml_resolve_prefix, ctx, LYD_XML, result_trees);
-            lyd_trees_free(result_trees, 0);
-            if (ret) {
-                lyd_free_all(*result);
-                *result = NULL;
-                break;
-            }
-        }
-        /* ... and attribute values */
-        for (unsigned int u = 0; u < xmlctx.incomplete_type_validation_attrs.count; u++) {
-            struct lyd_attr *attr = (struct lyd_attr*)xmlctx.incomplete_type_validation_attrs.objs[u];
-            const struct lyd_node **result_trees = NULL;
-
-            /* prepare sized array for validator */
-            if (*result) {
-                result_trees = lyd_trees_new(1, *result);
-            }
-            /* validate and store the value of the node */
-            ret = lyd_value_parse_attr(attr, attr->value.original, strlen(attr->value.original), 0, 1,
-                                       lydxml_resolve_prefix, ctx, LYD_XML, result_trees);
-            lyd_trees_free(result_trees, 0);
-            if (ret) {
-                lyd_free_all(*result);
-                *result = NULL;
-                break;
-            }
-        }
-
-        if (!(*result) || (parent && !parent->child)) {
-no_data:
-            /* no data */
-            if (options & (LYD_OPT_RPC | LYD_OPT_NOTIF)) {
-                /* error, missing top level node identify RPC and Notification */
-                LOGERR(ctx, LY_EINVAL, "Invalid input data of data parser - expected %s which cannot be empty.",
-                       lyd_parse_options_type2str(options));
-            } else {
-                /* others - no work is needed, just check for missing mandatory nodes */
-                /* TODO lyd_validate(&result, options, ctx);
-                 * - according to the data tree type */
-            }
-        }
     }
-
-    ly_set_erase(&xmlctx.incomplete_type_validation, NULL);
-    ly_set_erase(&xmlctx.incomplete_type_validation_attrs, NULL);
-    lyxml_context_clear((struct lyxml_context*)&xmlctx);
     return ret;
 }
diff --git a/src/validation.c b/src/validation.c
new file mode 100644
index 0000000..129af23
--- /dev/null
+++ b/src/validation.c
@@ -0,0 +1,218 @@
+/**
+ * @file validation.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Validation
+ *
+ * Copyright (c) 2019 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.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "common.h"
+#include "xpath.h"
+#include "tree_data_internal.h"
+
+/**
+ * @brief Evaluate a single "when" condition.
+ *
+ * @param[in] when When to evaluate.
+ * @param[in] node Node whose existence depends on this when.
+ * @param[in] trees Array of all data trees.
+ * @return LY_ERR value (LY_EINCOMPLETE if a referenced node does not have its when evaluated)
+ */
+static LY_ERR
+lyd_val_when(struct lysc_when *when, struct lyd_node *node, const struct lyd_node **trees)
+{
+    LY_ERR ret;
+    const struct lyd_node *ctx_node;
+    struct lyxp_set xp_set;
+
+    memset(&xp_set, 0, sizeof xp_set);
+
+    if (when->context == node->schema) {
+        ctx_node = node;
+    } else {
+        assert((!when->context && !node->parent) || (when->context == node->parent->schema));
+        ctx_node = (struct lyd_node *)node->parent;
+    }
+
+    /* evaluate when */
+    ret = lyxp_eval(when->cond, LYD_UNKNOWN, when->module, ctx_node, ctx_node ? LYXP_NODE_ELEM : LYXP_NODE_ROOT_CONFIG,
+                    trees, &xp_set, LYXP_SCHEMA);
+    lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
+
+    /* return error or LY_EINCOMPLETE for dependant unresolved when */
+    LY_CHECK_RET(ret);
+
+    /* take action based on the result */
+    if (!xp_set.val.bool) {
+        if (node->flags & LYD_WHEN_TRUE) {
+            /* autodelete */
+            lyd_free_tree(node);
+        } else {
+            /* invalid data */
+            LOGVAL(node->schema->module->ctx, LY_VLOG_LYD, node, LY_VCODE_NOWHEN, when->cond->expr);
+            ret = LY_EVALID;
+        }
+    } else {
+        /* remember that when evaluated to true */
+        node->flags |= LYD_WHEN_TRUE;
+    }
+
+    return ret;
+}
+
+LY_ERR
+lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
+                   ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees)
+{
+    LY_ERR ret = LY_SUCCESS;
+    uint32_t u;
+
+    /* finish incompletely validated terminal values */
+    for (u = 0; node_types && (u < node_types->count); u++) {
+        struct lyd_node_term *node = (struct lyd_node_term *)node_types->objs[u];
+
+        /* validate and store the value of the node */
+        ret = lyd_value_parse(node, node->value.original, strlen(node->value.original), 0, 1, get_prefix_clb,
+                            parser_data, format, trees);
+        LY_CHECK_RET(ret);
+    }
+
+    /* ... and attribute values */
+    for (u = 0; attr_types && (u < attr_types->count); u++) {
+        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);
+        LY_CHECK_RET(ret);
+    }
+
+    /* no when conditions */
+    if (!node_when || !node_when->count) {
+        return ret;
+    }
+
+    /* evaluate all when conditions */
+    uint32_t prev_count;
+    do {
+        prev_count = node_when->count;
+        u = 0;
+        while (u < node_when->count) {
+            /* evaluate all when expressions that affect this node's existence */
+            struct lyd_node *node = (struct lyd_node *)node_when->objs[u];
+            const struct lysc_node *schema = node->schema;
+            int unres_when = 0;
+
+            do {
+                uint32_t i;
+                LY_ARRAY_FOR(schema->when, i) {
+                    ret = lyd_val_when(schema->when[i], node, trees);
+                    if (ret) {
+                        break;
+                    }
+                }
+                if (ret == LY_EINCOMPLETE) {
+                    /* could not evaluate this when */
+                    unres_when = 1;
+                    break;
+                } else if (ret) {
+                    /* error */
+                    return ret;
+                }
+                schema = schema->parent;
+            } while (schema && (schema->nodetype & (LYS_CASE | LYS_CHOICE)));
+
+            if (unres_when) {
+                /* keep in set and go to the next node */
+                ++u;
+            } else {
+                /* remove this node from the set */
+                ly_set_rm_index(node_when, u, NULL);
+            }
+        }
+
+    /* there must have been some when conditions resolved */
+    } while (prev_count > node_when->count);
+
+    /* there could have been no cyclic when dependencies, checked during compilation */
+    assert(!node_when->count);
+
+    return ret;
+}
+
+static const struct lys_module *
+lyd_val_next_module(const struct lys_module **modules, int mod_count, struct ly_ctx *ctx, uint32_t *i)
+{
+    if (modules && mod_count) {
+        return modules[(*i)++];
+    }
+
+    return ly_ctx_get_module_iter(ctx, i);
+}
+
+static LY_ERR
+lyd_validate_children_r(struct lyd_node *sibling, const struct lysc_node *sparent, const struct lysc_module *mod, int options)
+{
+    LY_ERR ret;
+    const struct lysc_node *snode = NULL;
+    struct lyd_node *node;
+
+    while ((snode = lys_getnext(snode, sparent, mod, 0))) {
+        /* TODO mandatory - mandatory snode must exist */
+        /* TODO min/max elem - check snode element count */
+        /* TODO unique - check snode unique */
+        /* TODO choice - case duplicites/mandatory */
+    }
+
+    for (node = sibling; node; node = node->next) {
+        /* TODO node's must */
+        /* TODO node instance duplicites */
+        /* TODO node status */
+
+        /* validate all children */
+        LY_CHECK_RET(lyd_validate_children_r((struct lyd_node *)lyd_node_children(sibling), node->schema, mod, options));
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_validate_modules(const struct lyd_node **trees, const struct lys_module **modules, int mod_count, struct ly_ctx *ctx,
+                     int options)
+{
+    LY_ERR ret;
+    uint32_t i = 0, j;
+    const struct lys_module *mod;
+    struct lyd_node *tree;
+
+    while ((mod = lyd_val_next_module(modules, mod_count, ctx, &i))) {
+        if (!mod->implemented) {
+            continue;
+        }
+
+        /* find data of this module, if any */
+        tree = NULL;
+        if (trees) {
+            for (j = 0; j < LY_ARRAY_SIZE(trees); ++j) {
+                if (trees[j]->schema->module == mod) {
+                    tree = (struct lyd_node *)trees[j];
+                    break;
+                }
+            }
+        }
+
+        /* validate all top-level nodes and then inner nodes recursively */
+        LY_CHECK_RET(lyd_validate_children_r(tree, NULL, mod->compiled, options));
+    }
+
+    return LY_SUCCESS;
+}
diff --git a/src/validation.h b/src/validation.h
new file mode 100644
index 0000000..13608b8
--- /dev/null
+++ b/src/validation.h
@@ -0,0 +1,50 @@
+/**
+ * @file validation.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Validation routines.
+ *
+ * Copyright (c) 2019 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.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_VALIDATION_H_
+#define LY_VALIDATION_H_
+
+#include "log.h"
+#include "tree_data.h"
+
+/**
+ * @brief Finish validation of nodes and attributes. Specifically, type and when validation.
+ *
+ * @param[in] node_types Set with nodes with unresolved types, can be NULL
+ * @param[in] attr_types Set with attributes with unresolved types, can be NULL.
+ * @param[in] node_when Set with nodes with "when" conditions, can be NULL.
+ * @param[in] format Format of the unresolved data.
+ * @param[in] get_prefix_clb Format-specific getter to resolve prefixes.
+ * @param[in] parser_data Parser's data for @p get_prefix_clb.
+ * @param[in] trees Array of all data trees.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate_unres(struct ly_set *node_types, struct ly_set *attr_types, struct ly_set *node_when, LYD_FORMAT format,
+                          ly_clb_resolve_prefix get_prefix_clb, void *parser_data, const struct lyd_node **trees);
+
+/**
+ * @brief Perform all vaidation tasks that depend on other nodes, the data tree must
+ * be complete when calling this function.
+ *
+ * @param[in] trees Array of all data trees.
+ * @param[in] modules Array of modules that should be validated, NULL for all modules.
+ * @param[in] mod_count Modules count.
+ * @param[in] ctx Context if all modules should be validated, NULL for only selected modules.
+ * @param[in] options Validation options.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_validate_modules(const struct lyd_node **trees, const struct lys_module **modules, int mod_count,
+                            struct ly_ctx *ctx, int options);
+
+#endif /* LY_VALIDATION_H_ */
diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt
index 0aa4fa5..00bf835 100644
--- a/tests/src/CMakeLists.txt
+++ b/tests/src/CMakeLists.txt
@@ -13,7 +13,8 @@
     src_printer_yin
     src_tree_data
     src_parser_xml
-    src_printer_xml)
+    src_printer_xml
+    src_validation)
 set(local_tests_wraps
     " "
     "-Wl,--wrap=realloc"
@@ -29,6 +30,7 @@
     " "
     " "
     " "
+    " "
     " ")
 set(tests ${tests} ${local_tests} PARENT_SCOPE)
 set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/src/test_validation.c b/tests/src/test_validation.c
new file mode 100644
index 0000000..eaea51d
--- /dev/null
+++ b/tests/src/test_validation.c
@@ -0,0 +1,157 @@
+/*
+ * @file test_parser_xml.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for functions from parser_xml.c
+ *
+ * Copyright (c) 2019 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.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../../src/context.h"
+#include "../../src/tree_data_internal.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct ly_ctx *ctx; /* context for tests */
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    (void) level; /* unused */
+    if (store) {
+        if (path && path[0]) {
+            snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+        } else {
+            strncpy(logbuf, msg, BUFSIZE - 1);
+        }
+        if (store > 0) {
+            --store;
+        }
+    }
+}
+#endif
+
+static int
+setup(void **state)
+{
+    (void) state; /* unused */
+
+    const char *schema_a =
+        "module a {"
+            "namespace urn:tests:a;"
+            "prefix a;"
+            "yang-version 1.1;"
+
+            "container cont {"
+                "leaf a {"
+                    "when \"../../c = 'val_c'\";"
+                    "type string;"
+                "}"
+                "leaf b {"
+                    "type string;"
+                "}"
+            "}"
+            "leaf c {"
+                "when \"/cont/b = 'val_b'\";"
+                "type string;"
+            "}"
+        "}";
+
+#if ENABLE_LOGGER_CHECKING
+    ly_set_log_clb(logger, 1);
+#endif
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
+    assert_non_null(lys_parse_mem(ctx, schema_a, LYS_IN_YANG));
+
+    return 0;
+}
+
+static int
+teardown(void **state)
+{
+#if ENABLE_LOGGER_CHECKING
+    if (*state) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#else
+    (void) state; /* unused */
+#endif
+
+    ly_ctx_destroy(ctx, NULL);
+    ctx = NULL;
+
+    return 0;
+}
+
+void
+logbuf_clean(void)
+{
+    logbuf[0] = '\0';
+}
+
+#if ENABLE_LOGGER_CHECKING
+#   define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+#   define logbuf_assert(str)
+#endif
+
+static void
+test_when(void **state)
+{
+    *state = test_when;
+
+    const char *data;
+    struct lyd_node *tree;
+
+    data = "<c xmlns=\"urn:tests:a\">hey</c>";
+    assert_int_equal(LY_EVALID, lyd_parse_xml(ctx, data, 0, NULL, &tree));
+    assert_null(tree);
+    logbuf_assert("When condition \"/cont/b = 'val_b'\" not satisfied.");
+
+    data = "<cont xmlns=\"urn:tests:a\"><b>val_b</b></cont><c xmlns=\"urn:tests:a\">hey</c>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, 0, NULL, &tree));
+    assert_non_null(tree);
+    assert_string_equal("c", tree->next->schema->name);
+    assert_int_equal(LYD_WHEN_TRUE, tree->next->flags);
+    lyd_free_all(tree);
+
+    data = "<cont xmlns=\"urn:tests:a\"><a>val</a><b>val_b</b></cont><c xmlns=\"urn:tests:a\">val_c</c>";
+    assert_int_equal(LY_SUCCESS, lyd_parse_xml(ctx, data, 0, NULL, &tree));
+    assert_non_null(tree);
+    assert_string_equal("a", lyd_node_children(tree)->schema->name);
+    assert_int_equal(LYD_WHEN_TRUE, lyd_node_children(tree)->flags);
+    assert_string_equal("c", tree->next->schema->name);
+    assert_int_equal(LYD_WHEN_TRUE, tree->next->flags);
+    lyd_free_all(tree);
+
+    *state = NULL;
+}
+
+int main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_when, setup, teardown),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}