diff options
Diffstat (limited to 'liblogdiag/ld-library.c')
-rw-r--r-- | liblogdiag/ld-library.c | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/liblogdiag/ld-library.c b/liblogdiag/ld-library.c new file mode 100644 index 0000000..37e2bc9 --- /dev/null +++ b/liblogdiag/ld-library.c @@ -0,0 +1,524 @@ +/* + * ld-library.c + * + * This file is a part of logdiag. + * Copyright Přemysl Janouch 2010 - 2011. All rights reserved. + * + * See the file LICENSE for licensing information. + * + */ + +#include <string.h> + +#include "liblogdiag.h" +#include "config.h" + + +/** + * SECTION:ld-library + * @short_description: A symbol library. + * @see_also: #LdSymbol, #LdSymbolCategory + * + * #LdLibrary is used for loading symbols from their files. + */ + +/* + * LdLibraryPrivate: + * @lua: State of the scripting language. + * @children: Child objects of the library. + */ +struct _LdLibraryPrivate +{ + LdLua *lua; + GSList *children; +}; + +static void ld_library_finalize (GObject *gobject); + +static LdSymbolCategory *load_category (LdLibrary *self, + const gchar *path, const gchar *name); +static gboolean load_category_cb (const gchar *base, + const gchar *filename, gpointer userdata); +static void load_category_symbol_cb (LdSymbol *symbol, gpointer user_data); + +static gchar *read_human_name_from_file (const gchar *filename); + +static gboolean foreach_dir (const gchar *path, + gboolean (*callback) (const gchar *, const gchar *, gpointer), + gpointer userdata, GError **error); +static gboolean ld_library_load_cb + (const gchar *base, const gchar *filename, gpointer userdata); + + +G_DEFINE_TYPE (LdLibrary, ld_library, G_TYPE_OBJECT); + +static void +ld_library_class_init (LdLibraryClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = ld_library_finalize; + +/** + * LdLibrary::changed: + * @library: The library object. + * + * Contents of the library have changed. + */ + klass->changed_signal = g_signal_new + ("changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_type_class_add_private (klass, sizeof (LdLibraryPrivate)); +} + +static void +ld_library_init (LdLibrary *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE + (self, LD_TYPE_LIBRARY, LdLibraryPrivate); + + self->priv->lua = ld_lua_new (); + self->priv->children = NULL; +} + +static void +ld_library_finalize (GObject *gobject) +{ + LdLibrary *self; + + self = LD_LIBRARY (gobject); + + g_object_unref (self->priv->lua); + + g_slist_foreach (self->priv->children, (GFunc) g_object_unref, NULL); + g_slist_free (self->priv->children); + + /* Chain up to the parent class. */ + G_OBJECT_CLASS (ld_library_parent_class)->finalize (gobject); +} + +/** + * ld_library_new: + * + * Create an instance. + */ +LdLibrary * +ld_library_new (void) +{ + return g_object_new (LD_TYPE_LIBRARY, NULL); +} + +/* + * foreach_dir: + * + * Call a user-defined function for each file within a directory. + */ +static gboolean +foreach_dir (const gchar *path, + gboolean (*callback) (const gchar *, const gchar *, gpointer), + gpointer userdata, GError **error) +{ + GDir *dir; + const gchar *item; + + g_return_val_if_fail (path != NULL, FALSE); + g_return_val_if_fail (callback != NULL, FALSE); + + dir = g_dir_open (path, 0, error); + if (!dir) + return FALSE; + + while ((item = g_dir_read_name (dir))) + { + gchar *filename; + + filename = g_build_filename (path, item, NULL); + if (!callback (item, filename, userdata)) + break; + g_free (filename); + } + g_dir_close (dir); + return TRUE; +} + +/* + * LoadCategoryData: + * + * Data shared between load_category() and load_category_cb(). + */ +typedef struct +{ + LdLibrary *self; + LdSymbolCategory *cat; +} +LoadCategoryData; + +/* + * load_category: + * @self: An #LdLibrary object. + * @path: The path to the category. + * @name: The default name of the category. + * + * Loads a category into the library. + */ +static LdSymbolCategory * +load_category (LdLibrary *self, const gchar *path, const gchar *name) +{ + LdSymbolCategory *cat; + gchar *icon_file, *category_file; + gchar *human_name; + LoadCategoryData data; + + g_return_val_if_fail (LD_IS_LIBRARY (self), NULL); + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + if (!g_file_test (path, G_FILE_TEST_IS_DIR)) + goto load_category_fail_1; + + icon_file = g_build_filename (path, "icon.svg", NULL); + if (!g_file_test (icon_file, G_FILE_TEST_IS_REGULAR)) + { + g_warning ("The category in %s has no icon.", path); + goto load_category_fail_2; + } + + category_file = g_build_filename (path, "category.json", NULL); + human_name = read_human_name_from_file (category_file); + if (!human_name) + human_name = g_strdup (name); + + cat = ld_symbol_category_new (name, human_name); + ld_symbol_category_set_image_path (cat, icon_file); + + data.self = self; + data.cat = cat; + foreach_dir (path, load_category_cb, &data, NULL); + + g_free (human_name); + g_free (category_file); + g_free (icon_file); + return cat; + +load_category_fail_2: + g_free (icon_file); +load_category_fail_1: + return NULL; +} + +/* + * load_category_cb: + * + * Load script files from a directory into a symbol category. + */ +static gboolean +load_category_cb (const gchar *base, const gchar *filename, gpointer userdata) +{ + LoadCategoryData *data; + + g_return_val_if_fail (base != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (userdata != NULL, FALSE); + + data = (LoadCategoryData *) userdata; + + if (ld_lua_check_file (data->self->priv->lua, filename)) + ld_lua_load_file (data->self->priv->lua, filename, + load_category_symbol_cb, data->cat); + return TRUE; +} + +/* + * load_category_symbol_cb: + * + * Insert newly registered symbols into the category. + */ +static void +load_category_symbol_cb (LdSymbol *symbol, gpointer user_data) +{ + const gchar *name; + LdSymbolCategory *cat; + const GSList *children, *iter; + + g_return_if_fail (LD_IS_SYMBOL (symbol)); + g_return_if_fail (LD_IS_SYMBOL_CATEGORY (user_data)); + + cat = LD_SYMBOL_CATEGORY (user_data); + name = ld_symbol_get_name (symbol); + + /* Check for name collisions with other symbols. */ + children = ld_symbol_category_get_children (cat); + for (iter = children; iter; iter = iter->next) + { + if (!LD_IS_SYMBOL (iter->data)) + continue; + if (!strcmp (name, ld_symbol_get_name (LD_SYMBOL (iter->data)))) + { + g_warning ("Attempted to insert multiple '%s' symbols into" + " category '%s'.", name, ld_symbol_category_get_name (cat)); + return; + } + } + ld_symbol_category_insert_child (cat, G_OBJECT (symbol), -1); +} + +/* + * read_human_name_from_file: + * @filename: Location of the JSON file. + * + * Read the human name of the processed category. + */ +static gchar * +read_human_name_from_file (const gchar *filename) +{ + const gchar *const *lang; + JsonParser *parser; + JsonNode *root; + JsonObject *object; + GError *error; + + g_return_val_if_fail (filename != NULL, NULL); + + parser = json_parser_new (); + error = NULL; + if (!json_parser_load_from_file (parser, filename, &error)) + { + g_warning ("%s", error->message); + g_error_free (error); + goto read_human_name_from_file_end; + } + + root = json_parser_get_root (parser); + if (!JSON_NODE_HOLDS_OBJECT (root)) + { + g_warning ("Failed to parse '%s': %s", filename, + "The root node is not an object."); + goto read_human_name_from_file_end; + } + + object = json_node_get_object (root); + for (lang = g_get_language_names (); *lang; lang++) + { + const gchar *member; + + if (!json_object_has_member (object, *lang)) + continue; + member = json_object_get_string_member (object, *lang); + + if (member != NULL) + { + gchar *result; + + result = g_strdup (member); + g_object_unref (parser); + return result; + } + } + +read_human_name_from_file_end: + g_object_unref (parser); + return NULL; +} + +/* + * LibraryLoadData: + * + * Data shared between ld_library_load() and ld_library_load_cb(). + */ +typedef struct +{ + LdLibrary *self; + gboolean changed; +} +LibraryLoadData; + +/** + * ld_library_load: + * @self: An #LdLibrary object. + * @directory: A directory to be loaded. + * + * Load the contents of a directory into the library. + */ +gboolean +ld_library_load (LdLibrary *self, const gchar *directory) +{ + LibraryLoadData data; + + g_return_val_if_fail (LD_IS_LIBRARY (self), FALSE); + g_return_val_if_fail (directory != NULL, FALSE); + + data.self = self; + data.changed = FALSE; + foreach_dir (directory, ld_library_load_cb, &data, NULL); + + if (data.changed) + g_signal_emit (self, LD_LIBRARY_GET_CLASS (self)->changed_signal, 0); + + return TRUE; +} + +/* + * ld_library_load_cb: + * + * A callback that's called for each file in the root directory. + */ +static gboolean +ld_library_load_cb (const gchar *base, const gchar *filename, gpointer userdata) +{ + LdSymbolCategory *cat; + LibraryLoadData *data; + + g_return_val_if_fail (base != NULL, FALSE); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (userdata != NULL, FALSE); + + data = (LibraryLoadData *) userdata; + + cat = load_category (data->self, filename, base); + if (cat) + ld_library_insert_child (data->self, G_OBJECT (cat), -1); + + data->changed = TRUE; + return TRUE; +} + +/** + * ld_library_find_symbol: + * @self: An #LdLibrary object. + * @identifier: An identifier of the symbol to be searched for. + * + * Search for a symbol in the library. + * + * Return value: A symbol object if found, NULL otherwise. + */ +/* XXX: With this level of indentation, this function is really ugly. */ +LdSymbol * +ld_library_find_symbol (LdLibrary *self, const gchar *identifier) +{ + gchar **id_el_start, **id_el; + const GSList *list, *list_el; + + g_return_val_if_fail (LD_IS_LIBRARY (self), NULL); + g_return_val_if_fail (identifier != NULL, NULL); + + id_el_start = g_strsplit (identifier, LD_LIBRARY_IDENTIFIER_SEPARATOR, 0); + if (!id_el_start) + return NULL; + + list = ld_library_get_children (self); + for (id_el = id_el_start; id_el[0]; id_el++) + { + LdSymbolCategory *cat; + LdSymbol *symbol; + gboolean found = FALSE; + + for (list_el = list; list_el; list_el = g_slist_next (list_el)) + { + /* If the current identifier element is a category (not last) + * and this list element is a category. + */ + if (id_el[1] && LD_IS_SYMBOL_CATEGORY (list_el->data)) + { + cat = LD_SYMBOL_CATEGORY (list_el->data); + if (strcmp (id_el[0], ld_symbol_category_get_name (cat))) + continue; + + list = ld_symbol_category_get_children (cat); + found = TRUE; + break; + } + /* If the current identifier element is a symbol (last) + * and this list element is a symbol. + */ + else if (!id_el[1] && LD_IS_SYMBOL (list_el->data)) + { + symbol = LD_SYMBOL (list_el->data); + if (strcmp (id_el[0], ld_symbol_get_name (symbol))) + continue; + + g_strfreev (id_el_start); + return symbol; + } + } + + if (!found) + break; + } + g_strfreev (id_el_start); + return NULL; +} + +/** + * ld_library_clear: + * @self: An #LdLibrary object. + * + * Clear all the contents. + */ +void +ld_library_clear (LdLibrary *self) +{ + g_return_if_fail (LD_IS_LIBRARY (self)); + + g_slist_foreach (self->priv->children, (GFunc) g_object_unref, NULL); + g_slist_free (self->priv->children); + self->priv->children = NULL; + + g_signal_emit (self, + LD_LIBRARY_GET_CLASS (self)->changed_signal, 0); +} + +/** + * ld_library_insert_child: + * @self: An #LdLibrary object. + * @child: The child to be inserted. + * @pos: The position at which the child will be inserted. + * Negative values will append to the end of list. + * + * Insert a child into the library. + */ +void +ld_library_insert_child (LdLibrary *self, GObject *child, gint pos) +{ + g_return_if_fail (LD_IS_LIBRARY (self)); + g_return_if_fail (G_IS_OBJECT (child)); + + g_object_ref (child); + self->priv->children = g_slist_insert (self->priv->children, child, pos); +} + +/** + * ld_library_remove_child: + * @self: An #LdLibrary object. + * @child: The child to be removed. + * + * Remove a child from the library. + */ +void +ld_library_remove_child (LdLibrary *self, GObject *child) +{ + g_return_if_fail (LD_IS_LIBRARY (self)); + g_return_if_fail (G_IS_OBJECT (child)); + + if (g_slist_find (self->priv->children, child)) + { + g_object_unref (child); + self->priv->children = g_slist_remove (self->priv->children, child); + } +} + +/** + * ld_library_get_children: + * @self: An #LdLibrary object. + * + * Return value: The internal list of children. Do not modify. + */ +const GSList * +ld_library_get_children (LdLibrary *self) +{ + g_return_val_if_fail (LD_IS_LIBRARY (self), NULL); + return self->priv->children; +} + |