File indexing completed on 2024-04-28 11:48:37

0001 /*
0002     SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "purpose/configuration.h"
0008 #include "externalprocess/processjob.h"
0009 #include <QFileInfo>
0010 
0011 #include <KPluginFactory>
0012 #include <KPluginMetaData>
0013 
0014 #include <QDebug>
0015 #include <QDir>
0016 #include <QStandardPaths>
0017 
0018 #include "helper.h"
0019 #include "pluginbase.h"
0020 
0021 using namespace Purpose;
0022 
0023 class Purpose::ConfigurationPrivate
0024 {
0025 public:
0026     QJsonObject m_inputData;
0027     QString m_pluginTypeName;
0028     QJsonObject m_pluginType;
0029     const KPluginMetaData m_pluginData;
0030     bool m_useSeparateProcess;
0031 
0032     static void checkJobFinish(KJob *job)
0033     {
0034         const QStringList outputArgs = job->property("outputArgs").toStringList();
0035         const auto argsSet = QSet<QString>{outputArgs.cbegin(), outputArgs.cend()};
0036 
0037         const QStringList outputKeys = job->property("output").toJsonObject().keys();
0038         const auto keysSet = QSet<QString>{outputKeys.cbegin(), outputKeys.cend()};
0039 
0040         if (!keysSet.contains(argsSet) && job->error() == 0) {
0041             qWarning() << "missing output values for" << job->metaObject()->className() << ". Expected: " << outputArgs.join(QStringLiteral(", "))
0042                        << ". Got: " << outputKeys.join(QStringLiteral(", "));
0043         }
0044     }
0045 
0046     Purpose::Job *internalCreateJob(QObject *parent) const
0047     {
0048         if (m_useSeparateProcess)
0049             return new ProcessJob(m_pluginData.fileName(), m_pluginTypeName, m_inputData, parent);
0050         else {
0051             return createJob(parent);
0052         }
0053     }
0054 
0055     Purpose::Job *createJob(QObject *parent) const
0056     {
0057         const QString fileName = m_pluginData.metaDataFileName();
0058         if (fileName.endsWith(QLatin1String("/metadata.json"))) {
0059             return new ProcessJob(m_pluginData.fileName(), m_pluginTypeName, m_inputData, parent);
0060         } else {
0061             auto pluginResult = KPluginFactory::instantiatePlugin<QObject>(m_pluginData, parent, QVariantList());
0062 
0063             if (!pluginResult) {
0064                 qWarning() << "Couldn't load plugin:" << fileName << pluginResult.errorString;
0065                 return nullptr;
0066             }
0067 
0068             Purpose::PluginBase *plugin = dynamic_cast<Purpose::PluginBase *>(pluginResult.plugin);
0069             return plugin->createJob();
0070         }
0071     }
0072 };
0073 
0074 Configuration::Configuration(const QJsonObject &inputData, const QString &pluginTypeName, const KPluginMetaData &pluginInformation, QObject *parent)
0075     : Configuration(inputData, pluginTypeName, QJsonObject(), pluginInformation, parent)
0076 {
0077 }
0078 
0079 Configuration::Configuration(const QJsonObject &inputData,
0080                              const QString &pluginTypeName,
0081                              const QJsonObject &pluginType,
0082                              const KPluginMetaData &pluginInformation,
0083                              QObject *parent)
0084     : QObject(parent)
0085     , d_ptr(new ConfigurationPrivate{inputData, pluginTypeName, pluginType, pluginInformation, !qEnvironmentVariableIsSet("KDE_PURPOSE_LOCAL_JOBS")})
0086 {
0087 }
0088 
0089 Configuration::~Configuration()
0090 {
0091     delete d_ptr;
0092 }
0093 
0094 void Configuration::setData(const QJsonObject &data)
0095 {
0096     Q_D(Configuration);
0097 
0098     //     qDebug() << "datachanged" << data;
0099     if (d->m_inputData != data) {
0100         d->m_inputData = data;
0101         Q_EMIT dataChanged();
0102     }
0103 }
0104 
0105 QJsonObject Configuration::data() const
0106 {
0107     Q_D(const Configuration);
0108     return d->m_inputData;
0109 }
0110 
0111 bool Configuration::isReady() const
0112 {
0113     Q_D(const Configuration);
0114     bool ok = true;
0115     const auto arguments = neededArguments();
0116     for (const QJsonValue &arg : arguments) {
0117         if (!d->m_inputData.contains(arg.toString())) {
0118             qDebug() << "missing mandatory argument" << arg.toString();
0119             ok = false;
0120         }
0121     }
0122     return ok;
0123 }
0124 
0125 QJsonArray Configuration::neededArguments() const
0126 {
0127     Q_D(const Configuration);
0128     QJsonArray ret = d->m_pluginType.value(QStringLiteral("X-Purpose-InboundArguments")).toArray();
0129     const QJsonArray arr = d->m_pluginData.rawData().value(QStringLiteral("X-Purpose-Configuration")).toArray();
0130     for (const QJsonValue &val : arr)
0131         ret += val;
0132     return ret;
0133 }
0134 
0135 Purpose::Job *Configuration::createJob()
0136 {
0137     if (!isReady())
0138         return nullptr;
0139 
0140     Q_D(const Configuration);
0141 
0142     Purpose::Job *job = d->internalCreateJob(this);
0143     if (!job)
0144         return job;
0145 
0146     job->setData(d->m_inputData);
0147     job->setProperty("outputArgs", d->m_pluginType.value(QStringLiteral("X-Purpose-OutboundArguments")));
0148 
0149     connect(job, &Purpose::Job::finished, &ConfigurationPrivate::checkJobFinish);
0150     return job;
0151 }
0152 
0153 QUrl Configuration::configSourceCode() const
0154 {
0155     Q_D(const Configuration);
0156     const QString metaDataPath = d->m_pluginData.metaDataFileName();
0157     if (metaDataPath.endsWith(QLatin1String("/metadata.json"))) {
0158         const QFileInfo fi(metaDataPath);
0159         return QUrl::fromLocalFile(fi.dir().filePath(QStringLiteral("contents/config/config.qml")));
0160     } else {
0161         const QString configFile =
0162             QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("purpose/%1_config.qml").arg(d->m_pluginData.pluginId()));
0163         if (configFile.isEmpty())
0164             return QUrl();
0165 
0166         return QUrl::fromLocalFile(configFile);
0167     }
0168 }
0169 
0170 bool Configuration::useSeparateProcess() const
0171 {
0172     Q_D(const Configuration);
0173     return d->m_useSeparateProcess;
0174 }
0175 
0176 void Configuration::setUseSeparateProcess(bool use)
0177 {
0178     Q_D(Configuration);
0179     d->m_useSeparateProcess = use;
0180 }
0181 
0182 QString Configuration::pluginTypeName() const
0183 {
0184     Q_D(const Configuration);
0185     KPluginMetaData md(d->m_pluginType, {});
0186     return md.name();
0187 }
0188 
0189 QString Configuration::pluginName() const
0190 {
0191     Q_D(const Configuration);
0192     return d->m_pluginData.name();
0193 }
0194 
0195 #include "moc_configuration.cpp"