summaryrefslogtreecommitdiff
path: root/gio/gsettingsschema.c
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2013-10-27 17:18:10 -0700
committerRyan Lortie <desrt@desrt.ca>2013-10-27 17:18:10 -0700
commitcbf8cf8598e527a0d3b895cbfedef6b728ab8b82 (patch)
treee38135015ede29271ff8a9d1eba9aaf5cb8c31b8 /gio/gsettingsschema.c
parent3041d0a8dbc9b82d15291061814c4160a5d400ab (diff)
GSettings: properly support 'extends'
Support the 'extends' attribute that has been supported by the compiler for a long time by doing three things: - when creating a schema that extends another schema, lookup that other schema - when looking up keys and we can't find them in the schema, check (recursively) in the 'extends' schema - when listing all keys in a schema, also visit the extends schemas, but take care to avoid duplicates caused by overrides Extend the testsuite to verify that it works. https://bugzilla.gnome.org/show_bug.cgi?id=645453
Diffstat (limited to 'gio/gsettingsschema.c')
-rw-r--r--gio/gsettingsschema.c152
1 files changed, 96 insertions, 56 deletions
diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c
index 80b0bb61b..b79d91a57 100644
--- a/gio/gsettingsschema.c
+++ b/gio/gsettingsschema.c
@@ -146,6 +146,8 @@ struct _GSettingsSchema
GvdbTable *table;
gchar *id;
+ GSettingsSchema *extends;
+
gint ref_count;
};
@@ -407,6 +409,7 @@ g_settings_schema_source_lookup (GSettingsSchemaSource *source,
{
GSettingsSchema *schema;
GvdbTable *table;
+ const gchar *extends;
g_return_val_if_fail (source != NULL, NULL);
g_return_val_if_fail (schema_id != NULL, NULL);
@@ -432,6 +435,14 @@ g_settings_schema_source_lookup (GSettingsSchemaSource *source,
if (schema->gettext_domain)
bind_textdomain_codeset (schema->gettext_domain, "UTF-8");
+ extends = g_settings_schema_get_string (schema, ".extends");
+ if (extends)
+ {
+ schema->extends = g_settings_schema_source_lookup (source, extends, TRUE);
+ if (schema->extends == NULL)
+ g_warning ("Schema '%s' extends schema '%s' but we could not find it", schema_id, extends);
+ }
+
return schema;
}
@@ -892,6 +903,9 @@ g_settings_schema_unref (GSettingsSchema *schema)
{
if (g_atomic_int_dec_and_test (&schema->ref_count))
{
+ if (schema->extends)
+ g_settings_schema_unref (schema->extends);
+
g_settings_schema_source_unref (schema->source);
gvdb_table_unref (schema->table);
g_free (schema->items);
@@ -921,10 +935,15 @@ GVariantIter *
g_settings_schema_get_value (GSettingsSchema *schema,
const gchar *key)
{
+ GSettingsSchema *s = schema;
GVariantIter *iter;
GVariant *value;
- value = gvdb_table_get_raw_value (schema->table, key);
+ g_return_val_if_fail (schema != NULL, NULL);
+
+ for (s = schema; s; s = schema->extends)
+ if ((value = gvdb_table_get_raw_value (schema->table, key)))
+ break;
if G_UNLIKELY (value == NULL || !g_variant_is_of_type (value, G_VARIANT_TYPE_TUPLE))
g_error ("Settings schema '%s' does not contain a key named '%s'", schema->id, key);
@@ -976,79 +995,100 @@ const GQuark *
g_settings_schema_list (GSettingsSchema *schema,
gint *n_items)
{
- gint i, j;
-
if (schema->items == NULL)
{
- gchar **list;
+ GSettingsSchema *s;
+ GHashTableIter iter;
+ GHashTable *items;
+ gpointer name;
gint len;
+ gint i;
+
+ items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ for (s = schema; s; s = s->extends)
+ {
+ gchar **list;
- list = gvdb_table_list (schema->table, "");
- len = list ? g_strv_length (list) : 0;
+ list = gvdb_table_list (s->table, "");
- schema->items = g_new (GQuark, len);
- j = 0;
+ for (i = 0; list[i]; i++)
+ g_hash_table_add (items, list[i]); /* transfer ownership */
- for (i = 0; i < len; i++)
- if (list[i][0] != '.')
+ g_free (list); /* free container only */
+ }
+
+ /* Do a first pass to eliminate child items that do not map to
+ * valid schemas (ie: ones that would crash us if we actually
+ * tried to create them).
+ */
+ g_hash_table_iter_init (&iter, items);
+ while (g_hash_table_iter_next (&iter, &name, NULL))
+ if (g_str_has_suffix (name, "/"))
{
- if (g_str_has_suffix (list[i], "/"))
- {
- /* This is a child. Check to make sure that
- * instantiating the child would actually work before we
- * return it from list() and cause a crash.
- */
- GSettingsSchemaSource *source;
- GVariant *child_schema;
- GvdbTable *child_table;
+ GSettingsSchemaSource *source;
+ GVariant *child_schema;
+ GvdbTable *child_table;
- child_schema = gvdb_table_get_raw_value (schema->table, list[i]);
- if (!child_schema)
- continue;
+ child_schema = gvdb_table_get_raw_value (schema->table, name);
+ if (!child_schema)
+ continue;
- child_table = NULL;
+ child_table = NULL;
- for (source = schema_sources; source; source = source->parent)
- if ((child_table = gvdb_table_get_table (source->table, g_variant_get_string (child_schema, NULL))))
- break;
+ for (source = schema_sources; source; source = source->parent)
+ if ((child_table = gvdb_table_get_table (source->table, g_variant_get_string (child_schema, NULL))))
+ break;
- g_variant_unref (child_schema);
+ g_variant_unref (child_schema);
- /* Schema is not found -> don't add it to the list */
- if (child_table == NULL)
- continue;
+ /* Schema is not found -> remove it from the list */
+ if (child_table == NULL)
+ {
+ g_hash_table_iter_remove (&iter);
+ continue;
+ }
- /* Make sure the schema is relocatable or at the
- * expected path
+ /* Make sure the schema is relocatable or at the
+ * expected path
+ */
+ if (gvdb_table_has_value (child_table, ".path"))
+ {
+ GVariant *path;
+ gchar *expected;
+ gboolean same;
+
+ path = gvdb_table_get_raw_value (child_table, ".path");
+ expected = g_strconcat (schema->path, name, NULL);
+ same = g_str_equal (expected, g_variant_get_string (path, NULL));
+ g_variant_unref (path);
+ g_free (expected);
+
+ /* Schema is non-relocatable and did not have the
+ * expected path -> remove it from the list
*/
- if (gvdb_table_has_value (child_table, ".path"))
- {
- GVariant *path;
- gchar *expected;
- gboolean same;
-
- path = gvdb_table_get_raw_value (child_table, ".path");
- expected = g_strconcat (schema->path, list[i], NULL);
- same = g_str_equal (expected, g_variant_get_string (path, NULL));
- g_variant_unref (path);
- g_free (expected);
-
- if (!same)
- {
- gvdb_table_unref (child_table);
- continue;
- }
- }
-
- gvdb_table_unref (child_table);
- /* Else, it's good... */
+ if (!same)
+ g_hash_table_iter_remove (&iter);
}
- schema->items[j++] = g_quark_from_string (list[i]);
+ gvdb_table_unref (child_table);
}
- schema->n_items = j;
- g_strfreev (list);
+ /* Now create the list */
+ len = g_hash_table_size (items);
+ schema->items = g_new (GQuark, len + 1);
+ i = 0;
+ g_hash_table_iter_init (&iter, items);
+
+ while (g_hash_table_iter_next (&iter, &name, NULL))
+ {
+ schema->items[i++] = g_quark_from_string (name);
+ g_hash_table_iter_steal (&iter);
+ }
+ schema->n_items = i;
+ g_assert (i == len);
+
+ g_hash_table_unref (items);
}
*n_items = schema->n_items;