[folks] Change ImDetails.im_addresses to be a MultiMap<string, string>



commit 6b5c6befa0a4c392a1ffb6516963a0cbbbb1e4bd
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Tue Apr 19 18:12:15 2011 +0100

    Change ImDetails.im_addresses to be a MultiMap<string, string>
    
    Helps: bgo#640092

 NEWS                                        |    1 +
 backends/key-file/kf-persona-store.vala     |    6 +-
 backends/key-file/kf-persona.vala           |   55 +++++--------
 backends/libsocialweb/lib/swf-persona.vala  |   12 +--
 backends/telepathy/lib/tpf-persona.vala     |   17 ++---
 backends/tracker/lib/trf-persona-store.vala |  118 ++++++++++++++++++++++-----
 backends/tracker/lib/trf-persona.vala       |   52 ++++---------
 folks/im-details.vala                       |   13 ++--
 folks/individual-aggregator.vala            |   28 +++----
 folks/individual.vala                       |   25 +++---
 folks/potential-match.vala                  |   16 +---
 tests/tracker/add-persona.vala              |   17 ++---
 tests/tracker/im-details-interface.vala     |    2 +-
 tests/tracker/imaddresses-updates.vala      |   14 ++--
 tests/tracker/link-personas.vala            |   24 ++----
 tests/tracker/match-im-addresses.vala       |   27 +++----
 tests/tracker/set-im-addresses.vala         |   24 ++----
 tools/import-pidgin.vala                    |   20 +----
 tools/inspect/utils.vala                    |   14 ++--
 19 files changed, 235 insertions(+), 250 deletions(-)
---
diff --git a/NEWS b/NEWS
index 7d79285..27bb401 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,7 @@ API changes:
 * LinkedHashSet.list_iterator() is now disallowed (causes an assertion failure)
 * LinkedHashSet.iterator() now returns a BidirIterator instead of just an
   Iterator
+* ImDetails.im_addresses is now of type MultiMap<string, string>
 
 Overview of changes from libfolks 0.4.0 to libfolks 0.5.0
 =========================================================
diff --git a/backends/key-file/kf-persona-store.vala b/backends/key-file/kf-persona-store.vala
index e8f2604..aa8f6d3 100644
--- a/backends/key-file/kf-persona-store.vala
+++ b/backends/key-file/kf-persona-store.vala
@@ -300,9 +300,9 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
     {
       unowned Value? val = details.lookup (Folks.PersonaStore.detail_key (
             PersonaDetail.IM_ADDRESSES));
-      unowned HashTable<string, LinkedHashSet<string>> im_addresses
+      MultiMap<string, string> im_addresses
           = val != null
-          ? (HashTable<string, LinkedHashSet<string>>) val.get_boxed ()
+          ? (MultiMap<string, string>) val.get_object ()
           : null;
       unowned Value? val2 = details.lookup
           (this.detail_key (PersonaDetail.WEB_SERVICE_ADDRESSES));
@@ -311,7 +311,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
           ? (HashMap<string, LinkedHashSet<string>>) val2.get_object ()
           : null;
       uint im_addresses_size = (im_addresses == null)
-          ? 0 : im_addresses.size ();
+          ? 0 : im_addresses.size;
       uint web_service_addresses_size = (web_service_addresses == null)
           ? 0 : web_service_addresses.size;
 
diff --git a/backends/key-file/kf-persona.vala b/backends/key-file/kf-persona.vala
index 4e28349..187a38a 100644
--- a/backends/key-file/kf-persona.vala
+++ b/backends/key-file/kf-persona.vala
@@ -34,10 +34,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
     WebServiceDetails
 {
   private unowned GLib.KeyFile _key_file;
-  /* FIXME: As described in the ImDetails interface, we have to use
-   * GenericArray<string> here rather than just string[], as null-terminated
-   * arrays aren't supported as generic types. */
-  private HashTable<string, LinkedHashSet<string>> _im_addresses;
+  private HashMultiMap<string, string> _im_addresses;
   private HashMap<string, LinkedHashSet<string>> _web_service_addresses;
   private string _alias;
   private const string[] _linkable_properties =
@@ -80,7 +77,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
   /**
    * { inheritDoc}
    */
-  public HashTable<string, LinkedHashSet<string>> im_addresses
+  public MultiMap<string, string> im_addresses
     {
       get
         { return this._im_addresses; }
@@ -88,7 +85,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
       set
         {
           /* Remove the current IM addresses from the key file */
-          this._im_addresses.foreach ((protocol, v) =>
+          foreach (var protocol in this._im_addresses.get_keys ())
             {
               try
                 {
@@ -99,27 +96,23 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
                   /* Ignore the error, since it's just a group or key not found
                    * error. */
                 }
-            });
+            }
 
           /* Add the new IM addresses to the key file and build a normalised
            * table of them to set as the new property value */
-          HashTable<string, LinkedHashSet<string>> im_addresses =
-              new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+          var im_addresses = new HashMultiMap<string, string> ();
 
-          value.foreach ((k, v) =>
+          foreach (var protocol in value.get_keys ())
             {
-              unowned string protocol = (string) k;
-              unowned LinkedHashSet<string?> addresses =
-                  (LinkedHashSet<string?>) v;
-              LinkedHashSet<string> normalized_addresses =
-                  new LinkedHashSet<string> ();
+              var addresses = value.get (protocol);
+              var normalised_addresses = new HashSet<string> ();
 
               foreach (string address in addresses)
                 {
-                  string normalized_address;
+                  string normalised_address;
                   try
                     {
-                      normalized_address = ImDetails.normalise_im_address (
+                      normalised_address = ImDetails.normalise_im_address (
                           address, protocol);
                     }
                   catch (ImDetailsError e)
@@ -131,15 +124,15 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
                       continue;
                     }
 
-                    normalized_addresses.add (normalized_address);
+                  normalised_addresses.add (normalised_address);
+                  im_addresses.set (protocol, normalised_address);
                 }
 
-              string[] addrs = (string[]) normalized_addresses.to_array ();
-              addrs.length = normalized_addresses.size;
+              string[] addrs = (string[]) normalised_addresses.to_array ();
+              addrs.length = normalised_addresses.size;
 
               this._key_file.set_string_list (this.display_id, protocol, addrs);
-              im_addresses.insert (protocol, normalized_addresses);
-            });
+            }
 
           this._im_addresses = im_addresses;
 
@@ -219,8 +212,7 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
           id);
 
       this._key_file = key_file;
-      this._im_addresses = new HashTable<string, LinkedHashSet<string>> (
-          str_hash, str_equal);
+      this._im_addresses = new HashMultiMap<string, string> ();
       this._web_service_addresses
           = new HashMap<string, LinkedHashSet<string>> (str_hash,
               str_equal);
@@ -265,8 +257,6 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
               var im_addresses = this._key_file.get_string_list (
                   this.display_id, protocol);
 
-              var address_set = new LinkedHashSet<string> ();
-
               foreach (var im_address in im_addresses)
                 {
                   string address;
@@ -281,10 +271,9 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
                       warning (e.message);
                       continue;
                     }
-                  address_set.add (address);
-                }
 
-              this._im_addresses.insert (protocol, address_set);
+                  this._im_addresses.set (protocol, address);
+                }
             }
         }
       catch (KeyFileError e)
@@ -309,15 +298,13 @@ public class Folks.Backends.Kf.Persona : Folks.Persona,
     {
       if (prop_name == "im-addresses")
         {
-          this.im_addresses.foreach ((k, v) =>
+          foreach (var protocol in this._im_addresses.get_keys ())
             {
-              unowned string protocol = (string) k;
-              unowned LinkedHashSet<string> im_addresses =
-                  (LinkedHashSet<string>) v;
+              var im_addresses = this._im_addresses.get (protocol);
 
               foreach (string address in im_addresses)
                   callback (protocol + ":" + address);
-            });
+            }
         }
       else if (prop_name == "web-service-addresses")
         {
diff --git a/backends/libsocialweb/lib/swf-persona.vala b/backends/libsocialweb/lib/swf-persona.vala
index e3dc611..9b0259a 100644
--- a/backends/libsocialweb/lib/swf-persona.vala
+++ b/backends/libsocialweb/lib/swf-persona.vala
@@ -95,8 +95,8 @@ public class Swf.Persona : Folks.Persona,
         }
     }
 
-  private HashTable<string, LinkedHashSet<string>> _im_addresses =
-      new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+  private HashMultiMap<string, string> _im_addresses =
+      new HashMultiMap<string, string> ();
 
   private HashMap<string, LinkedHashSet<string>> _web_service_addresses =
       new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
@@ -104,7 +104,7 @@ public class Swf.Persona : Folks.Persona,
   /**
    * { inheritDoc}
    */
-  public HashTable<string, LinkedHashSet<string>> im_addresses
+  public MultiMap<string, string> im_addresses
     {
       get { return this._im_addresses; }
       private set {}
@@ -193,18 +193,14 @@ public class Swf.Persona : Folks.Persona,
       var facebook_jid = this._build_facebook_jid (store.id, id);
       if (facebook_jid != null)
         {
-          var im_address_array = new LinkedHashSet<string> ();
           try
             {
               var facebook_jid_copy = facebook_jid.dup();
               var normalised_addr = (owned) normalise_im_address
                   ((owned) facebook_jid_copy, "jabber");
               string im_proto = "jabber";
-              var proto_copy = im_proto.dup ();
 
-              im_address_array.add ((owned) normalised_addr);
-              this._im_addresses.insert ((owned) proto_copy,
-                  (owned) im_address_array);
+              this._im_addresses.set (im_proto, normalised_addr);
             }
           catch (ImDetailsError e)
             {
diff --git a/backends/telepathy/lib/tpf-persona.vala b/backends/telepathy/lib/tpf-persona.vala
index 1168788..fbc259c 100644
--- a/backends/telepathy/lib/tpf-persona.vala
+++ b/backends/telepathy/lib/tpf-persona.vala
@@ -38,7 +38,7 @@ public class Tpf.Persona : Folks.Persona,
   private HashTable<string, bool> _groups;
   private bool _is_favourite;
   private string _alias;
-  private HashTable<string, LinkedHashSet<string>> _im_addresses;
+  private HashMultiMap<string, string> _im_addresses;
   private const string[] _linkable_properties = { "im-addresses" };
 
   /* Whether we've finished being constructed; this is used to prevent
@@ -131,11 +131,11 @@ public class Tpf.Persona : Folks.Persona,
     }
 
   /**
-   * A mapping of IM protocol to an ordered set of IM addresses.
+   * A mapping of IM protocol to an (unordered) set of IM addresses.
    *
    * See { link Folks.ImDetails.im_addresses}.
    */
-  public HashTable<string, LinkedHashSet<string>> im_addresses
+  public MultiMap<string, string> im_addresses
     {
       get { return this._im_addresses; }
       private set {}
@@ -255,11 +255,12 @@ public class Tpf.Persona : Folks.Persona,
       this._is_constructed = true;
 
       /* Set our single IM address */
-      LinkedHashSet<string> im_address_set = new LinkedHashSet<string> ();
+      this._im_addresses = new HashMultiMap<string, string> ();
+
       try
         {
-          im_address_set.add (ImDetails.normalise_im_address (id,
-              account.get_protocol ()));
+          this._im_addresses.set (account.get_protocol (),
+              ImDetails.normalise_im_address (id, account.get_protocol ()));
         }
       catch (ImDetailsError e)
         {
@@ -267,10 +268,6 @@ public class Tpf.Persona : Folks.Persona,
           warning (e.message);
         }
 
-      this._im_addresses =
-          new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
-      this._im_addresses.insert (account.get_protocol (), im_address_set);
-
       /* Groups */
       this._groups = new HashTable<string, bool> (str_hash, str_equal);
 
diff --git a/backends/tracker/lib/trf-persona-store.vala b/backends/tracker/lib/trf-persona-store.vala
index 404f207..ba98f73 100644
--- a/backends/tracker/lib/trf-persona-store.vala
+++ b/backends/tracker/lib/trf-persona-store.vala
@@ -498,13 +498,12 @@ public class Trf.PersonaStore : Folks.PersonaStore
           else if (k == Folks.PersonaStore.detail_key (
                 PersonaDetail.IM_ADDRESSES))
             {
-              var im_addresses =
-                (HashTable<string, LinkedHashSet<string>>) v.get_boxed ();
+              var im_addresses = (MultiMap<string, string>) v.get_object ();
 
               int im_cnt = 0;
               foreach (var proto in im_addresses.get_keys ())
                 {
-                  var addrs_a = im_addresses.lookup (proto);
+                  var addrs_a = im_addresses.get (proto);
 
                   foreach (var addr in addrs_a)
                     {
@@ -1994,26 +1993,25 @@ public class Trf.PersonaStore : Folks.PersonaStore
     }
 
   internal async void _set_im_addresses (Folks.Persona persona,
-      owned HashTable<string, LinkedHashSet<string>> im_addresses)
+      MultiMap<string, string> im_addresses)
     {
       /* FIXME:
        * - this conversion should go away once we've switched to use the
        *   same data structure for each property that is a list of something.
        *   See: https://bugzilla.gnome.org/show_bug.cgi?id=646079 */
-      GLib.List <FieldDetails> ims = new GLib.List <FieldDetails> ();
+      var ims = new HashSet<FieldDetails> ();
       foreach (var proto in im_addresses.get_keys ())
         {
-          var addrs = im_addresses.lookup (proto);
+          var addrs = im_addresses.get (proto);
           foreach (var a in addrs)
             {
               var fd = new FieldDetails (a);
               fd.set_parameter ("proto", proto);
-              ims.prepend ((owned) fd);
+              ims.add (fd);
             }
         }
 
-       yield this._set_attrib (persona, (owned) ims,
-          Trf.Attrib.IM_ADDRESSES);
+       yield this._set_attrib_set (persona, ims, Trf.Attrib.IM_ADDRESSES);
     }
 
   internal async void _set_postal_addresses (Folks.Persona persona,
@@ -2292,13 +2290,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
             related_connection = Trf.OntologyDefs.NCO_URL;
             break;
           case Trf.Attrib.IM_ADDRESSES:
-            related_attrib = Trf.OntologyDefs.NCO_IMADDRESS;
-            related_prop = Trf.OntologyDefs.NCO_IMID;
-            related_prop_2 = Trf.OntologyDefs.NCO_IMPROTOCOL;
-            related_connection = Trf.OntologyDefs.NCO_HAS_IMADDRESS;
-            yield this._remove_attributes_from_persona (persona,
-                _REMOVE_IM_ADDRS);
-            break;
+            assert_not_reached ();
           case Trf.Attrib.POSTAL_ADDRESSES:
             related_attrib = Trf.OntologyDefs.NCO_POSTAL_ADDRESS;
             related_connection = Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS;
@@ -2371,9 +2363,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
               if (what == Trf.Attrib.IM_ADDRESSES)
                 {
                   builder.predicate (related_prop_2);
-                  unowned GLib.List<string> im_params =
-                      fd.get_parameter_values ("proto");
-                  builder.object_string (im_params.nth_data (0));
+                  var im_params = fd.get_parameter_values ("proto").to_array ();
+                  builder.object_string (im_params[0]);
                 }
             }
 
@@ -2400,6 +2391,95 @@ public class Trf.PersonaStore : Folks.PersonaStore
       yield this._tracker_update (builder.result, "set_attrib");
     }
 
+  /* NOTE:
+   * - first we nuke old attribs
+   * - we create new affls with the new attribs
+   */
+  private async void _set_attrib_set (Folks.Persona persona,
+      Set<Object> attribs, Trf.Attrib what)
+    {
+      var p_id = ((Trf.Persona) persona).tracker_id ();
+
+      unowned string? related_attrib = null;
+      unowned string? related_prop = null;
+      unowned string? related_prop_2 = null;
+      unowned string? related_connection = null;
+
+      switch (what)
+        {
+          case Trf.Attrib.IM_ADDRESSES:
+            related_attrib = Trf.OntologyDefs.NCO_IMADDRESS;
+            related_prop = Trf.OntologyDefs.NCO_IMID;
+            related_prop_2 = Trf.OntologyDefs.NCO_IMPROTOCOL;
+            related_connection = Trf.OntologyDefs.NCO_HAS_IMADDRESS;
+            yield this._remove_attributes_from_persona (persona,
+                _REMOVE_IM_ADDRS);
+            break;
+          case Trf.Attrib.POSTAL_ADDRESSES:
+          case Trf.Attrib.URLS:
+            assert_not_reached ();
+        }
+
+      var builder = new Tracker.Sparql.Builder.update ();
+      builder.insert_open (null);
+      int i = 0;
+      foreach (var p in attribs)
+        {
+          FieldDetails fd = null;
+          PostalAddress pa = null;
+
+          string affl = "_:a%d".printf (i);
+          string attr;
+
+          switch (what)
+            {
+              case Trf.Attrib.POSTAL_ADDRESSES:
+              case Trf.Attrib.URLS:
+                assert_not_reached ();
+              case Trf.Attrib.IM_ADDRESSES:
+              default:
+                fd = (FieldDetails) p;
+                attr = "_:p%d".printf (i);
+                builder.subject (attr);
+                builder.predicate ("a");
+                builder.object (related_attrib);
+                builder.predicate (related_prop);
+                builder.object_string (fd.value);
+
+                if (what == Trf.Attrib.IM_ADDRESSES)
+                  {
+                    builder.predicate (related_prop_2);
+                    var im_params =
+                        fd.get_parameter_values ("proto").to_array ();
+                    builder.object_string (im_params[0]);
+                  }
+
+                break;
+            }
+
+          builder.subject (affl);
+          builder.predicate ("a");
+          builder.object (Trf.OntologyDefs.NCO_AFFILIATION);
+          builder.predicate (related_connection);
+          builder.object (attr);
+          builder.subject ("?contact");
+          builder.predicate (Trf.OntologyDefs.NCO_HAS_AFFILIATION);
+          builder.object (affl);
+
+          i++;
+        }
+      builder.insert_close ();
+      builder.where_open ();
+      builder.subject ("?contact");
+      builder.predicate ("a");
+      builder.object (Trf.OntologyDefs.NCO_PERSON);
+      string filter = " FILTER(tracker:id(?contact) = %s) ".printf (p_id);
+      builder.append (filter);
+      builder.where_close ();
+
+      yield this._tracker_update (builder.result, "set_attrib");
+    }
+
   private async bool _tracker_update (string query, string caller)
     {
       bool ret = false;
diff --git a/backends/tracker/lib/trf-persona.vala b/backends/tracker/lib/trf-persona.vala
index 93cd440..3ed52bb 100644
--- a/backends/tracker/lib/trf-persona.vala
+++ b/backends/tracker/lib/trf-persona.vala
@@ -270,12 +270,13 @@ public class Trf.Persona : Folks.Persona,
   private HashTable<string, HashTable<string, string>> _tracker_ids_ims =
   new HashTable<string, HashTable<string, string>> (str_hash, str_equal);
 
-  private HashTable<string, LinkedHashSet<string>> _im_addresses =
-      new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+  private HashMultiMap<string, string> _im_addresses =
+      new HashMultiMap<string, string> ();
+
   /**
    * { inheritDoc}
    */
-  public HashTable<string, LinkedHashSet<string>> im_addresses
+  public MultiMap<string, string> im_addresses
     {
       get { return this._im_addresses; }
       public set
@@ -414,15 +415,13 @@ public class Trf.Persona : Folks.Persona,
     {
       if (prop_name == "im-addresses")
         {
-          this.im_addresses.foreach ((k, v) =>
+          foreach (var protocol in this._im_addresses.get_keys ())
             {
-              unowned string protocol = (string) k;
-              unowned LinkedHashSet<string> im_addresses =
-                  (LinkedHashSet<string>) v;
+              var im_addresses = this._im_addresses.get (protocol);
 
               foreach (string address in im_addresses)
                   callback (protocol + ":" + address);
-            });
+            }
         }
       else if (prop_name == "local-ids")
         {
@@ -844,7 +843,7 @@ public class Trf.Persona : Folks.Persona,
           return;
         }
 
-      this._im_addresses.remove_all ();
+      this._im_addresses.clear ();
 
       string[] addresses_a = addresses.split ("\n");
 
@@ -866,27 +865,13 @@ public class Trf.Persona : Folks.Persona,
   internal bool _add_im_address (string tracker_id, string im_proto,
       string account_id, bool notify = true)
     {
-      LinkedHashSet<string> im_address_array;
-
       try
         {
           var account_id_copy = account_id.dup ();
           var normalised_addr = (owned) normalise_im_address
               ((owned) account_id_copy, im_proto);
 
-          im_address_array = this._im_addresses.lookup (im_proto);
-          if (im_address_array == null)
-            {
-              im_address_array = new LinkedHashSet<string> ();
-              im_address_array.add ((owned) normalised_addr);
-              var proto_copy = im_proto.dup ();
-              this._im_addresses.insert ((owned) proto_copy,
-                  (owned) im_address_array);
-            }
-          else
-            {
-              im_address_array.add (normalised_addr);
-            }
+          this._im_addresses.set (im_proto, normalised_addr);
 
           var im_proto_hash = new HashTable<string, string> (str_hash,
               str_equal);
@@ -923,22 +908,15 @@ public class Trf.Persona : Folks.Persona,
       var proto = proto_im.get_keys ().nth_data (0);
       var im_addr = proto_im.lookup (proto);
 
-      var im_list = this._im_addresses.lookup (proto);
-      if (im_list != null)
+      if (this._im_addresses.remove (proto, im_addr))
         {
-          foreach (var addr_iter in im_list)
+          this._tracker_ids_ims.remove (tracker_id);
+          if (notify)
             {
-              if (addr_iter == im_addr)
-                {
-                  im_list.remove (im_addr);
-                  this._tracker_ids_ims.remove (tracker_id);
-                  if (notify)
-                    {
-                      this.notify_property ("im-addresses");
-                    }
-                  return true;
-                }
+              this.notify_property ("im-addresses");
             }
+
+          return true;
         }
 
       return false;
diff --git a/folks/im-details.vala b/folks/im-details.vala
index e7522a0..3069c43 100644
--- a/folks/im-details.vala
+++ b/folks/im-details.vala
@@ -19,6 +19,7 @@
  */
 
 using GLib;
+using Gee;
 
 /**
  * Errors related to IM addresses and IM address handling.
@@ -39,22 +40,20 @@ public errordomain Folks.ImDetailsError
 public interface Folks.ImDetails : Object
 {
   /**
-   * A mapping of IM protocol to an ordered set of IM addresses.
+   * A mapping of IM protocol to an (unordered) set of IM addresses.
    *
    * Each mapping is from an arbitrary protocol identifier to a set of IM
-   * addresses on that protocol for the contact, listed in preference order.
-   * The most-preferred IM address for each protocol comes first in that
-   * protocol's list.
+   * addresses on that protocol for the contact, listed in no particular order.
    *
-   * There must be no duplicate IM addresses in each ordered set, though a given
+   * There must be no duplicate IM addresses in each set, though a given
    * IM address may be present in the sets for different protocols.
    *
    * All the IM addresses must be normalised using
    * { link ImDetails.normalise_im_address} before being added to this property.
    *
-   * @since 0.3.4
+   * @since UNRELEASED
    */
-  public abstract HashTable<string, LinkedHashSet<string>> im_addresses
+  public abstract MultiMap<string, string> im_addresses
     {
       get; set;
     }
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index 1531400..7943698 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -982,8 +982,7 @@ public class Folks.IndividualAggregator : Object
           this._configured_writeable_store_type_id);
 
       /* `protocols_addrs_set` will be passed to the new Kf.Persona */
-      var protocols_addrs_set =
-          new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+      var protocols_addrs_set = new HashMultiMap<string, string> ();
       var web_service_addrs_set =
           new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
 
@@ -994,21 +993,18 @@ public class Folks.IndividualAggregator : Object
         {
           if (persona is ImDetails)
             {
-              ((ImDetails) persona).im_addresses.foreach ((k, v) =>
+              ImDetails im_details = (ImDetails) persona;
+
+              /* protocols_addrs_set = union (all personas' IM addresses) */
+              foreach (var protocol in im_details.im_addresses.get_keys ())
                 {
-                  unowned string protocol = (string) k;
-                  unowned LinkedHashSet<string> addresses =
-                    (LinkedHashSet<string>) v;
+                  var im_addresses = im_details.im_addresses.get (protocol);
 
-                  var address_set = protocols_addrs_set.lookup (protocol);
-                  if (address_set == null)
+                  foreach (var im_address in im_addresses)
                     {
-                      address_set = new LinkedHashSet<string> ();
-                      protocols_addrs_set.insert (protocol, address_set);
+                      protocols_addrs_set.set (protocol, im_address);
                     }
-
-                  address_set.add_all (addresses);
-                });
+                }
             }
 
           if (persona is WebServiceDetails)
@@ -1042,10 +1038,10 @@ public class Folks.IndividualAggregator : Object
 
       var details = new HashTable<string, Value?> (str_hash, str_equal);
 
-      if (protocols_addrs_set.size () > 0)
+      if (protocols_addrs_set.size > 0)
         {
-          var im_addresses_value = Value (typeof (HashTable));
-          im_addresses_value.set_boxed (protocols_addrs_set);
+          var im_addresses_value = Value (typeof (MultiMap));
+          im_addresses_value.set_object (protocols_addrs_set);
           details.insert (PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES),
               im_addresses_value);
         }
diff --git a/folks/individual.vala b/folks/individual.vala
index 0f4148c..727e277 100644
--- a/folks/individual.vala
+++ b/folks/individual.vala
@@ -99,7 +99,7 @@ public class Folks.Individual : Object,
   /* The number of Personas in this Individual which have
    * Persona.is_user == true. Iff this is > 0, Individual.is_user == true. */
   private uint _persona_user_count = 0;
-  private HashTable<string, LinkedHashSet<string>> _im_addresses;
+  private HashMultiMap<string, string> _im_addresses;
   private HashMap<string, LinkedHashSet<string>> _web_service_addresses;
 
   /**
@@ -420,7 +420,7 @@ public class Folks.Individual : Object,
   /**
    * { inheritDoc}
    */
-  public HashTable<string, LinkedHashSet<string>> im_addresses
+  public MultiMap<string, string> im_addresses
     {
       get { return this._im_addresses; }
       private set {}
@@ -598,8 +598,7 @@ public class Folks.Individual : Object,
    */
   public Individual (GLib.List<Persona>? personas)
     {
-      this._im_addresses =
-          new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+      this._im_addresses = new HashMultiMap<string, string> ();
       this._web_service_addresses =
           new HashMap<string, LinkedHashSet<string>> (str_hash, str_equal);
       this._persona_set = new HashSet<Persona> (null, null);
@@ -930,25 +929,23 @@ public class Folks.Individual : Object,
   private void _update_im_addresses ()
     {
       /* populate the IM addresses as the union of our Personas' addresses */
+      this._im_addresses.clear ();
+
       foreach (var persona in this.personas)
         {
           if (persona is ImDetails)
             {
               var im_details = (ImDetails) persona;
-              im_details.im_addresses.foreach ((k, v) =>
+              foreach (var cur_protocol in im_details.im_addresses.get_keys ())
                 {
-                  var cur_protocol = (string) k;
-                  var cur_addresses = (LinkedHashSet<string>) v;
-                  var im_set = this._im_addresses.lookup (cur_protocol);
+                  var cur_addresses =
+                      im_details.im_addresses.get (cur_protocol);
 
-                  if (im_set == null)
+                  foreach (var address in cur_addresses)
                     {
-                      im_set = new LinkedHashSet<string> ();
-                      this._im_addresses.insert (cur_protocol, im_set);
+                      this._im_addresses.set (cur_protocol, address);
                     }
-
-                  im_set.add_all (cur_addresses);
-                });
+                }
             }
         }
       this.notify_property ("im-addresses");
diff --git a/folks/potential-match.vala b/folks/potential-match.vala
index 9f074b7..ad89b40 100644
--- a/folks/potential-match.vala
+++ b/folks/potential-match.vala
@@ -219,21 +219,15 @@ public class Folks.PotentialMatch : Object
     {
       foreach (var proto in this._individual_a.im_addresses.get_keys ())
         {
-          var addrs_b = this._individual_b.im_addresses.lookup (proto);
-          if (addrs_b == null)
-            continue;
-
-          var addrs_a = this._individual_a.im_addresses.lookup (proto);
+          var addrs_a = this._individual_a.im_addresses.get (proto);
+          var addrs_b = this._individual_b.im_addresses.get (proto);
 
           foreach (var im_a in addrs_a)
             {
-              foreach (var im_b in addrs_b)
+              if (addrs_b.contains (im_a))
                 {
-                  if (im_a == im_b)
-                    {
-                      this._result = MatchResult.HIGH;
-                      return;
-                    }
+                  this._result = MatchResult.HIGH;
+                  return;
                 }
             }
         }
diff --git a/tests/tracker/add-persona.vala b/tests/tracker/add-persona.vala
index 8b25f9e..4ead527 100644
--- a/tests/tracker/add-persona.vala
+++ b/tests/tracker/add-persona.vala
@@ -233,16 +233,11 @@ public class AddPersonaTests : Folks.TestCase
           Folks.PersonaStore.detail_key (PersonaDetail.EMAIL_ADDRESSES),
           (owned) v8);
 
-      Value? v9 = Value (typeof (HashTable<string, LinkedHashSet<string>>));
-      HashTable<string, LinkedHashSet<string>> im_addrs =
-        new HashTable<string, LinkedHashSet<string>> (null, null);
-      LinkedHashSet<string> proto1 = new LinkedHashSet<string> ();
-      proto1.add (this._im_addr_1);
-      im_addrs.insert ("jabber", proto1);
-      LinkedHashSet<string> proto2 = new LinkedHashSet<string> ();
-      proto2.add (this._im_addr_2);
-      im_addrs.insert ("yahoo", proto2);
-      v9.set_boxed (im_addrs);
+      Value? v9 = Value (typeof (MultiMap<string, string>));
+      var im_addrs = new HashMultiMap<string, string> ();
+      im_addrs.set ("jabber", this._im_addr_1);
+      im_addrs.set ("yahoo", this._im_addr_2);
+      v9.set_object (im_addrs);
       details.insert (
           Folks.PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES), v9);
 
@@ -433,7 +428,7 @@ public class AddPersonaTests : Folks.TestCase
 
       foreach (var proto in i.im_addresses.get_keys ())
         {
-          var addrs = i.im_addresses.lookup (proto);
+          var addrs = i.im_addresses.get (proto);
           foreach (var a in addrs)
             {
               if (a == this._im_addr_1)
diff --git a/tests/tracker/im-details-interface.vala b/tests/tracker/im-details-interface.vala
index a1b6acd..58a63ca 100644
--- a/tests/tracker/im-details-interface.vala
+++ b/tests/tracker/im-details-interface.vala
@@ -116,7 +116,7 @@ public class ImDetailsInterfaceTests : Folks.TestCase
             {
               foreach (var proto in i.im_addresses.get_keys ())
                 {
-                  var addrs = i.im_addresses.lookup (proto);
+                  var addrs = i.im_addresses.get (proto);
 
                   if (proto == "jabber")
                     {
diff --git a/tests/tracker/imaddresses-updates.vala b/tests/tracker/imaddresses-updates.vala
index 1eb29d1..878d7e7 100644
--- a/tests/tracker/imaddresses-updates.vala
+++ b/tests/tracker/imaddresses-updates.vala
@@ -123,11 +123,10 @@ public class IMAddressesUpdatesTests : Folks.TestCase
             {
               this._individual_id = i.id;
 
-              foreach (unowned string proto in i.im_addresses.get_keys ())
+              foreach (var proto in i.im_addresses.get_keys ())
                 {
-                  var addrs = i.im_addresses.lookup (proto);
-                  var im_address_iter = addrs.get (0);
-                  if (im_address_iter == this._imaddress_1)
+                  var addrs = i.im_addresses.get (proto);
+                  if (addrs.size == 1 && addrs.contains (this._imaddress_1))
                     {
                       i.notify["im-addresses"].connect (this._notify_im_cb);
                       this._initial_imaddress_found = true;
@@ -143,11 +142,10 @@ public class IMAddressesUpdatesTests : Folks.TestCase
   private void _notify_im_cb (Object individual_obj, ParamSpec ps)
     {
       Folks.Individual i = (Folks.Individual) individual_obj;
-      foreach (unowned string proto in i.im_addresses.get_keys ())
+      foreach (var proto in i.im_addresses.get_keys ())
         {
-          var addrs = i.im_addresses.lookup (proto);
-          var im_address_iter = addrs.get (0);
-          if (im_address_iter == this._imaddress_2)
+          var addrs = i.im_addresses.get (proto);
+          if (addrs.size == 1 && addrs.contains (this._imaddress_2))
             {
               this._updated_imaddr_found = true;
               this._main_loop.quit ();
diff --git a/tests/tracker/link-personas.vala b/tests/tracker/link-personas.vala
index b146b9c..2ed7e57 100644
--- a/tests/tracker/link-personas.vala
+++ b/tests/tracker/link-personas.vala
@@ -167,13 +167,10 @@ public class LinkPersonasTests : Folks.TestCase
     {
       HashTable<string, Value?> details1 = new HashTable<string, Value?>
           (str_hash, str_equal);
-      Value? v1 = Value (typeof (HashTable<string, LinkedHashSet<string>>));
-      HashTable<string, LinkedHashSet<string>> im_addrs1 =
-        new HashTable<string, LinkedHashSet<string>> (null, null);
-      LinkedHashSet<string> addrs1 = new LinkedHashSet<string> ();
-      addrs1.add (this._im_address_1);
-      im_addrs1.insert (this._proto, addrs1);
-      v1.set_boxed (im_addrs1);
+      Value? v1 = Value (typeof (MultiMap<string, string>));
+      var im_addrs1 = new HashMultiMap<string, string> ();
+      im_addrs1.set (this._proto, this._im_address_1);
+      v1.set_object (im_addrs1);
       details1.insert ("im-addresses", (owned) v1);
 
       Value? v2 = Value (typeof (string));
@@ -182,13 +179,10 @@ public class LinkPersonasTests : Folks.TestCase
 
       HashTable<string, Value?> details2 = new HashTable<string, Value?>
           (str_hash, str_equal);
-      Value? v3 = Value (typeof (HashTable<string, LinkedHashSet<string>>));
-      HashTable<string, LinkedHashSet<string>> im_addrs2 =
-        new HashTable<string, LinkedHashSet<string>> (null, null);
-      LinkedHashSet<string> addrs2 = new LinkedHashSet<string> ();
-      addrs2.add (this._im_address_2);
-      im_addrs2.insert (this._proto, addrs2);
-      v3.set_boxed (im_addrs2);
+      Value? v3 = Value (typeof (MultiMap<string, string>));
+      var im_addrs2 = new HashMultiMap<string, string> ();
+      im_addrs2.set (this._proto, this._im_address_2);
+      v3.set_object (im_addrs2);
       details2.insert ("im-addresses", (owned) v3);
 
       Value? v4 = Value (typeof (string));
@@ -268,7 +262,7 @@ public class LinkPersonasTests : Folks.TestCase
           /* Lets check if it contains all the linking properties */
           foreach (var proto in i.im_addresses.get_keys ())
             {
-              var addrs = i.im_addresses.lookup (proto);
+              var addrs = i.im_addresses.get (proto);
               foreach (var a in addrs)
                 {
                   if (a == this._linking_props.get ("prop1"))
diff --git a/tests/tracker/match-im-addresses.vala b/tests/tracker/match-im-addresses.vala
index 7772c47..06734a9 100644
--- a/tests/tracker/match-im-addresses.vala
+++ b/tests/tracker/match-im-addresses.vala
@@ -165,23 +165,18 @@ public class MatchIMAddressesTests : Folks.TestCase
       HashTable<string, Value?> details2 = new HashTable<string, Value?>
           (str_hash, str_equal);
       Value? val;
-      HashTable<string, LinkedHashSet<string>> im_addrs;
-      LinkedHashSet<string> proto;
+      HashMultiMap<string, string> im_addrs;
 
       val = Value (typeof (string));
       val.set_string (this._persona_fullname_1);
       details1.insert (Folks.PersonaStore.detail_key (PersonaDetail.FULL_NAME),
           (owned) val);
 
-      val = Value (typeof (HashTable<string, LinkedHashSet<string>>));
-      im_addrs = new HashTable<string, LinkedHashSet<string>> (null, null);
-      proto = new LinkedHashSet<string> ();
-      proto.add (this._im_addr_1);
-      im_addrs.insert ("jabber", (owned) proto);
-      proto = new LinkedHashSet<string> ();
-      proto.add (this._im_addr_2);
-      im_addrs.insert ("yahoo", (owned) proto);
-      val.set_boxed ((owned) im_addrs);
+      val = Value (typeof (MultiMap<string, string>));
+      im_addrs = new HashMultiMap<string, string> ();
+      im_addrs.set ("jabber", this._im_addr_1);
+      im_addrs.set ("yahoo", this._im_addr_2);
+      val.set_object (im_addrs);
       details1.insert (
           Folks.PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES),
           (owned) val);
@@ -191,12 +186,10 @@ public class MatchIMAddressesTests : Folks.TestCase
       details2.insert (Folks.PersonaStore.detail_key (PersonaDetail.FULL_NAME),
           (owned) val);
 
-      val = Value (typeof (HashTable<string, LinkedHashSet<string>>));
-      im_addrs = new HashTable<string, LinkedHashSet<string>> (null, null);
-      proto = new LinkedHashSet<string> ();
-      proto.add (this._im_addr_1);
-      im_addrs.insert ("jabber", (owned) proto);
-      val.set_boxed ((owned) im_addrs);
+      val = Value (typeof (MultiMap<string, string>));
+      im_addrs = new HashMultiMap<string, string> ();
+      im_addrs.set ("jabber", this._im_addr_1);
+      val.set_object (im_addrs);
       details2.insert (
           Folks.PersonaStore.detail_key (PersonaDetail.IM_ADDRESSES),
           (owned) val);
diff --git a/tests/tracker/set-im-addresses.vala b/tests/tracker/set-im-addresses.vala
index 1f13a4f..ccd12c0 100644
--- a/tests/tracker/set-im-addresses.vala
+++ b/tests/tracker/set-im-addresses.vala
@@ -111,21 +111,13 @@ public class SetIMAddressesTests : Folks.TestCase
             {
               i.notify["im-addresses"].connect (this._notify_im_addresses_cb);
 
-              HashTable<string, LinkedHashSet<string>> im_addresses =
-                new HashTable<string, LinkedHashSet<string>>
-                  (str_hash, str_equal);
-
-              var addrs_1 = new LinkedHashSet<string> ();
-              addrs_1.add ("one example org");
-              addrs_1.add ("two example org");
-              im_addresses.insert ("aim",
-                  (owned) addrs_1);
-
-              var addrs_2 = new LinkedHashSet<string> ();
-              addrs_2.add ("three example org");
-              addrs_2.add ("four example org");
-              im_addresses.insert ("yahoo",
-                  (owned) addrs_2);
+              var im_addresses = new HashMultiMap<string, string> ();
+
+              im_addresses.set ("aim", "one example org");
+              im_addresses.set ("aim", "two example org");
+
+              im_addresses.set ("yahoo", "three example org");
+              im_addresses.set ("yahoo", "four example org");
 
               Trf.Persona p = (Trf.Persona)i.personas.nth_data (0);
               p.im_addresses = (owned) im_addresses;
@@ -142,7 +134,7 @@ public class SetIMAddressesTests : Folks.TestCase
         {
           foreach (var proto in i.im_addresses.get_keys ())
             {
-              var addrs = i.im_addresses.lookup (proto);
+              var addrs = i.im_addresses.get (proto);
               foreach (var a in addrs)
                 {
                   foreach (unowned string my_a in this._addresses)
diff --git a/tools/import-pidgin.vala b/tools/import-pidgin.vala
index 9493e70..d293ad9 100644
--- a/tools/import-pidgin.vala
+++ b/tools/import-pidgin.vala
@@ -167,8 +167,7 @@ public class Folks.Importers.Pidgin : Folks.Importer
   private async Persona? parse_contact (Xml.Node *contact_node)
     {
       string alias = null;
-      HashTable<string, LinkedHashSet<string>> im_addresses =
-          new HashTable<string, LinkedHashSet<string>> (str_hash, str_equal);
+      var im_addresses = new HashMultiMap<string, string> ();
       string im_address_string = "";
 
       /* Parse the <buddy> elements beneath <contact> */
@@ -202,23 +201,14 @@ public class Folks.Importers.Pidgin : Folks.Importer
                    * we need to insert into the Persona's im-addresses property
                    * for the linking to work. */
                   string im_address = subiter->get_content ();
-
-                  LinkedHashSet<string> im_address_set =
-                      im_addresses.lookup (tp_protocol);
-                  if (im_address_set == null)
-                    {
-                      im_address_set = new LinkedHashSet<string> ();
-                      im_addresses.insert (tp_protocol, im_address_set);
-                    }
-
-                  im_address_set.add (im_address);
+                  im_addresses.set (tp_protocol, im_address);
                   im_address_string += "    %s\n".printf (im_address);
                 }
             }
         }
 
       /* Don't bother if there's no alias and only one IM address */
-      if (im_addresses.size () < 2 &&
+      if (im_addresses.size < 2 &&
           (alias == null || alias.strip () == "" ||
            alias.strip () == im_address_string.strip ()))
         {
@@ -232,8 +222,8 @@ public class Folks.Importers.Pidgin : Folks.Importer
       /* Create or update the relevant Persona */
       HashTable<string, Value?> details =
           new HashTable<string, Value?> (str_hash, str_equal);
-      Value im_addresses_value = Value (typeof (HashTable));
-      im_addresses_value.set_boxed (im_addresses);
+      Value im_addresses_value = Value (typeof (MultiMap));
+      im_addresses_value.set_object (im_addresses);
       details.insert ("im-addresses", im_addresses_value);
 
       Persona persona;
diff --git a/tools/inspect/utils.vala b/tools/inspect/utils.vala
index ca54553..fd1b69f 100644
--- a/tools/inspect/utils.vala
+++ b/tools/inspect/utils.vala
@@ -266,21 +266,19 @@ private class Folks.Inspect.Utils
         }
       else if (prop_name == "im-addresses")
         {
-          HashTable<string, LinkedHashSet<string>> im_addresses =
-              (HashTable<string, LinkedHashSet<string>>)
-              prop_value.get_boxed ();
+          MultiMap<string, string> im_addresses =
+              (MultiMap<string, string>) prop_value.get_object ();
           output_string = "{ ";
           bool first = true;
 
-          /* FIXME: This is rather inefficient */
-          im_addresses.foreach ((k, v) =>
+          foreach (var protocol in im_addresses.get_keys ())
             {
               if (first == false)
                 output_string += ", ";
-              output_string += "'%s' : { ".printf ((string) k);
+              output_string += "'%s' : { ".printf (protocol);
               first = false;
 
-              LinkedHashSet<string> addresses = (LinkedHashSet<string>) v;
+              var addresses = im_addresses.get (protocol);
               bool _first = true;
               foreach (var a in addresses)
                 {
@@ -291,7 +289,7 @@ private class Folks.Inspect.Utils
                 }
 
               output_string += " }";
-            });
+            }
 
           output_string += " }";
           return output_string;



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