File indexing completed on 2024-04-14 03:58:03

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 #include <qregularexpression.h>
0018 
0019 #include "helper.h"
0020 #include "pluginbase.h"
0021 
0022 using namespace Purpose;
0023 
0024 class Purpose::ConfigurationPrivate
0025 {
0026 public:
0027     QJsonObject m_inputData;
0028     QString m_pluginTypeName;
0029     QJsonObject m_pluginType;
0030     const KPluginMetaData m_pluginData;
0031     bool m_useSeparateProcess;
0032 
0033     static void checkJobFinish(KJob *job)
0034     {
0035         const QStringList outputArgs = job->property("outputArgs").toStringList();
0036         const auto argsSet = QSet<QString>{outputArgs.cbegin(), outputArgs.cend()};
0037 
0038         const QStringList outputKeys = job->property("output").toJsonObject().keys();
0039         const auto keysSet = QSet<QString>{outputKeys.cbegin(), outputKeys.cend()};
0040 
0041         if (!keysSet.contains(argsSet) && job->error() == 0) {
0042             qWarning() << "missing output values for" << job->metaObject()->className() << ". Expected: " << outputArgs.join(QStringLiteral(", "))
0043                        << ". Got: " << outputKeys.join(QStringLiteral(", "));
0044         }
0045     }
0046 
0047     Purpose::Job *internalCreateJob(QObject *parent) const
0048     {
0049         if (m_useSeparateProcess)
0050             return new ProcessJob(m_pluginData.fileName(), m_pluginTypeName, m_inputData, parent);
0051         else {
0052             return createJob(parent);
0053         }
0054     }
0055 
0056     Purpose::Job *createJob(QObject *parent) const
0057     {
0058         if (m_pluginData.fileName().contains(QLatin1String("contents/code/main."))) {
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:" << m_pluginData.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(QLatin1String("X-Purpose-InboundArguments")).toArray();
0129     const QJsonArray arr = d->m_pluginData.rawData().value(QLatin1String("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(QLatin1String("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     if (d->m_pluginData.fileName().contains(QLatin1String("contents/code/main."))) {
0157         const QFileInfo fi(d->m_pluginData.fileName());
0158         QDir conentsDir = fi.dir();
0159         conentsDir.cdUp();
0160         return QUrl::fromLocalFile(conentsDir.filePath(QStringLiteral("config/config.qml")));
0161     } else {
0162         const QString configFile =
0163             QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf6/purpose/%1_config.qml").arg(d->m_pluginData.pluginId()));
0164         if (configFile.isEmpty())
0165             return QUrl();
0166 
0167         return QUrl::fromLocalFile(configFile);
0168     }
0169 }
0170 
0171 bool Configuration::useSeparateProcess() const
0172 {
0173     Q_D(const Configuration);
0174     return d->m_useSeparateProcess;
0175 }
0176 
0177 void Configuration::setUseSeparateProcess(bool use)
0178 {
0179     Q_D(Configuration);
0180     d->m_useSeparateProcess = use;
0181 }
0182 
0183 QString Configuration::pluginTypeName() const
0184 {
0185     Q_D(const Configuration);
0186     KPluginMetaData md(d->m_pluginType, {});
0187     return md.name();
0188 }
0189 
0190 QString Configuration::pluginName() const
0191 {
0192     Q_D(const Configuration);
0193     return d->m_pluginData.name();
0194 }
0195 
0196 #include "moc_configuration.cpp"