OSDN Git Service

extensionsystem: "soft dependencies" infrastructure
authorhjk <qtc-committer@nokia.com>
Wed, 5 Jan 2011 17:35:08 +0000 (18:35 +0100)
committerhjk <qtc-committer@nokia.com>
Thu, 6 Jan 2011 15:47:33 +0000 (16:47 +0100)
Reviewed-by: con
Reviewed-by: ckamm
src/libs/extensionsystem/extensionsystem.pro
src/libs/extensionsystem/invoker.cpp [new file with mode: 0644]
src/libs/extensionsystem/invoker.h [new file with mode: 0644]
src/libs/extensionsystem/pluginmanager.cpp
src/libs/extensionsystem/pluginmanager.h

index 027139c..de1f49f 100644 (file)
@@ -10,6 +10,7 @@ DEFINES += IDE_TEST_DIR=\\\"$$IDE_SOURCE_TREE\\\"
 
 HEADERS += pluginerrorview.h \
     plugindetailsview.h \
+    invoker.h \
     iplugin.h \
     iplugin_p.h \
     extensionsystem_global.h \
@@ -23,6 +24,7 @@ HEADERS += pluginerrorview.h \
     plugincollection.h
 SOURCES += pluginerrorview.cpp \
     plugindetailsview.cpp \
+    invoker.cpp \
     iplugin.cpp \
     pluginmanager.cpp \
     pluginspec.cpp \
diff --git a/src/libs/extensionsystem/invoker.cpp b/src/libs/extensionsystem/invoker.cpp
new file mode 100644 (file)
index 0000000..cbab529
--- /dev/null
@@ -0,0 +1,88 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "invoker.h"
+
+namespace ExtensionSystem {
+
+InvokerBase::InvokerBase()
+{
+    lastArg = 0;
+    useRet = false;
+    nag = true;
+    success = true;
+    target = 0;
+}
+
+InvokerBase::~InvokerBase()
+{
+    if (!success && nag)
+        qWarning("Could not invoke function '%s' in object of type '%s'.",
+            sig.constData(), target->metaObject()->className());
+}
+
+bool InvokerBase::wasSuccessful() const
+{
+    nag = false;
+    return success;
+}
+
+void InvokerBase::invoke(QObject *t, const char *slot)
+{
+    target = t;
+    success = false;
+    sig.append(slot, qstrlen(slot));
+    sig.append('(');
+    for (int paramCount = 0; paramCount < lastArg; ++paramCount) {
+        if (paramCount)
+            sig.append(',');
+        const char *type = arg[paramCount].name();
+        sig.append(type, strlen(type));
+    }
+    sig.append(')');
+    sig.append('\0');
+    int idx = target->metaObject()->indexOfMethod(sig.constData());
+    if (idx < 0)
+        return;
+    QMetaMethod method = target->metaObject()->method(idx);
+    if (useRet)
+        success = method.invoke(target, ret,
+           arg[0], arg[1], arg[2], arg[3], arg[4],
+           arg[5], arg[6], arg[7], arg[8], arg[9]);
+    else
+        success = method.invoke(target,
+           arg[0], arg[1], arg[2], arg[3], arg[4],
+           arg[5], arg[6], arg[7], arg[8], arg[9]);
+}
+
+} // namespace ExtensionSystem
diff --git a/src/libs/extensionsystem/invoker.h b/src/libs/extensionsystem/invoker.h
new file mode 100644 (file)
index 0000000..48815c9
--- /dev/null
@@ -0,0 +1,212 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** No Commercial Usage
+**
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#ifndef EXTENSIONSYSTEM_INVOKER_H
+#define EXTENSIONSYSTEM_INVOKER_H
+
+#include "extensionsystem_global.h"
+
+#include <QtCore/QMetaMethod>
+#include <QtCore/QMetaObject>
+#include <QtCore/QMetaType>
+#include <QtCore/QVarLengthArray>
+
+namespace ExtensionSystem {
+
+class InvokerBase
+{
+public:
+    InvokerBase();
+    ~InvokerBase();
+
+    bool wasSuccessful() const;
+
+    template <class T> void addArgument(const T &t)
+    {
+        arg[lastArg++] = QGenericArgument(typeName<T>(), &t);
+    }
+
+    template <class T> void setReturnValue(T &t)
+    {
+        useRet = true;
+        ret = QGenericReturnArgument(typeName<T>(), &t);
+    }
+
+    void invoke(QObject *target, const char *slot);
+
+private:
+    InvokerBase(const InvokerBase &); // Unimplemented.
+    template <class T> const char *typeName()
+    {
+        return QMetaType::typeName(qMetaTypeId<T>());
+    }
+    QObject *target;
+    QGenericArgument arg[10];
+    QGenericReturnArgument ret;
+    QVarLengthArray<char, 512> sig;
+    int lastArg;
+    bool success;
+    bool useRet;
+    mutable bool nag;
+};
+
+template <class Result>
+class Invoker : public InvokerBase
+{
+public:
+    Invoker(QObject *target, const char *slot)
+    {
+        InvokerBase::invoke(target, slot);
+    }
+
+    template <class T0>
+    Invoker(QObject *target, const char *slot, const T0 &t0)
+    {
+        setReturnValue(result);
+        addArgument(t0);
+        InvokerBase::invoke(target, slot);
+    }
+
+    template <class T0, class T1>
+    Invoker(QObject *target, const char *slot, const T0 &t0, const T1 &t1)
+    {
+        setReturnValue(result);
+        addArgument(t0);
+        addArgument(t1);
+        InvokerBase::invoke(target, slot);
+    }
+
+    template <class T0, class T1, class T2>
+    Invoker(QObject *target, const char *slot, const T0 &t0,
+        const T1 &t1, const T2 &t2)
+    {
+        setReturnValue(result);
+        addArgument(t0);
+        addArgument(t1);
+        addArgument(t2);
+        InvokerBase::invoke(target, slot);
+    }
+
+    operator Result() const { return result; }
+
+private:
+    Result result;
+};
+
+template<> class Invoker<void> : public InvokerBase
+{
+public:
+    Invoker(QObject *target, const char *slot)
+    {
+        InvokerBase::invoke(target, slot);
+    }
+
+    template <class T0>
+    Invoker(QObject *target, const char *slot, const T0 &t0)
+    {
+        addArgument(t0);
+        InvokerBase::invoke(target, slot);
+    }
+
+    template <class T0, class T1>
+    Invoker(QObject *target, const char *slot, const T0 &t0, const T1 &t1)
+    {
+        addArgument(t0);
+        addArgument(t1);
+        InvokerBase::invoke(target, slot);
+    }
+
+    template <class T0, class T1, class T2>
+    Invoker(QObject *target, const char *slot, const T0 &t0,
+        const T1 &t1, const T2 &t2)
+    {
+        addArgument(t0);
+        addArgument(t1);
+        addArgument(t2);
+        InvokerBase::invoke(target, slot);
+    }
+};
+
+template <class Result>
+Result invokeHelper(InvokerBase &in, QObject *target, const char *slot)
+{
+    Result result;
+    in.setReturnValue(result);
+    in.invoke(target, slot);
+    return result;
+}
+
+template <>
+inline void invokeHelper<void>(InvokerBase &in, QObject *target, const char *slot)
+{
+    in.invoke(target, slot);
+}
+
+template<class Result>
+Result invoke(QObject *target, const char *slot)
+{
+    InvokerBase in;
+    return invokeHelper<Result>(in, target, slot);
+}
+
+template<class Result, class T0>
+Result invoke(QObject *target, const char *slot, const T0 &t0)
+{
+    InvokerBase in;
+    in.addArgument(t0);
+    return invokeHelper<Result>(in, target, slot);
+}
+
+template<class Result, class T0, class T1>
+Result invoke(QObject *target, const char *slot, const T0 &t0, const T1 &t1)
+{
+    InvokerBase in;
+    in.addArgument(t0);
+    in.addArgument(t1);
+    return invokeHelper<Result>(in, target, slot);
+}
+
+template<class Result, class T0, class T1, class T2>
+Result invoke(QObject *target, const char *slot,
+    const T0 &t0, const T1 &t1, const T2 &t2)
+{
+    InvokerBase in;
+    in.addArgument(t0);
+    in.addArgument(t1);
+    in.addArgument(t2);
+    return invokeHelper<Result>(in, target, slot);
+}
+
+} // namespace ExtensionSystem
+
+#endif // EXTENSIONSYSTEM_INVOKER_H
index 7373e57..a8be0a3 100644 (file)
 #include "iplugin.h"
 #include "plugincollection.h"
 
-#include <QtCore/QMetaProperty>
+#include <QtCore/QDateTime>
 #include <QtCore/QDir>
+#include <QtCore/QMetaProperty>
+#include <QtCore/QSettings>
 #include <QtCore/QTextStream>
-#include <QtCore/QWriteLocker>
 #include <QtCore/QTime>
-#include <QtCore/QDateTime>
-#include <QtCore/QSettings>
-#include <QtDebug>
+#include <QtCore/QWriteLocker>
+#include <QtCore/QtDebug>
+
 #ifdef WITH_TESTS
 #include <QTest>
 #endif
@@ -60,9 +61,10 @@ enum { debugLeaks = 0 };
 
 /*!
     \namespace ExtensionSystem
-    \brief The ExtensionSystem namespace provides classes that belong to the core plugin system.
+    \brief The ExtensionSystem namespace provides classes that belong to the
+           core plugin system.
 
-    The basic extension system contains of the plugin manager and its supporting classes,
+    The basic extension system contains the plugin manager and its supporting classes,
     and the IPlugin interface that must be implemented by plugin providers.
 */
 
@@ -114,15 +116,83 @@ enum { debugLeaks = 0 };
     be implemented and added to the object pool. The plugin that provides the
     extension point looks for implementations of the class / interface in the object pool.
     \code
-        // plugin A provides a "MimeTypeHandler" extension point
+        // Plugin A provides a "MimeTypeHandler" extension point
         // in plugin B:
         MyMimeTypeHandler *handler = new MyMimeTypeHandler();
         ExtensionSystem::PluginManager::instance()->addObject(handler);
-        // in plugin A:
+        // In plugin A:
         QList<MimeTypeHandler *> mimeHandlers =
             ExtensionSystem::PluginManager::instance()->getObjects<MimeTypeHandler>();
     \endcode
 
+
+    The \c{ExtensionSystem::Invoker} class template provides "syntactic sugar"
+    for using "soft" extension points that may or may not be provided by an
+    object in the pool. This approach does neither require the "user" plugin being
+    linked against the "provider" plugin nor a common shared
+    header file. The exposed interface is implicitly given by the
+    invokable methods of the "provider" object in the object pool.
+
+    The \c{ExtensionSystem::invoke} function template encapsulates
+    {ExtensionSystem::Invoker} construction for the common case where 
+    the success of the call is not checked.
+
+    \code
+        // In the "provide" plugin A:
+        namespace PluginA {
+        class SomeProvider : public QObject
+        {
+            Q_OBJECT
+
+        public:
+            Q_INVOKABLE QString doit(const QString &msg, int n) {
+            {
+                qDebug() << "I AM DOING IT " << msg;
+                return QString::number(n);
+            }
+        };
+        } // namespace PluginA
+
+
+        // In the "user" plugin B:
+        int someFuntionUsingPluginA()
+        {
+            using namespace ExtensionSystem;
+
+            QObject *target = PluginManager::instance()
+                ->getObjectByClassName("PluginA::SomeProvider");
+
+            if (target) {
+                // Some random argument.
+                QString msg = "REALLY.";
+
+                // Plain function call, no return value.
+                invoke<void>(target, "doit", msg, 2);
+
+                // Plain function with no return value.
+                qDebug() << "Result: " << invoke<QString>(target, "doit", msg, 21);
+
+                // Record success of function call with return value.
+                Invoker<QString> in1(target, "doit", msg, 21);
+                qDebug() << "Success: (expected)" << in1.wasSuccessful();
+
+                // Try to invoke a non-existing function.
+                Invoker<QString> in2(target, "doitWrong", msg, 22);
+                qDebug() << "Success (not expected):" << in2.wasSuccessful();
+
+            } else {
+
+                // We have to cope with plugin A's absence.
+            }
+        };
+    \endcode
+
+    \bold Note: The type of the parameters passed to the \c{invoke()} calls
+    is deduced from the parameters themselves and must match the type of
+    the arguments of the called functions \e{exactly}. No conversion or even
+    integer promotions are applicable, so to invoke a function with a \c{long}
+    parameter explicitly use \c{long(43)} or such.
+
     \bold Note: The object pool manipulating functions are thread-safe.
 */
 
@@ -1095,3 +1165,38 @@ void PluginManagerPrivate::profilingReport(const char *what, const PluginSpec *s
         }
     }
 }
+
+/*!
+    \fn void PluginManager::getObjectByName()
+    Retrieves one object with a given name from the object pool.
+    \sa addObject()
+*/
+
+QObject *PluginManager::getObjectByName(const QString &name) const
+{
+    QReadLocker lock(&m_lock);
+    QList<QObject *> all = allObjects();
+    foreach (QObject *obj, all) {
+        if (obj->objectName() == name)
+            return obj;
+    }
+    return 0;
+}
+
+/*!
+    \fn void PluginManager::getObjectByClassName()
+    Retrieves one object inheriting a class with a given name from the object pool.
+    \sa addObject()
+*/
+
+QObject *PluginManager::getObjectByClassName(const QString &className) const
+{
+    const QByteArray ba = className.toUtf8();
+    QReadLocker lock(&m_lock);
+    QList<QObject *> all = allObjects();
+    foreach (QObject *obj, all) {
+        if (obj->inherits(ba.constData()))
+            return obj;
+    }
+    return 0;
+}
index ee70ecb..8d62ee8 100644 (file)
@@ -38,8 +38,8 @@
 #include <aggregation/aggregate.h>
 
 #include <QtCore/QObject>
-#include <QtCore/QStringList>
 #include <QtCore/QReadWriteLock>
+#include <QtCore/QStringList>
 
 QT_BEGIN_NAMESPACE
 class QTextStream;
@@ -95,6 +95,9 @@ public:
         return result;
     }
 
+    QObject *getObjectByName(const QString &name) const;
+    QObject *getObjectByClassName(const QString &className) const;
+
     // Plugin operations
     QList<PluginSpec *> loadQueue();
     void loadPlugins();