File indexing completed on 2024-12-29 04:49:58

0001 /*
0002    SPDX-FileCopyrightText: 2017-2021 Volker Krause <vkrause@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "scriptextractor.h"
0008 #include "extractorscriptengine_p.h"
0009 #include "logging.h"
0010 
0011 #include <KItinerary/ExtractorDocumentNode>
0012 #include <KItinerary/ExtractorEngine>
0013 #include <KItinerary/ExtractorFilter>
0014 #include <KItinerary/ExtractorResult>
0015 
0016 #include <QFile>
0017 #include <QFileInfo>
0018 #include <QJsonArray>
0019 #include <QJsonObject>
0020 
0021 using namespace KItinerary;
0022 
0023 namespace KItinerary {
0024 class ScriptExtractorPrivate
0025 {
0026 public:
0027     QString m_mimeType;
0028     QString m_fileName;
0029     QString m_scriptName;
0030     QString m_scriptFunction;
0031     std::vector<ExtractorFilter> m_filters;
0032     int m_index = -1;
0033 };
0034 }
0035 
0036 ScriptExtractor::ScriptExtractor()
0037     : d(std::make_unique<ScriptExtractorPrivate>())
0038 {
0039 }
0040 
0041 ScriptExtractor::~ScriptExtractor() = default;
0042 
0043 bool ScriptExtractor::load(const QJsonObject &obj, const QString &fileName, int index)
0044 {
0045     d->m_fileName = fileName;
0046     d->m_index = index;
0047 
0048     d->m_mimeType = obj.value(QLatin1StringView("mimeType")).toString();
0049 
0050     const auto filterArray = obj.value(QLatin1StringView("filter")).toArray();
0051     for (const auto &filterValue : filterArray) {
0052         ExtractorFilter f;
0053         if (!f.load(filterValue.toObject())) {
0054             qCDebug(Log) << "invalid filter expression:" << fileName;
0055             return false;
0056         }
0057         d->m_filters.push_back(std::move(f));
0058     }
0059 
0060     const auto scriptName = obj.value(QLatin1StringView("script")).toString();
0061     if (!scriptName.isEmpty()) {
0062         QFileInfo fi(fileName);
0063         d->m_scriptName = fi.path() + QLatin1Char('/') + scriptName;
0064     }
0065 
0066     if (!d->m_scriptName.isEmpty() && !QFile::exists(d->m_scriptName)) {
0067         qCWarning(Log) << "Script file not found:" << d->m_scriptName;
0068         return false;
0069     }
0070     d->m_scriptFunction = obj.value(QLatin1StringView("function"))
0071                               .toString(QStringLiteral("main"));
0072 
0073     return !d->m_filters.empty() && !d->m_mimeType.isEmpty();
0074 }
0075 
0076 QJsonObject ScriptExtractor::toJson() const
0077 {
0078     QJsonObject obj;
0079     obj.insert(QStringLiteral("mimeType"), d->m_mimeType);
0080 
0081     QFileInfo metaFi(d->m_fileName);
0082     QFileInfo scriptFi(d->m_scriptName);
0083     if (metaFi.canonicalPath() == scriptFi.canonicalPath()) {
0084         obj.insert(QStringLiteral("script"), scriptFi.fileName());
0085     } else {
0086         obj.insert(QStringLiteral("script"), d->m_scriptName);
0087     }
0088     obj.insert(QStringLiteral("function"), d->m_scriptFunction);
0089 
0090     QJsonArray filters;
0091     std::transform(d->m_filters.begin(), d->m_filters.end(), std::back_inserter(filters), std::mem_fn(&ExtractorFilter::toJson));
0092     obj.insert(QStringLiteral("filter"), filters);
0093 
0094     return obj;
0095 }
0096 
0097 QString ScriptExtractor::name() const
0098 {
0099     QFileInfo fi(d->m_fileName);
0100     if (d->m_index < 0) {
0101         return fi.baseName();
0102     }
0103     return fi.baseName() + QLatin1Char(':') + QString::number(d->m_index);
0104 }
0105 
0106 QString ScriptExtractor::mimeType() const
0107 {
0108     return d->m_mimeType;
0109 }
0110 
0111 void ScriptExtractor::setMimeType(const QString &mimeType)
0112 {
0113     d->m_mimeType = mimeType;
0114 }
0115 
0116 QString ScriptExtractor::scriptFileName() const
0117 {
0118     return d->m_scriptName;
0119 }
0120 
0121 void ScriptExtractor::setScriptFileName(const QString &script)
0122 {
0123     d->m_scriptName = script;
0124 }
0125 
0126 QString ScriptExtractor::scriptFunction() const
0127 {
0128     return d->m_scriptFunction;
0129 }
0130 
0131 void ScriptExtractor::setScriptFunction(const QString &func)
0132 {
0133     d->m_scriptFunction = func;
0134 }
0135 
0136 QString ScriptExtractor::fileName() const
0137 {
0138     return d->m_fileName;
0139 }
0140 
0141 const std::vector<ExtractorFilter>& ScriptExtractor::filters() const
0142 {
0143     return d->m_filters;
0144 }
0145 
0146 void ScriptExtractor::setFilters(std::vector<ExtractorFilter> &&filters)
0147 {
0148     d->m_filters = std::move(filters);
0149 }
0150 
0151 void ScriptExtractor::setFilters(const std::vector<ExtractorFilter> &filters)
0152 {
0153     d->m_filters = filters;
0154 }
0155 
0156 bool ScriptExtractor::canHandle(const ExtractorDocumentNode &node) const
0157 {
0158     if (node.mimeType() != d->m_mimeType) {
0159         return false;
0160     }
0161 
0162     // no filters matches always
0163     if (d->m_filters.empty()) {
0164         return true;
0165     }
0166 
0167     return std::any_of(d->m_filters.begin(), d->m_filters.end(), [&node](const auto &filter) {
0168         return filter.matches(node);
0169     });
0170 }
0171 
0172 ExtractorResult ScriptExtractor::extract(const ExtractorDocumentNode &node, const ExtractorEngine *engine) const
0173 {
0174     std::vector<ExtractorDocumentNode> triggerNodes;
0175     for (const auto &filter : d->m_filters) {
0176         if (filter.scope() != ExtractorFilter::Current) {
0177             filter.allMatches(node, triggerNodes);
0178         }
0179     }
0180 
0181     if (triggerNodes.empty()) {
0182         return engine->scriptEngine()->execute(this, node, node);
0183     } else {
0184         ExtractorResult result;
0185         for (const auto &triggerNode : triggerNodes) {
0186             result.append(engine->scriptEngine()->execute(this, node, triggerNode));
0187         }
0188         return result;
0189     }
0190 }