<commit_before>#include "version.hpp"
#include "dvpluginmanager.hpp"
#include "dvinputplugin.hpp"
#include "dvrenderplugin.hpp"
#include "dvenums.hpp"
#include <QQuickItem>
#include <QQmlContext>
#include <QMetaObject>
#include <QApplication>
#include <QOpenGLContext>
#include <QPluginLoader>
#include <QSettings>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <algorithm>

struct DVPluginInfo {
    QQmlContext* context = nullptr;

    QPluginLoader loader;

    DVPluginType::Type pluginType = DVPluginType::InvalidPlugin;

    DVRenderPlugin* renderPlugin = nullptr;
    DVInputPlugin* inputPlugin = nullptr;

    bool loaded = false;
    bool inited = false;

    QString errorString;
};

DVPluginManager::DVPluginManager(QObject* parent, QSettings& s) : QAbstractListModel(parent), settings(s) {
    /* Check to see if the table exists. */
    if (QSqlDatabase::database().record("plugins").isEmpty())
        resetPluginDatabase();
}

void DVPluginManager::postQmlInit() {
    if (settings.contains("PluginMode")) {
        /* Check to make sure the plugin mode is a valid loaded plugin before setting. */
        QString mode = settings.value("PluginMode").toString();

        if (pluginModes.contains(mode))
            emit pluginModeChanged(m_pluginMode = mode);
        else
            qWarning("Invalid plugin mode \"%s\" set in settings file!", qPrintable(mode));
    }
}

void DVPluginManager::loadPlugins(QQmlEngine* engine) {
    qmlEngine = engine;

    /* Start with the path the application is in. */
    pluginsDir.cd(qApp->applicationDirPath());

    /* Plugins start with "dv2_" on Windows and "libdv2_" on Linux. */
    pluginsDir.setNameFilters(QStringList({"dv2_*", "libdv2_*"}));
    pluginsDir.setFilter(QDir::Files);

#if defined(Q_OS_WIN)
    /* If we're in a "debug" or "release" folder go up a level, because that's where plugins are copied by the build system. */
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    /* I haven't actually tried this on Mac. This is just what the Qt plugin example said to do... */
    if (pluginsDir.dirName() == "MacOS") {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif

    /* Go into the "plugins" folder from there. */
    pluginsDir.cd("plugins");

#if defined(Q_OS_WIN)
    /* On Windows we can add the plugin dir to the DLL search path dynamically.
     * On Linux LD_LIBRARY_PATH must be set before running the program to get the same effect. */
    SetDllDirectory(pluginsDir.absolutePath().toStdWString().c_str());
#endif

    qDebug("Loading plugins from \"%s\"...", qPrintable(pluginsDir.absolutePath()));

    /* Tell the model system that we're going to be changing all the things. */
    beginResetModel();

    /* Try to load all files in the directory using the filters defined above. */
    for (const QString& filename : pluginsDir.entryList()) {
        /* If the file isn't a valid library for this platform, don't bother. */
        if (!QLibrary::isLibrary(filename)) continue;

        DVPluginInfo* plugin = new DVPluginInfo;
        plugin->loader.setFileName(pluginsDir.absoluteFilePath(filename));

        QString iid = plugin->loader.metaData().value("IID").toString();
        if (iid == DVRenderPlugin_iid) {
            plugin->pluginType = DVPluginType::RenderPlugin;
            qDebug("Found render plugin: \"%s\"", qPrintable(filename));
        } else if (iid == DVInputPlugin_iid) {
            plugin->pluginType = DVPluginType::InputPlugin;
            qDebug("Found input plugin: \"%s\"", qPrintable(filename));
        } else {
            qDebug("\"%s\" is not a valid plugin. Invalid IID \"%s\"!", qPrintable(filename), qPrintable(iid));
            delete plugin;
            continue;
        }

        plugins.insert(filename, plugin);

        QSqlRecord pluginRecord = getRecordForPlugin(filename);

        /* If there's a record for the plugin and it's set to enabled, load and init it. */
        if (!pluginRecord.isEmpty() && pluginRecord.value("enabled").toBool() && loadPlugin(filename)) {
            if (plugin->pluginType == DVPluginType::RenderPlugin)
                initRenderPluginQML(filename);
            else if (plugin->pluginType == DVPluginType::InputPlugin)
                initInputPlugin(filename);
        }
    }

    qDebug("Done loading plugins.");

    /* Tell the model system that we've finished changing all the things. */
    endResetModel();
}

void DVPluginManager::initRenderPlugins(QOpenGLContext* context) {
    openglContext = context;

    for (auto plugin : plugins) {
        if (plugin->pluginType != DVPluginType::RenderPlugin || renderPlugins.contains(plugin->renderPlugin) || !plugin->errorString.isEmpty())
            continue;

        initRenderPluginGL(plugins.key(plugin));
    }
}

bool DVPluginManager::loadPlugin(const QString& pluginName) {
    DVPluginInfo* plugin = plugins[pluginName];
    plugin->loader.load();
    QObject *obj = plugin->loader.instance();

    if (obj == nullptr) {
        plugin->errorString = "Unable to load \"" + pluginName + "\" as a plugin. " + plugin->loader.errorString();
        qWarning("%s", qPrintable(plugin->errorString));
        return false;
    }

    if (plugin->pluginType == DVPluginType::RenderPlugin)
        plugin->renderPlugin = qobject_cast<DVRenderPlugin*>(obj);
    else if (plugin->pluginType == DVPluginType::InputPlugin)
        plugin->inputPlugin = qobject_cast<DVInputPlugin*>(obj);

    return (plugin->loaded = plugin->renderPlugin != nullptr || plugin->inputPlugin != nullptr);
}
<commit_msg>Fix crash when a render plugin that isn't enabled is loaded.<commit_after>#include "version.hpp"
#include "dvpluginmanager.hpp"
#include "dvinputplugin.hpp"
#include "dvrenderplugin.hpp"
#include "dvenums.hpp"
#include <QQuickItem>
#include <QQmlContext>
#include <QMetaObject>
#include <QApplication>
#include <QOpenGLContext>
#include <QPluginLoader>
#include <QSettings>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <algorithm>

struct DVPluginInfo {
    QQmlContext* context = nullptr;

    QPluginLoader loader;

    DVPluginType::Type pluginType = DVPluginType::InvalidPlugin;

    DVRenderPlugin* renderPlugin = nullptr;
    DVInputPlugin* inputPlugin = nullptr;

    bool loaded = false;
    bool inited = false;

    QString errorString;
};

DVPluginManager::DVPluginManager(QObject* parent, QSettings& s) : QAbstractListModel(parent), settings(s) {
    /* Check to see if the table exists. */
    if (QSqlDatabase::database().record("plugins").isEmpty())
        resetPluginDatabase();
}

void DVPluginManager::postQmlInit() {
    if (settings.contains("PluginMode")) {
        /* Check to make sure the plugin mode is a valid loaded plugin before setting. */
        QString mode = settings.value("PluginMode").toString();

        if (pluginModes.contains(mode))
            emit pluginModeChanged(m_pluginMode = mode);
        else
            qWarning("Invalid plugin mode \"%s\" set in settings file!", qPrintable(mode));
    }
}

void DVPluginManager::loadPlugins(QQmlEngine* engine) {
    qmlEngine = engine;

    /* Start with the path the application is in. */
    pluginsDir.cd(qApp->applicationDirPath());

    /* Plugins start with "dv2_" on Windows and "libdv2_" on Linux. */
    pluginsDir.setNameFilters(QStringList({"dv2_*", "libdv2_*"}));
    pluginsDir.setFilter(QDir::Files);

#if defined(Q_OS_WIN)
    /* If we're in a "debug" or "release" folder go up a level, because that's where plugins are copied by the build system. */
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();
#elif defined(Q_OS_MAC)
    /* I haven't actually tried this on Mac. This is just what the Qt plugin example said to do... */
    if (pluginsDir.dirName() == "MacOS") {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif

    /* Go into the "plugins" folder from there. */
    pluginsDir.cd("plugins");

#if defined(Q_OS_WIN)
    /* On Windows we can add the plugin dir to the DLL search path dynamically.
     * On Linux LD_LIBRARY_PATH must be set before running the program to get the same effect. */
    SetDllDirectory(pluginsDir.absolutePath().toStdWString().c_str());
#endif

    qDebug("Loading plugins from \"%s\"...", qPrintable(pluginsDir.absolutePath()));

    /* Tell the model system that we're going to be changing all the things. */
    beginResetModel();

    /* Try to load all files in the directory using the filters defined above. */
    for (const QString& filename : pluginsDir.entryList()) {
        /* If the file isn't a valid library for this platform, don't bother. */
        if (!QLibrary::isLibrary(filename)) continue;

        DVPluginInfo* plugin = new DVPluginInfo;
        plugin->loader.setFileName(pluginsDir.absoluteFilePath(filename));

        QString iid = plugin->loader.metaData().value("IID").toString();
        if (iid == DVRenderPlugin_iid) {
            plugin->pluginType = DVPluginType::RenderPlugin;
            qDebug("Found render plugin: \"%s\"", qPrintable(filename));
        } else if (iid == DVInputPlugin_iid) {
            plugin->pluginType = DVPluginType::InputPlugin;
            qDebug("Found input plugin: \"%s\"", qPrintable(filename));
        } else {
            qDebug("\"%s\" is not a valid plugin. Invalid IID \"%s\"!", qPrintable(filename), qPrintable(iid));
            delete plugin;
            continue;
        }

        plugins.insert(filename, plugin);

        QSqlRecord pluginRecord = getRecordForPlugin(filename);

        /* If there's a record for the plugin and it's set to enabled, load and init it. */
        if (!pluginRecord.isEmpty() && pluginRecord.value("enabled").toBool() && loadPlugin(filename)) {
            if (plugin->pluginType == DVPluginType::RenderPlugin)
                initRenderPluginQML(filename);
            else if (plugin->pluginType == DVPluginType::InputPlugin)
                initInputPlugin(filename);
        }
    }

    qDebug("Done loading plugins.");

    /* Tell the model system that we've finished changing all the things. */
    endResetModel();
}

void DVPluginManager::initRenderPlugins(QOpenGLContext* context) {
    openglContext = context;

    for (auto plugin : plugins) {
        if (plugin->pluginType != DVPluginType::RenderPlugin || plugin->renderPlugin == nullptr ||
                renderPlugins.contains(plugin->renderPlugin) || !plugin->errorString.isEmpty())
            continue;

        initRenderPluginGL(plugins.key(plugin));
    }
}

bool DVPluginManager::loadPlugin(const QString& pluginName) {
    DVPluginInfo* plugin = plugins[pluginName];
    plugin->loader.load();
    QObject *obj = plugin->loader.instance();

    if (obj == nullptr) {
        plugin->errorString = "Unable to load \"" + pluginName + "\" as a plugin. " + plugin->loader.errorString();
        qWarning("%s", qPrintable(plugin->errorString));
        return false;
    }

    if (plugin->pluginType == DVPluginType::RenderPlugin)
        plugin->renderPlugin = qobject_cast<DVRenderPlugin*>(obj);
    else if (plugin->pluginType == DVPluginType::InputPlugin)
        plugin->inputPlugin = qobject_cast<DVInputPlugin*>(obj);

    return (plugin->loaded = plugin->renderPlugin != nullptr || plugin->inputPlugin != nullptr);
}

{'fraction_non_alphanumeric': 0.12858384013900956, 'fraction_numerical': 0.0006950477845351868, 'mean_word_length': 3.3258173618940248, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 74, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4719521', 'n_tokens_mistral': 3341, 'n_tokens_neox': 3214, 'n_words': 888}