Skip to content

Commit 99a7788

Browse files
committed
tree data UPDATE xpath trim function
Refs #2148
1 parent b8093b9 commit 99a7788

File tree

3 files changed

+293
-0
lines changed

3 files changed

+293
-0
lines changed

src/tree_data.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,6 +3015,139 @@ lyd_eval_xpath4(const struct lyd_node *ctx_node, const struct lyd_node *tree, co
30153015
return ret;
30163016
}
30173017

3018+
/**
3019+
* @brief Hash table node equal callback.
3020+
*/
3021+
static ly_bool
3022+
lyd_trim_equal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
3023+
{
3024+
struct lyd_node *node1, *node2;
3025+
3026+
node1 = *(struct lyd_node **)val1_p;
3027+
node2 = *(struct lyd_node **)val2_p;
3028+
3029+
return node1 == node2;
3030+
}
3031+
3032+
LIBYANG_API_DEF LY_ERR
3033+
lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars)
3034+
{
3035+
LY_ERR ret = LY_SUCCESS;
3036+
struct ly_ctx *ctx;
3037+
struct lyxp_set xp_set = {0};
3038+
struct lyxp_expr *exp = NULL;
3039+
struct lyd_node *node, *parent;
3040+
struct lyxp_set_hash_node hnode;
3041+
struct ly_ht *parent_ht = NULL;
3042+
struct ly_set free_set = {0};
3043+
uint32_t i, hash;
3044+
ly_bool is_result;
3045+
3046+
LY_CHECK_ARG_RET(NULL, tree, xpath, LY_EINVAL);
3047+
3048+
if (!*tree) {
3049+
/* nothing to do */
3050+
goto cleanup;
3051+
}
3052+
3053+
*tree = lyd_first_sibling(*tree);
3054+
ctx = (struct ly_ctx *)LYD_CTX(*tree);
3055+
3056+
/* parse expression */
3057+
ret = lyxp_expr_parse(ctx, xpath, 0, 1, &exp);
3058+
LY_CHECK_GOTO(ret, cleanup);
3059+
3060+
/* evaluate expression */
3061+
ret = lyxp_eval(ctx, exp, NULL, LY_VALUE_JSON, NULL, *tree, *tree, *tree, vars, &xp_set, LYXP_IGNORE_WHEN);
3062+
LY_CHECK_GOTO(ret, cleanup);
3063+
3064+
/* create hash table for all the parents of results */
3065+
parent_ht = lyht_new(32, sizeof *node, lyd_trim_equal_cb, NULL, 1);
3066+
LY_CHECK_GOTO(!parent_ht, cleanup);
3067+
3068+
for (i = 0; i < xp_set.used; ++i) {
3069+
if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
3070+
/* ignore */
3071+
continue;
3072+
}
3073+
3074+
for (parent = lyd_parent(xp_set.val.nodes[i].node); parent; parent = lyd_parent(parent)) {
3075+
/* add the parent into parent_ht */
3076+
ret = lyht_insert(parent_ht, &parent, parent->hash, NULL);
3077+
if (ret == LY_EEXIST) {
3078+
/* shared parent, we are done */
3079+
break;
3080+
}
3081+
LY_CHECK_GOTO(ret, cleanup);
3082+
}
3083+
}
3084+
3085+
hnode.type = LYXP_NODE_ELEM;
3086+
LY_LIST_FOR(*tree, parent) {
3087+
LYD_TREE_DFS_BEGIN(parent, node) {
3088+
if (lysc_is_key(node->schema)) {
3089+
/* ignore */
3090+
goto next_iter;
3091+
}
3092+
3093+
/* check the results */
3094+
is_result = 0;
3095+
if (xp_set.ht) {
3096+
hnode.node = node;
3097+
hash = lyht_hash_multi(0, (const char *)&hnode.node, sizeof hnode.node);
3098+
hash = lyht_hash_multi(hash, (const char *)&hnode.type, sizeof hnode.type);
3099+
hash = lyht_hash_multi(hash, NULL, 0);
3100+
3101+
if (!lyht_find(xp_set.ht, &hnode, hash, NULL)) {
3102+
is_result = 1;
3103+
}
3104+
} else {
3105+
/* not enough elements for a hash table */
3106+
for (i = 0; i < xp_set.used; ++i) {
3107+
if (xp_set.val.nodes[i].type != LYXP_NODE_ELEM) {
3108+
/* ignore */
3109+
continue;
3110+
}
3111+
3112+
if (xp_set.val.nodes[i].node == node) {
3113+
is_result = 1;
3114+
break;
3115+
}
3116+
}
3117+
}
3118+
3119+
if (is_result) {
3120+
/* keep the whole subtree if the node is in the results */
3121+
LYD_TREE_DFS_continue = 1;
3122+
} else if (lyht_find(parent_ht, &node, node->hash, NULL)) {
3123+
/* free the whole subtree if the node is not even among the selected parents */
3124+
ret = ly_set_add(&free_set, node, 1, NULL);
3125+
LY_CHECK_GOTO(ret, cleanup);
3126+
LYD_TREE_DFS_continue = 1;
3127+
} /* else keep the parent node because a subtree is in the results */
3128+
3129+
next_iter:
3130+
LYD_TREE_DFS_END(parent, node);
3131+
}
3132+
}
3133+
3134+
/* free */
3135+
for (i = 0; i < free_set.count; ++i) {
3136+
node = free_set.dnodes[i];
3137+
if (*tree == node) {
3138+
*tree = (*tree)->next;
3139+
}
3140+
lyd_free_tree(node);
3141+
}
3142+
3143+
cleanup:
3144+
lyxp_set_free_content(&xp_set);
3145+
lyxp_expr_free(ctx, exp);
3146+
lyht_free(parent_ht, NULL);
3147+
ly_set_erase(&free_set, NULL);
3148+
return ret;
3149+
}
3150+
30183151
LIBYANG_API_DEF LY_ERR
30193152
lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, struct lyd_node **match)
30203153
{

src/tree_data.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2606,6 +2606,17 @@ LIBYANG_API_DECL LY_ERR lyd_eval_xpath4(const struct lyd_node *ctx_node, const s
26062606
const struct lyxp_var *vars, LY_XPATH_TYPE *ret_type, struct ly_set **node_set, char **string,
26072607
long double *number, ly_bool *boolean);
26082608

2609+
/**
2610+
* @brief Evaluate an XPath on data and free all the nodes except the subtrees selected by the expression.
2611+
*
2612+
* @param[in,out] tree Data tree to evaluate on and trim.
2613+
* @param[in] xpath [XPath](@ref howtoXPath) to select in JSON format.
2614+
* @param[in] vars Optional [sized array](@ref sizedarrays) of XPath variables.
2615+
* @return LY_SUCCESS on success.
2616+
* @return LY_ERR value on error.
2617+
*/
2618+
LIBYANG_API_DEF LY_ERR lyd_trim_xpath(struct lyd_node **tree, const char *xpath, const struct lyxp_var *vars);
2619+
26092620
/**
26102621
* @brief Search in given data for a node uniquely identified by a path.
26112622
*

tests/utests/basic/test_xpath.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,154 @@ test_axes(void **state)
10921092
lyd_free_all(tree);
10931093
}
10941094

1095+
static void
1096+
test_trim(void **state)
1097+
{
1098+
const char *data;
1099+
char *str1;
1100+
struct lyd_node *tree;
1101+
1102+
data =
1103+
"<l1 xmlns=\"urn:tests:a\">"
1104+
" <a>a1</a>"
1105+
" <b>b1</b>"
1106+
" <c>c1</c>"
1107+
"</l1>"
1108+
"<l1 xmlns=\"urn:tests:a\">"
1109+
" <a>a2</a>"
1110+
" <b>b2</b>"
1111+
"</l1>"
1112+
"<l1 xmlns=\"urn:tests:a\">"
1113+
" <a>a3</a>"
1114+
" <b>b3</b>"
1115+
"</l1>"
1116+
"<l1 xmlns=\"urn:tests:a\">"
1117+
" <a>a4</a>"
1118+
" <b>b4</b>"
1119+
" <c>c4</c>"
1120+
"</l1>"
1121+
"<l1 xmlns=\"urn:tests:a\">"
1122+
" <a>a5</a>"
1123+
" <b>b5</b>"
1124+
" <c>c5</c>"
1125+
"</l1>"
1126+
"<foo2 xmlns=\"urn:tests:a\">50</foo2>"
1127+
"<c xmlns=\"urn:tests:a\">"
1128+
" <x>key2</x>"
1129+
" <ll>"
1130+
" <a>key1</a>"
1131+
" <ll>"
1132+
" <a>key11</a>"
1133+
" <b>val11</b>"
1134+
" </ll>"
1135+
" <ll>"
1136+
" <a>key12</a>"
1137+
" <b>val12</b>"
1138+
" </ll>"
1139+
" <ll>"
1140+
" <a>key13</a>"
1141+
" <b>val13</b>"
1142+
" </ll>"
1143+
" </ll>"
1144+
" <ll>"
1145+
" <a>key2</a>"
1146+
" <ll>"
1147+
" <a>key21</a>"
1148+
" <b>val21</b>"
1149+
" </ll>"
1150+
" <ll>"
1151+
" <a>key22</a>"
1152+
" <b>val22</b>"
1153+
" </ll>"
1154+
" </ll>"
1155+
" <ll>"
1156+
" <a>key3</a>"
1157+
" <ll>"
1158+
" <a>key31</a>"
1159+
" <b>val31</b>"
1160+
" </ll>"
1161+
" <ll>"
1162+
" <a>key32</a>"
1163+
" <b>val32</b>"
1164+
" </ll>"
1165+
" </ll>"
1166+
"</c>";
1167+
1168+
/* trim #1 */
1169+
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
1170+
assert_non_null(tree);
1171+
1172+
assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[a='key11']", NULL));
1173+
lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
1174+
assert_string_equal(str1,
1175+
"<c xmlns=\"urn:tests:a\">\n"
1176+
" <ll>\n"
1177+
" <a>key1</a>\n"
1178+
" <ll>\n"
1179+
" <a>key11</a>\n"
1180+
" <b>val11</b>\n"
1181+
" </ll>\n"
1182+
" </ll>\n"
1183+
"</c>\n");
1184+
1185+
free(str1);
1186+
lyd_free_all(tree);
1187+
1188+
/* trim #2 */
1189+
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
1190+
assert_non_null(tree);
1191+
1192+
assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/a:c/ll/ll[contains(.,'2')]", NULL));
1193+
lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
1194+
assert_string_equal(str1,
1195+
"<c xmlns=\"urn:tests:a\">\n"
1196+
" <ll>\n"
1197+
" <a>key1</a>\n"
1198+
" <ll>\n"
1199+
" <a>key12</a>\n"
1200+
" <b>val12</b>\n"
1201+
" </ll>\n"
1202+
" </ll>\n"
1203+
" <ll>\n"
1204+
" <a>key2</a>\n"
1205+
" <ll>\n"
1206+
" <a>key21</a>\n"
1207+
" <b>val21</b>\n"
1208+
" </ll>\n"
1209+
" <ll>\n"
1210+
" <a>key22</a>\n"
1211+
" <b>val22</b>\n"
1212+
" </ll>\n"
1213+
" </ll>\n"
1214+
" <ll>\n"
1215+
" <a>key3</a>\n"
1216+
" <ll>\n"
1217+
" <a>key32</a>\n"
1218+
" <b>val32</b>\n"
1219+
" </ll>\n"
1220+
" </ll>\n"
1221+
"</c>\n");
1222+
1223+
free(str1);
1224+
lyd_free_all(tree);
1225+
1226+
/* trim #3 */
1227+
assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &tree));
1228+
assert_non_null(tree);
1229+
1230+
assert_int_equal(LY_SUCCESS, lyd_trim_xpath(&tree, "/l1[4]//.", NULL));
1231+
lyd_print_mem(&str1, tree, LYD_XML, LYD_PRINT_WITHSIBLINGS);
1232+
assert_string_equal(str1,
1233+
"<l1 xmlns=\"urn:tests:a\">\n"
1234+
" <a>a4</a>\n"
1235+
" <b>b4</b>\n"
1236+
" <c>c4</c>\n"
1237+
"</l1>\n");
1238+
1239+
free(str1);
1240+
lyd_free_all(tree);
1241+
}
1242+
10951243
int
10961244
main(void)
10971245
{
@@ -1108,6 +1256,7 @@ main(void)
11081256
UTEST(test_augment, setup),
11091257
UTEST(test_variables, setup),
11101258
UTEST(test_axes, setup),
1259+
UTEST(test_trim, setup),
11111260
};
11121261

11131262
return cmocka_run_group_tests(tests, NULL, NULL);

0 commit comments

Comments
 (0)