[dconf] DConfChangeset: add "database mode" and change()



commit a088be7c43575620309c952798c8c40cd3fa1f20
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Nov 8 12:50:22 2012 -0500

    DConfChangeset: add "database mode" and change()
    
    Add a new mode for DConfChangeset to represent the entire contents of a
    dconf database.
    
    Also add a dconf_changeset_change() call to either merge changesets or
    apply a changeset to a database-mode changeset.
    
    Doing this allows us to have only one instance of the algorithm to deal
    with resets (ie: when resetting '/a/' we must reset all keys starting
    with '/a/').

 common/dconf-changeset.c |  131 ++++++++++++++++++++++++++++++++++++++++++++--
 common/dconf-changeset.h |    4 ++
 2 files changed, 131 insertions(+), 4 deletions(-)
---
diff --git a/common/dconf-changeset.c b/common/dconf-changeset.c
index a5c98da..4c56b93 100644
--- a/common/dconf-changeset.c
+++ b/common/dconf-changeset.c
@@ -54,6 +54,7 @@
 struct _DConfChangeset
 {
   GHashTable *table;
+  gboolean is_database;
   gint ref_count;
 
   gchar *prefix;
@@ -88,6 +89,57 @@ dconf_changeset_new (void)
 }
 
 /**
+ * dconf_changeset_new_database:
+ * @copy_of: (allow none): a #DConfChangeset to copy
+ *
+ * Creates a new #DConfChangeset in "database" mode, possibly
+ * initialising it with the values of another changeset.
+ *
+ * In a certain sense it's possible to imagine that a #DConfChangeset
+ * could express the contents of an entire dconf database -- the
+ * contents are the database are what you would have if you applied the
+ * changeset to an empty database.  One thing that fails to map in this
+ * analogy are reset operations -- if we start with an empty database
+ * then reset operations are meaningless.
+ *
+ * A "database" mode changeset is therefore a changeset which is
+ * incapable of containing reset operations.
+ *
+ * It is not permitted to use a database-mode changeset for most
+ * operations (such as the @change argument to dconf_changeset_change()
+ * or the @changeset argument to #DConfClient APIs).
+ *
+ * If @copy_of is non-%NULL then its contents will be copied into the
+ * created changeset.  @copy_of must be a database-mode changeset.
+ *
+ * Returns: a new #DConfChangeset in "database" mode
+ *
+ * Since: 0.16
+ */
+DConfChangeset *
+dconf_changeset_new_database (DConfChangeset *copy_of)
+{
+  DConfChangeset *changeset;
+
+  g_return_val_if_fail (copy_of == NULL || copy_of->is_database, NULL);
+
+  changeset = dconf_changeset_new ();
+  changeset->is_database = TRUE;
+
+  if (copy_of)
+    {
+      GHashTableIter iter;
+      gpointer key, value;
+
+      g_hash_table_iter_init (&iter, copy_of->table);
+      while (g_hash_table_iter_next (&iter, &key, &value))
+        g_hash_table_insert (changeset->table, g_strdup (key), g_variant_ref (value));
+    }
+
+  return changeset;
+}
+
+/**
  * dconf_changeset_unref:
  * @changeset: a #DConfChangeset
  *
@@ -162,13 +214,26 @@ dconf_changeset_set (DConfChangeset *changeset,
         if (g_str_has_prefix (key, path))
           g_hash_table_iter_remove (&iter);
 
-      /* Record the reset itself. */
-      g_hash_table_insert (changeset->table, g_strdup (path), NULL);
+      /* If this is a non-database then record the reset itself. */
+      if (!changeset->is_database)
+        g_hash_table_insert (changeset->table, g_strdup (path), NULL);
+    }
+
+  /* ...or a value reset */
+  else if (value == NULL)
+    {
+      /* If we're a non-database, record the reset explicitly.
+       * Otherwise, just reset whatever may be there already.
+       */
+      if (!changeset->is_database)
+        g_hash_table_insert (changeset->table, g_strdup (path), NULL);
+      else
+        g_hash_table_remove (changeset->table, path);
     }
 
-  /* else, just a normal value write or reset */
+  /* ...or a normal write. */
   else
-    g_hash_table_insert (changeset->table, g_strdup (path), value ? g_variant_ref_sink (value) : NULL);
+    g_hash_table_insert (changeset->table, g_strdup (path), g_variant_ref_sink (value));
 }
 
 /**
@@ -574,3 +639,61 @@ dconf_changeset_is_empty (DConfChangeset *changeset)
 {
   return !g_hash_table_size (changeset->table);
 }
+
+/**
+ * dconf_changeset_change:
+ * @changeset: a #DConfChangeset (to be changed)
+ * @changes: the changes to make to @changeset
+ *
+ * Applies @changes to @changeset.
+ *
+ * If @changeset is a normal changeset then reset requests in @changes
+ * will be allied to @changeset and then copied down into it.  In this
+ * case the two changesets are effectively being merged.
+ *
+ * If @changeset is in database mode then the reset operations in
+ * @changes will simply be applied to @changeset.
+ *
+ * Since: 0.16
+ **/
+void
+dconf_changeset_change (DConfChangeset *changeset,
+                        DConfChangeset *changes)
+{
+  gsize prefix_len;
+  gint i;
+
+  /* Handling resets is a little bit tricky...
+   *
+   * Consider the case that we have @changeset containing a key /a/b and
+   * @changes containing a reset request for /a/ and a set request for
+   * /a/c.
+   *
+   * It's clear that at the end of this all, we should have only /a/c
+   * but in order for that to be the case, we need to make sure that we
+   * process the reset of /a/ before we process the set of /a/c.
+   *
+   * The easiest way to do this is to visit the strings in sorted order.
+   * That removes the possibility of iterating over the hash table, but
+   * dconf_changeset_build_description() makes the list in the order we
+   * need so just call it and then iterate over the result.
+   */
+
+  if (!dconf_changeset_describe (changes, NULL, NULL, NULL))
+    return;
+
+  prefix_len = strlen (changes->prefix);
+  for (i = 0; changes->paths[i]; i++)
+    {
+      const gchar *path;
+      GVariant *value;
+
+      /* The changes->paths are just pointers into the keys of the
+       * hashtable, fast-forwarded past the prefix.  Rewind a bit.
+       */
+      path = changes->paths[i] - prefix_len;
+      value = changes->values[i];
+
+      dconf_changeset_set (changeset, path, value);
+    }
+}
diff --git a/common/dconf-changeset.h b/common/dconf-changeset.h
index 1aecabe..eb8dde1 100644
--- a/common/dconf-changeset.h
+++ b/common/dconf-changeset.h
@@ -31,6 +31,7 @@ typedef gboolean     (* DConfChangesetPredicate)                        (const g
                                                                          gpointer                  user_data);
 
 DConfChangeset *        dconf_changeset_new                             (void);
+DConfChangeset *        dconf_changeset_new_database                    (DConfChangeset           *copy_of);
 
 DConfChangeset *        dconf_changeset_new_write                       (const gchar              *path,
                                                                          GVariant                 *value);
@@ -63,4 +64,7 @@ guint                   dconf_changeset_describe                        (DConfCh
 GVariant *              dconf_changeset_serialise                       (DConfChangeset           *changeset);
 DConfChangeset *        dconf_changeset_deserialise                     (GVariant                 *serialised);
 
+void                    dconf_changeset_change                          (DConfChangeset           *changeset,
+                                                                         DConfChangeset           *changes);
+
 #endif /* __dconf_changeset_h__ */



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