Commit 37b9bf84 authored by hjk's avatar hjk

extensionsystem: "soft dependencies" infrastructure

Reviewed-by: con
Reviewed-by: ckamm
parent 2c26c6b1
......@@ -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 \
......
/**************************************************************************
**
** 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
/**************************************************************************
**
** 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
......@@ -39,14 +39,15 @@
#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;
}
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment