[glib/wip/settings-backend: 12/13] GSettingsBackend: add list support vfuncs



commit 839e660c443ada35c15000518dba9dec18f023da
Author: Ryan Lortie <desrt desrt ca>
Date:   Sat Dec 31 15:04:28 2011 -0500

    GSettingsBackend: add list support vfuncs
    
    Implement it for GMemorySettingsBackend.

 gio/gmemorysettingsbackend.c   |  331 +++++++++++++++++++++++++++++++++++++++-
 gio/gsettingsbackend.c         |  148 ++++++++++++++++++
 gio/gsettingsbackend.h         |   27 ++++
 gio/gsettingsbackendinternal.h |   36 +++++
 4 files changed, 540 insertions(+), 2 deletions(-)
---
diff --git a/gio/gmemorysettingsbackend.c b/gio/gmemorysettingsbackend.c
index 584caa8..b5f9d91 100644
--- a/gio/gmemorysettingsbackend.c
+++ b/gio/gmemorysettingsbackend.c
@@ -21,21 +21,43 @@
 
 #include "config.h"
 
-#include "gsimplepermission.h"
 #include "gsettingsbackendinternal.h"
+#include "gsimplepermission.h"
 #include "giomodule.h"
 
+#include <string.h>
+
 
 #define G_TYPE_MEMORY_SETTINGS_BACKEND  (g_memory_settings_backend_get_type())
 #define G_MEMORY_SETTINGS_BACKEND(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
                                          G_TYPE_MEMORY_SETTINGS_BACKEND,     \
                                          GMemorySettingsBackend))
 
+typedef struct
+{
+  GSList *removes;
+  GSList *adds;
+} Directory;
+
+static void
+directory_free (gpointer data)
+{
+  Directory *d = data;
+
+  g_slist_free_full (d->removes, g_free);
+  g_slist_free_full (d->adds, g_free);
+
+  g_slice_free (Directory, d);
+}
+
+
 typedef GSettingsBackendClass GMemorySettingsBackendClass;
 typedef struct
 {
   GSettingsBackend parent_instance;
+  GHashTable *directories;
   GHashTable *table;
+  guint64 unique_id;
 } GMemorySettingsBackend;
 
 G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
@@ -44,6 +66,116 @@ G_DEFINE_TYPE_WITH_CODE (GMemorySettingsBackend,
                          g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME,
                                                          g_define_type_id, "memory", 10))
 
+static gboolean
+g_memory_settings_backend_verify (GMemorySettingsBackend *memory,
+                                  const gchar            *path)
+{
+  const gchar *end = path;
+
+  /* Given the path '/a/b:/:c/d/e:/f/g' we need to verify that each
+   * explicit item exists.  An explicit item is one that is in an
+   * explicit directory (ie: directory ending with ':/').  In this case,
+   * we have to check:
+   *
+   *   item ':c/' exists in explicit directory '/a/b:/'
+   *
+   * and
+   *
+   *   'f/' exists in '/a/b:/:c/d/e:/'.
+   *
+   * Items in explicit directories must be directories themselves (ie:
+   * they must be terminated with '/').
+   *
+   * We do this by looking for the first ':/' then the '/' to follow it
+   * to grab the directory name and the item name, then repeating until
+   * we have found all instances of ':/'.
+   *
+   * It is possible to directly nest explicit dirs, so we also have to
+   * take care to properly handle cases like '/a/b:/:c:/d/' by checking
+   * both that ':c:/' exists in '/a/b:/' and also that 'd/' exists in
+   * '/a/b:/:c:/' (ie: the 'item' in one iteration can become the 'dir'
+   * in the next).
+   *
+   * We also have to consider being asked for an explicit directory
+   * itself.  These implicitly exist just the same as normal items --
+   * it's the items in explicit directories that must exist explicitly.
+   * Therefore, if we find ':/' at the end of the string, we should not
+   * treat it specially.
+   */
+  while (TRUE)
+    {
+      const gchar *end_item;
+      gchar *dir, *item;
+      gboolean exists;
+      Directory *d;
+
+      end = strstr (end, ":/");
+
+      /* No more ':/' instances */
+      if (end == NULL)
+        return TRUE;
+
+      /* Skip past the ':/' which is part of the explicit dir name */
+      end += 2;
+
+      /* If ':/' was at the end of the string, we're done */
+      if (*end != '\0')
+        return TRUE;
+
+      /* Find the end of the item name */
+      end_item = strchr (end, '/');
+
+      /* No trailing slash, so surely this can't exist */
+      if (end_item == NULL)
+        return FALSE;
+
+      /* Skip past the '/' which is part of the item name */
+      end_item++;
+
+      dir = g_strndup (path, end - path);
+      item = g_strndup (end, end_item - end);
+
+      /* We assume that items starting with a character other than ':'
+       * already exist (unless explicitly deleted) and that items
+       * starting with ':' only exist if they have been explicitly
+       * added.
+       */
+      exists = item[0] != ':';
+
+      d = g_hash_table_lookup (memory->directories, dir);
+      if (d != NULL)
+        {
+          GSList *list;
+
+          /* For assumed-existing items, we want to search in the
+           * removes list.  For assumed-non-existing items, we want to
+           * search in the adds list.
+           */
+          if (exists)
+            list = d->removes;
+          else
+            list = d->adds;
+
+          while (list)
+            if (g_str_equal (list->data, item))
+              break;
+
+          /* If we found it in the list we were looking for, flip our
+           * initial assumption.
+           */
+          if (list)
+            exists = !exists;
+        }
+
+      g_free (item);
+      g_free (dir);
+
+      if (!exists)
+        return FALSE;
+    }
+}
+
+
 static GVariant *
 g_memory_settings_backend_read (GSettingsBackend   *backend,
                                 const gchar        *key,
@@ -68,6 +200,9 @@ g_memory_settings_backend_write (GSettingsBackend *backend,
   GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
   GVariant *old_value;
 
+  if (!g_memory_settings_backend_verify (memory, key))
+    return FALSE;
+
   old_value = g_hash_table_lookup (memory->table, key);
   g_variant_ref_sink (value);
 
@@ -99,7 +234,9 @@ static gboolean
 g_memory_settings_backend_get_writable (GSettingsBackend *backend,
                                         const gchar      *name)
 {
-  return TRUE;
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+  return g_memory_settings_backend_verify (memory, name);
 }
 
 static GPermission *
@@ -109,6 +246,189 @@ g_memory_settings_backend_get_permission (GSettingsBackend *backend,
   return g_simple_permission_new (TRUE);
 }
 
+static gboolean
+g_memory_settings_backend_check_exists (GSettingsBackend *backend,
+                                        const gchar      *path)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+  gboolean exists;
+
+  exists = g_memory_settings_backend_verify (memory, path);
+
+  return exists;
+}
+
+static gchar **
+g_memory_settings_backend_list (GSettingsBackend    *backend,
+                                const gchar         *dir,
+                                const gchar * const *schema_items)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+  GPtrArray *array;
+  Directory *d;
+  GSList *node;
+
+  d = g_hash_table_lookup (memory->directories, dir);
+
+  /* fast path: by default, just return the schema items */
+  if (d == NULL)
+    return g_strdupv ((gchar **) schema_items);
+
+  array = g_ptr_array_new ();
+  while (schema_items)
+    {
+      const gchar *item = *schema_items++;
+
+      /* check if this item was removed */
+      for (node = d->removes; node; node = node->next)
+        if (g_str_equal (node->data, item))
+          break;
+
+      /* if not removed, put it in the result */
+      if (!node)
+        g_ptr_array_add (array, g_strdup (item));
+    }
+
+  for (node = d->adds; node; node = node->next)
+    g_ptr_array_add (array, g_strdup (node->data));
+
+  g_ptr_array_add (array, NULL);
+
+  return (gchar **) g_ptr_array_free (array, FALSE);
+}
+
+static gboolean
+g_memory_settings_backend_can_insert (GSettingsBackend *backend,
+                                      const gchar      *dir,
+                                      const gchar      *before)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+  return g_memory_settings_backend_verify (memory, dir);
+}
+
+static gboolean
+g_memory_settings_backend_insert (GSettingsBackend  *backend,
+                                  const gchar       *dir,
+                                  const gchar       *before,
+                                  gchar            **item)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+  GSList **ptr;
+  Directory *d;
+  gchar *id;
+
+  if (!g_memory_settings_backend_verify (memory, dir))
+    return FALSE;
+
+  d = g_hash_table_lookup (memory->directories, dir);
+
+  if (d == NULL)
+    {
+      d = g_slice_new0 (Directory);
+      g_hash_table_insert (memory->directories, g_strdup (dir), d);
+    }
+
+  id = g_strdup_printf (":%"G_GINT64_FORMAT, memory->unique_id++);
+
+  for (ptr = &d->adds; ptr; ptr = &(*ptr)->next)
+    if (before && g_str_equal ((*ptr)->data, before))
+      break;
+
+  *ptr = g_slist_prepend (*ptr, id);
+
+  if (item != NULL)
+    *item = g_strdup (id);
+
+  return TRUE;
+}
+
+static gboolean
+g_memory_settings_backend_can_remove (GSettingsBackend *backend,
+                                      const gchar      *dir,
+                                      const gchar      *item)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+
+  return g_memory_settings_backend_verify (memory, dir);
+}
+
+static void
+g_memory_settings_backend_clear (GMemorySettingsBackend *memory,
+                                 const gchar            *dir,
+                                 const gchar            *item)
+{
+  GHashTableIter iter;
+  gchar *prefix;
+  gpointer key;
+
+  prefix = g_strconcat (dir, item, NULL);
+
+  /* slow and steady... */
+  g_hash_table_iter_init (&iter, memory->table);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    if (g_str_has_prefix (key, prefix))
+      g_hash_table_iter_remove (&iter);
+
+  g_hash_table_iter_init (&iter, memory->directories);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    if (g_str_has_prefix (key, prefix))
+      g_hash_table_iter_remove (&iter);
+
+  g_free (prefix);
+}
+
+static gboolean
+g_memory_settings_backend_remove (GSettingsBackend *backend,
+                                  const gchar      *dir,
+                                  const gchar      *item)
+{
+  GMemorySettingsBackend *memory = G_MEMORY_SETTINGS_BACKEND (backend);
+  Directory *d;
+
+  if (!g_memory_settings_backend_verify (memory, dir))
+    return FALSE;
+
+  d = g_hash_table_lookup (memory->directories, dir);
+  if (d == NULL)
+    {
+      d = g_slice_new0 (Directory);
+      g_hash_table_insert (memory->directories, g_strdup (dir), d);
+    }
+
+  /* If the item starts with ':' that we just have to remove it from the
+   * 'adds' list.  If it starts with a non-':' then we need to add a
+   * remove entry for it.
+   */
+  if (item[0] == ':')
+    {
+      GSList **ptr;
+
+      for (ptr = &d->adds; ptr; ptr = &(*ptr)->next)
+        if (g_str_equal ((*ptr)->data, item))
+          break;
+
+      g_free ((*ptr)->data);
+      *ptr = g_slist_delete_link (*ptr, *ptr);
+    }
+  else
+    {
+      GSList *node;
+
+      for (node = d->removes; node; node = node->next)
+        if (g_str_equal (node->data, item))
+          break;
+
+      if (!node)
+        d->removes = g_slist_prepend (d->removes, g_strdup (item));
+    }
+
+  /* Remove all subitems that may have been stored */
+  g_memory_settings_backend_clear (memory, dir, item);
+
+  return TRUE;
+}
+
 static void
 g_memory_settings_backend_finalize (GObject *object)
 {
@@ -125,6 +445,7 @@ g_memory_settings_backend_init (GMemorySettingsBackend *memory)
 {
   memory->table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
                                          (GDestroyNotify) g_variant_unref);
+  memory->directories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, directory_free);
 }
 
 static void
@@ -138,6 +459,12 @@ g_memory_settings_backend_class_init (GMemorySettingsBackendClass *class)
   backend_class->reset = g_memory_settings_backend_reset;
   backend_class->get_writable = g_memory_settings_backend_get_writable;
   backend_class->get_permission = g_memory_settings_backend_get_permission;
+  backend_class->check_exists = g_memory_settings_backend_check_exists;
+  backend_class->list = g_memory_settings_backend_list;
+  backend_class->can_insert = g_memory_settings_backend_can_insert;
+  backend_class->insert = g_memory_settings_backend_insert;
+  backend_class->can_remove = g_memory_settings_backend_can_remove;
+  backend_class->remove = g_memory_settings_backend_remove;
   object_class->finalize = g_memory_settings_backend_finalize;
 }
 
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index 9cafe9b..2890b25 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -649,6 +649,72 @@ ignore_subscription (GSettingsBackend *backend,
 {
 }
 
+static gboolean
+g_settings_backend_real_check_exists (GSettingsBackend *backend,
+                                      const gchar      *path)
+{
+  return !strstr (path, ":/:");
+}
+
+static gchar **
+g_settings_backend_real_list (GSettingsBackend    *backend,
+                              const gchar         *dir,
+                              const gchar * const *schema_items)
+{
+  return g_strdupv ((gchar **) schema_items);
+}
+
+static gboolean
+g_settings_backend_real_can_insert (GSettingsBackend *backend,
+                                    const gchar      *dir,
+                                    const gchar      *before)
+{
+  return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_insert (GSettingsBackend  *backend,
+                                const gchar       *dir,
+                                const gchar       *before,
+                                gchar            **item)
+{
+  return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_can_move (GSettingsBackend *backend,
+                                  const gchar      *dir,
+                                  const gchar      *item,
+                                  const gchar      *before)
+{
+  return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_move (GSettingsBackend *backend,
+                              const gchar      *dir,
+                              const gchar      *item,
+                              const gchar      *before)
+{
+  return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_can_remove (GSettingsBackend *backend,
+                                    const gchar      *dir,
+                                    const gchar      *item)
+{
+  return FALSE;
+}
+
+static gboolean
+g_settings_backend_real_remove (GSettingsBackend *backend,
+                                const gchar      *dir,
+                                const gchar      *item)
+{
+  return FALSE;
+}
+
 static GSettingsBackend *
 g_settings_backend_real_delay (GSettingsBackend *backend)
 {
@@ -675,6 +741,14 @@ g_settings_backend_class_init (GSettingsBackendClass *class)
 
   class->subscribe = ignore_subscription;
   class->unsubscribe = ignore_subscription;
+  class->check_exists = g_settings_backend_real_check_exists;
+  class->list = g_settings_backend_real_list;
+  class->can_insert = g_settings_backend_real_can_insert;
+  class->insert = g_settings_backend_real_insert;
+  class->can_move = g_settings_backend_real_can_move;
+  class->move = g_settings_backend_real_move;
+  class->can_remove = g_settings_backend_real_can_remove;
+  class->remove = g_settings_backend_real_remove;
   class->delay = g_settings_backend_real_delay;
   class->apply = ignore_apply;
   class->revert = ignore_apply;
@@ -779,6 +853,80 @@ g_settings_backend_sync_default (void)
     }
 }
 
+gboolean
+g_settings_backend_check_exists (GSettingsBackend *backend,
+                                 const gchar      *path)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->check_exists (backend, path);
+}
+
+gchar **
+g_settings_backend_list (GSettingsBackend    *backend,
+                         const gchar         *dir,
+                         const gchar * const *schema_items)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->list (backend, dir, schema_items);
+}
+
+gboolean
+g_settings_backend_can_insert (GSettingsBackend *backend,
+                               const gchar      *dir,
+                               const gchar      *before)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->can_insert (backend, dir, before);
+}
+
+gboolean
+g_settings_backend_insert (GSettingsBackend  *backend,
+                           const gchar       *dir,
+                           const gchar       *before,
+                           gchar            **item)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->insert (backend, dir, before, item);
+}
+
+gboolean
+g_settings_backend_can_move (GSettingsBackend *backend,
+                             const gchar      *dir,
+                             const gchar      *item,
+                             const gchar      *before)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->can_move (backend, dir, item, before);
+}
+
+gboolean
+g_settings_backend_move (GSettingsBackend *backend,
+                         const gchar      *dir,
+                         const gchar      *item,
+                         const gchar      *before)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->move (backend, dir, item, before);
+}
+
+gboolean
+g_settings_backend_can_remove (GSettingsBackend *backend,
+                               const gchar      *dir,
+                               const gchar      *item)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->can_remove (backend, dir, item);
+}
+
+gboolean
+g_settings_backend_remove (GSettingsBackend *backend,
+                           const gchar      *dir,
+                           const gchar      *item)
+{
+  return G_SETTINGS_BACKEND_GET_CLASS (backend)
+    ->remove (backend, dir, item);
+}
+
 GSettingsBackend *
 g_settings_backend_delay (GSettingsBackend *backend)
 {
diff --git a/gio/gsettingsbackend.h b/gio/gsettingsbackend.h
index c6624bd..779f0ae 100644
--- a/gio/gsettingsbackend.h
+++ b/gio/gsettingsbackend.h
@@ -87,6 +87,33 @@ struct _GSettingsBackendClass
   GPermission *      (* get_permission) (GSettingsBackend    *backend,
                                          const gchar         *path);
 
+  gboolean           (* check_exists)   (GSettingsBackend     *backend,
+                                         const gchar          *path);
+  gchar **           (* list)           (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar * const  *schema_items);
+  gboolean           (* can_insert)     (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar          *before);
+  gboolean           (* insert)         (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar          *before,
+                                         gchar               **item);
+  gboolean           (* can_move)       (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar          *item,
+                                         const gchar          *before);
+  gboolean           (* move)           (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar          *item,
+                                         const gchar          *before);
+  gboolean           (* can_remove)     (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar          *item);
+  gboolean           (* remove)         (GSettingsBackend     *backend,
+                                         const gchar          *dir,
+                                         const gchar          *item);
+
   GSettingsBackend * (* delay)          (GSettingsBackend    *backend);
   void               (* apply)          (GSettingsBackend    *backend);
   void               (* revert)         (GSettingsBackend    *backend);
diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h
index ca1402a..713ae02 100644
--- a/gio/gsettingsbackendinternal.h
+++ b/gio/gsettingsbackendinternal.h
@@ -64,6 +64,42 @@ G_GNUC_INTERNAL
 GPermission *           g_settings_backend_get_permission               (GSettingsBackend     *backend,
                                                                          const gchar          *path);
 G_GNUC_INTERNAL
+gboolean                g_settings_backend_check_exists                 (GSettingsBackend     *backend,
+                                                                         const gchar          *path);
+G_GNUC_INTERNAL
+gchar **                g_settings_backend_list                         (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar * const  *schema_items);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_can_insert                   (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar          *before);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_insert                       (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar          *before,
+                                                                         gchar               **item);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_can_move                     (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar          *item,
+                                                                         const gchar          *before);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_move                         (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar          *item,
+                                                                         const gchar          *before);
+G_GNUC_INTERNAL
+GSettingsBackend *      g_settings_backend_delay                        (GSettingsBackend     *backend);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_can_remove                   (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar          *item);
+G_GNUC_INTERNAL
+gboolean                g_settings_backend_remove                       (GSettingsBackend     *backend,
+                                                                         const gchar          *dir,
+                                                                         const gchar          *item);
+G_GNUC_INTERNAL
 GSettingsBackend *      g_settings_backend_delay                        (GSettingsBackend     *backend);
 G_GNUC_INTERNAL
 gboolean                g_settings_backend_get_has_unapplied            (GSettingsBackend     *backend);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]