[dconf-editor] Introduce SourceManager and SchemasUtility.
- From: Arnaud Bonatti <arnaudb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dconf-editor] Introduce SourceManager and SchemasUtility.
- Date: Wed, 17 Jan 2018 16:08:34 +0000 (UTC)
commit 2bcc60e6cd819aafcbe79de1c906ef8871b2fa58
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date: Wed Jan 17 17:07:37 2018 +0100
Introduce SourceManager and SchemasUtility.
editor/dconf-model.vala | 390 +++----------------------------------------
editor/dconf-window.vala | 7 +-
editor/meson.build | 4 +-
editor/schemas-utility.vala | 57 +++++++
editor/source-manager.vala | 363 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 452 insertions(+), 369 deletions(-)
---
diff --git a/editor/dconf-model.vala b/editor/dconf-model.vala
index 2ca1e26..2c321db 100644
--- a/editor/dconf-model.vala
+++ b/editor/dconf-model.vala
@@ -17,10 +17,7 @@
public class SettingsModel : Object
{
- private SettingsSchemaSource? settings_schema_source = null;
- private HashTable<string, GenericSet<string>> relocatable_schema_paths = new HashTable<string,
GenericSet<string>> (str_hash, str_equal);
- private HashTable<string, GenericSet<string>> startup_relocatable_schema_paths = new HashTable<string,
GenericSet<string>> (str_hash, str_equal);
- private SchemaPathTree cached_schemas = new SchemaPathTree ("/"); // prefix tree for quick lookup and
diff'ing on changes
+ private SourceManager source_manager = new SourceManager ();
private DConf.Client client = new DConf.Client ();
private string? last_change_tag = null;
@@ -33,40 +30,24 @@ public class SettingsModel : Object
bool startup_schemas,
Variant user_paths_variant)
{
- relocatable_schema_paths.remove_all ();
- if (user_schemas)
- {
- VariantIter entries_iter;
- user_paths_variant.get ("a{ss}", out entries_iter);
- string schema_id;
- string path_spec;
- while (entries_iter.next ("{ss}", out schema_id, out path_spec))
- add_relocatable_schema_info (relocatable_schema_paths, schema_id, path_spec);
- }
- if (built_in_schemas)
- {
- string [,] known_mappings = ConfigurationEditor.known_mappings;
- for (int i = 0; i < known_mappings.length [0]; i++)
- add_relocatable_schema_info (relocatable_schema_paths, known_mappings [i,0], known_mappings
[i,1]);
- }
- if (startup_schemas)
- {
- startup_relocatable_schema_paths.foreach ((schema_id, paths) => {
- paths.foreach ((path_spec) => add_relocatable_schema_info (relocatable_schema_paths,
schema_id, path_spec));
- });
- }
+ source_manager.refresh_relocatable_schema_paths (user_schemas,
+ built_in_schemas,
+ internal_schemas,
+ startup_schemas,
+ user_paths_variant);
}
public void add_mapping (string schema, string path)
{
- add_relocatable_schema_info (startup_relocatable_schema_paths, schema, path);
+ source_manager.add_mapping (schema, path);
}
public void finalize_model ()
{
- refresh_schema_source ();
+ source_manager.paths_changed.connect ((modified_path_specs) => { paths_changed (modified_path_specs,
false); });
+ source_manager.refresh_schema_source ();
Timeout.add (3000, () => {
- refresh_schema_source ();
+ source_manager.refresh_schema_source ();
return true;
});
@@ -92,7 +73,7 @@ public class SettingsModel : Object
string? path_spec;
while ((path_spec = iter.next_value ()) != null)
{
- if (cached_schemas.get_schema_count ((!) path_spec) > 0)
+ if (source_manager.cached_schemas.get_schema_count ((!) path_spec) > 0)
iter.remove ();
}
paths_changed (modified_path_specs, internal_changes);
@@ -100,107 +81,17 @@ public class SettingsModel : Object
client.watch_sync ("/");
}
- private void refresh_schema_source ()
- {
- SettingsSchemaSource? settings_schema_source = create_schema_source ();
- if (settings_schema_source == null)
- return;
-
- GenericSet<string> modified_path_specs = new GenericSet<string> (str_hash, str_equal);
-
- string [] non_relocatable_schemas;
- string [] relocatable_schemas;
- ((!) settings_schema_source).list_schemas (true, out non_relocatable_schemas, out
relocatable_schemas);
-
- foreach (string schema_id in non_relocatable_schemas)
- {
- SettingsSchema? settings_schema = ((!) settings_schema_source).lookup (schema_id, true);
- if (settings_schema == null)
- continue; // TODO better
-
- cached_schemas.add_schema ((!) settings_schema, modified_path_specs);
- }
-
- foreach (string schema_id in relocatable_schemas)
- {
- GenericSet<string>? path_specs = relocatable_schema_paths.lookup (schema_id);
- if (path_specs == null)
- continue;
-
- SettingsSchema? settings_schema = ((!) settings_schema_source).lookup (schema_id, true);
- if (settings_schema == null || ((string?) ((!) settings_schema).get_path ()) != null)
- continue; // TODO better
-
- cached_schemas.add_schema_with_path_specs ((!) settings_schema, (!) path_specs,
modified_path_specs);
- }
-
- cached_schemas.remove_unmarked (modified_path_specs);
-
- this.settings_schema_source = settings_schema_source;
-
- if (modified_path_specs.length > 0)
- paths_changed (modified_path_specs, false);
- }
-
- private void add_relocatable_schema_info (HashTable<string, GenericSet<string>> map, string schema_id,
...)
- {
- GenericSet<string>? schema_info = map.lookup (schema_id);
- if (schema_info == null)
- schema_info = new GenericSet<string> (str_hash, str_equal);
-
- var args = va_list ();
- var next_arg = null;
- while ((next_arg = args.arg ()) != null)
- {
- string path_spec = (string) next_arg;
- if (path_spec == "")
- continue;
- if (!path_spec.has_prefix ("/"))
- path_spec = "/" + path_spec;
- if (!path_spec.has_suffix ("/"))
- path_spec += "/"; // TODO proper validation
- ((!) schema_info).add (path_spec);
- }
- if (((!) schema_info).length > 0)
- map.insert (schema_id, (!) schema_info);
- }
-
-
-
- public static ulong compute_schema_fingerprint (SettingsSchema? schema)
- {
- return 0; // TODO do not take path into consideration, only keys
- }
-
- // We need to create new schema sources in order to detect schema changes, since schema sources cache
the info and the default schema source is also a cached instance
- // This code is adapted from GLib (https://git.gnome.org/browse/glib/tree/gio/gsettingsschema.c#n332)
- private SettingsSchemaSource? create_schema_source ()
- {
- SettingsSchemaSource? source = null;
- string[] system_data_dirs = GLib.Environment.get_system_data_dirs ();
- for (int i = system_data_dirs.length - 1; i >= 0; i--)
- source = try_prepend_dir (source, Path.build_filename (system_data_dirs [i], "glib-2.0",
"schemas"));
- string user_data_dir = GLib.Environment.get_user_data_dir ();
- source = try_prepend_dir (source, Path.build_filename (user_data_dir, "glib-2.0", "schemas"));
- string? var_schema_dir = GLib.Environment.get_variable ("GSETTINGS_SCHEMA_DIR");
- if (var_schema_dir != null)
- source = try_prepend_dir (source, (!) var_schema_dir);
- return source;
- }
-
- private SettingsSchemaSource? try_prepend_dir (SettingsSchemaSource? source, string schemas_dir)
- {
- try {
- return new SettingsSchemaSource.from_directory (schemas_dir, source, true);
- } catch (GLib.Error e) {
- }
- return source;
- }
-
/*\
* * Content lookup
\*/
+ private enum LookupResultType
+ {
+ KEY,
+ FOLDER,
+ NOT_FOUND
+ }
+
private LookupResultType lookup (string path, out GLib.ListStore? key_model, out bool multiple_schemas)
{
key_model = null;
@@ -245,12 +136,12 @@ public class SettingsModel : Object
private void lookup_gsettings (string path, GLib.ListStore key_model, out bool multiple_schemas)
{
multiple_schemas = false;
- if (settings_schema_source == null)
+ if (source_manager.source_is_null ())
return;
GenericSet<SettingsSchema> schemas;
GenericSet<string> folders;
- cached_schemas.lookup (path, out schemas, out folders);
+ source_manager.cached_schemas.lookup (path, out schemas, out folders);
if (schemas.length > 0)
{
bool content_found = false;
@@ -281,49 +172,6 @@ public class SettingsModel : Object
}
/*\
- * * Schemas manipulation
- \*/
-
- public bool is_relocatable_schema (string id)
- {
- string [] non_relocatable_schemas;
- string [] relocatable_schemas;
-
- refresh_schema_source (); // first call
- if (settings_schema_source == null)
- return false; // TODO better
-
- ((!) settings_schema_source).list_schemas (true, out non_relocatable_schemas, out
relocatable_schemas);
-
- return (id in relocatable_schemas);
- }
-
- public bool is_non_relocatable_schema (string id)
- {
- string [] non_relocatable_schemas;
- string [] relocatable_schemas;
-
- if (settings_schema_source == null)
- return false; // TODO better
-
- ((!) settings_schema_source).list_schemas (true, out non_relocatable_schemas, out
relocatable_schemas);
-
- return (id in non_relocatable_schemas);
- }
-
- public string? get_schema_path (string id)
- {
- if (settings_schema_source == null)
- return null; // TODO better
-
- SettingsSchema? schema = ((!) settings_schema_source).lookup (id, true);
- if (schema == null)
- return null;
-
- return ((!) schema).get_path ();
- }
-
- /*\
* * Path requests
\*/
@@ -340,7 +188,7 @@ public class SettingsModel : Object
Directory? dir = null;
uint schemas_count = 0;
uint subpaths_count = 0;
- cached_schemas.get_content_count (path, out schemas_count, out subpaths_count);
+ source_manager.cached_schemas.get_content_count (path, out schemas_count, out subpaths_count);
if (schemas_count + subpaths_count > 0 || client.list (path).length > 0)
{
dir = new Directory (path, get_name (path));
@@ -649,201 +497,13 @@ public class SettingsModel : Object
delayed_settings_hashtable.foreach_remove ((key_descriptor, schema_settings) => {
schema_settings.apply (); return true; });
- try {
- client.change_sync (dconf_changeset, out last_change_tag);
- } catch (Error error) {
- warning (error.message);
- }
- }
-}
-
-public enum LookupResultType
-{
- KEY,
- FOLDER,
- NOT_FOUND
-}
-
-class CachedSchemaInfo
-{
- public SettingsSchema schema;
- public ulong fingerprint;
- public bool marked;
-
- public CachedSchemaInfo (SettingsSchema schema, ulong? fingerprint=null)
- {
- this.schema = schema;
- if (fingerprint != null)
- this.fingerprint = (!) fingerprint;
- else
- this.fingerprint = SettingsModel.compute_schema_fingerprint (schema);
- marked = true;
- }
-}
-
-class SchemaPathTree
-{
- private string path_segment;
- private HashTable<string, CachedSchemaInfo> schemas = new HashTable<string, CachedSchemaInfo> (str_hash,
str_equal);
- private HashTable<string, SchemaPathTree> subtrees = new HashTable<string, SchemaPathTree> (str_hash,
str_equal);
- private SchemaPathTree? wildcard_subtree = null;
-
- public SchemaPathTree (string path_segment)
- {
- this.path_segment = path_segment;
- }
-
- public uint get_schema_count (string path)
- {
- uint schemas_count = 0;
- uint subpaths_count = 0;
- get_content_count (path, out schemas_count, out subpaths_count);
- return schemas_count;
- }
-
- public void get_content_count (string path, out uint schemas_count, out uint subpaths_count)
- {
- GenericSet<SettingsSchema> path_schemas;
- GenericSet<string> subpaths;
- lookup (path, out path_schemas, out subpaths);
- schemas_count = path_schemas.length;
- subpaths_count = subpaths.length;
- }
-
- public bool lookup (string path, out GenericSet<SettingsSchema> path_schemas, out GenericSet<string>
subpaths)
- {
- path_schemas = new GenericSet<SettingsSchema> ((schema) => { return str_hash (schema.get_id ()); },
- (schema1, schema2) => { return str_equal
(schema1.get_id (), schema2.get_id ()); });
- subpaths = new GenericSet<string> (str_hash, str_equal);
- return lookup_segments (SettingsModel.to_segments (path), 0, ref path_schemas, ref subpaths);
- }
-
- private bool lookup_segments (string[] path_segments, int matched_prefix_length, ref
GenericSet<SettingsSchema> path_schemas, ref GenericSet<string> subpaths)
- {
- if (matched_prefix_length == path_segments.length)
- {
- foreach (CachedSchemaInfo schema_info in schemas.get_values ())
- path_schemas.add (schema_info.schema);
- foreach (SchemaPathTree subtree in subtrees.get_values ())
- if (subtree.has_non_wildcard_content ())
- subpaths.add (subtree.path_segment);
- return true;
- }
- bool found = false;
- if (wildcard_subtree != null)
- if (((!) wildcard_subtree).lookup_segments (path_segments, matched_prefix_length + 1, ref
path_schemas, ref subpaths))
- found = true;
- SchemaPathTree? existing_subtree = subtrees.lookup (path_segments [matched_prefix_length]);
- if (existing_subtree != null)
- if (((!) existing_subtree).lookup_segments (path_segments, matched_prefix_length + 1, ref
path_schemas, ref subpaths))
- found = true;
- return found;
- }
-
- public void add_schema (SettingsSchema schema, GenericSet<string> modified_path_specs)
- {
- string? schema_path = schema.get_path ();
- if (schema_path == null)
- return;
- add_schema_to_path_spec (new CachedSchemaInfo (schema), SettingsModel.to_segments ((!) schema_path),
0, modified_path_specs);
- }
-
- public void add_schema_with_path_specs (SettingsSchema schema, GenericSet<string> path_specs,
GenericSet<string> modified_path_specs)
- {
- ulong fingerprint = SettingsModel.compute_schema_fingerprint (schema);
- path_specs.foreach ((path_spec) => {
- add_schema_to_path_spec (new CachedSchemaInfo (schema, fingerprint),
SettingsModel.to_segments (path_spec), 0, modified_path_specs);
- });
- }
-
- private bool add_schema_to_path_spec (CachedSchemaInfo schema_info, string[] path_spec, int
matched_prefix_length, GenericSet<string> modified_path_specs)
- {
- if (matched_prefix_length == path_spec.length)
- {
- CachedSchemaInfo? existing_schema_info = schemas.lookup (schema_info.schema.get_id ());
- if (existing_schema_info != null && ((!) existing_schema_info).fingerprint ==
schema_info.fingerprint)
- {
- ((!) existing_schema_info).schema = schema_info.schema; // drop old schemas to avoid keeping
more than one schema source in memory
- ((!) existing_schema_info).marked = true;
- return false;
- }
- schemas.insert (schema_info.schema.get_id (), schema_info);
- modified_path_specs.add (SettingsModel.to_path (path_spec));
- return true;
- }
- string segment = path_spec [matched_prefix_length];
- if (segment == "")
- {
- if (wildcard_subtree == null)
- wildcard_subtree = new SchemaPathTree (""); // doesn't add an immediate subtree, so doesn't
count as modification for this node
- return ((!) wildcard_subtree).add_schema_to_path_spec (schema_info, path_spec,
matched_prefix_length + 1, modified_path_specs);
- }
- else
- {
- SchemaPathTree? existing_subtree = subtrees.lookup (segment);
- if (existing_subtree == null)
- {
- SchemaPathTree new_subtree = new SchemaPathTree (segment);
- subtrees.insert (segment, new_subtree);
- existing_subtree = new_subtree;
- modified_path_specs.add (SettingsModel.to_path (path_spec [0:matched_prefix_length]));
- }
- return ((!) existing_subtree).add_schema_to_path_spec (schema_info, path_spec,
matched_prefix_length + 1, modified_path_specs);
- }
- }
-
- public void remove_unmarked (GenericSet<string> modified_path_specs)
- {
- remove_unmarked_from_path ("/", modified_path_specs);
- }
-
- private void remove_unmarked_from_path (string path_spec, GenericSet<string> modified_path_specs)
- {
- bool modified = false;
- schemas.foreach_remove ((schema_id, cached_schema_info) => {
- if (!cached_schema_info.marked)
- {
- modified = true;
- return true;
- }
- cached_schema_info.marked = false;
- return false;
- });
-
- if (wildcard_subtree != null)
- {
- ((!) wildcard_subtree).remove_unmarked_from_path (path_spec + "/", modified_path_specs);
- if (((!) wildcard_subtree).is_empty ())
- {
- wildcard_subtree = null; // doesn't remove an immediate subtree, so doesn't count as
modification for this node
- }
- }
-
- string [] empty_subtrees = {};
- foreach (SchemaPathTree subtree in subtrees.get_values ())
+ try
{
- subtree.remove_unmarked_from_path (path_spec + subtree.path_segment + "/", modified_path_specs);
- if (subtree.is_empty ())
- empty_subtrees += subtree.path_segment;
+ client.change_sync (dconf_changeset, out last_change_tag);
}
- if (empty_subtrees.length > 0)
+ catch (Error error)
{
- foreach (string empty_subtree_segment in empty_subtrees)
- subtrees.remove (empty_subtree_segment);
- modified = true;
+ warning (error.message);
}
-
- if (modified)
- modified_path_specs.add (path_spec);
- }
-
- private bool has_non_wildcard_content ()
- {
- return schemas.size() > 0 || subtrees.size () > 0;
- }
-
- private bool is_empty ()
- {
- return schemas.size () == 0 && wildcard_subtree == null && subtrees.size () == 0;
}
}
diff --git a/editor/dconf-window.vala b/editor/dconf-window.vala
index 7d1a35c..d52f3b4 100644
--- a/editor/dconf-window.vala
+++ b/editor/dconf-window.vala
@@ -87,6 +87,7 @@ class DConfWindow : ApplicationWindow
settings.bind ("mouse-forward-button", this, "mouse-forward-button",
SettingsBindFlags.GET|SettingsBindFlags.NO_SENSITIVITY);
/* init current_path */
+ SchemasUtility schemas_utility = new SchemasUtility ();
bool strict = false;
string? first_path = path;
if (schema == null)
@@ -97,7 +98,7 @@ class DConfWindow : ApplicationWindow
if (first_path == null && settings.get_boolean ("restore-view"))
first_path = settings.get_string ("saved-view");
}
- else if (model.is_relocatable_schema ((!) schema))
+ else if (schemas_utility.is_relocatable_schema ((!) schema))
{
if (first_path == null)
{
@@ -120,9 +121,9 @@ class DConfWindow : ApplicationWindow
first_path = (!) first_path + (!) key_name;
}
}
- else if (model.is_non_relocatable_schema ((!) schema))
+ else if (schemas_utility.is_non_relocatable_schema ((!) schema))
{
- string? schema_path = model.get_schema_path ((!) schema);
+ string? schema_path = schemas_utility.get_schema_path ((!) schema);
if (schema_path == null) // something wrong is happening
assert_not_reached (); // TODO warning?
else if (first_path != null && first_path != schema_path)
diff --git a/editor/meson.build b/editor/meson.build
index 0437556..a2cea62 100644
--- a/editor/meson.build
+++ b/editor/meson.build
@@ -81,7 +81,9 @@ sources = files(
'registry-placeholder.vala',
'registry-search.vala',
'registry-view.vala',
- 'setting-object.vala'
+ 'schemas-utility.vala',
+ 'setting-object.vala',
+ 'source-manager.vala'
)
resource_data = files(
diff --git a/editor/schemas-utility.vala b/editor/schemas-utility.vala
new file mode 100644
index 0000000..f8b1aad
--- /dev/null
+++ b/editor/schemas-utility.vala
@@ -0,0 +1,57 @@
+/*
+ This file is part of Dconf Editor
+
+ Dconf Editor is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Dconf Editor 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Dconf Editor. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+public class SchemasUtility : Object
+{
+ private SettingsSchemaSource? settings_schema_source = SettingsSchemaSource.get_default ();
+
+ public bool is_relocatable_schema (string id)
+ {
+ if (settings_schema_source == null)
+ return false; // TODO better
+
+ string [] non_relocatable_schemas;
+ string [] relocatable_schemas;
+ ((!) settings_schema_source).list_schemas (true, out non_relocatable_schemas, out
relocatable_schemas);
+
+ return (id in relocatable_schemas);
+ }
+
+ public bool is_non_relocatable_schema (string id)
+ {
+ if (settings_schema_source == null)
+ return false; // TODO better
+
+ string [] non_relocatable_schemas;
+ string [] relocatable_schemas;
+ ((!) settings_schema_source).list_schemas (true, out non_relocatable_schemas, out
relocatable_schemas);
+
+ return (id in non_relocatable_schemas);
+ }
+
+ public string? get_schema_path (string id)
+ {
+ if (settings_schema_source == null)
+ return null; // TODO better
+
+ SettingsSchema? schema = ((!) settings_schema_source).lookup (id, true);
+ if (schema == null)
+ return null;
+
+ return ((!) schema).get_path ();
+ }
+}
diff --git a/editor/source-manager.vala b/editor/source-manager.vala
new file mode 100644
index 0000000..64dec9f
--- /dev/null
+++ b/editor/source-manager.vala
@@ -0,0 +1,363 @@
+/*
+ This file is part of Dconf Editor
+
+ Dconf Editor is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Dconf Editor 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Dconf Editor. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+public class SourceManager : Object
+{
+ public SchemaPathTree cached_schemas { get; private set; default = new SchemaPathTree ("/"); } // prefix
tree for quick lookup and diff'ing on changes
+
+ /*\
+ * * Schema source
+ \*/
+
+ public signal void paths_changed (GenericSet<string> modified_path_specs);
+
+ private SettingsSchemaSource? settings_schema_source = null;
+
+ public bool source_is_null ()
+ {
+ return settings_schema_source == null;
+ }
+
+ public SettingsSchemaSource get_source ()
+ requires (settings_schema_source != null)
+ {
+ return (!) settings_schema_source;
+ }
+
+ public void refresh_schema_source ()
+ {
+ SettingsSchemaSource? settings_schema_source = create_schema_source ();
+ if (settings_schema_source == null)
+ return;
+
+ GenericSet<string> modified_path_specs = new GenericSet<string> (str_hash, str_equal);
+
+ string [] non_relocatable_schemas;
+ string [] relocatable_schemas;
+ ((!) settings_schema_source).list_schemas (true, out non_relocatable_schemas, out
relocatable_schemas);
+
+ foreach (string schema_id in non_relocatable_schemas)
+ {
+ SettingsSchema? settings_schema = ((!) settings_schema_source).lookup (schema_id, true);
+ if (settings_schema == null)
+ continue; // TODO better
+
+ cached_schemas.add_schema ((!) settings_schema, modified_path_specs);
+ }
+
+ foreach (string schema_id in relocatable_schemas)
+ {
+ GenericSet<string>? path_specs = relocatable_schema_paths.lookup (schema_id);
+ if (path_specs == null)
+ continue;
+
+ SettingsSchema? settings_schema = ((!) settings_schema_source).lookup (schema_id, true);
+ if (settings_schema == null || ((string?) ((!) settings_schema).get_path ()) != null)
+ continue; // TODO better
+
+ cached_schemas.add_schema_with_path_specs ((!) settings_schema, (!) path_specs,
modified_path_specs);
+ }
+
+ cached_schemas.remove_unmarked (modified_path_specs);
+
+ this.settings_schema_source = settings_schema_source;
+
+ if (modified_path_specs.length > 0)
+ paths_changed (modified_path_specs);
+ }
+
+ // We need to create new schema sources in order to detect schema changes, since schema sources cache
the info and the default schema source is also a cached instance
+ // This code is adapted from GLib (https://git.gnome.org/browse/glib/tree/gio/gsettingsschema.c#n332)
+ private SettingsSchemaSource? create_schema_source ()
+ {
+ SettingsSchemaSource? source = null;
+ string[] system_data_dirs = GLib.Environment.get_system_data_dirs ();
+ for (int i = system_data_dirs.length - 1; i >= 0; i--)
+ source = try_prepend_dir (source, Path.build_filename (system_data_dirs [i], "glib-2.0",
"schemas"));
+ string user_data_dir = GLib.Environment.get_user_data_dir ();
+ source = try_prepend_dir (source, Path.build_filename (user_data_dir, "glib-2.0", "schemas"));
+ string? var_schema_dir = GLib.Environment.get_variable ("GSETTINGS_SCHEMA_DIR");
+ if (var_schema_dir != null)
+ source = try_prepend_dir (source, (!) var_schema_dir);
+ return source;
+ }
+
+ private static SettingsSchemaSource? try_prepend_dir (SettingsSchemaSource? source, string schemas_dir)
+ {
+ try
+ {
+ return new SettingsSchemaSource.from_directory (schemas_dir, source, true);
+ }
+ catch (GLib.Error e)
+ {}
+ return source;
+ }
+
+ /*\
+ * * Relocatable schemas
+ \*/
+
+ private HashTable<string, GenericSet<string>> relocatable_schema_paths = new HashTable<string,
GenericSet<string>> (str_hash, str_equal);
+ private HashTable<string, GenericSet<string>> startup_relocatable_schema_paths = new HashTable<string,
GenericSet<string>> (str_hash, str_equal);
+
+ public void refresh_relocatable_schema_paths (bool user_schemas,
+ bool built_in_schemas,
+ bool internal_schemas,
+ bool startup_schemas,
+ Variant user_paths_variant)
+ {
+ relocatable_schema_paths.remove_all ();
+ if (user_schemas)
+ {
+ VariantIter entries_iter;
+ user_paths_variant.get ("a{ss}", out entries_iter);
+ string schema_id;
+ string path_spec;
+ while (entries_iter.next ("{ss}", out schema_id, out path_spec))
+ add_relocatable_schema_info (relocatable_schema_paths, schema_id, path_spec);
+ }
+ if (built_in_schemas)
+ {
+ string [,] known_mappings = ConfigurationEditor.known_mappings;
+ for (int i = 0; i < known_mappings.length [0]; i++)
+ add_relocatable_schema_info (relocatable_schema_paths, known_mappings [i,0], known_mappings
[i,1]);
+ }
+ if (startup_schemas)
+ {
+ startup_relocatable_schema_paths.foreach ((schema_id, paths) => {
+ paths.foreach ((path_spec) => add_relocatable_schema_info (relocatable_schema_paths,
schema_id, path_spec));
+ });
+ }
+ }
+
+ public void add_mapping (string schema, string path)
+ {
+ add_relocatable_schema_info (startup_relocatable_schema_paths, schema, path);
+ }
+
+ private void add_relocatable_schema_info (HashTable<string, GenericSet<string>> map, string schema_id,
...)
+ {
+ GenericSet<string>? schema_info = map.lookup (schema_id);
+ if (schema_info == null)
+ schema_info = new GenericSet<string> (str_hash, str_equal);
+
+ var args = va_list ();
+ var next_arg = null;
+ while ((next_arg = args.arg ()) != null)
+ {
+ string path_spec = (string) next_arg;
+ if (path_spec == "")
+ continue;
+ if (!path_spec.has_prefix ("/"))
+ path_spec = "/" + path_spec;
+ if (!path_spec.has_suffix ("/"))
+ path_spec += "/"; // TODO proper validation
+ ((!) schema_info).add (path_spec);
+ }
+ if (((!) schema_info).length > 0)
+ map.insert (schema_id, (!) schema_info);
+ }
+}
+
+public class SchemaPathTree
+{
+ private string path_segment;
+ private HashTable<string, CachedSchemaInfo> schemas = new HashTable<string, CachedSchemaInfo> (str_hash,
str_equal);
+ private HashTable<string, SchemaPathTree> subtrees = new HashTable<string, SchemaPathTree> (str_hash,
str_equal);
+ private SchemaPathTree? wildcard_subtree = null;
+
+ public SchemaPathTree (string path_segment)
+ {
+ this.path_segment = path_segment;
+ }
+
+ public uint get_schema_count (string path)
+ {
+ uint schemas_count = 0;
+ uint subpaths_count = 0;
+ get_content_count (path, out schemas_count, out subpaths_count);
+ return schemas_count;
+ }
+
+ public void get_content_count (string path, out uint schemas_count, out uint subpaths_count)
+ {
+ GenericSet<SettingsSchema> path_schemas;
+ GenericSet<string> subpaths;
+ lookup (path, out path_schemas, out subpaths);
+ schemas_count = path_schemas.length;
+ subpaths_count = subpaths.length;
+ }
+
+ public bool lookup (string path, out GenericSet<SettingsSchema> path_schemas, out GenericSet<string>
subpaths)
+ {
+ path_schemas = new GenericSet<SettingsSchema> ((schema) => { return str_hash (schema.get_id ()); },
+ (schema1, schema2) => { return str_equal
(schema1.get_id (), schema2.get_id ()); });
+ subpaths = new GenericSet<string> (str_hash, str_equal);
+ return lookup_segments (SettingsModel.to_segments (path), 0, ref path_schemas, ref subpaths);
+ }
+
+ private bool lookup_segments (string[] path_segments, int matched_prefix_length, ref
GenericSet<SettingsSchema> path_schemas, ref GenericSet<string> subpaths)
+ {
+ if (matched_prefix_length == path_segments.length)
+ {
+ foreach (CachedSchemaInfo schema_info in schemas.get_values ())
+ path_schemas.add (schema_info.schema);
+ foreach (SchemaPathTree subtree in subtrees.get_values ())
+ if (subtree.has_non_wildcard_content ())
+ subpaths.add (subtree.path_segment);
+ return true;
+ }
+ bool found = false;
+ if (wildcard_subtree != null)
+ if (((!) wildcard_subtree).lookup_segments (path_segments, matched_prefix_length + 1, ref
path_schemas, ref subpaths))
+ found = true;
+ SchemaPathTree? existing_subtree = subtrees.lookup (path_segments [matched_prefix_length]);
+ if (existing_subtree != null)
+ if (((!) existing_subtree).lookup_segments (path_segments, matched_prefix_length + 1, ref
path_schemas, ref subpaths))
+ found = true;
+ return found;
+ }
+
+ public void add_schema (SettingsSchema schema, GenericSet<string> modified_path_specs)
+ {
+ string? schema_path = schema.get_path ();
+ if (schema_path == null)
+ return;
+ add_schema_to_path_spec (new CachedSchemaInfo (schema), SettingsModel.to_segments ((!) schema_path),
0, modified_path_specs);
+ }
+
+ public void add_schema_with_path_specs (SettingsSchema schema, GenericSet<string> path_specs,
GenericSet<string> modified_path_specs)
+ {
+ ulong fingerprint = CachedSchemaInfo.compute_schema_fingerprint (schema);
+ path_specs.foreach ((path_spec) => {
+ add_schema_to_path_spec (new CachedSchemaInfo (schema, fingerprint),
SettingsModel.to_segments (path_spec), 0, modified_path_specs);
+ });
+ }
+
+ private bool add_schema_to_path_spec (CachedSchemaInfo schema_info, string[] path_spec, int
matched_prefix_length, GenericSet<string> modified_path_specs)
+ {
+ if (matched_prefix_length == path_spec.length)
+ {
+ CachedSchemaInfo? existing_schema_info = schemas.lookup (schema_info.schema.get_id ());
+ if (existing_schema_info != null && ((!) existing_schema_info).fingerprint ==
schema_info.fingerprint)
+ {
+ ((!) existing_schema_info).schema = schema_info.schema; // drop old schemas to avoid keeping
more than one schema source in memory
+ ((!) existing_schema_info).marked = true;
+ return false;
+ }
+ schemas.insert (schema_info.schema.get_id (), schema_info);
+ modified_path_specs.add (SettingsModel.to_path (path_spec));
+ return true;
+ }
+ string segment = path_spec [matched_prefix_length];
+ if (segment == "")
+ {
+ if (wildcard_subtree == null)
+ wildcard_subtree = new SchemaPathTree (""); // doesn't add an immediate subtree, so doesn't
count as modification for this node
+ return ((!) wildcard_subtree).add_schema_to_path_spec (schema_info, path_spec,
matched_prefix_length + 1, modified_path_specs);
+ }
+ else
+ {
+ SchemaPathTree? existing_subtree = subtrees.lookup (segment);
+ if (existing_subtree == null)
+ {
+ SchemaPathTree new_subtree = new SchemaPathTree (segment);
+ subtrees.insert (segment, new_subtree);
+ existing_subtree = new_subtree;
+ modified_path_specs.add (SettingsModel.to_path (path_spec [0:matched_prefix_length]));
+ }
+ return ((!) existing_subtree).add_schema_to_path_spec (schema_info, path_spec,
matched_prefix_length + 1, modified_path_specs);
+ }
+ }
+
+ public void remove_unmarked (GenericSet<string> modified_path_specs)
+ {
+ remove_unmarked_from_path ("/", modified_path_specs);
+ }
+
+ private void remove_unmarked_from_path (string path_spec, GenericSet<string> modified_path_specs)
+ {
+ bool modified = false;
+ schemas.foreach_remove ((schema_id, cached_schema_info) => {
+ if (!cached_schema_info.marked)
+ {
+ modified = true;
+ return true;
+ }
+ cached_schema_info.marked = false;
+ return false;
+ });
+
+ if (wildcard_subtree != null)
+ {
+ ((!) wildcard_subtree).remove_unmarked_from_path (path_spec + "/", modified_path_specs);
+ if (((!) wildcard_subtree).is_empty ())
+ {
+ wildcard_subtree = null; // doesn't remove an immediate subtree, so doesn't count as
modification for this node
+ }
+ }
+
+ string [] empty_subtrees = {};
+ foreach (SchemaPathTree subtree in subtrees.get_values ())
+ {
+ subtree.remove_unmarked_from_path (path_spec + subtree.path_segment + "/", modified_path_specs);
+ if (subtree.is_empty ())
+ empty_subtrees += subtree.path_segment;
+ }
+ if (empty_subtrees.length > 0)
+ {
+ foreach (string empty_subtree_segment in empty_subtrees)
+ subtrees.remove (empty_subtree_segment);
+ modified = true;
+ }
+
+ if (modified)
+ modified_path_specs.add (path_spec);
+ }
+
+ private bool has_non_wildcard_content ()
+ {
+ return schemas.size() > 0 || subtrees.size () > 0;
+ }
+
+ private bool is_empty ()
+ {
+ return schemas.size () == 0 && wildcard_subtree == null && subtrees.size () == 0;
+ }
+}
+
+class CachedSchemaInfo
+{
+ public SettingsSchema schema;
+ public ulong fingerprint;
+ public bool marked;
+
+ public static ulong compute_schema_fingerprint (SettingsSchema? schema)
+ {
+ return 0; // TODO do not take path into consideration, only keys
+ }
+
+ public CachedSchemaInfo (SettingsSchema schema, ulong? fingerprint = null)
+ {
+ this.schema = schema;
+ if (fingerprint != null)
+ this.fingerprint = (!) fingerprint;
+ else
+ this.fingerprint = compute_schema_fingerprint (schema);
+ marked = true;
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]