[folks] Bug 648767 — Return read-only views of Sets and Maps where possible



commit fbd2a2d588541177c1e2441d28f1bc70f7e75c88
Author: Philip Withnall <philip tecnocode co uk>
Date:   Wed Apr 27 15:49:33 2011 +0100

    Bug 648767 â?? Return read-only views of Sets and Maps where possible
    
    This prevents clients from messing up libfolks' state by modifying Sets or
    Maps returned to them by libfolks when those Sets or Maps are just references
    to the ones used internally by libfolks, as is the case with many
    properties.
    
    Closes: bgo#648767

 backends/key-file/kf-backend.vala                |    4 +-
 backends/key-file/kf-persona-store.vala          |   14 ++---
 backends/libsocialweb/lib/swf-persona-store.vala |   10 ++--
 backends/libsocialweb/lib/swf-persona.vala       |    4 +-
 backends/libsocialweb/sw-backend.vala            |    8 ++-
 backends/telepathy/lib/tpf-persona-store.vala    |   28 ++++------
 backends/telepathy/lib/tpf-persona.vala          |    4 +-
 backends/telepathy/tp-backend.vala               |    4 +-
 backends/tracker/lib/trf-persona-store.vala      |   13 ++--
 backends/tracker/lib/trf-persona.vala            |   65 ++++++++++++++-------
 backends/tracker/tr-backend.vala                 |    4 +-
 folks/backend-store.vala                         |    7 ++-
 folks/field-details.vala                         |    2 +-
 folks/individual-aggregator.vala                 |   66 ++++++++++++++++++----
 folks/individual.vala                            |   38 +++++++++++-
 folks/persona-store.vala                         |   30 ++++++++++
 folks/postal-address-details.vala                |    4 +-
 17 files changed, 219 insertions(+), 86 deletions(-)
---
diff --git a/backends/key-file/kf-backend.vala b/backends/key-file/kf-backend.vala
index beefe73..53b8191 100644
--- a/backends/key-file/kf-backend.vala
+++ b/backends/key-file/kf-backend.vala
@@ -36,6 +36,7 @@ public class Folks.Backends.Kf.Backend : Folks.Backend
 {
   private bool _is_prepared = false;
   private HashMap<string, PersonaStore> _persona_stores;
+  private Map<string, PersonaStore> _persona_stores_ro;
 
   /**
    * Whether this Backend has been prepared.
@@ -59,7 +60,7 @@ public class Folks.Backends.Kf.Backend : Folks.Backend
    */
   public override Map<string, PersonaStore> persona_stores
     {
-      get { return this._persona_stores; }
+      get { return this._persona_stores_ro; }
     }
 
   /**
@@ -68,6 +69,7 @@ public class Folks.Backends.Kf.Backend : Folks.Backend
   public Backend ()
     {
       this._persona_stores = new HashMap<string, PersonaStore> ();
+      this._persona_stores_ro = this._persona_stores.read_only_view;
     }
 
   /**
diff --git a/backends/key-file/kf-persona-store.vala b/backends/key-file/kf-persona-store.vala
index f5fee5b..32478f1 100644
--- a/backends/key-file/kf-persona-store.vala
+++ b/backends/key-file/kf-persona-store.vala
@@ -33,6 +33,7 @@ using Folks.Backends.Kf;
 public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
 {
   private HashMap<string, Persona> _personas;
+  private Map<string, Persona> _personas_ro;
   private File _file;
   private GLib.KeyFile _key_file;
   private unowned Cancellable _save_key_file_cancellable = null;
@@ -108,7 +109,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
    */
   public override Map<string, Persona> personas
     {
-      get { return this._personas; }
+      get { return this._personas_ro; }
     }
 
   /**
@@ -127,6 +128,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
       this.trust_level = PersonaStoreTrust.FULL;
       this._file = key_file;
       this._personas = new HashMap<string, Persona> ();
+      this._personas_ro = this._personas.read_only_view;
     }
 
   /**
@@ -239,9 +241,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
                 {
                   /* FIXME: GroupDetails.ChangeReason is not the right enum to
                    * use here */
-                  this.personas_changed (added_personas,
-                      new HashSet<Persona> (),
-                      null, null, GroupDetails.ChangeReason.NONE);
+                  this._emit_personas_changed (added_personas, null);
                 }
 
               this._is_prepared = true;
@@ -282,8 +282,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
           var personas = new HashSet<Folks.Persona> ();
           personas.add (persona);
 
-          this.personas_changed (new HashSet<Persona> (), personas,
-              null, null, 0);
+          this._emit_personas_changed (null, personas);
         }
       catch (KeyFileError e)
         {
@@ -357,8 +356,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
       var personas = new HashSet<Persona> ();
       personas.add (persona);
 
-      this.personas_changed (personas, new HashSet<Persona> (),
-          null, null, GroupDetails.ChangeReason.NONE);
+      this._emit_personas_changed (personas, null);
 
       return persona;
     }
diff --git a/backends/libsocialweb/lib/swf-persona-store.vala b/backends/libsocialweb/lib/swf-persona-store.vala
index c6f420a..9215e75 100644
--- a/backends/libsocialweb/lib/swf-persona-store.vala
+++ b/backends/libsocialweb/lib/swf-persona-store.vala
@@ -35,6 +35,7 @@ extern const string BACKEND_NAME;
 public class Swf.PersonaStore : Folks.PersonaStore
 {
   private HashMap<string, Persona> _personas;
+  private Map<string, Persona> _personas_ro;
   private bool _is_prepared = false;
   private ClientService _service;
   private ClientContactView _contact_view;
@@ -113,7 +114,7 @@ public class Swf.PersonaStore : Folks.PersonaStore
    */
   public override Map<string, Persona> personas
     {
-      get { return this._personas; }
+      get { return this._personas_ro; }
     }
 
   /**
@@ -130,6 +131,7 @@ public class Swf.PersonaStore : Folks.PersonaStore
       this.trust_level = PersonaStoreTrust.PARTIAL;
       this._service = service;
       this._personas = new HashMap<string, Persona> ();
+      this._personas_ro = this._personas.read_only_view;
     }
 
   ~PersonaStore ()
@@ -229,8 +231,7 @@ public class Swf.PersonaStore : Folks.PersonaStore
 
       if (added_personas.size > 0)
         {
-          this.personas_changed (added_personas, new HashSet<Persona> (),
-              null, null, 0);
+          this._emit_personas_changed (added_personas, null);
         }
     }
 
@@ -269,8 +270,7 @@ public class Swf.PersonaStore : Folks.PersonaStore
 
       if (removed_personas.size > 0)
         {
-          this.personas_changed (new HashSet<Persona> (), removed_personas,
-              null, null, 0);
+          this._emit_personas_changed (null, removed_personas);
         }
     }
 }
diff --git a/backends/libsocialweb/lib/swf-persona.vala b/backends/libsocialweb/lib/swf-persona.vala
index c913331..b2d1bda 100644
--- a/backends/libsocialweb/lib/swf-persona.vala
+++ b/backends/libsocialweb/lib/swf-persona.vala
@@ -80,16 +80,18 @@ public class Swf.Persona : Folks.Persona,
   public Gender gender { get; private set; }
 
   private HashSet<FieldDetails> _urls;
+  private Set<FieldDetails> _urls_ro;
 
   /**
    * { inheritDoc}
    */
   public Set<FieldDetails> urls
     {
-      get { return this._urls; }
+      get { return this._urls_ro; }
       private set
         {
           this._urls = new HashSet<FieldDetails> ();
+          this._urls_ro = this._urls.read_only_view;
           foreach (var ps in value)
             this._urls.add (ps);
         }
diff --git a/backends/libsocialweb/sw-backend.vala b/backends/libsocialweb/sw-backend.vala
index efbc9e6..120bac5 100644
--- a/backends/libsocialweb/sw-backend.vala
+++ b/backends/libsocialweb/sw-backend.vala
@@ -35,8 +35,8 @@ public class Folks.Backends.Sw.Backend : Folks.Backend
 {
   private bool _is_prepared = false;
   private Client _client;
-  private HashMap<string, PersonaStore> _persona_stores =
-      new HashMap<string, PersonaStore> ();
+  private HashMap<string, PersonaStore> _persona_stores;
+  private Map<string, PersonaStore> _persona_stores_ro;
 
   /**
    * { inheritDoc}
@@ -48,7 +48,7 @@ public class Folks.Backends.Sw.Backend : Folks.Backend
    */
   public override Map<string, PersonaStore> persona_stores
     {
-      get { return this._persona_stores; }
+      get { return this._persona_stores_ro; }
     }
 
 
@@ -57,6 +57,8 @@ public class Folks.Backends.Sw.Backend : Folks.Backend
    */
   public Backend ()
     {
+      this._persona_stores = new HashMap<string, PersonaStore> ();
+      this._persona_stores_ro = this._persona_stores.read_only_view;
     }
 
   /**
diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala
index 472035b..5adca55 100644
--- a/backends/telepathy/lib/tpf-persona-store.vala
+++ b/backends/telepathy/lib/tpf-persona-store.vala
@@ -61,6 +61,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
       };
 
   private HashMap<string, Persona> _personas;
+  private Map<string, Persona> _personas_ro;
   private HashSet<Persona> _persona_set;
   /* universal, contact owner handles (not channel-specific) */
   private HashMap<uint, Persona> _handle_persona_map;
@@ -172,7 +173,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
    */
   public override Map<string, Persona> personas
     {
-      get { return this._personas; }
+      get { return this._personas_ro; }
     }
 
   /**
@@ -203,6 +204,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
         this.trust_level = PersonaStoreTrust.PARTIAL;
 
       this._personas = new HashMap<string, Persona> ();
+      this._personas_ro = this._personas.read_only_view;
       this._persona_set = new HashSet<Persona> ();
 
       if (this._conn != null)
@@ -273,8 +275,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
                 {
                   if (this.account == a)
                     {
-                      this.personas_changed (new HashSet<Persona> (),
-                          this._persona_set, null, null, 0);
+                      this._emit_personas_changed (null, this._persona_set);
                       this.removed ();
                     }
                 });
@@ -282,8 +283,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
                 {
                   if (this.account == a)
                     {
-                      this.personas_changed (new HashSet<Persona> (),
-                          this._persona_set, null, null, 0);
+                      this._emit_personas_changed (null, this._persona_set);
                       this.removed ();
                     }
                 });
@@ -292,8 +292,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
                     {
                       if (!valid && this.account == a)
                         {
-                          this.personas_changed (new HashSet<Persona> (),
-                              this._persona_set, null, null, 0);
+                          this._emit_personas_changed (null, this._persona_set);
                           this.removed ();
                         }
                     });
@@ -487,8 +486,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
           /* When disconnecting, we want the PersonaStore to remain alive, but
            * all its Personas to be removed. We do *not* want the PersonaStore
            * to be destroyed, as that makes coming back online hard. */
-          this.personas_changed (new HashSet<Persona> (),
-              this._persona_set, null, null, 0);
+          this._emit_personas_changed (null, this._persona_set);
           this._reset ();
           return;
         }
@@ -650,8 +648,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
               personas.add (persona);
 
               this._self_contact = contact;
-              this.personas_changed (personas, new HashSet<Persona> (),
-                  null, null, 0);
+              this._emit_personas_changed (personas, null);
             },
           this);
     }
@@ -1022,8 +1019,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
       var personas = new HashSet<Persona> ();
       personas.add (persona);
 
-      this.personas_changed (new HashSet<Persona> (), personas,
-          message, actor, reason);
+      this._emit_personas_changed (null, personas, message, actor, reason);
       this._personas.unset (persona.iid);
       this._persona_set.remove (persona);
     }
@@ -1383,8 +1379,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
 
       if (personas.size > 0)
         {
-          this.personas_changed (personas, new HashSet<Persona> (),
-              null, null, 0);
+          this._emit_personas_changed (personas, null);
         }
 
       return personas;
@@ -1459,8 +1454,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
 
       if (personas.size > 0)
         {
-          this.personas_changed (personas, new HashSet<Persona> (),
-              null, null, 0);
+          this._emit_personas_changed (personas, null);
         }
     }
 
diff --git a/backends/telepathy/lib/tpf-persona.vala b/backends/telepathy/lib/tpf-persona.vala
index 0e2a09e..309cd1f 100644
--- a/backends/telepathy/lib/tpf-persona.vala
+++ b/backends/telepathy/lib/tpf-persona.vala
@@ -36,6 +36,7 @@ public class Tpf.Persona : Folks.Persona,
     PresenceDetails
 {
   private HashSet<string> _groups;
+  private Set<string> _groups_ro;
   private bool _is_favourite;
   private string _alias;
   private HashMultiMap<string, string> _im_addresses;
@@ -148,7 +149,7 @@ public class Tpf.Persona : Folks.Persona,
    */
   public Set<string> groups
     {
-      get { return this._groups; }
+      get { return this._groups_ro; }
 
       set
         {
@@ -268,6 +269,7 @@ public class Tpf.Persona : Folks.Persona,
 
       /* Groups */
       this._groups = new HashSet<string> ();
+      this._groups_ro = this._groups.read_only_view;
 
       contact.notify["avatar-file"].connect ((s, p) =>
         {
diff --git a/backends/telepathy/tp-backend.vala b/backends/telepathy/tp-backend.vala
index 9ff9194..b67de00 100644
--- a/backends/telepathy/tp-backend.vala
+++ b/backends/telepathy/tp-backend.vala
@@ -35,6 +35,7 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
   private AccountManager _account_manager;
   private bool _is_prepared = false;
   private HashMap<string, PersonaStore> _persona_stores;
+  private Map<string, PersonaStore> _persona_stores_ro;
 
   /**
    * { inheritDoc}
@@ -46,7 +47,7 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
    */
   public override Map<string, PersonaStore> persona_stores
     {
-      get { return this._persona_stores; }
+      get { return this._persona_stores_ro; }
     }
 
   /**
@@ -55,6 +56,7 @@ public class Folks.Backends.Tp.Backend : Folks.Backend
   public Backend ()
     {
       this._persona_stores = new HashMap<string, PersonaStore> ();
+      this._persona_stores_ro = this._persona_stores.read_only_view;
     }
 
   /**
diff --git a/backends/tracker/lib/trf-persona-store.vala b/backends/tracker/lib/trf-persona-store.vala
index 0ce7695..8a806d4 100644
--- a/backends/tracker/lib/trf-persona-store.vala
+++ b/backends/tracker/lib/trf-persona-store.vala
@@ -162,6 +162,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
   private const string _OBJECT_IFACE = "org.freedesktop.Tracker1.Resources";
   private const string _OBJECT_PATH = "/org/freedesktop/Tracker1/Resources";
   private HashMap<string, Persona> _personas;
+  private Map<string, Persona> _personas_ro;
   private bool _is_prepared = false;
   private static const int _default_timeout = 100;
   private Resources _resources_object;
@@ -358,7 +359,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
    */
   public override Map<string, Persona> personas
     {
-      get { return this._personas; }
+      get { return this._personas_ro; }
     }
 
   /**
@@ -370,6 +371,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
     {
       Object (id: BACKEND_NAME, display_name: BACKEND_NAME);
       this._personas = new HashMap<string, Persona> ();
+      this._personas_ro = this._personas.read_only_view;
       debug ("Initial query : \n%s\n", this._INITIAL_QUERY);
     }
 
@@ -1235,8 +1237,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
 
       if (removed_personas.size > 0)
         {
-          this.personas_changed (new HashSet<Persona> (), removed_personas,
-              null, null, 0);
+          this._emit_personas_changed (null, removed_personas);
         }
     }
 
@@ -1266,8 +1267,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
 
       if (added_personas.size > 0)
         {
-          this.personas_changed (added_personas, new HashSet<Persona> (),
-              null, null, 0);
+          this._emit_personas_changed (added_personas, null);
         }
     }
 
@@ -1293,8 +1293,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
 
         if (added_personas.size > 0)
           {
-            this.personas_changed (added_personas,
-                new HashSet<Persona> (), null, null, 0);
+            this._emit_personas_changed (added_personas, null);
           }
       } catch (GLib.Error e) {
         warning ("Couldn't perform queries: %s %s", query, e.message);
diff --git a/backends/tracker/lib/trf-persona.vala b/backends/tracker/lib/trf-persona.vala
index da5c01f..5db69c6 100644
--- a/backends/tracker/lib/trf-persona.vala
+++ b/backends/tracker/lib/trf-persona.vala
@@ -50,8 +50,10 @@ public class Trf.Persona : Folks.Persona,
   private bool _is_favourite;
   private const string[] _linkable_properties =
       {"im-addresses", "local-ids", "web-service-addresses"};
-  private HashSet<FieldDetails> _phone_numbers = new HashSet<FieldDetails> ();
-  private HashSet<FieldDetails> _email_addresses = new HashSet<FieldDetails> ();
+  private HashSet<FieldDetails> _phone_numbers;
+  private Set<FieldDetails> _phone_numbers_ro;
+  private HashSet<FieldDetails> _email_addresses;
+  private Set<FieldDetails> _email_addresses_ro;
   private weak Sparql.Cursor _cursor;
   private string _tracker_id;
 
@@ -79,7 +81,7 @@ public class Trf.Persona : Folks.Persona,
    */
   public Set<FieldDetails> phone_numbers
     {
-      get { return this._phone_numbers; }
+      get { return this._phone_numbers_ro; }
       public set
         {
           ((Trf.PersonaStore) this.store)._set_phones (this, value);
@@ -91,7 +93,7 @@ public class Trf.Persona : Folks.Persona,
    */
   public Set<FieldDetails> email_addresses
     {
-      get { return this._email_addresses; }
+      get { return this._email_addresses_ro; }
       public set
         {
           ((Trf.PersonaStore) this.store)._set_emails (this, value);
@@ -166,11 +168,6 @@ public class Trf.Persona : Folks.Persona,
         }
     }
 
-  private HashSet<Role> _roles =
-      new HashSet<Role> ((GLib.HashFunc) Role.hash,
-      (GLib.EqualFunc) Role.equal);
-
-
   private DateTime _birthday;
   /**
    * { inheritDoc}
@@ -186,57 +183,60 @@ public class Trf.Persona : Folks.Persona,
 
   public string calendar_event_id { get; set; }
 
+  private HashSet<Role> _roles;
+  private Set<Role> _roles_ro;
+
   /**
    * { inheritDoc}
    */
   public Set<Role> roles
     {
-      get { return this._roles; }
+      get { return this._roles_ro; }
       public set
         {
           ((Trf.PersonaStore) this.store)._set_roles (this, value);
         }
     }
 
-  private HashSet<Note> _notes =
-      new HashSet<Note> ((GLib.HashFunc) Note.hash,
-      (GLib.EqualFunc) Note.equal);
+  private HashSet<Note> _notes;
+  private Set<Note> _notes_ro;
 
   /**
    * { inheritDoc}
    */
   public Set<Note> notes
     {
-      get { return this._notes; }
+      get { return this._notes_ro; }
       private set
         {
           ((Trf.PersonaStore) this.store)._set_notes (this, value);
         }
     }
 
-  private HashSet<FieldDetails> _urls = new HashSet<FieldDetails> ();
+  private HashSet<FieldDetails> _urls;
+  private Set<FieldDetails> _urls_ro;
 
   /**
    * { inheritDoc}
    */
   public Set<FieldDetails> urls
     {
-      get { return this._urls; }
+      get { return this._urls_ro; }
       public set
         {
           ((Trf.PersonaStore) this.store)._set_urls (this, value);
         }
     }
 
-  private HashSet<PostalAddress> _postal_addresses =
-      new HashSet<PostalAddress> ();
+  private HashSet<PostalAddress> _postal_addresses;
+  private Set<PostalAddress> _postal_addresses_ro;
 
   /**
    * { inheritDoc}
    */
   public Set<PostalAddress> postal_addresses
     {
-      get { return this._postal_addresses; }
+      get { return this._postal_addresses_ro; }
       private set
         {
           ((Trf.PersonaStore) this.store)._set_postal_addresses (this, value);
@@ -282,7 +282,8 @@ public class Trf.Persona : Folks.Persona,
           }
       }
 
-  private HashSet<string> _local_ids = new HashSet<string> ();
+  private HashSet<string> _local_ids;
+  private Set<string> _local_ids_ro;
 
   /**
    * IDs used to link { link Trf.Persona}s.
@@ -295,7 +296,7 @@ public class Trf.Persona : Folks.Persona,
             {
               this._local_ids.add (this.uid);
             }
-          return this._local_ids;
+          return this._local_ids_ro;
         }
       set
         {
@@ -372,6 +373,22 @@ public class Trf.Persona : Folks.Persona,
       this._full_name = fullname;
       this._tracker_id = tracker_id;
       this._structured_name = new StructuredName (null, null, null, null, null);
+      this._phone_numbers = new HashSet<FieldDetails> ();
+      this._phone_numbers_ro = this._phone_numbers.read_only_view;
+      this._email_addresses = new HashSet<FieldDetails> ();
+      this._email_addresses_ro = this._email_addresses.read_only_view;
+      this._roles = new HashSet<Role> ((GLib.HashFunc) Role.hash,
+          (GLib.EqualFunc) Role.equal);
+      this._roles_ro = this._roles.read_only_view;
+      this._notes = new HashSet<Note> ((GLib.HashFunc) Note.hash,
+          (GLib.EqualFunc) Note.equal);
+      this._notes_ro = this._notes.read_only_view;
+      this._urls = new HashSet<FieldDetails> ();
+      this._urls_ro = this._urls.read_only_view;
+      this._postal_addresses = new HashSet<PostalAddress> ();
+      this._postal_addresses_ro = this._postal_addresses.read_only_view;
+      this._local_ids = new HashSet<string> ();
+      this._local_ids_ro = this._local_ids.read_only_view;
 
       if (cursor != null)
         {
@@ -566,6 +583,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       this._postal_addresses = postal_addresses;
+      this._postal_addresses_ro = this._postal_addresses.read_only_view;
     }
 
  private void _update_local_ids ()
@@ -719,6 +737,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       this._roles = roles;
+      this._roles_ro = this._roles.read_only_view;
     }
 
   internal bool _add_role (string tracker_id, string? title, string? org)
@@ -797,6 +816,7 @@ public class Trf.Persona : Folks.Persona,
     {
       this._local_ids =
           (HashSet<string>) Trf.PersonaStore.unserialize_local_ids (local_ids);
+      this._local_ids_ro = this._local_ids.read_only_view;
       this.notify_property ("local-ids");
       return true;
     }
@@ -924,6 +944,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       this._phone_numbers = phones;
+      this._phone_numbers_ro = this._phone_numbers.read_only_view;
     }
 
   internal bool _add_phone (string phone, string tracker_id)
@@ -1043,6 +1064,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       this._email_addresses = email_addresses;
+      this._email_addresses_ro = this._email_addresses.read_only_view;
     }
 
   private void _update_urls ()
@@ -1087,6 +1109,7 @@ public class Trf.Persona : Folks.Persona,
         }
 
       this._urls = urls;
+      this._urls_ro = this._urls.read_only_view;
     }
 
   internal bool _add_url (string url, string tracker_id, string type = "")
diff --git a/backends/tracker/tr-backend.vala b/backends/tracker/tr-backend.vala
index 09f4178..f67b0cd 100644
--- a/backends/tracker/tr-backend.vala
+++ b/backends/tracker/tr-backend.vala
@@ -34,6 +34,7 @@ public class Folks.Backends.Tr.Backend : Folks.Backend
 {
   private bool _is_prepared = false;
   private HashMap<string, PersonaStore> _persona_stores;
+  private Map<string, PersonaStore> _persona_stores_ro;
 
   /**
    * { inheritDoc}
@@ -45,7 +46,7 @@ public class Folks.Backends.Tr.Backend : Folks.Backend
    */
   public override Map<string, PersonaStore> persona_stores
     {
-      get { return this._persona_stores; }
+      get { return this._persona_stores_ro; }
     }
 
   /**
@@ -54,6 +55,7 @@ public class Folks.Backends.Tr.Backend : Folks.Backend
   public Backend ()
     {
       this._persona_stores = new HashMap<string, PersonaStore> ();
+      this._persona_stores_ro = this._persona_stores.read_only_view;
     }
 
   /**
diff --git a/folks/backend-store.vala b/folks/backend-store.vala
index 8fa7dcc..92ad1d3 100644
--- a/folks/backend-store.vala
+++ b/folks/backend-store.vala
@@ -44,6 +44,7 @@ public class Folks.BackendStore : Object {
   /* this contains all backends, regardless of enabled or prepared state */
   private HashMap<string,Backend> _backend_hash;
   private HashMap<string, Backend> _prepared_backends;
+  private Map<string, Backend> _prepared_backends_ro;
   private File _config_file;
   private GLib.KeyFile _backends_key_file;
   private HashMap<string,unowned Module> _modules;
@@ -91,7 +92,8 @@ public class Folks.BackendStore : Object {
    */
   public Map<string, Backend> enabled_backends
     {
-      get { return this._prepared_backends; }
+      /* Return a read-only view of the map */
+      get { return this._prepared_backends_ro; }
 
       private set {}
     }
@@ -138,6 +140,7 @@ public class Folks.BackendStore : Object {
       this._backend_hash = new HashMap<string,Backend> (str_hash, str_equal);
       this._prepared_backends = new HashMap<string,Backend> (str_hash,
           str_equal);
+      this._prepared_backends_ro = this._prepared_backends.read_only_view;
     }
 
   ~BackendStore ()
@@ -403,7 +406,7 @@ public class Folks.BackendStore : Object {
    */
   public Collection<Backend> list_backends ()
     {
-      return this._backend_hash.values;
+      return this._backend_hash.values.read_only_view;
     }
 
   /**
diff --git a/folks/field-details.vala b/folks/field-details.vala
index c3b9afe..daa670e 100644
--- a/folks/field-details.vala
+++ b/folks/field-details.vala
@@ -86,7 +86,7 @@ public class Folks.FieldDetails : Object
           return null;
         }
 
-      return this.parameters.get (parameter_name);
+      return this.parameters.get (parameter_name).read_only_view;
     }
 
   /**
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index 154311f..be208e7 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -95,6 +95,9 @@ public class Folks.IndividualAggregator : Object
       get { return this._writeable_store; }
     }
 
+  private Map<string, Individual> _individuals;
+  private Map<string, Individual> _individuals_ro;
+
   /**
    * A map from { link Individual.id}s to their { link Individual}s.
    *
@@ -107,7 +110,15 @@ public class Folks.IndividualAggregator : Object
    *
    * @since UNRELEASED
    */
-  public Map<string, Individual> individuals { get; private set; }
+  public Map<string, Individual> individuals
+    {
+      get { return this._individuals_ro; }
+      private set
+        {
+          this._individuals = value;
+          this._individuals_ro = this._individuals.read_only_view;
+        }
+    }
 
   /**
    * The { link Individual} representing the user.
@@ -160,7 +171,8 @@ public class Folks.IndividualAggregator : Object
   public IndividualAggregator ()
     {
       this._stores = new HashMap<string, PersonaStore> ();
-      this.individuals = new HashMap<string, Individual> ();
+      this._individuals = new HashMap<string, Individual> ();
+      this._individuals_ro = this._individuals.read_only_view;
       this._link_map = new HashTable<string, Individual> (str_hash, str_equal);
 
       this._backends = new HashSet<Backend> ();
@@ -247,7 +259,7 @@ public class Folks.IndividualAggregator : Object
           new HashMap<Individual, MatchResult> ();
       Folks.PotentialMatch matchObj = new Folks.PotentialMatch ();
 
-      foreach (var i in this.individuals.values)
+      foreach (var i in this._individuals.values)
         {
           if (i.id == matchee.id)
                 continue;
@@ -273,7 +285,7 @@ public class Folks.IndividualAggregator : Object
     {
       HashMap<Individual, HashMap<Individual, MatchResult>> matches =
         new HashMap<Individual, HashMap<Individual, MatchResult>> ();
-      var individuals = this.individuals.values.to_array ();
+      var individuals = this._individuals.values.to_array ();
       Folks.PotentialMatch matchObj = new Folks.PotentialMatch ();
 
       for (var i = 0; i < individuals.length; i++)
@@ -390,15 +402,46 @@ public class Folks.IndividualAggregator : Object
       return type_id + ":" + id;
     }
 
+  /* Emit the individuals-changed signal ensuring that null parameters are
+   * turned into empty sets, and both sets passed to signal handlers are
+   * read-only. */
+  private void _emit_individuals_changed (Set<Individual>? added,
+      Set<Individual>? removed,
+      string? message = null,
+      Persona? actor = null,
+      GroupDetails.ChangeReason reason = GroupDetails.ChangeReason.NONE)
+    {
+      var _added = added;
+      var _removed = removed;
+
+      if ((added == null || added.size == 0) &&
+          (removed == null || removed.size == 0))
+        {
+          /* Don't bother emitting it if nothing's changed */
+          return;
+        }
+      else if (added == null)
+        {
+          _added = new HashSet<Individual> ();
+        }
+      else if (removed == null)
+        {
+          _removed = new HashSet<Individual> ();
+        }
+
+      this.individuals_changed (_added.read_only_view, _removed.read_only_view,
+          message, actor, reason);
+    }
+
   private void _connect_to_individual (Individual individual)
     {
       individual.removed.connect (this._individual_removed_cb);
-      this.individuals.set (individual.id, individual);
+      this._individuals.set (individual.id, individual);
     }
 
   private void _disconnect_from_individual (Individual individual)
     {
-      this.individuals.unset (individual.id);
+      this._individuals.unset (individual.id);
       individual.removed.disconnect (this._individual_removed_cb);
     }
 
@@ -700,7 +743,7 @@ public class Folks.IndividualAggregator : Object
       foreach (var individual in removed_individuals)
         {
           /* Ensure we don't remove the same Individual twice */
-          if (this.individuals.has_key (individual.id) == false)
+          if (this._individuals.has_key (individual.id) == false)
             continue;
 
           debug ("    %s", individual.id);
@@ -755,8 +798,8 @@ public class Folks.IndividualAggregator : Object
        * aggregator */
       if (added_individuals.size > 0 || removed_individuals.size > 0)
         {
-          this.individuals_changed (added_individuals, removed_individuals,
-              null, null, 0);
+          this._emit_individuals_changed (added_individuals,
+              removed_individuals);
         }
 
       /* Signal the replacement of various Individuals as a consequence of
@@ -795,7 +838,7 @@ public class Folks.IndividualAggregator : Object
 
       /* Only signal if the individual is still in this.individuals. This allows
        * us to group removals together in, e.g., _personas_changed_cb(). */
-      if (this.individuals.get (i.id) != i)
+      if (this._individuals.get (i.id) != i)
         return;
 
       var individuals = new HashSet<Individual> ();
@@ -814,8 +857,7 @@ public class Folks.IndividualAggregator : Object
       /* If the individual has 0 personas, we've already signaled removal */
       if (i.personas.size > 0)
         {
-          this.individuals_changed (new HashSet<Individual> (),
-              individuals, null, null, 0);
+          this._emit_individuals_changed (null, individuals);
         }
 
       this._disconnect_from_individual (i);
diff --git a/folks/individual.vala b/folks/individual.vala
index b4309f8..98c90e4 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -86,6 +86,8 @@ public class Folks.Individual : Object,
   private HashSet<string> _groups;
   /* Stores the Personas contained in this Individual. */
   private HashSet<Persona> _persona_set;
+  /* Read-only view of the above set */
+  private Set<Persona> _persona_set_ro;
   /* Mapping from PersonaStore -> number of Personas from that store contained
    * in this Individual. There shouldn't be any entries with a number < 1.
    * This is used for working out when to disconnect from store signals. */
@@ -455,7 +457,7 @@ public class Folks.Individual : Object,
    */
   public Set<Persona> personas
     {
-      get { return this._persona_set; }
+      get { return this._persona_set_ro; }
       set { this._set_personas (value, null); }
     }
 
@@ -611,11 +613,39 @@ public class Folks.Individual : Object,
       this._web_service_addresses = new HashMultiMap<string, string> ();
       this._persona_set =
           new HashSet<Persona> (direct_hash, direct_equal);
+      this._persona_set_ro = this._persona_set.read_only_view;
       this._stores = new HashMap<PersonaStore, uint> (null, null);
       this._gender = Gender.UNSPECIFIED;
       this.personas = personas;
     }
 
+  /* Emit the personas-changed signal, turning null parameters into empty sets
+   * and ensuring that the signal is emitted with read-only views of the sets
+   * so that signal handlers can't modify the sets. */
+  private void _emit_personas_changed (Set<Persona>? added,
+      Set<Persona>? removed)
+    {
+      var _added = added;
+      var _removed = removed;
+
+      if ((added == null || added.size == 0) &&
+          (removed == null || removed.size == 0))
+        {
+          /* Emitting it with no added or removed personas is pointless */
+          return;
+        }
+      else if (added == null)
+        {
+          _added = new HashSet<Persona> ();
+        }
+      else if (removed == null)
+        {
+          _removed = new HashSet<Persona> ();
+        }
+
+      this.personas_changed (_added.read_only_view, _removed.read_only_view);
+    }
+
   private void _store_removed_cb (PersonaStore store)
     {
       var removed_personas = new HashSet<Persona> ();
@@ -629,7 +659,7 @@ public class Folks.Individual : Object,
         }
 
       if (removed_personas != null)
-        this.personas_changed (new HashSet<Persona> (), removed_personas);
+        this._emit_personas_changed (null, removed_personas);
 
       if (store != null)
         this._stores.unset (store);
@@ -660,7 +690,7 @@ public class Folks.Individual : Object,
         }
 
       if (removed_personas != null)
-        this.personas_changed (new HashSet<Persona> (), removed_personas);
+        this._emit_personas_changed (null, removed_personas);
 
       if (this._persona_set.size < 1)
         {
@@ -1451,7 +1481,7 @@ public class Folks.Individual : Object,
             }
         }
 
-      this.personas_changed (added, removed);
+      this._emit_personas_changed (added, removed);
 
       /* Update this.is_user */
       var new_is_user = (this._persona_user_count > 0) ? true : false;
diff --git a/folks/persona-store.vala b/folks/persona-store.vala
index 9b26b07..e2f439f 100644
--- a/folks/persona-store.vala
+++ b/folks/persona-store.vala
@@ -204,6 +204,36 @@ public abstract class Folks.PersonaStore : Object
       Persona? actor,
       GroupDetails.ChangeReason reason);
 
+  /* Emit the personas-changed signal, turning null parameters into empty sets
+   * and only passing a read-only view to the signal handlers. */
+  protected void _emit_personas_changed (Set<Persona>? added,
+      Set<Persona>? removed,
+      string? message = null,
+      Persona? actor = null,
+      GroupDetails.ChangeReason reason = GroupDetails.ChangeReason.NONE)
+    {
+      var _added = added;
+      var _removed = removed;
+
+      if ((added == null || added.size == 0) &&
+          (removed == null || removed.size == 0))
+        {
+          /* Don't bother signalling if nothing's changed */
+          return;
+        }
+      else if (added == null)
+        {
+          _added = new HashSet<Persona> ();
+        }
+      else if (removed == null)
+        {
+          _removed = new HashSet<Persona> ();
+        }
+
+      this.personas_changed (_added.read_only_view, _removed.read_only_view,
+          message, actor, reason);
+    }
+
   /**
    * Emitted when the backing store for this PersonaStore has been removed.
    *
diff --git a/folks/postal-address-details.vala b/folks/postal-address-details.vala
index 8ac7de4..9fc16cb 100644
--- a/folks/postal-address-details.vala
+++ b/folks/postal-address-details.vala
@@ -129,6 +129,7 @@ public class Folks.PostalAddress : Object
     }
 
   private HashSet<string> _types;
+  private Set<string> _types_ro;
 
   /**
    * The types of the address.
@@ -138,10 +139,11 @@ public class Folks.PostalAddress : Object
    */
   public Set<string> types
     {
-      get { return this._types; }
+      get { return this._types_ro; }
       construct set
         {
           this._types = new HashSet<string> ();
+          this._types_ro = this._types.read_only_view;
           foreach (var type in value)
             this._types.add (type);
         }



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