Coverage Report

Created: 2025-07-18 06:26

/src/libxmlb/src/xb-node.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "XbNode"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
#include <glib-object.h>
13
14
#include "xb-node-private.h"
15
#include "xb-node-silo.h"
16
#include "xb-silo-export-private.h"
17
18
typedef struct {
19
  XbSilo *silo;
20
  XbSiloNode *sn;
21
} XbNodePrivate;
22
23
G_DEFINE_TYPE_WITH_PRIVATE(XbNode, xb_node, G_TYPE_OBJECT)
24
0
#define GET_PRIVATE(o) (xb_node_get_instance_private(o))
25
26
/**
27
 * XbNodeAttrIter:
28
 *
29
 * A #XbNodeAttrIter structure represents an iterator that can be used
30
 * to iterate over the attributes of a #XbNode. #XbNodeAttrIter
31
 * structures are typically allocated on the stack and then initialized
32
 * with xb_node_attr_iter_init().
33
 *
34
 * The iteration order of a #XbNodeAttrIter is not defined.
35
 *
36
 * Since: 0.3.4
37
 */
38
39
typedef struct {
40
  XbNode *node;
41
  guint8 position;
42
  gpointer dummy3;
43
  gpointer dummy4;
44
  gpointer dummy5;
45
  gpointer dummy6;
46
} RealAttrIter;
47
48
G_STATIC_ASSERT(sizeof(XbNodeAttrIter) == sizeof(RealAttrIter));
49
50
/**
51
 * XbNodeChildIter:
52
 *
53
 * A #XbNodeChildIter structure represents an iterator that can be used
54
 * to iterate over the children of a #XbNode. #XbNodeChildIter
55
 * structures are typically allocated on the stack and then initialized
56
 * with xb_node_child_iter_init().
57
 *
58
 * Since: 0.3.4
59
 */
60
61
typedef struct {
62
  XbNode *node;
63
  XbSiloNode *position;
64
  gboolean first_iter;
65
  gpointer dummy4;
66
  gpointer dummy5;
67
  gpointer dummy6;
68
} RealChildIter;
69
70
G_STATIC_ASSERT(sizeof(XbNodeChildIter) == sizeof(RealChildIter));
71
72
/**
73
 * xb_node_get_data:
74
 * @self: a #XbNode
75
 * @key: a string key, e.g. `fwupd::RemoteId`
76
 *
77
 * Gets any data that has been set on the node using xb_node_set_data().
78
 *
79
 * This will only work across queries to the associated silo if the silo has
80
 * its #XbSilo:enable-node-cache property set to %TRUE. Otherwise a new #XbNode
81
 * may be constructed for future queries which return the same element as a
82
 * result.
83
 *
84
 * Returns: (transfer none): a #GBytes, or %NULL if not found
85
 *
86
 * Since: 0.1.0
87
 **/
88
GBytes *
89
xb_node_get_data(XbNode *self, const gchar *key)
90
0
{
91
0
  XbNodePrivate *priv = GET_PRIVATE(self);
92
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
93
0
  g_return_val_if_fail(key != NULL, NULL);
94
0
  g_return_val_if_fail(priv->silo, NULL);
95
0
  return g_object_get_data(G_OBJECT(self), key);
96
0
}
97
98
/**
99
 * xb_node_set_data:
100
 * @self: a #XbNode
101
 * @key: a string key, e.g. `fwupd::RemoteId`
102
 * @data: a #GBytes
103
 *
104
 * Sets some data on the node which can be retrieved using xb_node_get_data().
105
 *
106
 * This will only work across queries to the associated silo if the silo has
107
 * its #XbSilo:enable-node-cache property set to %TRUE. Otherwise a new #XbNode
108
 * may be constructed for future queries which return the same element as a
109
 * result.
110
 *
111
 * Since: 0.1.0
112
 **/
113
void
114
xb_node_set_data(XbNode *self, const gchar *key, GBytes *data)
115
0
{
116
0
  XbNodePrivate *priv = GET_PRIVATE(self);
117
0
  g_return_if_fail(XB_IS_NODE(self));
118
0
  g_return_if_fail(key != NULL);
119
0
  g_return_if_fail(data != NULL);
120
0
  g_return_if_fail(priv->silo);
121
0
  g_object_set_data_full(G_OBJECT(self),
122
0
             key,
123
0
             g_bytes_ref(data),
124
0
             (GDestroyNotify)g_bytes_unref);
125
0
}
126
127
/**
128
 * xb_node_get_sn: (skip)
129
 * @self: a #XbNode
130
 *
131
 * Gets the #XbSiloNode for the node.
132
 *
133
 * Returns: (transfer none): a #XbSiloNode
134
 *
135
 * Since: 0.1.0
136
 **/
137
XbSiloNode *
138
xb_node_get_sn(XbNode *self)
139
0
{
140
0
  XbNodePrivate *priv = GET_PRIVATE(self);
141
0
  return priv->sn;
142
0
}
143
144
/**
145
 * xb_node_get_silo:
146
 * @self: a #XbNode
147
 *
148
 * Gets the #XbSilo for the node.
149
 *
150
 * Returns: (transfer none): a #XbSilo
151
 *
152
 * Since: 0.2.0
153
 **/
154
XbSilo *
155
xb_node_get_silo(XbNode *self)
156
0
{
157
0
  XbNodePrivate *priv = GET_PRIVATE(self);
158
0
  return priv->silo;
159
0
}
160
161
/**
162
 * xb_node_get_root:
163
 * @self: a #XbNode
164
 *
165
 * Gets the root node for the node.
166
 *
167
 * Returns: (transfer full): a #XbNode, or %NULL
168
 *
169
 * Since: 0.1.0
170
 **/
171
XbNode *
172
xb_node_get_root(XbNode *self)
173
0
{
174
0
  XbNodePrivate *priv = GET_PRIVATE(self);
175
0
  XbSiloNode *sn;
176
177
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
178
179
0
  sn = xb_silo_get_root_node(priv->silo, NULL);
180
0
  if (sn == NULL)
181
0
    return NULL;
182
0
  return xb_silo_create_node(priv->silo, sn, FALSE);
183
0
}
184
185
/**
186
 * xb_node_get_parent:
187
 * @self: a #XbNode
188
 *
189
 * Gets the parent node for the current node.
190
 *
191
 * Returns: (transfer full): a #XbNode, or %NULL
192
 *
193
 * Since: 0.1.0
194
 **/
195
XbNode *
196
xb_node_get_parent(XbNode *self)
197
0
{
198
0
  XbNodePrivate *priv = GET_PRIVATE(self);
199
0
  XbSiloNode *sn;
200
201
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
202
203
0
  if (priv->sn == NULL)
204
0
    return NULL;
205
0
  sn = xb_silo_get_parent_node(priv->silo, priv->sn, NULL);
206
0
  if (sn == NULL)
207
0
    return NULL;
208
0
  return xb_silo_create_node(priv->silo, sn, FALSE);
209
0
}
210
211
/**
212
 * xb_node_get_next:
213
 * @self: a #XbNode
214
 *
215
 * Gets the next sibling node for the current node.
216
 *
217
 * Returns: (transfer full): a #XbNode, or %NULL
218
 *
219
 * Since: 0.1.0
220
 **/
221
XbNode *
222
xb_node_get_next(XbNode *self)
223
0
{
224
0
  XbNodePrivate *priv = GET_PRIVATE(self);
225
0
  XbSiloNode *sn;
226
227
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
228
229
0
  if (priv->sn == NULL)
230
0
    return NULL;
231
0
  sn = xb_silo_get_next_node(priv->silo, priv->sn, NULL);
232
0
  if (sn == NULL)
233
0
    return NULL;
234
0
  return xb_silo_create_node(priv->silo, sn, FALSE);
235
0
}
236
237
/**
238
 * xb_node_get_child:
239
 * @self: a #XbNode
240
 *
241
 * Gets the first child node for the current node.
242
 *
243
 * Returns: (transfer full): a #XbNode, or %NULL
244
 *
245
 * Since: 0.1.0
246
 **/
247
XbNode *
248
xb_node_get_child(XbNode *self)
249
0
{
250
0
  XbNodePrivate *priv = GET_PRIVATE(self);
251
0
  XbSiloNode *sn;
252
253
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
254
255
0
  if (priv->sn == NULL)
256
0
    return NULL;
257
0
  sn = xb_silo_get_child_node(priv->silo, priv->sn, NULL);
258
0
  if (sn == NULL)
259
0
    return NULL;
260
0
  return xb_silo_create_node(priv->silo, sn, FALSE);
261
0
}
262
263
/**
264
 * xb_node_get_children:
265
 * @self: a #XbNode
266
 *
267
 * Gets all the children for the current node.
268
 *
269
 * Returns: (transfer container) (element-type XbNode): an array of children
270
 *
271
 * Since: 0.1.0
272
 **/
273
GPtrArray *
274
xb_node_get_children(XbNode *self)
275
0
{
276
0
  XbNode *n;
277
0
  GPtrArray *array = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
278
279
  /* add all children */
280
0
  n = xb_node_get_child(self);
281
0
  while (n != NULL) {
282
0
    g_ptr_array_add(array, n);
283
0
    n = xb_node_get_next(n);
284
0
  }
285
0
  return array;
286
0
}
287
288
/**
289
 * xb_node_child_iter_init:
290
 * @iter: an uninitialized #XbNodeChildIter
291
 * @self: a #XbNode
292
 *
293
 * Initializes a child iterator for the node's children and associates
294
 * it with @self.
295
 * The #XbNodeChildIter structure is typically allocated on the stack
296
 * and does not need to be freed explicitly.
297
 *
298
 * Since: 0.3.4
299
 */
300
void
301
xb_node_child_iter_init(XbNodeChildIter *iter, XbNode *self)
302
0
{
303
0
  XbNodePrivate *priv = GET_PRIVATE(self);
304
0
  RealChildIter *ri = (RealChildIter *)iter;
305
306
0
  g_return_if_fail(iter != NULL);
307
0
  g_return_if_fail(XB_IS_NODE(self));
308
309
0
  ri->node = self;
310
0
  ri->position = priv->sn != NULL ? xb_silo_get_child_node(priv->silo, priv->sn, NULL) : NULL;
311
0
  ri->first_iter = TRUE;
312
0
}
313
314
/**
315
 * xb_node_child_iter_next:
316
 * @iter: an initialized #XbNodeAttrIter
317
 * @child: (out) (optional) (not nullable): Destination of the returned child
318
 *
319
 * Returns the current child and advances the iterator.
320
 * The retrieved #XbNode child needs to be dereferenced with g_object_unref().
321
 * Example:
322
 * |[<!-- language="C" -->
323
 * XbNodeChildIter iter;
324
 * g_autoptr(XbNode) child = NULL;
325
 *
326
 * xb_node_child_iter_init (&iter, node);
327
 * while (xb_node_child_iter_next (&iter, &child)) {
328
 *     // do something with the node child
329
 *     g_clear_pointer (&child, g_object_unref);
330
 * }
331
 * ]|
332
 *
333
 * Returns: %FALSE if the last child has been reached.
334
 *
335
 * Since: 0.3.4
336
 */
337
gboolean
338
xb_node_child_iter_next(XbNodeChildIter *iter, XbNode **child)
339
0
{
340
0
  XbNodePrivate *priv;
341
0
  RealChildIter *ri = (RealChildIter *)iter;
342
343
0
  g_return_val_if_fail(iter != NULL, FALSE);
344
0
  g_return_val_if_fail(child != NULL, FALSE);
345
0
  priv = GET_PRIVATE(ri->node);
346
347
  /* check if the iteration was finished */
348
0
  if (ri->position == NULL) {
349
0
    *child = NULL;
350
0
    return FALSE;
351
0
  }
352
353
0
  *child = xb_silo_create_node(priv->silo, ri->position, FALSE);
354
0
  ri->position = xb_silo_get_next_node(priv->silo, ri->position, NULL);
355
356
0
  return TRUE;
357
0
}
358
359
/**
360
 * xb_node_child_iter_loop: (skip)
361
 * @iter: an initialized #XbNodeAttrIter
362
 * @child: (out) (optional) (nullable): Destination of the returned child
363
 *
364
 * Returns the current child and advances the iterator.
365
 * On the first call to this function, the @child pointer is assumed to point
366
 * at uninitialised memory.
367
 * On any later calls, it is assumed that the same pointers
368
 * will be given and that they will point to the memory as set by the
369
 * previous call to this function. This allows the previous values to
370
 * be freed, as appropriate.
371
 *
372
 * Example:
373
 * |[<!-- language="C" -->
374
 * XbNodeChildIter iter;
375
 * XbNode *child;
376
 *
377
 * xb_node_child_iter_init (&iter, node);
378
 * while (xb_node_child_iter_loop (&iter, &child)) {
379
 *     // do something with the node child
380
 *     // no need to free 'child' unless breaking out of this loop
381
 * }
382
 * ]|
383
 *
384
 * Returns: %FALSE if the last child has been reached.
385
 *
386
 * Since: 0.3.4
387
 */
388
gboolean
389
xb_node_child_iter_loop(XbNodeChildIter *iter, XbNode **child)
390
0
{
391
0
  XbNodePrivate *priv;
392
0
  RealChildIter *ri = (RealChildIter *)iter;
393
394
0
  g_return_val_if_fail(iter != NULL, FALSE);
395
0
  g_return_val_if_fail(child != NULL, FALSE);
396
0
  priv = GET_PRIVATE(ri->node);
397
398
  /* unref child from previous iterations, if there were any */
399
0
  if (ri->first_iter)
400
0
    ri->first_iter = FALSE;
401
0
  else
402
0
    g_object_unref(*child);
403
404
  /* check if the iteration was finished */
405
0
  if (ri->position == NULL) {
406
0
    *child = NULL;
407
0
    return FALSE;
408
0
  }
409
410
0
  *child = xb_silo_create_node(priv->silo, ri->position, FALSE);
411
0
  ri->position = xb_silo_get_next_node(priv->silo, ri->position, NULL);
412
413
0
  return TRUE;
414
0
}
415
416
/**
417
 * xb_node_get_text:
418
 * @self: a #XbNode
419
 *
420
 * Gets the text data for a specific node.
421
 *
422
 * Returns: a string, or %NULL for unset
423
 *
424
 * Since: 0.1.0
425
 **/
426
const gchar *
427
xb_node_get_text(XbNode *self)
428
0
{
429
0
  XbNodePrivate *priv = GET_PRIVATE(self);
430
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
431
0
  if (priv->sn == NULL)
432
0
    return NULL;
433
0
  if (xb_silo_node_get_text_idx(priv->sn) == XB_SILO_UNSET)
434
0
    return NULL;
435
0
  return xb_silo_from_strtab(priv->silo, xb_silo_node_get_text_idx(priv->sn), NULL);
436
0
}
437
438
/**
439
 * xb_node_get_text_as_uint:
440
 * @self: a #XbNode
441
 *
442
 * Gets some attribute text data for a specific node.
443
 *
444
 * Returns: a guint64, or %G_MAXUINT64 if unfound
445
 *
446
 * Since: 0.1.0
447
 **/
448
guint64
449
xb_node_get_text_as_uint(XbNode *self)
450
0
{
451
0
  const gchar *tmp;
452
0
  g_return_val_if_fail(XB_IS_NODE(self), G_MAXUINT64);
453
0
  tmp = xb_node_get_text(self);
454
0
  if (tmp == NULL)
455
0
    return G_MAXUINT64;
456
0
  if (g_str_has_prefix(tmp, "0x"))
457
0
    return g_ascii_strtoull(tmp + 2, NULL, 16);
458
0
  return g_ascii_strtoull(tmp, NULL, 10);
459
0
}
460
461
/**
462
 * xb_node_get_tail:
463
 * @self: a #XbNode
464
 *
465
 * Gets the tail data for a specific node.
466
 *
467
 * Returns: a string, or %NULL for unset
468
 *
469
 * Since: 0.1.12
470
 **/
471
const gchar *
472
xb_node_get_tail(XbNode *self)
473
0
{
474
0
  XbNodePrivate *priv = GET_PRIVATE(self);
475
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
476
0
  if (priv->sn == NULL)
477
0
    return NULL;
478
0
  if (xb_silo_node_get_tail_idx(priv->sn) == XB_SILO_UNSET)
479
0
    return NULL;
480
0
  return xb_silo_from_strtab(priv->silo, xb_silo_node_get_tail_idx(priv->sn), NULL);
481
0
}
482
483
/**
484
 * xb_node_get_element:
485
 * @self: a #XbNode
486
 *
487
 * Gets the element name for a specific node.
488
 *
489
 * Returns: a string, or %NULL for the root node
490
 *
491
 * Since: 0.1.0
492
 **/
493
const gchar *
494
xb_node_get_element(XbNode *self)
495
0
{
496
0
  XbNodePrivate *priv = GET_PRIVATE(self);
497
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
498
0
  if (priv->sn == NULL)
499
0
    return NULL;
500
0
  return xb_silo_get_node_element(priv->silo, priv->sn, NULL);
501
0
}
502
503
/**
504
 * xb_node_get_attr:
505
 * @self: a #XbNode
506
 * @name: an attribute name, e.g. "type"
507
 *
508
 * Gets some attribute text data for a specific node.
509
 *
510
 * Returns: a string, or %NULL for unset
511
 *
512
 * Since: 0.1.0
513
 **/
514
const gchar *
515
xb_node_get_attr(XbNode *self, const gchar *name)
516
0
{
517
0
  XbNodePrivate *priv = GET_PRIVATE(self);
518
0
  XbSiloNodeAttr *a;
519
520
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
521
0
  g_return_val_if_fail(name != NULL, NULL);
522
523
0
  if (priv->sn == NULL)
524
0
    return NULL;
525
0
  a = xb_silo_get_node_attr_by_str(priv->silo, priv->sn, name);
526
0
  if (a == NULL)
527
0
    return NULL;
528
0
  return xb_silo_from_strtab(priv->silo, a->attr_value, NULL);
529
0
}
530
531
/**
532
 * xb_node_get_attr_as_uint:
533
 * @self: a #XbNode
534
 * @name: an attribute name, e.g. `type`
535
 *
536
 * Gets some attribute text data for a specific node.
537
 *
538
 * Returns: a guint64, or %G_MAXUINT64 if unfound
539
 *
540
 * Since: 0.1.0
541
 **/
542
guint64
543
xb_node_get_attr_as_uint(XbNode *self, const gchar *name)
544
0
{
545
0
  const gchar *tmp;
546
547
0
  g_return_val_if_fail(XB_IS_NODE(self), G_MAXUINT64);
548
0
  g_return_val_if_fail(name != NULL, G_MAXUINT64);
549
550
0
  tmp = xb_node_get_attr(self, name);
551
0
  if (tmp == NULL)
552
0
    return G_MAXUINT64;
553
0
  if (g_str_has_prefix(tmp, "0x"))
554
0
    return g_ascii_strtoull(tmp + 2, NULL, 16);
555
0
  return g_ascii_strtoull(tmp, NULL, 10);
556
0
}
557
558
/**
559
 * xb_node_attr_iter_init:
560
 * @iter: an uninitialized #XbNodeAttrIter
561
 * @self: a #XbNode
562
 *
563
 * Initializes a name/value pair iterator for the node attributes
564
 * and associates it with @self.
565
 * The #XbNodeAttrIter structure is typically allocated on the stack
566
 * and does not need to be freed explicitly.
567
 *
568
 * Since: 0.3.4
569
 */
570
void
571
xb_node_attr_iter_init(XbNodeAttrIter *iter, XbNode *self)
572
0
{
573
0
  XbNodePrivate *priv = GET_PRIVATE(self);
574
0
  RealAttrIter *ri = (RealAttrIter *)iter;
575
576
0
  g_return_if_fail(iter != NULL);
577
0
  g_return_if_fail(XB_IS_NODE(self));
578
579
0
  ri->node = self;
580
0
  ri->position = priv->sn != NULL ? xb_silo_node_get_attr_count(priv->sn) : 0;
581
0
}
582
583
/**
584
 * xb_node_attr_iter_next:
585
 * @iter: an initialized #XbNodeAttrIter
586
 * @name: (out) (optional) (not nullable): Destination of the returned attribute name
587
 * @value: (out) (optional) (not nullable): Destination of the returned attribute value
588
 *
589
 * Returns the current attribute name and value and advances the iterator.
590
 * Example:
591
 * |[<!-- language="C" -->
592
 * XbNodeAttrIter iter;
593
 * const gchar *attr_name, *attr_value;
594
 *
595
 * xb_node_attr_iter_init (&iter, node);
596
 * while (xb_node_attr_iter_next (&iter, &attr_name, &attr_value)) {
597
 *     // use attr_name and attr_value; no need to free them
598
 * }
599
 * ]|
600
 *
601
 * Returns: %TRUE if there are more attributes.
602
 *
603
 * Since: 0.3.4
604
 */
605
gboolean
606
xb_node_attr_iter_next(XbNodeAttrIter *iter, const gchar **name, const gchar **value)
607
0
{
608
0
  XbSiloNodeAttr *a;
609
0
  XbNodePrivate *priv;
610
0
  RealAttrIter *ri = (RealAttrIter *)iter;
611
612
0
  g_return_val_if_fail(iter != NULL, FALSE);
613
0
  priv = GET_PRIVATE(ri->node);
614
615
  /* check if the iteration was finished */
616
0
  if (ri->position == 0) {
617
0
    if (name != NULL)
618
0
      *name = NULL;
619
0
    if (value != NULL)
620
0
      *value = NULL;
621
0
    return FALSE;
622
0
  }
623
624
0
  ri->position--;
625
0
  a = xb_silo_node_get_attr(priv->sn, ri->position);
626
0
  if (name != NULL)
627
0
    *name = xb_silo_from_strtab(priv->silo, a->attr_name, NULL);
628
0
  if (value != NULL)
629
0
    *value = xb_silo_from_strtab(priv->silo, a->attr_value, NULL);
630
631
0
  return TRUE;
632
0
}
633
634
/**
635
 * xb_node_get_depth:
636
 * @self: a #XbNode
637
 *
638
 * Gets the depth of the node to a root.
639
 *
640
 * Returns: a integer, where 0 is the root node itself.
641
 *
642
 * Since: 0.1.0
643
 **/
644
guint
645
xb_node_get_depth(XbNode *self)
646
0
{
647
0
  XbNodePrivate *priv = GET_PRIVATE(self);
648
0
  g_return_val_if_fail(XB_IS_NODE(self), 0);
649
0
  if (priv->sn == NULL)
650
0
    return 0;
651
0
  return xb_silo_get_node_depth(priv->silo, priv->sn);
652
0
}
653
654
/**
655
 * xb_node_export:
656
 * @self: a #XbNode
657
 * @flags: some #XbNodeExportFlags, e.g. #XB_NODE_EXPORT_FLAG_NONE
658
 * @error: the #GError, or %NULL
659
 *
660
 * Exports the node back to XML.
661
 *
662
 * Returns: XML data, or %NULL for an error
663
 *
664
 * Since: 0.1.0
665
 **/
666
gchar *
667
xb_node_export(XbNode *self, XbNodeExportFlags flags, GError **error)
668
0
{
669
0
  GString *xml;
670
0
  g_return_val_if_fail(XB_IS_NODE(self), NULL);
671
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
672
0
  xml = xb_silo_export_with_root(xb_node_get_silo(self), xb_node_get_sn(self), flags, error);
673
0
  if (xml == NULL)
674
0
    return NULL;
675
0
  return g_string_free(xml, FALSE);
676
0
}
677
678
/**
679
 * xb_node_transmogrify:
680
 * @self: a #XbNode
681
 * @func_text: (scope call): (allow-none): a #XbBuilderNodeTraverseFunc
682
 * @func_tail: (scope call): (allow-none): a #XbBuilderNodeTraverseFunc
683
 * @user_data: user pointer to pass to @func, or %NULL
684
 *
685
 * Traverses a tree starting from @self. It calls the given functions for each
686
 * node visited. This allows transmogrification of the source, for instance
687
 * converting the XML description to PangoMarkup or even something completely
688
 * different like markdown.
689
 *
690
 * The traversal can be halted at any point by returning TRUE from @func.
691
 *
692
 * Returns: %TRUE if all nodes were visited
693
 *
694
 * Since: 0.1.12
695
 **/
696
gboolean
697
xb_node_transmogrify(XbNode *self,
698
         XbNodeTransmogrifyFunc func_text,
699
         XbNodeTransmogrifyFunc func_tail,
700
         gpointer user_data)
701
0
{
702
0
  g_autoptr(XbNode) n = NULL;
703
704
0
  g_return_val_if_fail(XB_IS_NODE(self), FALSE);
705
706
  /* all siblings */
707
0
  n = g_object_ref(self);
708
0
  while (n != NULL) {
709
0
    g_autoptr(XbNode) c = NULL;
710
0
    g_autoptr(XbNode) tmp = NULL;
711
712
    /* head */
713
0
    if (func_text != NULL) {
714
0
      if (func_text(n, user_data))
715
0
        return FALSE;
716
0
    }
717
718
    /* all children */
719
0
    c = xb_node_get_child(n);
720
0
    if (c != NULL) {
721
0
      if (!xb_node_transmogrify(c, func_text, func_tail, user_data))
722
0
        return FALSE;
723
0
    }
724
725
    /* tail */
726
0
    if (func_tail != NULL) {
727
0
      if (func_tail(n, user_data))
728
0
        return FALSE;
729
0
    }
730
731
    /* next sibling */
732
0
    tmp = xb_node_get_next(n);
733
0
    g_set_object(&n, tmp);
734
0
  }
735
0
  return TRUE;
736
0
}
737
738
static void
739
xb_node_init(XbNode *self)
740
0
{
741
0
}
742
743
static void
744
xb_node_finalize(GObject *obj)
745
0
{
746
0
  G_OBJECT_CLASS(xb_node_parent_class)->finalize(obj);
747
0
}
748
749
static void
750
xb_node_class_init(XbNodeClass *klass)
751
0
{
752
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
753
0
  object_class->finalize = xb_node_finalize;
754
0
}
755
756
/**
757
 * xb_node_new: (skip)
758
 * @silo: A #XbSilo
759
 * @sn: A #XbSiloNode
760
 *
761
 * Creates a new node.
762
 *
763
 * Returns: a new #XbNode
764
 *
765
 * Since: 0.1.0
766
 **/
767
XbNode *
768
xb_node_new(XbSilo *silo, XbSiloNode *sn)
769
0
{
770
0
  XbNode *self = g_object_new(XB_TYPE_NODE, NULL);
771
0
  XbNodePrivate *priv = GET_PRIVATE(self);
772
0
  priv->silo = silo;
773
0
  priv->sn = sn;
774
0
  return self;
775
0
}