[telepathy-mission-control/master] McdDispatcher: sort possible handlers by "quality"

Simon McVittie simon.mcvittie at collabora.co.uk
Wed May 6 06:18:30 PDT 2009


Quality is currently defined as: handlers that bypass approval are better
than handlers that don't; otherwise, handlers get one point for each
matching property in a filter, and the handler with most points is best.

In practice this should mean that the most specific bypass-approval filter
(if any), or the most specific filter (otherwise), wins.
---
 src/mcd-dispatcher.c |  106 +++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 91 insertions(+), 15 deletions(-)

diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c
index e1d548e..24c3725 100644
--- a/src/mcd-dispatcher.c
+++ b/src/mcd-dispatcher.c
@@ -505,15 +505,19 @@ channel_classes_equals (GHashTable *channel_class1, GHashTable *channel_class2)
     return TRUE;
 }
 
-/* returns TRUE if the channel matches one of the channel filters
+/* if the channel matches one of the channel filters, returns a positive
+ * number that increases with more specific matches; otherwise, returns 0
+ *
+ * (implementation detail: the positive number is 1 + the number of keys in the
+ * largest filter that matched)
  */
-static gboolean
+static guint
 match_filters (McdChannel *channel, GList *filters)
 {
     GHashTable *channel_properties;
-    gboolean matched = FALSE;
     McdChannelStatus status;
     GList *list;
+    guint best_quality = 0;
 
     status = mcd_channel_get_status (channel);
     channel_properties =
@@ -529,6 +533,17 @@ match_filters (McdChannel *channel, GList *filters)
         gboolean filter_matched = TRUE;
         gchar *property_name;
         GValue *filter_value;
+        guint quality;
+
+        /* +1 because the empty hash table matches everything :-) */
+        quality = g_hash_table_size (filter) + 1;
+
+        if (quality <= best_quality)
+        {
+            /* even if this filter matches, there's no way it can be a
+             * better-quality match than the best one we saw so far */
+            continue;
+        }
 
         g_hash_table_iter_init (&filter_iter, filter);
         while (g_hash_table_iter_next (&filter_iter,
@@ -545,12 +560,11 @@ match_filters (McdChannel *channel, GList *filters)
 
         if (filter_matched)
         {
-            matched = TRUE;
-            break;
+            best_quality = quality;
         }
     }
 
-    return matched;
+    return best_quality;
 }
 
 static McdClient *
@@ -566,7 +580,7 @@ get_default_handler (McdDispatcher *dispatcher, McdChannel *channel)
             !(client->interfaces & MCD_CLIENT_HANDLER))
             continue;
 
-        if (match_filters (channel, client->handler_filters))
+        if (match_filters (channel, client->handler_filters) > 0)
             return client;
     }
     return NULL;
@@ -623,6 +637,46 @@ handle_channels_cb (TpProxy *proxy, const GError *error, gpointer user_data,
     mcd_dispatcher_context_unref (context);
 }
 
+typedef struct
+{
+    McdClient *client;
+    gsize quality;
+} PossibleHandler;
+
+static gint
+possible_handler_cmp (gconstpointer a_,
+                      gconstpointer b_)
+{
+    const PossibleHandler *a = a_;
+    const PossibleHandler *b = b_;
+
+    if (a->client->bypass_approver)
+    {
+        if (!b->client->bypass_approver)
+        {
+            /* BypassApproval wins, so a is better than b */
+            return 1;
+        }
+    }
+    else if (b->client->bypass_approver)
+    {
+        /* BypassApproval wins, so b is better than a */
+        return -1;
+    }
+
+    if (a->quality < b->quality)
+    {
+        return -1;
+    }
+
+    if (b->quality < a->quality)
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
 static GStrv
 mcd_dispatcher_get_possible_handlers (McdDispatcher *self,
                                       const GList *channels)
@@ -640,7 +694,7 @@ mcd_dispatcher_get_possible_handlers (McdDispatcher *self,
     while (g_hash_table_iter_next (&client_iter, NULL, &client_p))
     {
         McdClient *client = client_p;
-        gboolean can_do = TRUE;
+        gsize total_quality = 0;
 
         if (client->proxy == NULL ||
             !(client->interfaces & MCD_CLIENT_HANDLER))
@@ -652,18 +706,29 @@ mcd_dispatcher_get_possible_handlers (McdDispatcher *self,
         for (iter = channels; iter != NULL; iter = iter->next)
         {
             McdChannel *channel = MCD_CHANNEL (iter->data);
+            guint quality;
+
+            quality = match_filters (channel, client->handler_filters);
 
-            if (!match_filters (channel, client->handler_filters))
+            if (quality == 0)
             {
-                can_do = FALSE;
+                total_quality = 0;
                 break;
             }
+            else
+            {
+                total_quality += quality;
+            }
         }
 
-        if (can_do)
+        if (total_quality > 0)
         {
-            handlers = g_list_prepend (handlers,
-                g_strconcat (MC_CLIENT_BUS_NAME_BASE, client->name, NULL));
+            PossibleHandler *ph = g_slice_new0 (PossibleHandler);
+
+            ph->client = client;
+            ph->quality = total_quality;
+
+            handlers = g_list_prepend (handlers, ph);
             n_handlers++;
         }
     }
@@ -674,16 +739,27 @@ mcd_dispatcher_get_possible_handlers (McdDispatcher *self,
         return NULL;
     }
 
-    /* we have at least one handler that can take the whole batch */
+    /* We have at least one handler that can take the whole batch. Sort
+     * the possible handlers, most preferred first (i.e. sort by ascending
+     * quality then reverse) */
+    handlers = g_list_sort (handlers, possible_handler_cmp);
+    handlers = g_list_reverse (handlers);
+
     ret = g_new0 (gchar *, n_handlers + 1);
 
     for (iter = handlers, i = 0; iter != NULL; iter = iter->next, i++)
     {
-        ret[i] = iter->data;
+        PossibleHandler *ph = iter->data;
+
+        ret[i] = g_strconcat (MC_CLIENT_BUS_NAME_BASE,
+                              ph->client->name, NULL);
+        g_slice_free (PossibleHandler, ph);
     }
 
     ret[n_handlers] = NULL;
+
     g_list_free (handlers);
+
     return ret;
 }
 
-- 
1.5.6.5




More information about the telepathy-commits mailing list