[tepl] MetadataStore: start to create class
- From: Sébastien Wilmet <swilmet src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tepl] MetadataStore: start to create class
- Date: Sat, 18 Apr 2020 17:20:07 +0000 (UTC)
commit 2b14b8cfd81423cbfd622564767c4e84af5e5544
Author: Sébastien Wilmet <swilmet gnome org>
Date: Thu Apr 16 23:36:49 2020 +0200
MetadataStore: start to create class
docs/reference/tepl-docs.xml | 5 +
docs/reference/tepl-sections.txt | 16 ++
po/POTFILES.in | 2 +
tepl/meson.build | 4 +
tepl/tepl-metadata-store-loader.c | 380 ++++++++++++++++++++++++++++++++++++++
tepl/tepl-metadata-store-loader.h | 34 ++++
tepl/tepl-metadata-store.c | 304 ++++++++++++++++++++++++++++++
tepl/tepl-metadata-store.h | 86 +++++++++
tepl/tepl.h | 1 +
9 files changed, 832 insertions(+)
---
diff --git a/docs/reference/tepl-docs.xml b/docs/reference/tepl-docs.xml
index 73222f9..3a00cdb 100644
--- a/docs/reference/tepl-docs.xml
+++ b/docs/reference/tepl-docs.xml
@@ -49,6 +49,11 @@
<xi:include href="xml/io-error-info-bars.xml"/>
</chapter>
+ <chapter id="file-metadata">
+ <title>File Metadata</title>
+ <xi:include href="xml/metadata-store.xml"/>
+ </chapter>
+
<chapter id="code-folding">
<title>Code Folding</title>
<xi:include href="xml/fold-region.xml"/>
diff --git a/docs/reference/tepl-sections.txt b/docs/reference/tepl-sections.txt
index 985b5d0..eb65833 100644
--- a/docs/reference/tepl-sections.txt
+++ b/docs/reference/tepl-sections.txt
@@ -290,6 +290,22 @@ tepl_iter_get_line_indentation
tepl_menu_shell_append_edit_actions
</SECTION>
+<SECTION>
+<FILE>metadata-store</FILE>
+TeplMetadataStore
+tepl_metadata_store_get_singleton
+<SUBSECTION Standard>
+TEPL_IS_METADATA_STORE
+TEPL_IS_METADATA_STORE_CLASS
+TEPL_METADATA_STORE
+TEPL_METADATA_STORE_CLASS
+TEPL_METADATA_STORE_GET_CLASS
+TEPL_TYPE_METADATA_STORE
+TeplMetadataStoreClass
+TeplMetadataStorePrivate
+tepl_metadata_store_get_type
+</SECTION>
+
<SECTION>
<FILE>tab</FILE>
TeplTab
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6747813..35f7f07 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,6 +19,8 @@ tepl/tepl-init.c
tepl/tepl-io-error-info-bars.c
tepl/tepl-iter.c
tepl/tepl-menu-shell.c
+tepl/tepl-metadata-store.c
+tepl/tepl-metadata-store-loader.c
tepl/tepl-notebook.c
tepl/tepl-signal-group.c
tepl/tepl-tab.c
diff --git a/tepl/meson.build b/tepl/meson.build
index da9d786..59fd1be 100644
--- a/tepl/meson.build
+++ b/tepl/meson.build
@@ -17,6 +17,7 @@ tepl_public_headers = [
'tepl-io-error-info-bars.h',
'tepl-iter.h',
'tepl-menu-shell.h',
+ 'tepl-metadata-store.h',
'tepl-notebook.h',
'tepl-tab.h',
'tepl-tab-group.h',
@@ -43,6 +44,7 @@ tepl_public_c_files = [
'tepl-io-error-info-bars.c',
'tepl-iter.c',
'tepl-menu-shell.c',
+ 'tepl-metadata-store.c',
'tepl-notebook.c',
'tepl-tab.c',
'tepl-tab-group.c',
@@ -59,6 +61,7 @@ TEPL_PRIVATE_HEADERS = [
'tepl-file-content.h',
'tepl-file-content-loader.h',
'tepl-io-error-info-bar.h',
+ 'tepl-metadata-store-loader.h',
'tepl-progress-info-bar.h',
'tepl-signal-group.h',
'tepl-tab-saving.h'
@@ -71,6 +74,7 @@ tepl_private_c_files = [
'tepl-file-content.c',
'tepl-file-content-loader.c',
'tepl-io-error-info-bar.c',
+ 'tepl-metadata-store-loader.c',
'tepl-progress-info-bar.c',
'tepl-signal-group.c',
'tepl-tab-saving.c'
diff --git a/tepl/tepl-metadata-store-loader.c b/tepl/tepl-metadata-store-loader.c
new file mode 100644
index 0000000..cfbf091
--- /dev/null
+++ b/tepl/tepl-metadata-store-loader.c
@@ -0,0 +1,380 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2020 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * Tepl is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "tepl-metadata-store-loader.h"
+#include <glib/gi18n-lib.h>
+#include "tepl-file-metadata.h"
+#include "tepl-utils.h"
+
+typedef struct _ParsingData ParsingData;
+struct _ParsingData
+{
+ GHashTable *hash_table;
+
+ gchar *cur_document_uri;
+ TeplFileMetadata *cur_file_metadata;
+
+ guint metadata_element_open : 1;
+ guint document_element_open : 1;
+};
+
+static ParsingData *
+parsing_data_new (GHashTable *hash_table)
+{
+ ParsingData *parsing_data;
+
+ parsing_data = g_new0 (ParsingData, 1);
+ parsing_data->hash_table = g_hash_table_ref (hash_table);
+
+ return parsing_data;
+}
+
+static void
+parsing_data_check_invariants (ParsingData *parsing_data)
+{
+ if (!parsing_data->metadata_element_open)
+ {
+ g_assert (!parsing_data->document_element_open);
+ g_assert (parsing_data->cur_document_uri == NULL);
+ g_assert (parsing_data->cur_file_metadata == NULL);
+ return;
+ }
+
+ if (!parsing_data->document_element_open)
+ {
+ g_assert (parsing_data->cur_document_uri == NULL);
+ g_assert (parsing_data->cur_file_metadata == NULL);
+ return;
+ }
+
+ g_assert (parsing_data->cur_document_uri != NULL);
+ g_assert (parsing_data->cur_file_metadata != NULL);
+}
+
+static void
+parsing_data_free (ParsingData *parsing_data)
+{
+ if (parsing_data != NULL)
+ {
+ g_hash_table_unref (parsing_data->hash_table);
+ g_free (parsing_data->cur_document_uri);
+ g_clear_object (&parsing_data->cur_file_metadata);
+
+ g_free (parsing_data);
+ }
+}
+
+/* <metadata> */
+static void
+parse_metadata_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ ParsingData *parsing_data,
+ GError **error)
+{
+ g_assert (!parsing_data->metadata_element_open);
+
+ if (!g_str_equal (element_name, "metadata"))
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ /* Translators: do not translate <metadata>. */
+ _("The XML file must start with a <metadata> element, not “%s”."),
+ element_name);
+ return;
+ }
+
+ parsing_data->metadata_element_open = TRUE;
+}
+
+/* <document uri="..." atime="..."> */
+static void
+parse_document_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParsingData *parsing_data,
+ GError **error)
+{
+ gboolean got_uri = FALSE;
+ gboolean got_atime = FALSE;
+ gint attr_num;
+
+ g_assert (parsing_data->metadata_element_open);
+ g_assert (!parsing_data->document_element_open);
+ g_assert (parsing_data->cur_document_uri == NULL);
+ g_assert (parsing_data->cur_file_metadata == NULL);
+
+ if (!g_str_equal (element_name, "document"))
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ /* Translators: do not translate <document>. */
+ _("Expected a <document> element, got “%s” instead."),
+ element_name);
+ return;
+ }
+
+ parsing_data->cur_file_metadata = tepl_file_metadata_new ();
+
+ for (attr_num = 0; attribute_names[attr_num] != NULL; attr_num++)
+ {
+ const gchar *cur_attr_name = attribute_names[attr_num];
+ const gchar *cur_attr_value = attribute_values[attr_num];
+
+ if (!got_uri && g_str_equal (cur_attr_name, "uri"))
+ {
+ parsing_data->cur_document_uri = g_strdup (cur_attr_value);
+ got_uri = TRUE;
+ }
+ else if (!got_atime && g_str_equal (cur_attr_name, "atime"))
+ {
+ if (!_tepl_file_metadata_set_atime_str (parsing_data->cur_file_metadata,
+ cur_attr_value))
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ /* Translators: do not translate “atime”. */
+ _("Failed to parse the “atime” attribute value “%s”."),
+ cur_attr_value);
+ return;
+ }
+
+ got_atime = TRUE;
+ }
+ }
+
+ if (!got_uri || !got_atime)
+ {
+ g_set_error_literal (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ /* Translators: do not translate <document>, “uri” and “atime”. */
+ _("The <document> element must contain the “uri” and “atime”
attributes."));
+ }
+
+ parsing_data->document_element_open = TRUE;
+}
+
+/* <entry key="..." value="..." /> */
+static void
+parse_entry_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ ParsingData *parsing_data,
+ GError **error)
+{
+ const gchar *key = NULL;
+ const gchar *value = NULL;
+ gint attr_num;
+
+ g_assert (parsing_data->metadata_element_open);
+ g_assert (parsing_data->document_element_open);
+ g_assert (parsing_data->cur_file_metadata != NULL);
+
+ if (!g_str_equal (element_name, "entry"))
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ /* Translators: do not translate <entry>. */
+ _("Expected an <entry> element, got “%s” instead."),
+ element_name);
+ return;
+ }
+
+ for (attr_num = 0; attribute_names[attr_num] != NULL; attr_num++)
+ {
+ const gchar *cur_attr_name = attribute_names[attr_num];
+ const gchar *cur_attr_value = attribute_values[attr_num];
+
+ if (key == NULL && g_str_equal (cur_attr_name, "key"))
+ {
+ key = cur_attr_value;
+ }
+ else if (value == NULL && g_str_equal (cur_attr_name, "value"))
+ {
+ value = cur_attr_value;
+ }
+ }
+
+ if (key == NULL || value == NULL)
+ {
+ g_set_error_literal (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_MISSING_ATTRIBUTE,
+ /* Translators: do not translate <entry>, “key” and “value”. */
+ _("The <entry> element is missing the “key” or “value” attribute."));
+ return;
+ }
+
+ _tepl_file_metadata_insert_entry (parsing_data->cur_file_metadata, key, value);
+}
+
+static void
+parser_start_element_cb (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParsingData *parsing_data = user_data;
+
+ g_return_if_fail (element_name != NULL);
+
+ parsing_data_check_invariants (parsing_data);
+
+ /* <metadata> */
+ if (!parsing_data->metadata_element_open)
+ {
+ parse_metadata_element (context,
+ element_name,
+ parsing_data,
+ error);
+ return;
+ }
+
+ /* <document uri="..." atime="..."> */
+ if (!parsing_data->document_element_open)
+ {
+ parse_document_element (context,
+ element_name,
+ attribute_names,
+ attribute_values,
+ parsing_data,
+ error);
+ return;
+ }
+
+ /* <entry key="..." value="..." /> */
+ parse_entry_element (context,
+ element_name,
+ attribute_names,
+ attribute_values,
+ parsing_data,
+ error);
+}
+
+static void
+insert_document_to_hash_table (ParsingData *parsing_data)
+{
+ g_assert (parsing_data->document_element_open);
+ parsing_data_check_invariants (parsing_data);
+
+ g_hash_table_replace (parsing_data->hash_table,
+ g_file_new_for_uri (parsing_data->cur_document_uri),
+ parsing_data->cur_file_metadata);
+
+ g_free (parsing_data->cur_document_uri);
+ parsing_data->cur_document_uri = NULL;
+ parsing_data->cur_file_metadata = NULL;
+
+ parsing_data->document_element_open = FALSE;
+}
+
+static void
+parser_end_element_cb (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParsingData *parsing_data = user_data;
+
+ g_return_if_fail (element_name != NULL);
+
+ /* </document> */
+ if (g_str_equal (element_name, "document"))
+ {
+ g_return_if_fail (parsing_data->document_element_open);
+ insert_document_to_hash_table (parsing_data);
+ }
+}
+
+static void
+parse_xml_file_content (GBytes *xml_file_bytes,
+ GHashTable *hash_table,
+ GError **error)
+{
+ GMarkupParser parser = { parser_start_element_cb, parser_end_element_cb, NULL, NULL, NULL };
+ GMarkupParseContext *parse_context;
+ ParsingData *parsing_data;
+ gboolean ok;
+
+ parsing_data = parsing_data_new (hash_table);
+ parse_context = g_markup_parse_context_new (&parser, 0, parsing_data, NULL);
+ ok = g_markup_parse_context_parse (parse_context,
+ g_bytes_get_data (xml_file_bytes, NULL),
+ g_bytes_get_size (xml_file_bytes),
+ error);
+ if (ok)
+ {
+ g_markup_parse_context_end_parse (parse_context, error);
+ }
+
+ g_markup_parse_context_free (parse_context);
+ parsing_data_free (parsing_data);
+}
+
+gboolean
+_tepl_metadata_store_loader (GFile *from_file,
+ GHashTable *hash_table,
+ GError **error)
+{
+ GBytes *bytes;
+ GError *my_error = NULL;
+ gboolean ok = TRUE;
+
+ g_return_val_if_fail (G_IS_FILE (from_file), FALSE);
+ g_return_val_if_fail (hash_table != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ bytes = g_file_load_bytes (from_file, NULL, NULL, &my_error);
+
+ /* If the XML file has not yet been created, e.g. on the first run of
+ * the application.
+ */
+ if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&my_error);
+ goto out;
+ }
+
+ if (my_error != NULL)
+ {
+ g_propagate_error (error, my_error);
+ ok = FALSE;
+ goto out;
+ }
+
+ parse_xml_file_content (bytes, hash_table, &my_error);
+ if (my_error != NULL)
+ {
+ g_propagate_error (error, my_error);
+ ok = FALSE;
+ }
+
+out:
+ g_bytes_unref (bytes);
+ return ok;
+}
diff --git a/tepl/tepl-metadata-store-loader.h b/tepl/tepl-metadata-store-loader.h
new file mode 100644
index 0000000..fcde6cc
--- /dev/null
+++ b/tepl/tepl-metadata-store-loader.h
@@ -0,0 +1,34 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2020 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * Tepl is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_METADATA_STORE_LOADER_H
+#define TEPL_METADATA_STORE_LOADER_H
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL
+gboolean _tepl_metadata_store_loader (GFile *from_file,
+ GHashTable *hash_table,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* TEPL_METADATA_STORE_LOADER_H */
diff --git a/tepl/tepl-metadata-store.c b/tepl/tepl-metadata-store.c
new file mode 100644
index 0000000..f1cc515
--- /dev/null
+++ b/tepl/tepl-metadata-store.c
@@ -0,0 +1,304 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2020 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * Tepl is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tepl-metadata-store.h"
+#include "tepl-metadata-store-loader.h"
+#include "tepl-utils.h"
+
+/**
+ * SECTION:metadata-store
+ * @Title: TeplMetadataStore
+ * @Short_description: To store file metadata on disk
+ */
+
+/* This code is inspired by TeplMetadataManager, which was itself a modified
+ * version of GeditMetadataManager, coming from gedit:
+ *
+ * Copyright 2003-2007 - Paolo Maggi
+ *
+ * The XML format is the same. TeplMetadataStore can read a file generated by
+ * TeplMetadataManager; but the reverse is maybe not true, it hasn't been
+ * tested. Not tested either with the older GeditMetadataManager.
+ *
+ * A better implementation would be to use a database, so that several processes
+ * can read and write to it at the same time, to be able to share metadata
+ * between apps.
+ */
+
+struct _TeplMetadataStorePrivate
+{
+ /* Keys: GFile *
+ * Values: TeplFileMetadata *
+ * Never NULL.
+ */
+ GHashTable *hash_table;
+
+ guint modified : 1;
+};
+
+/* TeplMetadataStore is a singleton. */
+static TeplMetadataStore *singleton = NULL;
+
+#define DEFAULT_MAX_NUMBER_OF_LOCATIONS (1000)
+
+G_DEFINE_TYPE_WITH_PRIVATE (TeplMetadataStore, tepl_metadata_store, G_TYPE_OBJECT)
+
+static void
+tepl_metadata_store_finalize (GObject *object)
+{
+ TeplMetadataStore *store = TEPL_METADATA_STORE (object);
+
+ if (singleton == store)
+ {
+ singleton = NULL;
+ }
+
+ g_hash_table_unref (store->priv->hash_table);
+
+ G_OBJECT_CLASS (tepl_metadata_store_parent_class)->finalize (object);
+}
+
+static void
+tepl_metadata_store_class_init (TeplMetadataStoreClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tepl_metadata_store_finalize;
+}
+
+static void
+tepl_metadata_store_init (TeplMetadataStore *store)
+{
+ store->priv = tepl_metadata_store_get_instance_private (store);
+
+ store->priv->hash_table = g_hash_table_new_full (g_file_hash,
+ (GEqualFunc) g_file_equal,
+ g_object_unref,
+ g_object_unref);
+}
+
+/**
+ * tepl_metadata_store_get_singleton:
+ *
+ * Returns: (transfer none): the #TeplMetadataStore singleton instance.
+ * Since: 5.0
+ */
+TeplMetadataStore *
+tepl_metadata_store_get_singleton (void)
+{
+ if (singleton == NULL)
+ {
+ singleton = g_object_new (TEPL_TYPE_METADATA_STORE, NULL);
+ }
+
+ return singleton;
+}
+
+void
+_tepl_metadata_store_unref_singleton (void)
+{
+ if (singleton != NULL)
+ {
+ g_object_unref (singleton);
+ }
+
+ /* singleton is not set to NULL here, it is set to NULL in
+ * tepl_metadata_store_finalize() (i.e. when we are sure that the ref
+ * count reaches 0).
+ */
+}
+
+void
+tepl_metadata_store_trim (TeplMetadataStore *store,
+ gint max_number_of_locations)
+{
+ guint my_max_number_of_locations;
+
+ g_return_if_fail (TEPL_IS_METADATA_STORE (store));
+ g_return_if_fail (max_number_of_locations >= -1);
+
+ if (max_number_of_locations == -1)
+ {
+ my_max_number_of_locations = DEFAULT_MAX_NUMBER_OF_LOCATIONS;
+ }
+ else
+ {
+ my_max_number_of_locations = max_number_of_locations;
+ }
+
+ while (g_hash_table_size (store->priv->hash_table) > my_max_number_of_locations)
+ {
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+ GFile *oldest_location = NULL;
+ TeplFileMetadata *oldest_file_metadata = NULL;
+
+ g_hash_table_iter_init (&iter, store->priv->hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ GFile *location = key;
+ TeplFileMetadata *file_metadata = value;
+
+ if (oldest_location == NULL ||
+ _tepl_file_metadata_compare_atime (file_metadata, oldest_file_metadata) < 0)
+ {
+ oldest_location = location;
+ oldest_file_metadata = file_metadata;
+ }
+ }
+
+ g_hash_table_remove (store->priv->hash_table, oldest_location);
+ store->priv->modified = TRUE;
+ }
+}
+
+gboolean
+tepl_metadata_store_load (TeplMetadataStore *store,
+ GFile *from_file,
+ GError **error)
+{
+ g_return_val_if_fail (TEPL_IS_METADATA_STORE (store), FALSE);
+ g_return_val_if_fail (G_IS_FILE (from_file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ return _tepl_metadata_store_loader (from_file,
+ store->priv->hash_table,
+ error);
+}
+
+static GBytes *
+to_string (TeplMetadataStore *store)
+{
+ GString *string;
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+
+ string = g_string_new (NULL);
+ g_string_append (string, "<metadata>\n");
+
+ g_hash_table_iter_init (&iter, store->priv->hash_table);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ GFile *location = key;
+ TeplFileMetadata *file_metadata = value;
+
+ _tepl_file_metadata_append_xml_to_string (file_metadata, location, string);
+ }
+
+ g_string_append (string, "</metadata>\n");
+
+ return g_string_free_to_bytes (string);
+}
+
+gboolean
+tepl_metadata_store_save (TeplMetadataStore *store,
+ GFile *to_file,
+ gboolean trim,
+ GError **error)
+{
+ GBytes *bytes;
+ gboolean ok;
+
+ g_return_val_if_fail (TEPL_IS_METADATA_STORE (store), FALSE);
+ g_return_val_if_fail (G_IS_FILE (to_file), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (trim)
+ {
+ tepl_metadata_store_trim (store, -1);
+ }
+
+ if (!store->priv->modified)
+ {
+ return TRUE;
+ }
+
+ if (!tepl_utils_create_parent_directories (to_file, NULL, error))
+ {
+ return FALSE;
+ }
+
+ bytes = to_string (store);
+
+ ok = g_file_replace_contents (to_file,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ NULL,
+ FALSE,
+ G_FILE_CREATE_NONE,
+ NULL,
+ NULL,
+ error);
+
+ if (ok)
+ {
+ store->priv->modified = FALSE;
+ }
+
+ g_bytes_unref (bytes);
+ return ok;
+}
+
+void
+tepl_metadata_store_load_file_metadata (TeplMetadataStore *store,
+ GFile *location,
+ TeplFileMetadata *file_metadata)
+{
+ TeplFileMetadata *file_metadata_from_store;
+
+ g_return_if_fail (TEPL_IS_METADATA_STORE (store));
+ g_return_if_fail (G_IS_FILE (location));
+ g_return_if_fail (TEPL_IS_FILE_METADATA (file_metadata));
+
+ file_metadata_from_store = g_hash_table_lookup (store->priv->hash_table, location);
+
+ if (file_metadata_from_store != NULL)
+ {
+ _tepl_file_metadata_copy_into (file_metadata_from_store, file_metadata);
+ }
+}
+
+void
+tepl_metadata_store_save_file_metadata (TeplMetadataStore *store,
+ GFile *location,
+ TeplFileMetadata *file_metadata)
+{
+ TeplFileMetadata *file_metadata_from_store;
+
+ g_return_if_fail (TEPL_IS_METADATA_STORE (store));
+ g_return_if_fail (G_IS_FILE (location));
+ g_return_if_fail (TEPL_IS_FILE_METADATA (file_metadata));
+
+ file_metadata_from_store = g_hash_table_lookup (store->priv->hash_table, location);
+
+ if (file_metadata_from_store == NULL)
+ {
+ file_metadata_from_store = tepl_file_metadata_new ();
+
+ g_hash_table_replace (store->priv->hash_table,
+ g_object_ref (location),
+ file_metadata_from_store);
+ }
+
+ _tepl_file_metadata_copy_into (file_metadata, file_metadata_from_store);
+
+ store->priv->modified = TRUE;
+}
diff --git a/tepl/tepl-metadata-store.h b/tepl/tepl-metadata-store.h
new file mode 100644
index 0000000..6cba6b1
--- /dev/null
+++ b/tepl/tepl-metadata-store.h
@@ -0,0 +1,86 @@
+/*
+ * This file is part of Tepl, a text editor library.
+ *
+ * Copyright 2020 - Sébastien Wilmet <swilmet gnome org>
+ *
+ * Tepl is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * Tepl is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TEPL_METADATA_STORE_H
+#define TEPL_METADATA_STORE_H
+
+#if !defined (TEPL_H_INSIDE) && !defined (TEPL_COMPILATION)
+#error "Only <tepl/tepl.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <tepl/tepl-file-metadata.h>
+
+G_BEGIN_DECLS
+
+#define TEPL_TYPE_METADATA_STORE (tepl_metadata_store_get_type ())
+#define TEPL_METADATA_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEPL_TYPE_METADATA_STORE,
TeplMetadataStore))
+#define TEPL_METADATA_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEPL_TYPE_METADATA_STORE,
TeplMetadataStoreClass))
+#define TEPL_IS_METADATA_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEPL_TYPE_METADATA_STORE))
+#define TEPL_IS_METADATA_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEPL_TYPE_METADATA_STORE))
+#define TEPL_METADATA_STORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEPL_TYPE_METADATA_STORE,
TeplMetadataStoreClass))
+
+typedef struct _TeplMetadataStore TeplMetadataStore;
+typedef struct _TeplMetadataStoreClass TeplMetadataStoreClass;
+typedef struct _TeplMetadataStorePrivate TeplMetadataStorePrivate;
+
+struct _TeplMetadataStore
+{
+ GObject parent;
+
+ TeplMetadataStorePrivate *priv;
+};
+
+struct _TeplMetadataStoreClass
+{
+ GObjectClass parent_class;
+
+ gpointer padding[12];
+};
+
+GType tepl_metadata_store_get_type (void);
+
+TeplMetadataStore * tepl_metadata_store_get_singleton (void);
+
+G_GNUC_INTERNAL
+void _tepl_metadata_store_unref_singleton (void);
+
+void tepl_metadata_store_trim (TeplMetadataStore *store,
+ gint max_number_of_locations);
+
+gboolean tepl_metadata_store_load (TeplMetadataStore *store,
+ GFile *from_file,
+ GError **error);
+
+gboolean tepl_metadata_store_save (TeplMetadataStore *store,
+ GFile *to_file,
+ gboolean trim,
+ GError **error);
+
+void tepl_metadata_store_load_file_metadata (TeplMetadataStore *store,
+ GFile *location,
+ TeplFileMetadata *file_metadata);
+
+void tepl_metadata_store_save_file_metadata (TeplMetadataStore *store,
+ GFile *location,
+ TeplFileMetadata *file_metadata);
+
+G_END_DECLS
+
+#endif /* TEPL_METADATA_STORE_H */
diff --git a/tepl/tepl.h b/tepl/tepl.h
index 2ccad32..d9e59d0 100644
--- a/tepl/tepl.h
+++ b/tepl/tepl.h
@@ -43,6 +43,7 @@
#include <tepl/tepl-io-error-info-bars.h>
#include <tepl/tepl-iter.h>
#include <tepl/tepl-menu-shell.h>
+#include <tepl/tepl-metadata-store.h>
#include <tepl/tepl-notebook.h>
#include <tepl/tepl-tab.h>
#include <tepl/tepl-tab-group.h>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]