pluginmanager.h 6.95 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/
#ifndef OSDEV_COMPONENTS_PLUGINMANAGER_H
#define OSDEV_COMPONENTS_PLUGINMANAGER_H

#include <QObject>
#include <QUuid>
#include <QHash>
#include <QList>
#include <QStringList>

#include "plugin.h"

#include "globallibexport.h"

#include <memory>

namespace osdev  {
namespace components {

/**
 * \brief This component is responsible for loading plugins.
 *
 * It also has support for supplying interfaces on the loaded pluins. The plugin
 * manager is a singleton component.
 *//*
 *   ________________________________________
 *  / One of the signs of Napoleon's         \
 *  | greatness is the fact that he once had |
 *  | a publisher shot.                      |
 *  |                                        |
 *  \ -- Siegfried Unseld                    /
 *   ----------------------------------------
 *      \
 *       \
 *          .--.
 *         |o_o |
 *         |:_/ |
 *        //   \ \
 *       (|     | )
 *      /'\_   _/`\
 *      \___)=(___/
 *
 */
class GLOBALLIBINTERFACE PluginManager : public QObject
{
    Q_OBJECT

public:
    //! Constructs the plugin manager
    static PluginManager& Instance();

    /// Deleted copy-constructor
    PluginManager( const PluginManager& ) = delete;
    /// Deleted assignment operator
    PluginManager& operator=(const PluginManager&) = delete;
    /// Deleted move-constructor
    PluginManager( PluginManager&& ) = delete;
    /// Deleted move operator
    PluginManager& operator=( PluginManager&& ) = delete;

    //! Adds a plugin not listed in the configuration.
    bool addLibrary( const QString& pluginName );

    //! Load the plugin by its name and location
    bool loadPlugin(const QString& pluginName);

    //! Loads a list of plugins. Names are resolved against the paths added with addLibraryPaths().
    bool loadPlugins(const QStringList& pluginNames);

    //! Load all plugins set in the staging table.
    bool loadPlugins();

    //! Unloads a plugin and removes it from the manager
    bool unloadPlugin( const QString& pluginName );

    //! Specify where the plugin manager will look for plugins
    void addLibraryPaths(const QStringList& paths);

    //! Specify a single plugin path
    void addLibraryPath(const QString& path);

    //! Retrieve a loaded plugin with its object ID.
    QObject* getPlugin(const QUuid &id) const;

    //! Retrieve all objects that have the specified interface.
    QList<QObject*> getPlugins(const QString& i_interface) const;

    //! Check if a specific plugin was already loaded...
    bool isLoaded( const QString& pluginName ) const;

    //! Retrieve the uuid of the given plugin by its filename.
    QUuid getPluginId( const QString& pluginName ) const;

    //! Retrieve the uuid of the given plugin by its Interface_id
    QUuid getPluginIdByInterfaceId( const QString &interface_id );

    //! Retrieve the name of the given plugin by its uuid
    QString getPluginNameByUuid( const QUuid &uuid );

    //! Retrieve the plugin_id by its systemname.
    QUuid getPluginUuidByName( const QString &name );

    /**
     * @brief Locate the interface with the specified name
     * @tparam T Type of interface needed
     * @param i_interface Name of the interface
     * @return Interface implementation, or nullptr if no valid interface could
     *         be found
     */
    template<class T>
    T* queryInterface(const QString& i_interface);

public slots:
    /**
     * @brief Slot called when a configuration file changed
     * @param fileName Name of the changed file
     * @todo Implement me!
     */
    void    slotConfigChanged( const QString& fileName );

private:

    //! Constructs the plugin manager (CTor)
    PluginManager();

    /**
     * @brief Get all the plugins and create their representing objects
     */
    void buildLibraryList();

    /**
     * @brief Find the library associated with a plugin-name
     * @param pluginName Plugin name to look for
     * @return Library-name, or an empty string if no library was found
     */
    QString getLibrary(const QString& pluginName) const;

    // ------------------------------------------------------

    //! Contains the only instance of a Pluginmanager
    static std::unique_ptr<PluginManager> s_instance;

    //! Hash table of all plugins. Loaded or not...
    QHash<QString, Plugin*> m_pluginHash;

    //! List of all possible libraries that may be loaded by the PM....
    QStringList m_libraryList;

    //! Member holding the classname.
    QString m_className;
};

// ------------------------------------------------------

template <class T>
T* PluginManager::queryInterface(const QString& i_interface)
{
    // Hash used to store queried interfaces
    static QHash<QString, T*> ifaceCache;

    T* pInterface = nullptr;

    if ( ifaceCache.contains(i_interface) )
    {
        pInterface = ifaceCache[i_interface];
    }
    else
    {
        QList<QObject*> pPluginList = PluginManager::Instance().getPlugins( i_interface );
        if ( !pPluginList.isEmpty() )
        {
            // Return the first plugin with the wanted interface.
            pInterface = qobject_cast<T*>(pPluginList[0]);
            /// @todo What if we specify the wrong T for this plugin?
            Q_ASSERT(pInterface);
            ifaceCache.insert(i_interface, pInterface);
        }
    }
    return pInterface;
}

} // End namespace components
} // End namespace osdev

#endif // OSDEV_COMPONENTS_PLUGINMANAGER_H