[geary/mjog/misc-fixes: 4/6] De-cruftify Geary.Collection utility functions



commit e4cbff8bfa9e7d50d6c6319a1acc86bea15bd0fd
Author: Michael Gratton <mike vee net>
Date:   Tue Nov 19 14:20:42 2019 +1100

    De-cruftify Geary.Collection utility functions
    
    Remove functions that have as-convenient methods in up-tp-date Gee,
    rename ::get_first to ::first so it reads better and add API docs,
    replace ::to_array_list with ::copy, since that's what's actually
    needed.

 .../application-attachment-manager.vala            |  2 +-
 src/client/application/application-client.vala     |  4 +-
 .../application/application-main-window.vala       |  6 +-
 src/client/composer/composer-widget.vala           | 14 ++--
 .../conversation-list/conversation-list-view.vala  | 24 +++----
 .../conversation-viewer/conversation-list-box.vala |  2 +-
 .../conversation-viewer/conversation-web-view.vala |  2 +-
 .../conversation-monitor/app-conversation-set.vala |  2 +-
 src/engine/app/email-store/app-copy-operation.vala |  6 +-
 .../app/email-store/app-fetch-operation.vala       |  2 +-
 src/engine/app/email-store/app-mark-operation.vala |  6 +-
 .../imap-db/search/imap-db-search-folder.vala      |  3 +-
 .../replay-ops/imap-engine-create-email.vala       |  2 +-
 src/engine/imap/api/imap-account-session.vala      | 10 ++-
 src/engine/imap/api/imap-folder-session.vala       |  3 +-
 src/engine/imap/transport/imap-client-session.vala |  2 +-
 src/engine/rfc822/rfc822-mailbox-addresses.vala    | 18 ++---
 src/engine/smtp/smtp-client-service.vala           |  2 +-
 src/engine/util/util-collection.vala               | 77 ++++------------------
 src/engine/util/util-config-file.vala              | 12 ++--
 src/engine/util/util-iterable.vala                 | 12 +++-
 src/engine/util/util-object.vala                   | 10 ++-
 test/engine/app/app-conversation-monitor-test.vala |  6 +-
 test/engine/app/app-conversation-set-test.vala     |  8 +--
 .../common/common-contact-harvester-test.vala      |  8 +--
 .../common/common-contact-store-impl-test.vala     |  8 +--
 test/engine/rfc822-mailbox-addresses-test.vala     | 33 ++++++++++
 27 files changed, 135 insertions(+), 149 deletions(-)
---
diff --git a/src/client/application/application-attachment-manager.vala 
b/src/client/application/application-attachment-manager.vala
index 805ecb6f..f4b5eb06 100644
--- a/src/client/application/application-attachment-manager.vala
+++ b/src/client/application/application-attachment-manager.vala
@@ -40,7 +40,7 @@ public class Application.AttachmentManager : GLib.Object {
                                        GLib.Cancellable? cancellable) {
         if (attachments.size == 1) {
             return yield save_attachment(
-                Geary.Collection.get_first(attachments), null, cancellable
+                Geary.Collection.first(attachments), null, cancellable
             );
         } else {
             return yield save_all(attachments, cancellable);
diff --git a/src/client/application/application-client.vala b/src/client/application/application-client.vala
index 579158c9..88d32887 100644
--- a/src/client/application/application-client.vala
+++ b/src/client/application/application-client.vala
@@ -813,7 +813,7 @@ public class Application.Client : Gtk.Application {
                 // likely still loading folders after being
                 // opened. Add a listener to try again later.
                 try {
-                    Geary.Account first = Geary.Collection.get_first(
+                    Geary.Account? first = Geary.Collection.first(
                         this.engine.get_accounts()
                     );
                     if (first != null) {
@@ -1131,7 +1131,7 @@ public class Application.Client : Gtk.Application {
         if (main != null) {
             this.controller.unregister_window(main);
             if (this.last_active_main_window == main) {
-                this.last_active_main_window = Geary.Collection.get_first(
+                this.last_active_main_window = Geary.Collection.first(
                     get_main_windows()
                 );
             }
diff --git a/src/client/application/application-main-window.vala 
b/src/client/application/application-main-window.vala
index 6f13a07e..e163d1bc 100644
--- a/src/client/application/application-main-window.vala
+++ b/src/client/application/application-main-window.vala
@@ -586,7 +586,7 @@ public class Application.MainWindow :
             if (loaded.size == 1) {
                 // A single conversation was loaded, so ensure we
                 // scroll to the email in the conversation.
-                Geary.App.Conversation target = Geary.Collection.get_first(loaded);
+                Geary.App.Conversation? target = Geary.Collection.first(loaded);
                 ConversationListBox? current_list =
                     this.conversation_viewer.current_list;
                 if (current_list != null &&
@@ -860,7 +860,7 @@ public class Application.MainWindow :
     private Geary.Folder? get_first_inbox() {
         Geary.Folder? inbox = null;
         try {
-            Geary.Account first = Geary.Collection.get_first(
+            Geary.Account? first = Geary.Collection.first(
                 this.application.engine.get_accounts()
             );
             if (first != null) {
@@ -1285,7 +1285,7 @@ public class Application.MainWindow :
 
             case 1:
                 update_conversation_actions(SINGLE);
-                Geary.App.Conversation convo = Geary.Collection.get_first(to_select);
+                Geary.App.Conversation? convo = Geary.Collection.first(to_select);
 
                 // It's possible for a conversation with zero email to
                 // be selected, when it has just evaporated after its
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index d47af36c..9bbd6660 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -624,24 +624,26 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
 
             // Assemble the headers.
             if (email.length > 0 && headers.contains("to"))
-                this.to = "%s,%s".printf(email, Geary.Collection.get_first(headers.get("to")));
+                this.to = "%s,%s".printf(
+                    email, Geary.Collection.first(headers.get("to"))
+                );
             else if (email.length > 0)
                 this.to = email;
             else if (headers.contains("to"))
-                this.to = Geary.Collection.get_first(headers.get("to"));
+                this.to = Geary.Collection.first(headers.get("to"));
 
             if (headers.contains("cc"))
-                this.cc = Geary.Collection.get_first(headers.get("cc"));
+                this.cc = Geary.Collection.first(headers.get("cc"));
 
             if (headers.contains("bcc"))
-                this.bcc = Geary.Collection.get_first(headers.get("bcc"));
+                this.bcc = Geary.Collection.first(headers.get("bcc"));
 
             if (headers.contains("subject"))
-                this.subject = Geary.Collection.get_first(headers.get("subject"));
+                this.subject = Geary.Collection.first(headers.get("subject"));
 
             if (headers.contains("body"))
                 this.body_html = Geary.HTML.preserve_whitespace(Geary.HTML.escape_markup(
-                    Geary.Collection.get_first(headers.get("body"))));
+                    Geary.Collection.first(headers.get("body"))));
 
             Gee.List<string> attachments = new Gee.LinkedList<string>();
             attachments.add_all(headers.get("attach"));
diff --git a/src/client/conversation-list/conversation-list-view.vala 
b/src/client/conversation-list/conversation-list-view.vala
index 6440222f..96acc4b8 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -492,8 +492,8 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
         }
 
         // only notify if different than what was previously reported
-        if (!Geary.Collection.are_sets_equal<Geary.App.Conversation>(
-                this.selected, new_selection)) {
+        if (this.selected.size != new_selection.size ||
+            !this.selected.contains_all(new_selection)) {
             this.selected = new_selection;
             conversations_selected(this.selected.read_only_view);
         }
@@ -520,18 +520,18 @@ public class ConversationListView : Gtk.TreeView, Geary.BaseInterface {
 
     // Always returns false, so it can be used as a one-time SourceFunc
     private bool update_visible_conversations() {
+        bool changed = false;
         Gee.Set<Geary.App.Conversation> visible_conversations = get_visible_conversations();
-        if (current_visible_conversations != null
-            && Geary.Collection.are_sets_equal<Geary.App.Conversation>(
-            current_visible_conversations, visible_conversations)) {
-            return false;
+        if (this.current_visible_conversations == null ||
+            this.current_visible_conversations.size != visible_conversations.size ||
+            !this.current_visible_conversations.contains_all(visible_conversations)) {
+            this.current_visible_conversations = visible_conversations;
+            visible_conversations_changed(
+                this.current_visible_conversations.read_only_view
+            );
+            changed = true;
         }
-
-        current_visible_conversations = visible_conversations;
-
-        visible_conversations_changed(current_visible_conversations.read_only_view);
-
-        return false;
+        return changed;
     }
 
     private void schedule_visible_conversations_changed() {
diff --git a/src/client/conversation-viewer/conversation-list-box.vala 
b/src/client/conversation-viewer/conversation-list-box.vala
index 377f66e6..4e501aa4 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -678,7 +678,7 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
                 id => this.conversation.contains_email_by_id(id)
             ).to_array_list();
             valid_scroll_to.sort((a, b) => a.natural_sort_comparator(b));
-            var first_scroll = Geary.Collection.get_first(valid_scroll_to);
+            var first_scroll = Geary.Collection.first(valid_scroll_to);
 
             if (first_scroll != null) {
                 foreach (Geary.Email email in all_email) {
diff --git a/src/client/conversation-viewer/conversation-web-view.vala 
b/src/client/conversation-viewer/conversation-web-view.vala
index 217528ab..d22a836e 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -132,7 +132,7 @@ public class ConversationWebView : ClientWebView {
             });
 
         controller.search(
-            Geary.Collection.get_first(terms),
+            Geary.Collection.first(terms),
             WebKit.FindOptions.CASE_INSENSITIVE |
             WebKit.FindOptions.WRAP_AROUND,
             128
diff --git a/src/engine/app/conversation-monitor/app-conversation-set.vala 
b/src/engine/app/conversation-monitor/app-conversation-set.vala
index ca626f6f..e9eb016a 100644
--- a/src/engine/app/conversation-monitor/app-conversation-set.vala
+++ b/src/engine/app/conversation-monitor/app-conversation-set.vala
@@ -302,7 +302,7 @@ private class Geary.App.ConversationSet : BaseObject {
         } else {
             Gee.Set<Conversation> associated =
                 get_associated_conversations(email);
-            conversation = Collection.get_first<Conversation>(associated);
+            conversation = Collection.first(associated);
             if (conversation == null) {
                 // Not in or related to any existing conversations, so
                 // create one
diff --git a/src/engine/app/email-store/app-copy-operation.vala 
b/src/engine/app/email-store/app-copy-operation.vala
index 44c76fd7..f6d37079 100644
--- a/src/engine/app/email-store/app-copy-operation.vala
+++ b/src/engine/app/email-store/app-copy-operation.vala
@@ -19,9 +19,9 @@ private class Geary.App.CopyOperation : Geary.App.AsyncFolderOperation {
         Geary.FolderSupport.Copy? copy = folder as Geary.FolderSupport.Copy;
         assert(copy != null);
 
-        Gee.List<Geary.EmailIdentifier> list
-            = Geary.Collection.to_array_list<Geary.EmailIdentifier>(ids);
-        yield copy.copy_email_async(list, destination, cancellable);
+        yield copy.copy_email_async(
+            Collection.copy(ids), destination, cancellable
+        );
         return ids;
     }
 }
diff --git a/src/engine/app/email-store/app-fetch-operation.vala 
b/src/engine/app/email-store/app-fetch-operation.vala
index eef93d10..4db6c457 100644
--- a/src/engine/app/email-store/app-fetch-operation.vala
+++ b/src/engine/app/email-store/app-fetch-operation.vala
@@ -20,7 +20,7 @@ private class Geary.App.FetchOperation : Geary.App.AsyncFolderOperation {
         Geary.Folder folder, Gee.Collection<Geary.EmailIdentifier> ids,
         Cancellable? cancellable) throws Error {
         assert(result == null);
-        Geary.EmailIdentifier? id = Geary.Collection.get_first(ids);
+        Geary.EmailIdentifier? id = Collection.first(ids);
         assert(id != null);
 
         result = yield folder.fetch_email_async(
diff --git a/src/engine/app/email-store/app-mark-operation.vala 
b/src/engine/app/email-store/app-mark-operation.vala
index f69b9049..00a0858c 100644
--- a/src/engine/app/email-store/app-mark-operation.vala
+++ b/src/engine/app/email-store/app-mark-operation.vala
@@ -21,9 +21,9 @@ private class Geary.App.MarkOperation : Geary.App.AsyncFolderOperation {
         Geary.FolderSupport.Mark? mark = folder as Geary.FolderSupport.Mark;
         assert(mark != null);
 
-        Gee.List<Geary.EmailIdentifier> list
-            = Geary.Collection.to_array_list<Geary.EmailIdentifier>(ids);
-        yield mark.mark_email_async(list, flags_to_add, flags_to_remove, cancellable);
+        yield mark.mark_email_async(
+            Collection.copy(ids), flags_to_add, flags_to_remove, cancellable
+        );
         return ids;
     }
 }
diff --git a/src/engine/imap-db/search/imap-db-search-folder.vala 
b/src/engine/imap-db/search/imap-db-search-folder.vala
index 5664b361..34e3ee71 100644
--- a/src/engine/imap-db/search/imap-db-search-folder.vala
+++ b/src/engine/imap-db/search/imap-db-search-folder.vala
@@ -319,8 +319,7 @@ private class Geary.ImapDB.SearchFolder : Geary.SearchFolder, Geary.FolderSuppor
                 yield folder.open_async(Geary.Folder.OpenFlags.NONE, cancellable);
                 open = true;
                 yield remove.remove_email_async(
-                    Geary.Collection.to_array_list<Geary.EmailIdentifier>(ids),
-                    cancellable
+                    Collection.copy(ids), cancellable
                 );
             } finally {
                 if (open) {
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
index 9d519c47..51866395 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-create-email.vala
@@ -73,7 +73,7 @@ private class Geary.ImapEngine.CreateEmail : SendReplayOperation {
                 );
 
             if (results.size > 0) {
-                this.created_id = Collection.get_first<Email>(results.keys).id;
+                this.created_id = Collection.first(results.keys).id;
             } else {
                 // Something went wrong creating/merging the message,
                 // so pretend we don't know what its UID is so the
diff --git a/src/engine/imap/api/imap-account-session.vala b/src/engine/imap/api/imap-account-session.vala
index ff335262..63838b2f 100644
--- a/src/engine/imap/api/imap-account-session.vala
+++ b/src/engine/imap/api/imap-account-session.vala
@@ -397,9 +397,13 @@ internal class Geary.Imap.AccountSession : Geary.Imap.SessionObject {
             cancellable
         );
 
-        assert(responses.size == 1);
-
-        return Geary.Collection.get_first(responses.values);
+        var response = Collection.first(responses.values);
+        if (response == null) {
+            throw new ImapError.SERVER_ERROR(
+                "No status response received from server"
+            );
+        }
+        return response;
     }
 
     private async Gee.Map<Command, StatusResponse>
diff --git a/src/engine/imap/api/imap-folder-session.vala b/src/engine/imap/api/imap-folder-session.vala
index 00f48ca8..d07199b2 100644
--- a/src/engine/imap/api/imap-folder-session.vala
+++ b/src/engine/imap/api/imap-folder-session.vala
@@ -688,7 +688,8 @@ private class Geary.Imap.FolderSession : Geary.Imap.SessionObject {
                 debug("Unable to retrieve COPYUID UIDs: %s", ierr.message);
             }
 
-            if (!Collection.is_empty(src_uids) && !Collection.is_empty(dst_uids)) {
+            if (src_uids != null && !src_uids.is_empty &&
+                dst_uids != null && !dst_uids.is_empty) {
                 Gee.Map<UID, UID> copyuids = new Gee.HashMap<UID, UID>();
                 int ctr = 0;
                 for (;;) {
diff --git a/src/engine/imap/transport/imap-client-session.vala 
b/src/engine/imap/transport/imap-client-session.vala
index d3ff645a..d26aef7a 100644
--- a/src/engine/imap/transport/imap-client-session.vala
+++ b/src/engine/imap/transport/imap-client-session.vala
@@ -1281,7 +1281,7 @@ public class Geary.Imap.ClientSession : BaseObject {
             check_unsupported_send_command(cmd);
 
         // only issue one event to the state machine for all commands; either all succeed or all fail
-        MachineParams params = new MachineParams(Geary.Collection.get_first(cmds));
+        MachineParams params = new MachineParams(Collection.first(cmds));
         fsm.issue(Event.SEND_CMD, null, params);
 
         if (params.err != null)
diff --git a/src/engine/rfc822/rfc822-mailbox-addresses.vala b/src/engine/rfc822/rfc822-mailbox-addresses.vala
index f218944f..616c28ab 100644
--- a/src/engine/rfc822/rfc822-mailbox-addresses.vala
+++ b/src/engine/rfc822/rfc822-mailbox-addresses.vala
@@ -184,19 +184,11 @@ public class Geary.RFC822.MailboxAddresses :
     }
 
     public bool equal_to(MailboxAddresses other) {
-        if (this == other)
-            return true;
-
-        if (addrs.size != other.addrs.size)
-            return false;
-
-        Gee.HashSet<RFC822.MailboxAddress> first = new Gee.HashSet<RFC822.MailboxAddress>();
-        first.add_all(addrs);
-
-        Gee.HashSet<RFC822.MailboxAddress> second = new Gee.HashSet<RFC822.MailboxAddress>();
-        second.add_all(other.addrs);
-
-        return Collection.are_sets_equal<RFC822.MailboxAddress>(first, second);
+        return (
+            this == other ||
+            (this.addrs.size == other.addrs.size &&
+             this.addrs.contains_all(other.addrs))
+        );
     }
 
     /**
diff --git a/src/engine/smtp/smtp-client-service.vala b/src/engine/smtp/smtp-client-service.vala
index 69071d00..70893b38 100644
--- a/src/engine/smtp/smtp-client-service.vala
+++ b/src/engine/smtp/smtp-client-service.vala
@@ -428,7 +428,7 @@ public class Geary.Smtp.ClientService : Geary.ClientService {
                     null, 1, REFERENCES, NONE, cancellable
                 );
                 if (list != null && !list.is_empty) {
-                    Email listed = Collection.get_first<Email>(list);
+                    Email listed = Collection.first(list);
                     if (listed.message_id != null &&
                         listed.message_id.equal_to(id)) {
                         break;
diff --git a/src/engine/util/util-collection.vala b/src/engine/util/util-collection.vala
index 46ba01d1..4cf8316e 100644
--- a/src/engine/util/util-collection.vala
+++ b/src/engine/util/util-collection.vala
@@ -8,9 +8,6 @@ namespace Geary.Collection {
 
     public delegate uint8 ByteTransformer(uint8 b);
 
-    public inline bool is_empty(Gee.Collection? c) {
-        return c == null || c.size == 0;
-    }
 
     /** Returns a modifiable collection containing a single element. */
     public Gee.Collection<T> single<T>(T element) {
@@ -26,80 +23,34 @@ namespace Geary.Collection {
         return single;
     }
 
-    // A substitute for ArrayList<G>.wrap() for compatibility with older versions of Gee.
-    public Gee.ArrayList<G> array_list_wrap<G>(G[] a, owned Gee.EqualDataFunc<G>? equal_func = null) {
-        Gee.ArrayList<G> list = new Gee.ArrayList<G>((owned) equal_func);
-        add_all_array<G>(list, a);
-        return list;
-    }
-
-    public Gee.ArrayList<G> to_array_list<G>(Gee.Collection<G> c) {
-        Gee.ArrayList<G> list = new Gee.ArrayList<G>();
-        list.add_all(c);
-        return list;
-    }
-
-    public Gee.HashMap<Key, Value> to_hash_map<Key, Value>(
-        Gee.Collection<Value> c, Gee.MapFunc<Key, Value> key_selector) {
-        Gee.HashMap<Key, Value> map = new Gee.HashMap<Key, Value>();
-        foreach (Value v in c) {
-            map.set(key_selector(v), v);
-        }
-        return map;
-    }
-
-    public void add_all_array<G>(Gee.Collection<G> c, G[] ar) {
-        foreach (G g in ar) {
-            c.add(g);
-        }
+    /** Returns a copy of the given collection in a new collection. */
+    public Gee.Collection<V> copy<V>(Gee.Collection<V> original) {
+        // Use a linked list, the returned value can't be accessed by
+        // index anyway
+        var copy = new Gee.LinkedList<V>();
+        copy.add_all(original);
+        return copy;
     }
 
-    public G? get_first<G>(Gee.Collection<G> c) {
+    /** Returns the first element from a collection. */
+    public G? first<G>(Gee.Collection<G> c) {
         Gee.Iterator<G> iter = c.iterator();
         return iter.next() ? iter.get() : null;
     }
 
     /**
-     * Returns the first element in the Collection that passes the Predicte function.
-     *
-     * The Collection is walked in Iterator order.
-     */
-    public G? find_first<G>(Gee.Collection<G> c, owned Gee.Predicate<G> pred) {
-        Gee.Iterator<G> iter = c.iterator();
-        while (iter.next()) {
-            if (pred(iter.get()))
-                return iter.get();
-        }
-
-        return null;
-    }
-
-    public bool are_sets_equal<G>(Gee.Set<G> a, Gee.Set<G> b) {
-        if (a.size != b.size) {
-            return false;
-        }
-
-        foreach (G element in a) {
-            if (!b.contains(element)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Removes all elements from the Collection that do pass the Predicate function.
+     * Removes all elements that pass the given predicate.
      *
      * Note that this modifies the supplied Collection.
      */
-    public Gee.Collection<G> remove_if<G>(Gee.Collection<G> c, owned Gee.Predicate<G> pred) {
+    public Gee.Collection<G> remove_if<G>(Gee.Collection<G> c,
+                                          owned Gee.Predicate<G> pred) {
         Gee.Iterator<G> iter = c.iterator();
         while (iter.next()) {
-            if (pred(iter.get()))
+            if (pred(iter.get())) {
                 iter.remove();
+            }
         }
-
         return c;
     }
 
diff --git a/src/engine/util/util-config-file.vala b/src/engine/util/util-config-file.vala
index 61ced4c4..6b938285 100644
--- a/src/engine/util/util-config-file.vala
+++ b/src/engine/util/util-config-file.vala
@@ -126,20 +126,20 @@ public class Geary.ConfigFile {
         }
 
         public Gee.List<string> get_string_list(string key) {
+            var strs = new Gee.ArrayList<string>();
             try {
-                string[] list = this.backing.get_string_list(this.name, key);
-                if (list.length > 0)
-                    return Geary.Collection.array_list_wrap<string>(list);
+                strs.add_all_array(this.backing.get_string_list(this.name, key));
             } catch (GLib.KeyFileError err) {
                 // Oh well
             }
-            return new Gee.ArrayList<string>();
+            return strs;
         }
 
         public Gee.List<string> get_required_string_list(string key)
             throws GLib.KeyFileError {
-            string[] list = this.backing.get_string_list(this.name, key);
-            return Geary.Collection.array_list_wrap<string>(list);
+            var strs = new Gee.ArrayList<string>();
+            strs.add_all_array(this.backing.get_string_list(this.name, key));
+            return strs;
         }
 
         public void set_string_list(string key, Gee.List<string> value) {
diff --git a/src/engine/util/util-iterable.vala b/src/engine/util/util-iterable.vala
index 2cff4b16..29ff8e27 100644
--- a/src/engine/util/util-iterable.vala
+++ b/src/engine/util/util-iterable.vala
@@ -20,7 +20,9 @@ namespace Geary {
         va_list args = va_list();
         G arg = g;
 
-        Gee.ArrayList<G> list = new Gee.ArrayList<G>();
+        // Use a linked list since we will only ever be iterating over
+        // it
+        var list = new Gee.LinkedList<G>();
         do {
             list.add(arg);
         } while((arg = args.arg()) != null);
@@ -31,8 +33,12 @@ namespace Geary {
     /**
      * Take an array of items and return a Geary.Iterable for convenience.
      */
-    public Geary.Iterable<G> iterate_array<G>(G[] a) {
-        return Geary.traverse<G>(Geary.Collection.array_list_wrap<G>(a));
+    public Geary.Iterable<G> iterate_array<G>(G[] a, owned Gee.EqualDataFunc<G>? equal_func = null) {
+        // Use a linked list since we will only ever be iterating over
+        // it
+        var list = new Gee.LinkedList<G>((owned) equal_func);
+        list.add_all_array(a);
+        return Geary.traverse<G>(list);
     }
 }
 
diff --git a/src/engine/util/util-object.vala b/src/engine/util/util-object.vala
index 2bbc0dd8..071da6f1 100644
--- a/src/engine/util/util-object.vala
+++ b/src/engine/util/util-object.vala
@@ -12,12 +12,10 @@ namespace Geary.ObjectUtils {
 public Gee.List<Binding>? mirror_properties(Object source, Object dest, BindingFlags
     flags = GLib.BindingFlags.DEFAULT | GLib.BindingFlags.SYNC_CREATE) {
     // Make sets of both object's properties.
-    Gee.HashSet<ParamSpec> source_properties = new Gee.HashSet<ParamSpec>();
-    Collection.add_all_array<ParamSpec>(source_properties,
-        source.get_class().list_properties());
-    Gee.HashSet<ParamSpec> dest_properties = new Gee.HashSet<ParamSpec>();
-    Collection.add_all_array<ParamSpec>(dest_properties,
-        dest.get_class().list_properties());
+    Gee.HashSet<ParamSpec> source_properties =
+        iterate_array(source.get_class().list_properties()).to_hash_set();
+    Gee.HashSet<ParamSpec> dest_properties =
+        iterate_array(dest.get_class().list_properties()).to_hash_set();
 
     // Remove properties from source_properties that are not in both sets.
     source_properties.retain_all(dest_properties);
diff --git a/test/engine/app/app-conversation-monitor-test.vala 
b/test/engine/app/app-conversation-monitor-test.vala
index 3d10684e..b38ad0a3 100644
--- a/test/engine/app/app-conversation-monitor-test.vala
+++ b/test/engine/app/app-conversation-monitor-test.vala
@@ -133,7 +133,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
         assert_non_null(monitor.window_lowest, "Lowest window id");
         assert_equal(e1.id, monitor.window_lowest, "Lowest window id");
 
-        Conversation c1 = Geary.Collection.get_first(monitor.read_only_view);
+        Conversation c1 = Collection.first(monitor.read_only_view);
         assert_equal(e1, c1.get_email_by_id(e1.id), "Email not present in conversation");
     }
 
@@ -175,7 +175,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
         assert_non_null(monitor.window_lowest, "Lowest window id");
         assert_equal(e2.id, monitor.window_lowest, "Lowest window id");
 
-        Conversation c1 = Geary.Collection.get_first(monitor.read_only_view);
+        Conversation c1 = Collection.first(monitor.read_only_view);
         assert_equal(e1, c1.get_email_by_id(e1.id), "Related email not present in conversation");
         assert_equal(e2, c1.get_email_by_id(e2.id), "In folder not present in conversation");
     }
@@ -329,7 +329,7 @@ class Geary.App.ConversationMonitorTest : TestCase {
 
         assert_int(1, monitor.size, "Conversation count");
 
-        Conversation c1 = Geary.Collection.get_first(monitor.read_only_view);
+        Conversation c1 = Collection.first(monitor.read_only_view);
         assert_int(2, c1.get_count(), "Conversation message count");
         assert_equal(e3, c1.get_email_by_id(e3.id),
                      "Appended email not present in conversation");
diff --git a/test/engine/app/app-conversation-set-test.vala b/test/engine/app/app-conversation-set-test.vala
index 29ac6512..1b3dc97b 100644
--- a/test/engine/app/app-conversation-set-test.vala
+++ b/test/engine/app/app-conversation-set-test.vala
@@ -116,7 +116,7 @@ class Geary.App.ConversationSetTest : TestCase {
         assert(this.test.get_email_count() == 1);
 
         assert(added.size == 1);
-        assert(Geary.Collection.get_first(added).get_email_by_id(e1.id) == e1);
+        assert(Collection.first(added).get_email_by_id(e1.id) == e1);
 
         assert(appended.size == 0);
         assert(removed.is_empty);
@@ -165,7 +165,7 @@ class Geary.App.ConversationSetTest : TestCase {
 
         assert(added.size == 1);
 
-        Conversation convo1 = Geary.Collection.get_first(added);
+        Conversation convo1 = Collection.first(added);
         assert(convo1.get_email_by_id(e1.id) == e1);
         assert(convo1.get_email_by_id(e2.id) == e2);
 
@@ -195,7 +195,7 @@ class Geary.App.ConversationSetTest : TestCase {
         assert(added.is_empty);
 
         assert(appended.size == 1);
-        Conversation convo2 = Geary.Collection.get_first(appended.get_keys());
+        Conversation convo2 = Collection.first(appended.get_keys());
         assert(convo2.get_email_by_id(e1.id) == e1);
         assert(convo2.get_email_by_id(e2.id) == e2);
         assert(convo2.get_email_by_id(e3.id) == e3);
@@ -234,7 +234,7 @@ class Geary.App.ConversationSetTest : TestCase {
         assert(added.is_empty);
 
         assert(appended.size == 1);
-        Conversation convo = Geary.Collection.get_first(appended.get_keys());
+        Conversation convo = Collection.first(appended.get_keys());
         assert(convo.get_email_by_id(e1.id) == e1);
         assert(convo.get_email_by_id(e2.id) == e2);
 
diff --git a/test/engine/common/common-contact-harvester-test.vala 
b/test/engine/common/common-contact-harvester-test.vala
index 02bc73d0..e73ccedf 100644
--- a/test/engine/common/common-contact-harvester-test.vala
+++ b/test/engine/common/common-contact-harvester-test.vala
@@ -73,7 +73,7 @@ class Geary.ContactHarvesterImplTest : TestCase {
 
         Gee.Collection<Contact> contacts = update_call.called_arg<Gee.Collection<Contact>>(0);
         assert_int(1, contacts.size, "contacts length");
-        Contact? created = Collection.get_first<Contact>(contacts) as Contact;
+        Contact? created = Collection.first(contacts) as Contact;
         assert_non_null(created, "contacts contents");
 
         assert_string("Test", created.real_name);
@@ -121,7 +121,7 @@ class Geary.ContactHarvesterImplTest : TestCase {
         this.store.assert_expectations();
 
         Gee.Collection<Contact> contacts = update_call.called_arg<Gee.Collection<Contact>>(0);
-        Contact? created = Collection.get_first<Contact>(contacts) as Contact;
+        Contact? created = Collection.first(contacts) as Contact;
         assert_int(
             Contact.Importance.SEEN,
             created.highest_importance,
@@ -150,7 +150,7 @@ class Geary.ContactHarvesterImplTest : TestCase {
         this.store.assert_expectations();
 
         Gee.Collection<Contact> contacts = update_call.called_arg<Gee.Collection<Contact>>(0);
-        Contact? created = Collection.get_first<Contact>(contacts) as Contact;
+        Contact? created = Collection.first(contacts) as Contact;
         assert_int(
             Contact.Importance.SENT_TO,
             created.highest_importance,
@@ -179,7 +179,7 @@ class Geary.ContactHarvesterImplTest : TestCase {
         this.store.assert_expectations();
 
         Gee.Collection<Contact> contacts = update_call.called_arg<Gee.Collection<Contact>>(0);
-        Contact? created = Collection.get_first<Contact>(contacts) as Contact;
+        Contact? created = Collection.first(contacts) as Contact;
         assert_int(
             Contact.Importance.RECEIVED_FROM,
             created.highest_importance,
diff --git a/test/engine/common/common-contact-store-impl-test.vala 
b/test/engine/common/common-contact-store-impl-test.vala
index 3974c00f..2bc50dea 100644
--- a/test/engine/common/common-contact-store-impl-test.vala
+++ b/test/engine/common/common-contact-store-impl-test.vala
@@ -125,7 +125,7 @@ class Geary.ContactStoreImplTest : TestCase {
         );
         assert_int(1, results.size, "results.size");
 
-        Contact search_hit = Collection.get_first(results);
+        Contact search_hit = Collection.first(results);
         assert_string("Test example com", search_hit.email, "Existing email");
         assert_string("test example com", search_hit.normalized_email, "Existing normalized_email");
         assert_string("Test Name", search_hit.real_name, "Existing real_name");
@@ -146,7 +146,7 @@ class Geary.ContactStoreImplTest : TestCase {
         );
         assert_int(1, results.size, "results.size");
 
-        Contact search_hit = Collection.get_first(results);
+        Contact search_hit = Collection.first(results);
         assert_string("Test example com", search_hit.email, "Existing email");
         assert_string("test example com", search_hit.normalized_email, "Existing normalized_email");
         assert_string("Test Name", search_hit.real_name, "Existing real_name");
@@ -180,7 +180,7 @@ class Geary.ContactStoreImplTest : TestCase {
         );
         assert_int(1, results.size, "results.size");
 
-        Contact search_hit = Collection.get_first(results);
+        Contact search_hit = Collection.first(results);
         assert_string("Germán", search_hit.real_name, "Existing real_name");
     }
 
@@ -211,7 +211,7 @@ class Geary.ContactStoreImplTest : TestCase {
         );
         assert_int(1, results.size, "results.size");
 
-        Contact search_hit = Collection.get_first(results);
+        Contact search_hit = Collection.first(results);
         assert_string("年収1億円目指せ", search_hit.real_name, "Existing real_name");
     }
 
diff --git a/test/engine/rfc822-mailbox-addresses-test.vala b/test/engine/rfc822-mailbox-addresses-test.vala
index 6c3a59b1..a9b2415e 100644
--- a/test/engine/rfc822-mailbox-addresses-test.vala
+++ b/test/engine/rfc822-mailbox-addresses-test.vala
@@ -12,6 +12,7 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
         add_test("from_rfc822_string_encoded", from_rfc822_string_encoded);
         add_test("from_rfc822_string_quoted", from_rfc822_string_quoted);
         add_test("to_rfc822_string", to_rfc822_string);
+        add_test("equal_to", equal_to);
     }
 
     public void from_rfc822_string_encoded() throws Error {
@@ -49,6 +50,38 @@ class Geary.RFC822.MailboxAddressesTest : TestCase {
                .to_rfc822_string() == "test1 example com, test2 example com");
     }
 
+    public void equal_to() throws Error {
+        var mailboxes_a = new_addreses({ "test1 example com" });
+        var mailboxes_b = new_addreses({ "test1 example com" });
+        var mailboxes_c = new_addreses({ "test2 example com" });
+
+        assert_true(mailboxes_a.equal_to(mailboxes_a));
+        assert_true(mailboxes_a.equal_to(mailboxes_b));
+        assert_false(mailboxes_a.equal_to(mailboxes_c));
+
+        assert_true(
+            new_addreses({ "test1 example com", "test2 example com" }).equal_to(
+                new_addreses({ "test1 example com", "test2 example com" })
+            )
+        );
+        assert_true(
+            new_addreses({ "test1 example com", "test2 example com" }).equal_to(
+                new_addreses({ "test2 example com", "test1 example com" })
+            )
+        );
+
+        assert_false(
+            new_addreses({ "test1 example com", "test2 example com" }).equal_to(
+                new_addreses({ "test1 example com" })
+            )
+        );
+        assert_false(
+            new_addreses({ "test1 example com", "test2 example com" }).equal_to(
+                new_addreses({ "test1 example com", "test3 example com" })
+            )
+        );
+    }
+
     private MailboxAddresses new_addreses(string[] address_strings) {
         Gee.List<MailboxAddress> addresses = new Gee.LinkedList<MailboxAddress>();
         foreach (string address in address_strings) {


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