[gnome-builder/wip/extensions] extensions: use external data in libpeas plugin info



commit 15c09a33c6cd92a06b79b0db93bdd9a5e6c7687c
Author: Christian Hergert <christian hergert me>
Date:   Sun Jun 28 00:41:04 2015 -0700

    extensions: use external data in libpeas plugin info
    
    This required a lot of churn in the IdeExtensionPoint API (which is a good
    thing). It also required making services and a few other base classes use
    GTypeInterface instead.
    
    However, this allows for using dependency injection from .plugin files
    for a concrete implementation. We can even swap out the old version at
    runtime for a higher priority version when a plugin is loaded/unloaded.

 libide/ide-extension-point.c |  282 +++++++++++++++++++-----------------------
 libide/ide-extension-point.h |   15 ++-
 2 files changed, 133 insertions(+), 164 deletions(-)
---
diff --git a/libide/ide-extension-point.c b/libide/ide-extension-point.c
index 7d502ff..0a145a2 100644
--- a/libide/ide-extension-point.c
+++ b/libide/ide-extension-point.c
@@ -20,27 +20,22 @@
 
 #include <glib/gi18n.h>
 #include <libpeas/peas.h>
+#include <stdlib.h>
 
 #include "ide-extension-point.h"
 
 struct _IdeExtensionPoint
 {
   GObject      parent_instance;
-  const gchar *name;
-  GPtrArray   *extensions;
-};
 
-typedef struct
-{
-  const gchar *module_name;
-  GType        type;
-  gint         priority;
-  guint        loaded : 1;
-} IdeExtension;
+  GType        interface_type;
+  const gchar *match_key;
+  const gchar *priority_key;
+};
 
 enum {
   PROP_0,
-  PROP_NAME,
+  PROP_REQUIRED_TYPE,
   LAST_PROP
 };
 
@@ -56,38 +51,11 @@ static GRecMutex   gExtensionsMutex;
 static GHashTable *gExtensions;
 static guint       gSignals [LAST_SIGNAL];
 
-static IdeExtension *
-ide_extension_new (GType type,
-                   gint  priority)
-{
-  IdeExtension *exten;
-  GTypePlugin *plugin;
-
-  exten = g_slice_new0 (IdeExtension);
-  exten->type = type;
-  exten->priority = priority;
-  exten->loaded = TRUE;
-
-  plugin = g_type_get_plugin (type);
-
-  if (PEAS_IS_OBJECT_MODULE (plugin))
-    {
-      gchar *module_name;
-
-      g_object_get (plugin, "module-name", &module_name, NULL);
-      exten->module_name = g_intern_string (module_name);
-      g_free (module_name);
-    }
-
-  return exten;
-}
-
 static void
 load_plugin_cb (PeasEngine     *engine,
                 PeasPluginInfo *plugin_info,
                 gpointer        unused)
 {
-  const gchar *module_name;
   GHashTableIter iter;
   gpointer key;
   gpointer value;
@@ -95,13 +63,6 @@ load_plugin_cb (PeasEngine     *engine,
   g_assert (PEAS_IS_ENGINE (engine));
   g_assert (plugin_info != NULL);
 
-  /*
-   * FIXME: Since we don't have loader information, we could possibly have
-   *        two plugins from different loaders with the same module name.
-   */
-
-  module_name = peas_plugin_info_get_module_name (plugin_info);
-
   g_rec_mutex_lock (&gExtensionsMutex);
 
   g_hash_table_iter_init (&iter, gExtensions);
@@ -109,24 +70,8 @@ load_plugin_cb (PeasEngine     *engine,
   while (g_hash_table_iter_next (&iter, &key, &value))
     {
       IdeExtensionPoint *point = value;
-      gboolean emit_changed = FALSE;
-      gsize i;
 
-      for (i = 0; i < point->extensions->len; i++)
-        {
-          IdeExtension *exten = g_ptr_array_index (point->extensions, i);
-
-          if (g_strcmp0 (exten->module_name, module_name) == 0)
-            {
-              if (exten->loaded != TRUE)
-                {
-                  exten->loaded = TRUE;
-                  emit_changed = TRUE;
-                }
-            }
-        }
-
-      if (emit_changed)
+      if (peas_engine_provides_extension (engine, plugin_info, point->interface_type))
         g_signal_emit (point, gSignals [CHANGED], 0);
     }
 
@@ -138,7 +83,6 @@ unload_plugin_cb (PeasEngine     *engine,
                   PeasPluginInfo *plugin_info,
                   gpointer        unused)
 {
-  const gchar *module_name;
   GHashTableIter iter;
   gpointer key;
   gpointer value;
@@ -146,13 +90,6 @@ unload_plugin_cb (PeasEngine     *engine,
   g_assert (PEAS_IS_ENGINE (engine));
   g_assert (plugin_info != NULL);
 
-  /*
-   * FIXME: Since we don't have loader information, we could possibly have
-   *        two plugins from different loaders with the same module name.
-   */
-
-  module_name = peas_plugin_info_get_module_name (plugin_info);
-
   g_rec_mutex_lock (&gExtensionsMutex);
 
   g_hash_table_iter_init (&iter, gExtensions);
@@ -160,24 +97,8 @@ unload_plugin_cb (PeasEngine     *engine,
   while (g_hash_table_iter_next (&iter, &key, &value))
     {
       IdeExtensionPoint *point = value;
-      gboolean emit_changed = FALSE;
-      gsize i;
 
-      for (i = 0; i < point->extensions->len; i++)
-        {
-          IdeExtension *exten = g_ptr_array_index (point->extensions, i);
-
-          if (g_strcmp0 (exten->module_name, module_name) == 0)
-            {
-              if (exten->loaded != FALSE)
-                {
-                  exten->loaded = FALSE;
-                  emit_changed = TRUE;
-                }
-            }
-        }
-
-      if (emit_changed)
+      if (peas_engine_provides_extension (engine, plugin_info, point->interface_type))
         g_signal_emit (point, gSignals [CHANGED], 0);
     }
 
@@ -207,23 +128,17 @@ ensure_plugin_signals_locked (void)
 }
 
 static void
-ide_extension_point_finalize (GObject *object)
-{
-  G_OBJECT_CLASS (ide_extension_point_parent_class)->finalize (object);
-}
-
-static void
 ide_extension_point_get_property (GObject    *object,
                                   guint       prop_id,
                                   GValue     *value,
                                   GParamSpec *pspec)
 {
-  IdeExtensionPoint *self = IDE_EXTENSION_POINT (object);
+  IdeExtensionPoint *self = (IdeExtensionPoint *)object;
 
   switch (prop_id)
     {
-    case PROP_NAME:
-      g_value_set_static_string (value, self->name);
+    case PROP_REQUIRED_TYPE:
+      g_value_set_gtype (value, self->interface_type);
       break;
 
     default:
@@ -237,12 +152,12 @@ ide_extension_point_set_property (GObject      *object,
                                   const GValue *value,
                                   GParamSpec   *pspec)
 {
-  IdeExtensionPoint *self = IDE_EXTENSION_POINT (object);
+  IdeExtensionPoint *self = (IdeExtensionPoint *)object;
 
   switch (prop_id)
     {
-    case PROP_NAME:
-      self->name = g_intern_string (g_value_get_string (value));
+    case PROP_REQUIRED_TYPE:
+      self->interface_type = g_value_get_gtype (value);
       break;
 
     default:
@@ -255,15 +170,14 @@ ide_extension_point_class_init (IdeExtensionPointClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  object_class->finalize = ide_extension_point_finalize;
   object_class->get_property = ide_extension_point_get_property;
   object_class->set_property = ide_extension_point_set_property;
 
-  gParamSpecs [PROP_NAME] =
-    g_param_spec_string("name",
-                        _("Name"),
-                        _("Name"),
-                        NULL,
+  gParamSpecs [PROP_REQUIRED_TYPE] =
+    g_param_spec_gtype ("required-type",
+                        _("Required Type"),
+                        _("Required Type"),
+                        G_TYPE_INTERFACE,
                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
@@ -278,7 +192,39 @@ ide_extension_point_class_init (IdeExtensionPointClass *klass)
 static void
 ide_extension_point_init (IdeExtensionPoint *self)
 {
-  self->extensions = g_ptr_array_new ();
+}
+
+void
+ide_extension_point_register (GType        interface_type,
+                              const gchar *match_key,
+                              const gchar *priority_key)
+{
+  IdeExtensionPoint *point;
+
+  g_return_if_fail (G_TYPE_IS_INTERFACE (interface_type));
+
+  point = g_object_new (IDE_TYPE_EXTENSION_POINT,
+                        "required-type", interface_type,
+                        NULL);
+  point->match_key = g_intern_string (match_key);
+  point->priority_key = g_intern_string (priority_key);
+
+  g_rec_mutex_lock (&gExtensionsMutex);
+
+  if (gExtensions == NULL)
+    gExtensions = g_hash_table_new (NULL, NULL);
+
+  if (g_hash_table_contains (gExtensions, (gpointer)interface_type))
+    {
+      g_warning ("IdeExtensionPoint for interface \"%s\" is already registered.",
+                 g_type_name (interface_type));
+      goto unlock;
+    }
+
+  g_hash_table_insert (gExtensions, (gpointer)interface_type, point);
+
+unlock:
+  g_rec_mutex_unlock (&gExtensionsMutex);
 }
 
 /**
@@ -289,67 +235,54 @@ ide_extension_point_init (IdeExtensionPoint *self)
  * Returns: (transfer none): An #IdeExtensionPoint.
  */
 IdeExtensionPoint *
-ide_extension_point_lookup (const gchar *name)
+ide_extension_point_lookup (GType type)
 {
-  IdeExtensionPoint *self;
+  IdeExtensionPoint *point = NULL;
+
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (type), NULL);
 
   g_rec_mutex_lock (&gExtensionsMutex);
 
   ensure_plugin_signals_locked ();
 
-  name = g_intern_string (name);
-
-  if (gExtensions == NULL)
-    gExtensions = g_hash_table_new (NULL, NULL);
-
-  self = g_hash_table_lookup (gExtensions, name);
-
-  if (self == NULL)
-    {
-      self = g_object_new (IDE_TYPE_EXTENSION_POINT,
-                           "name", name,
-                           NULL);
-      g_hash_table_insert (gExtensions, (gchar *)name, self);
-    }
+  if (gExtensions != NULL)
+    point = g_hash_table_lookup (gExtensions, (gpointer)type);
 
   g_rec_mutex_unlock (&gExtensionsMutex);
 
-  return self;
+  return point;
 }
 
-static gint
-compare_extension (gconstpointer a,
-                   gconstpointer b)
+static gboolean
+matches_value (IdeExtensionPoint *point,
+               PeasPluginInfo    *plugin_info,
+               const gchar       *match_value)
 {
-  const IdeExtension *exta = a;
-  const IdeExtension *extb = b;
+  const gchar *line;
 
-  if (exta->priority == extb->priority)
-    return g_strcmp0 (g_type_name (exta->type), g_type_name (extb->type));
+  if ((point->match_key == NULL) || (match_value == NULL))
+    return TRUE;
 
-  return extb->priority - exta->priority;
-}
+  line = peas_plugin_info_get_external_data (plugin_info, point->match_key);
 
-void
-ide_extension_point_implement (const gchar *name,
-                               GType        implementation_type,
-                               gint         priority)
-{
-  IdeExtensionPoint *self;
-  IdeExtension *exten;
+  if (line != NULL)
+    {
+      g_auto(GStrv) parts = NULL;
+      gint i;
 
-  g_return_if_fail (name);
-  g_return_if_fail (*name != '\0');
-  g_return_if_fail (implementation_type != G_TYPE_INVALID);
-  g_return_if_fail (G_TYPE_IS_OBJECT (implementation_type));
+      if (strstr (line, ",") == NULL)
+        return (g_strcmp0 (match_value, line) == 0);
 
-  self = ide_extension_point_lookup (name);
-  exten = ide_extension_new (implementation_type, priority);
+      parts = g_strsplit (line, ",", 0);
 
-  g_ptr_array_add (self->extensions, exten);
-  g_ptr_array_sort (self->extensions, compare_extension);
+      for (i = 0; parts [i]; i++)
+        {
+          if (g_strcmp0 (parts [i], match_value))
+            return TRUE;
+        }
+    }
 
-  g_signal_emit (self, gSignals [CHANGED], 0);
+  return FALSE;
 }
 
 /**
@@ -360,30 +293,65 @@ ide_extension_point_implement (const gchar *name,
  * Returns: (transfer full) (nullable) (type GObject.Object): A new #GObject or %NULL.
  */
 gpointer
-ide_extension_point_create (const gchar *name,
+ide_extension_point_create (GType        type,
+                            const gchar *match_value,
                             const gchar *first_property,
                             ...)
 {
-  IdeExtensionPoint *self;
+  IdeExtensionPoint *point;
+  const GList *list;
+  PeasEngine *engine;
+  PeasPluginInfo *best_match = NULL;
+  gint best_match_priority = G_MAXINT32;
   gpointer ret = NULL;
   va_list args;
-  gint i;
 
-  self = ide_extension_point_lookup (name);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (type), NULL);
+
+  point = ide_extension_point_lookup (type);
+
+  if (point == NULL)
+    {
+      g_warning ("No IdeExtensionPoint has been registered for type \"%s\".",
+                 g_type_name (type));
+      return NULL;
+    }
+
+  engine = peas_engine_get_default ();
+  list = peas_engine_get_plugin_list (engine);
 
-  for (i = 0; (ret == NULL) && (i < self->extensions->len); i++)
+  for (; list; list = list->next)
     {
-      IdeExtension *exten;
+      PeasPluginInfo *plugin_info = list->data;
+      const gchar *priority_str;
+      gint priority = 0;
 
-      exten = g_ptr_array_index (self->extensions, i);
+      if (!peas_plugin_info_is_loaded (plugin_info))
+        continue;
+
+      if (!peas_engine_provides_extension (engine, plugin_info, type))
+        continue;
 
-      if (!exten->loaded)
+      if (!matches_value (point, plugin_info, match_value))
         continue;
 
+      priority_str = peas_plugin_info_get_external_data (plugin_info, point->priority_key);
+
+      if (priority_str != NULL)
+        priority = atoi (priority_str);
+
+      if (priority < best_match_priority)
+        best_match = plugin_info;
+    }
+
+  if (best_match)
+    {
       va_start (args, first_property);
-      ret = g_object_new_valist (exten->type, first_property, args);
+      ret = peas_engine_create_extension_valist (engine, best_match, type, first_property, args);
       va_end (args);
     }
 
+  g_print ("Created Type: %s\n", ret ? G_OBJECT_TYPE_NAME (ret) : "<None>");
+
   return ret;
 }
diff --git a/libide/ide-extension-point.h b/libide/ide-extension-point.h
index e917a68..b757459 100644
--- a/libide/ide-extension-point.h
+++ b/libide/ide-extension-point.h
@@ -27,13 +27,14 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeExtensionPoint, ide_extension_point, IDE, EXTENSION_POINT, GObject)
 
-IdeExtensionPoint *ide_extension_point_lookup    (const gchar *name);
-void               ide_extension_point_implement (const gchar *name,
-                                                  GType        implementation_type,
-                                                  gint         priority);
-gpointer           ide_extension_point_create    (const gchar *name,
-                                                  const gchar *first_property,
-                                                  ...);
+void               ide_extension_point_register (GType        interface_type,
+                                                 const gchar *match_key,
+                                                 const gchar *priority_key);
+IdeExtensionPoint *ide_extension_point_lookup   (GType        interface_type);
+gpointer           ide_extension_point_create   (GType        interface_type,
+                                                 const gchar *match_key,
+                                                 const gchar *first_property,
+                                                 ...);
 
 G_END_DECLS
 


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