/* * Copyright (C) 2018 Richard Hughes * * SPDX-License-Identifier: LGPL-2.1+ */ #define G_LOG_DOMAIN "XbNode" #include "config.h" #include #include #include "xb-node-private.h" #include "xb-silo-export-private.h" typedef struct { GObject parent_instance; XbSilo *silo; XbSiloNode *sn; } XbNodePrivate; G_DEFINE_TYPE_WITH_PRIVATE (XbNode, xb_node, G_TYPE_OBJECT) #define GET_PRIVATE(o) (xb_node_get_instance_private (o)) /** * xb_node_get_data: * @self: a #XbNode * @key: a string key, e.g. `fwupd::RemoteId` * * Gets any data that has been set on the node using xb_node_set_data(). * * Returns: (transfer none): a #GBytes, or %NULL if not found * * Since: 0.1.0 **/ GBytes * xb_node_get_data (XbNode *self, const gchar *key) { g_return_val_if_fail (XB_IS_NODE (self), NULL); g_return_val_if_fail (key != NULL, NULL); return g_object_get_data (G_OBJECT (self), key); } /** * xb_node_set_data: * @self: a #XbNode * @key: a string key, e.g. `fwupd::RemoteId` * @data: a #GBytes * * Sets some data on the node which can be retrieved using xb_node_get_data(). * * Since: 0.1.0 **/ void xb_node_set_data (XbNode *self, const gchar *key, GBytes *data) { g_return_if_fail (XB_IS_NODE (self)); g_return_if_fail (key != NULL); g_return_if_fail (data != NULL); g_object_set_data_full (G_OBJECT (self), key, g_bytes_ref (data), (GDestroyNotify) g_bytes_unref); } /** * xb_node_get_sn: (skip) * @self: a #XbNode * * Gets the #XbSiloNode for the node. * * Returns: (transfer none): a #XbSiloNode * * Since: 0.1.0 **/ XbSiloNode * xb_node_get_sn (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); return priv->sn; } /** * xb_node_get_silo: (skip) * @self: a #XbNode * * Gets the #XbSilo for the node. * * Returns: (transfer none): a #XbSilo * * Since: 0.1.0 **/ XbSilo * xb_node_get_silo (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); return priv->silo; } /** * xb_node_get_root: * @self: a #XbNode * * Gets the root node for the node. * * Returns: (transfer full): a #XbNode, or %NULL * * Since: 0.1.0 **/ XbNode * xb_node_get_root (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); XbSiloNode *sn; g_return_val_if_fail (XB_IS_NODE (self), NULL); sn = xb_silo_get_sroot (priv->silo); if (sn == NULL) return NULL; return xb_silo_node_create (priv->silo, sn); } /** * xb_node_get_parent: * @self: a #XbNode * * Gets the parent node for the current node. * * Returns: (transfer full): a #XbNode, or %NULL * * Since: 0.1.0 **/ XbNode * xb_node_get_parent (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); XbSiloNode *sn; g_return_val_if_fail (XB_IS_NODE (self), NULL); sn = xb_silo_node_get_parent (priv->silo, priv->sn); if (sn == NULL) return NULL; return xb_silo_node_create (priv->silo, sn); } /** * xb_node_get_next: * @self: a #XbNode * * Gets the next sibling node for the current node. * * Returns: (transfer full): a #XbNode, or %NULL * * Since: 0.1.0 **/ XbNode * xb_node_get_next (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); XbSiloNode *sn; g_return_val_if_fail (XB_IS_NODE (self), NULL); sn = xb_silo_node_get_next (priv->silo, priv->sn); if (sn == NULL) return NULL; return xb_silo_node_create (priv->silo, sn); } /** * xb_node_get_child: * @self: a #XbNode * * Gets the first child node for the current node. * * Returns: (transfer full): a #XbNode, or %NULL * * Since: 0.1.0 **/ XbNode * xb_node_get_child (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); XbSiloNode *sn; g_return_val_if_fail (XB_IS_NODE (self), NULL); sn = xb_silo_node_get_child (priv->silo, priv->sn); if (sn == NULL) return NULL; return xb_silo_node_create (priv->silo, sn); } /** * xb_node_get_children: * @self: a #XbNode * * Gets all the children for the current node. * * Returns: (transfer container) (element-type XbNode): an array of children * * Since: 0.1.0 **/ GPtrArray * xb_node_get_children (XbNode *self) { XbNode *n; GPtrArray *array = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); /* add all children */ n = xb_node_get_child (self); while (n != NULL) { g_ptr_array_add (array, n); n = xb_node_get_next (n); } return array; } /** * xb_node_get_text: * @self: a #XbNode * * Gets the text data for a specific node. * * Returns: a string, or %NULL for unset * * Since: 0.1.0 **/ const gchar * xb_node_get_text (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (XB_IS_NODE (self), NULL); return xb_silo_node_get_text (priv->silo, priv->sn); } /** * xb_node_get_text_as_uint: * @self: a #XbNode * * Gets some attribute text data for a specific node. * * Returns: a guint64, or %G_MAXUINT64 if unfound * * Since: 0.1.0 **/ guint64 xb_node_get_text_as_uint (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); const gchar *tmp; g_return_val_if_fail (XB_IS_NODE (self), G_MAXUINT64); tmp = xb_silo_node_get_text (priv->silo, priv->sn);; if (tmp == NULL) return G_MAXUINT64; if (g_str_has_prefix (tmp, "0x")) return g_ascii_strtoull (tmp + 2, NULL, 16); return g_ascii_strtoull (tmp, NULL, 10); } /** * xb_node_get_tail: * @self: a #XbNode * * Gets the tail data for a specific node. * * Returns: a string, or %NULL for unset * * Since: 0.1.12 **/ const gchar * xb_node_get_tail (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (XB_IS_NODE (self), NULL); return xb_silo_node_get_tail (priv->silo, priv->sn); } /** * xb_node_get_element: * @self: a #XbNode * * Gets the element name for a specific node. * * Returns: a string, or %NULL for unset * * Since: 0.1.0 **/ const gchar * xb_node_get_element (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (XB_IS_NODE (self), NULL); return xb_silo_node_get_element (priv->silo, priv->sn); } /** * xb_node_get_attr: * @self: a #XbNode * @name: an attribute name, e.g. "type" * * Gets some attribute text data for a specific node. * * Returns: a string, or %NULL for unset * * Since: 0.1.0 **/ const gchar * xb_node_get_attr (XbNode *self, const gchar *name) { XbNodePrivate *priv = GET_PRIVATE (self); XbSiloAttr *a; g_return_val_if_fail (XB_IS_NODE (self), NULL); g_return_val_if_fail (name != NULL, NULL); a = xb_silo_node_get_attr_by_str (priv->silo, priv->sn, name); if (a == NULL) return NULL; return xb_silo_from_strtab (priv->silo, a->attr_value); } /** * xb_node_get_attr_as_uint: * @self: a #XbNode * @name: an attribute name, e.g. `type` * * Gets some attribute text data for a specific node. * * Returns: a guint64, or %G_MAXUINT64 if unfound * * Since: 0.1.0 **/ guint64 xb_node_get_attr_as_uint (XbNode *self, const gchar *name) { const gchar *tmp; g_return_val_if_fail (XB_IS_NODE (self), G_MAXUINT64); g_return_val_if_fail (name != NULL, G_MAXUINT64); tmp = xb_node_get_attr (self, name); if (tmp == NULL) return G_MAXUINT64; if (g_str_has_prefix (tmp, "0x")) return g_ascii_strtoull (tmp + 2, NULL, 16); return g_ascii_strtoull (tmp, NULL, 10); } /** * xb_node_get_depth: * @self: a #XbNode * * Gets the depth of the node to a root. * * Returns: a integer, where 0 is the root node iself. * * Since: 0.1.0 **/ guint xb_node_get_depth (XbNode *self) { XbNodePrivate *priv = GET_PRIVATE (self); g_return_val_if_fail (XB_IS_NODE (self), 0); return xb_silo_node_get_depth (priv->silo, priv->sn); } /** * xb_node_export: * @self: a #XbNode * @flags: some #XbNodeExportFlags, e.g. #XB_NODE_EXPORT_FLAG_NONE * @error: the #GError, or %NULL * * Exports the node back to XML. * * Returns: XML data, or %NULL for an error * * Since: 0.1.0 **/ gchar * xb_node_export (XbNode *self, XbNodeExportFlags flags, GError **error) { GString *xml; g_return_val_if_fail (XB_IS_NODE (self), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); xml = xb_silo_export_with_root (xb_node_get_silo (self), self, flags, error); if (xml == NULL) return NULL; return g_string_free (xml, FALSE); } /** * xb_node_transmogrify: * @self: a #XbNode * @func_text: (scope call): (allow-none): a #XbBuilderNodeTraverseFunc * @func_tail: (scope call): (allow-none): a #XbBuilderNodeTraverseFunc * @user_data: user pointer to pass to @func, or %NULL * * Traverses a tree starting from @self. It calls the given functions for each * node visited. This allows transmogrification of the source, for instance * converting the XML description to PangoMarkup or even something completely * different like markdown. * * The traversal can be halted at any point by returning TRUE from @func. * * Returns: %TRUE if all nodes were visited * * Since: 0.1.12 **/ gboolean xb_node_transmogrify (XbNode *self, XbNodeTransmogrifyFunc func_text, XbNodeTransmogrifyFunc func_tail, gpointer user_data) { g_autoptr(XbNode) n = NULL; g_return_val_if_fail (XB_IS_NODE (self), FALSE); /* all siblings */ n = g_object_ref (self); while (n != NULL) { g_autoptr(XbNode) c = NULL; g_autoptr(XbNode) tmp = NULL; /* head */ if (func_text != NULL) { if (func_text (n, user_data)) return FALSE; } /* all children */ c = xb_node_get_child (n); if (c != NULL) { if (!xb_node_transmogrify (c, func_text, func_tail, user_data)) return FALSE; } /* tail */ if (func_tail != NULL) { if (func_tail (n, user_data)) return FALSE; } /* next sibling */ tmp = xb_node_get_next (n); g_set_object (&n, tmp); } return TRUE; } static void xb_node_init (XbNode *self) { } static void xb_node_finalize (GObject *obj) { G_OBJECT_CLASS (xb_node_parent_class)->finalize (obj); } static void xb_node_class_init (XbNodeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = xb_node_finalize; } /** * xb_node_new: (skip) * @silo: A #XbSilo * @sn: A #XbSiloNode * * Creates a new node. * * Returns: a new #XbNode * * Since: 0.1.0 **/ XbNode * xb_node_new (XbSilo *silo, XbSiloNode *sn) { XbNode *self = g_object_new (XB_TYPE_NODE, NULL); XbNodePrivate *priv = GET_PRIVATE (self); priv->silo = silo; priv->sn = sn; return self; }