blob: 37ebc0d40a7e83856d18aecb43d0194174f7c4bb [file] [log] [blame]
Radek Krejcie7b95092019-05-15 11:03:07 +02001/**
2 * @file printer_xml.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @author Radek Krejci <rkrejci@cesnet.cz>
5 * @brief XML printer for libyang data structure
6 *
7 * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
8 *
9 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * https://opensource.org/licenses/BSD-3-Clause
14 */
15
16#include "common.h"
17
18#include <stdlib.h>
19#include <string.h>
20
21#include "log.h"
22#include "plugins_types.h"
23#include "printer_data.h"
24#include "printer_internal.h"
25#include "tree.h"
26#include "tree_data.h"
27#include "tree_schema.h"
28#include "xml.h"
29
30/**
31 * @brief XML printer context.
32 */
33struct xmlpr_ctx {
34 struct lyout *out; /**< output specification */
35 unsigned int level; /**< current indentation level: 0 - no formatting, >= 1 indentation levels */
36 int options; /**< [Data printer flags](@ref dataprinterflags) */
37 int toplevel; /**< top-level flag */
38};
39
40#define LEVEL ctx->level /**< current level */
41#define INDENT ((LEVEL) ? (LEVEL)*2 : 0),"" /**< indentation parameters for printer functions */
42#define LEVEL_INC if (LEVEL) {LEVEL++;} /**< increase indentation level */
43#define LEVEL_DEC if (LEVEL) {LEVEL--;} /**< decrease indentation level */
44
45/**
46 * TODO
47 */
48struct mlist {
49 struct mlist *next;
50 struct lys_module *module;
51} *mlist = NULL, *mlist_new;
52
53#if 0
54static LY_ERR
55modlist_add(struct mlist **mlist, const struct lys_module *mod)
56{
57 struct mlist *iter;
58
59 for (iter = *mlist; iter; iter = iter->next) {
60 if (mod == iter->module) {
61 break;
62 }
63 }
64
65 if (!iter) {
66 iter = malloc(sizeof *iter);
67 LY_CHECK_ERR_RET(!iter, LOGMEM(mod->ctx), LY_EMEM);
68 iter->next = *mlist;
69 iter->module = (struct lys_module *)mod;
70 *mlist = iter;
71 }
72
73 return LY_SUCCESS;
74}
75#endif
76
77/**
78 * TODO
79 */
80static void
81xml_print_ns(struct xmlpr_ctx *ctx, const struct lyd_node *node)
82{
83 struct lyd_node *next, *cur, *child;
84 struct lyd_attr *attr;
85 struct mlist *mlist = NULL, *miter;
86 int r = 0;
87
88#if 0
89 const struct lys_module *wdmod = NULL;
90
91 /* add node attribute modules */
92 for (attr = node->attr; attr; attr = attr->next) {
93 if (!strcmp(node->schema->name, "filter") &&
94 (!strcmp(node->schema->module->name, "ietf-netconf") ||
95 !strcmp(node->schema->module->name, "notifications"))) {
96 /* exception for NETCONF's filter attributes */
97 continue;
98 } else {
99 r = modlist_add(&mlist, lys_main_module(attr->annotation->module));
100 }
101 if (r) {
102 goto print;
103 }
104 }
105#endif
106
107 /* add node children nodes and attribute modules */
108 switch (node->schema->nodetype) {
109 case LYS_LEAFLIST:
110 case LYS_LEAF:
111 /* TODO ietf-netconf-with-defaults namespace */
112#if 0
113 if (node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) {
114 /* get with-defaults module and print its namespace */
115 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
116 if (wdmod && modlist_add(&mlist, wdmod)) {
117 goto print;
118 }
119 }
120#endif
121 break;
122 case LYS_CONTAINER:
123 case LYS_LIST:
124#if 0
125 case LYS_RPC:
126 case LYS_ACTION:
127 case LYS_NOTIF:
128 if (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG)) {
129 /* get with-defaults module and print its namespace */
130 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
131 if (wdmod && modlist_add(&mlist, wdmod)) {
132 goto print;
133 }
134 }
135#endif
136 LY_LIST_FOR(((struct lyd_node_inner*)node)->child, child) {
137 LYD_TREE_DFS_BEGIN(child, next, cur) {
138 for (attr = cur->attr; attr; attr = attr->next) {
139 if (!strcmp(cur->schema->name, "filter") &&
140 (!strcmp(cur->schema->module->name, "ietf-netconf") ||
141 !strcmp(cur->schema->module->name, "notifications"))) {
142 /* exception for NETCONF's filter attributes */
143 continue;
144 } else {
145 /* TODO annotations r = modlist_add(&mlist, lys_main_module(attr->annotation->module)); */
146 }
147 if (r) {
148 goto print;
149 }
150 }
151 LYD_TREE_DFS_END(child, next, cur)}
152 }
153 break;
154 default:
155 break;
156 }
157
158print:
159 /* print used namespaces */
160 while (mlist) {
161 miter = mlist;
162 mlist = mlist->next;
163
164 ly_print(ctx->out, " xmlns:%s=\"%s\"", miter->module->prefix, miter->module->ns);
165 free(miter);
166 }
167}
168
169/**
170 * TODO
171 */
172static LY_ERR
173xml_print_attrs(struct xmlpr_ctx *ctx, const struct lyd_node *node)
174{
175 (void) ctx;
176 (void) node;
177
178#if 0
179 struct lyd_attr *attr;
180 const char **prefs, **nss;
181 const char *xml_expr = NULL, *mod_name;
182 uint32_t ns_count, i;
183 int rpc_filter = 0;
184 const struct lys_module *wdmod = NULL;
185 char *p;
186 size_t len;
187
188 LY_PRINT_SET;
189
190 /* with-defaults */
191 if (node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
192 if ((node->dflt && (options & (LYP_WD_ALL_TAG | LYP_WD_IMPL_TAG))) ||
193 (!node->dflt && (options & LYP_WD_ALL_TAG) && lyd_wd_default((struct lyd_node_leaf_list *)node))) {
194 /* we have implicit OR explicit default node */
195 /* get with-defaults module */
196 wdmod = ly_ctx_get_module(node->schema->module->ctx, "ietf-netconf-with-defaults", NULL, 1);
197 if (wdmod) {
198 /* print attribute only if context include with-defaults schema */
199 ly_print(out, " %s:default=\"true\"", wdmod->prefix);
200 }
201 }
202 }
203 /* technically, check for the extension get-filter-element-attributes from ietf-netconf */
204 if (!strcmp(node->schema->name, "filter")
205 && (!strcmp(node->schema->module->name, "ietf-netconf") || !strcmp(node->schema->module->name, "notifications"))) {
206 rpc_filter = 1;
207 }
208
209 for (attr = node->attr; attr; attr = attr->next) {
210 if (rpc_filter) {
211 /* exception for NETCONF's filter's attributes */
212 if (!strcmp(attr->name, "select")) {
213 /* xpath content, we have to convert the JSON format into XML first */
214 xml_expr = transform_json2xml(node->schema->module, attr->value_str, 0, &prefs, &nss, &ns_count);
215 if (!xml_expr) {
216 /* error */
217 return EXIT_FAILURE;
218 }
219
220 for (i = 0; i < ns_count; ++i) {
221 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
222 }
223 free(prefs);
224 free(nss);
225 }
226 ly_print(out, " %s=\"", attr->name);
227 } else {
228 ly_print(out, " %s:%s=\"", attr->annotation->module->prefix, attr->name);
229 }
230
231 switch (attr->value_type) {
232 case LY_TYPE_BINARY:
233 case LY_TYPE_STRING:
234 case LY_TYPE_BITS:
235 case LY_TYPE_ENUM:
236 case LY_TYPE_BOOL:
237 case LY_TYPE_DEC64:
238 case LY_TYPE_INT8:
239 case LY_TYPE_INT16:
240 case LY_TYPE_INT32:
241 case LY_TYPE_INT64:
242 case LY_TYPE_UINT8:
243 case LY_TYPE_UINT16:
244 case LY_TYPE_UINT32:
245 case LY_TYPE_UINT64:
246 if (attr->value_str) {
247 /* xml_expr can contain transformed xpath */
248 lyxml_dump_text(out, xml_expr ? xml_expr : attr->value_str, LYXML_DATA_ATTR);
249 }
250 break;
251
252 case LY_TYPE_IDENT:
253 if (!attr->value_str) {
254 break;
255 }
256 p = strchr(attr->value_str, ':');
257 assert(p);
258 len = p - attr->value_str;
259 mod_name = attr->annotation->module->name;
260 if (!strncmp(attr->value_str, mod_name, len) && !mod_name[len]) {
261 lyxml_dump_text(out, ++p, LYXML_DATA_ATTR);
262 } else {
263 /* avoid code duplication - use instance-identifier printer which gets necessary namespaces to print */
264 goto printinst;
265 }
266 break;
267 case LY_TYPE_INST:
268printinst:
269 xml_expr = transform_json2xml(node->schema->module, ((struct lyd_node_leaf_list *)node)->value_str, 1,
270 &prefs, &nss, &ns_count);
271 if (!xml_expr) {
272 /* error */
273 return EXIT_FAILURE;
274 }
275
276 for (i = 0; i < ns_count; ++i) {
277 ly_print(out, " xmlns:%s=\"%s\"", prefs[i], nss[i]);
278 }
279 free(prefs);
280 free(nss);
281
282 lyxml_dump_text(out, xml_expr, LYXML_DATA_ATTR);
283 lydict_remove(node->schema->module->ctx, xml_expr);
284 break;
285
286 /* LY_TYPE_LEAFREF not allowed */
287 case LY_TYPE_EMPTY:
288 break;
289
290 default:
291 /* error */
292 LOGINT(node->schema->module->ctx);
293 return EXIT_FAILURE;
294 }
295
296 ly_print(out, "\"");
297
298 if (xml_expr) {
299 lydict_remove(node->schema->module->ctx, xml_expr);
300 }
301 }
302#endif
303
304 return LY_SUCCESS;
305}
306
307/**
308 * @brief Print generic XML element despite of the data node type.
309 *
310 * Prints the element name, attributes and necessary namespaces.
311 *
312 * @param[in] ctx XML printer context.
313 * @param[in] node Data node to be printed.
314 * @return LY_ERR value.
315 */
316static LY_ERR
317xml_print_node_open(struct xmlpr_ctx *ctx, const struct lyd_node *node)
318{
319 if (ctx->toplevel || !node->parent || node->schema->module != node->parent->schema->module) {
320 /* print "namespace" */
321 ly_print(ctx->out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, node->schema->module->ns);
322 } else {
323 ly_print(ctx->out, "%*s<%s", INDENT, node->schema->name);
324 }
325
326 if (ctx->toplevel) {
327 xml_print_ns(ctx, node);
328 ctx->toplevel = 0;
329 }
330
331 LY_CHECK_RET(xml_print_attrs(ctx, node));
332
333 return LY_SUCCESS;
334}
335
336static LY_ERR xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node);
337
338/**
339 * @brief Print XML element representing lyd_node_term.
340 *
341 * @param[in] ctx XML printer context.
342 * @param[in] node Data node to be printed.
343 * @return LY_ERR value.
344 */
345static LY_ERR
346xml_print_term(struct xmlpr_ctx *ctx, const struct lyd_node_term *node)
347{
348 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
349
350 if (((struct lysc_node_leaf*)node->schema)->type->plugin->flags & LY_TYPE_FLAG_PREFIXES) {
351 /* TODO get prefixes from the value and print namespaces */
352 }
353
354 if (!node->value.canonized || !node->value.canonized[0]) {
355 ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
356 } else {
357 ly_print(ctx->out, ">");
358 lyxml_dump_text(ctx->out, node->value.canonized, 0);
359 ly_print(ctx->out, "</%s>%s", node->schema->name, LEVEL ? "\n" : "");
360 }
361
362 return LY_SUCCESS;
363}
364
365/**
366 * @brief Print XML element representing lyd_node_inner.
367 *
368 * @param[in] ctx XML printer context.
369 * @param[in] node Data node to be printed.
370 * @return LY_ERR value.
371 */
372static LY_ERR
373xml_print_inner(struct xmlpr_ctx *ctx, const struct lyd_node_inner *node)
374{
375 LY_ERR ret;
376 struct lyd_node *child;
377
378 LY_CHECK_RET(xml_print_node_open(ctx, (struct lyd_node *)node));
379
380 if (!node->child) {
381 ly_print(ctx->out, "/>%s", ctx->level ? "\n" : "");
382 return LY_SUCCESS;
383 }
384
385 /* children */
386 ly_print(ctx->out, ">%s", ctx->level ? "\n" : "");
387
388 LEVEL_INC;
389 LY_LIST_FOR(node->child, child) {
390 ret = xml_print_node(ctx, child);
391 LY_CHECK_ERR_RET(ret, LEVEL_DEC, ret);
392 }
393 LEVEL_DEC;
394
395 ly_print(ctx->out, "%*s</%s>%s", INDENT, node->schema->name, LEVEL ? "\n" : "");
396
397 return LY_SUCCESS;
398}
399
400#if 0
401static int
402xml_print_anydata(struct lyout *out, int level, const struct lyd_node *node, int toplevel, int options)
403{
404 char *buf;
405 struct lyd_node_anydata *any = (struct lyd_node_anydata *)node;
406 struct lyd_node *iter;
407 const char *ns;
408
409 LY_PRINT_SET;
410
411 if (toplevel || !node->parent || nscmp(node, node->parent)) {
412 /* print "namespace" */
413 ns = lyd_node_module(node)->ns;
414 ly_print(out, "%*s<%s xmlns=\"%s\"", INDENT, node->schema->name, ns);
415 } else {
416 ly_print(out, "%*s<%s", INDENT, node->schema->name);
417 }
418
419 if (toplevel) {
420 xml_print_ns(out, node, options);
421 }
422 if (xml_print_attrs(out, node, options)) {
423 return EXIT_FAILURE;
424 }
425 if (!(void*)any->value.tree || (any->value_type == LYD_ANYDATA_CONSTSTRING && !any->value.str[0])) {
426 /* no content */
427 ly_print(out, "/>%s", level ? "\n" : "");
428 } else {
429 if (any->value_type == LYD_ANYDATA_DATATREE) {
430 /* print namespaces in the anydata data tree */
431 LY_TREE_FOR(any->value.tree, iter) {
432 xml_print_ns(out, iter, options);
433 }
434 }
435 /* close opening tag ... */
436 ly_print(out, ">");
437 /* ... and print anydata content */
438 switch (any->value_type) {
439 case LYD_ANYDATA_CONSTSTRING:
440 lyxml_dump_text(out, any->value.str, LYXML_DATA_ELEM);
441 break;
442 case LYD_ANYDATA_DATATREE:
443 if (any->value.tree) {
444 if (level) {
445 ly_print(out, "\n");
446 }
447 LY_TREE_FOR(any->value.tree, iter) {
448 if (xml_print_node(out, level ? level + 1 : 0, iter, 0, (options & ~(LYP_WITHSIBLINGS | LYP_NETCONF)))) {
449 return EXIT_FAILURE;
450 }
451 }
452 }
453 break;
454 case LYD_ANYDATA_XML:
455 lyxml_print_mem(&buf, any->value.xml, (level ? LYXML_PRINT_FORMAT | LYXML_PRINT_NO_LAST_NEWLINE : 0)
456 | LYXML_PRINT_SIBLINGS);
457 ly_print(out, "%s%s", level ? "\n" : "", buf);
458 free(buf);
459 break;
460 case LYD_ANYDATA_SXML:
461 /* print without escaping special characters */
462 ly_print(out, "%s", any->value.str);
463 break;
464 case LYD_ANYDATA_JSON:
465 case LYD_ANYDATA_LYB:
466 /* JSON and LYB format is not supported */
467 LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
468 break;
469 case LYD_ANYDATA_STRING:
470 case LYD_ANYDATA_SXMLD:
471 case LYD_ANYDATA_JSOND:
472 case LYD_ANYDATA_LYBD:
473 /* dynamic strings are used only as input parameters */
474 assert(0);
475 break;
476 }
477
478 /* closing tag */
479 ly_print(out, "</%s>%s", node->schema->name, level ? "\n" : "");
480 }
481
482 LY_PRINT_RET(node->schema->module->ctx);
483}
484#endif
485
486/**
487 * @brief Print XML element representing lyd_node.
488 *
489 * @param[in] ctx XML printer context.
490 * @param[in] node Data node to be printed.
491 * @return LY_ERR value.
492 */
493static LY_ERR
494xml_print_node(struct xmlpr_ctx *ctx, const struct lyd_node *node)
495{
496 LY_ERR ret = LY_SUCCESS;
497
498#if 0
499 if (!lyd_wd_toprint(node, ctx->options)) {
500 /* wd says do not print */
501 return EXIT_SUCCESS;
502 }
503#endif
504
505 switch (node->schema->nodetype) {
506#if 0
507 case LYS_NOTIF:
508 case LYS_ACTION:
509#endif
510 case LYS_CONTAINER:
511 case LYS_LIST:
512 ret = xml_print_inner(ctx, (const struct lyd_node_inner*)node);
513 break;
514 case LYS_LEAF:
515 case LYS_LEAFLIST:
516 ret = xml_print_term(ctx, (const struct lyd_node_term*)node);
517 break;
518#if 0
519 case LYS_ANYXML:
520 case LYS_ANYDATA:
521 ret = xml_print_anydata(ctx, node);
522 break;
523#endif
524 default:
525 LOGINT(node->schema->module->ctx);
526 ret = LY_EINT;
527 break;
528 }
529
530 return ret;
531}
532
533LY_ERR
534xml_print_data(struct lyout *out, const struct lyd_node *root, int options)
535{
536 const struct lyd_node *node;
537 struct xmlpr_ctx ctx_ = {.out = out, .level = (options & LYDP_FORMAT ? 1 : 0), .options = options, .toplevel = 1}, *ctx = &ctx_;
538
539 if (!root) {
540 if (out->type == LYOUT_MEMORY || out->type == LYOUT_CALLBACK) {
541 ly_print(out, "");
542 }
543 goto finish;
544 }
545
546 /* content */
547 LY_LIST_FOR(root, node) {
548 if (xml_print_node(ctx, node)) {
549 return EXIT_FAILURE;
550 }
551 if (!(options & LYDP_WITHSIBLINGS)) {
552 break;
553 }
554 }
555
556finish:
557 ly_print_flush(out);
558 return LY_SUCCESS;
559}
560