File indexing completed on 2024-04-28 15:33:58
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"