File indexing completed on 2025-02-09 04:28:37

0001 /*
0002   This file is part of the KTextTemplate library
0003 
0004   SPDX-FileCopyrightText: 2009, 2010 Stephen Kelly <steveire@gmail.com>
0005 
0006   SPDX-License-Identifier: LGPL-2.1-or-later
0007 
0008 */
0009 
0010 #include "engine.h"
0011 #include "engine_p.h"
0012 
0013 #include "exception.h"
0014 #include "ktexttemplate_config_p.h"
0015 #ifdef QT_QML_LIB
0016 #include "scriptabletags.h"
0017 #endif
0018 #include "template_p.h"
0019 
0020 #include <QCoreApplication>
0021 #include <QDir>
0022 #include <QPluginLoader>
0023 #include <QTextStream>
0024 
0025 using namespace KTextTemplate;
0026 
0027 static const char s_scriptableLibName[] = "ktexttemplate_scriptabletags";
0028 
0029 Engine::Engine(QObject *parent)
0030     : QObject(parent)
0031     , d_ptr(new EnginePrivate(this))
0032 {
0033     d_ptr->m_defaultLibraries << QStringLiteral("ktexttemplate_defaulttags") << QStringLiteral("ktexttemplate_loadertags")
0034                               << QStringLiteral("ktexttemplate_defaultfilters");
0035 
0036     d_ptr->m_pluginDirs = QCoreApplication::libraryPaths();
0037     d_ptr->m_pluginDirs << QString::fromLocal8Bit(KTEXTTEMPLATE_PLUGIN_PATH);
0038 }
0039 
0040 Engine::~Engine()
0041 {
0042 #ifdef QT_QML_LIB
0043     qDeleteAll(d_ptr->m_scriptableLibraries);
0044 #endif
0045     d_ptr->m_libraries.clear();
0046     delete d_ptr;
0047 }
0048 
0049 QList<QSharedPointer<AbstractTemplateLoader>> Engine::templateLoaders()
0050 {
0051     Q_D(Engine);
0052     return d->m_loaders;
0053 }
0054 
0055 void Engine::addTemplateLoader(QSharedPointer<AbstractTemplateLoader> loader)
0056 {
0057     Q_D(Engine);
0058     d->m_loaders << loader;
0059 }
0060 
0061 std::pair<QString, QString> Engine::mediaUri(const QString &fileName) const
0062 {
0063     Q_D(const Engine);
0064 
0065     std::pair<QString, QString> uri;
0066     for (auto &loader : d->m_loaders) {
0067         uri = loader->getMediaUri(fileName);
0068         if (!uri.second.isEmpty())
0069             break;
0070     }
0071     return uri;
0072 }
0073 
0074 void Engine::setPluginPaths(const QStringList &dirs)
0075 {
0076     Q_D(Engine);
0077     d->m_pluginDirs = dirs;
0078 }
0079 
0080 void Engine::addPluginPath(const QString &dir)
0081 {
0082     Q_D(Engine);
0083     QStringList temp;
0084     temp << dir;
0085     temp << d->m_pluginDirs;
0086     d->m_pluginDirs = temp;
0087 }
0088 
0089 void Engine::removePluginPath(const QString &dir)
0090 {
0091     Q_D(Engine);
0092     d->m_pluginDirs.removeAll(dir);
0093 }
0094 
0095 QStringList Engine::pluginPaths() const
0096 {
0097     Q_D(const Engine);
0098     return d->m_pluginDirs;
0099 }
0100 
0101 QStringList Engine::defaultLibraries() const
0102 {
0103     Q_D(const Engine);
0104     return d->m_defaultLibraries;
0105 }
0106 
0107 void Engine::addDefaultLibrary(const QString &libName)
0108 {
0109     Q_D(Engine);
0110     d->m_defaultLibraries << libName;
0111 }
0112 
0113 void Engine::removeDefaultLibrary(const QString &libName)
0114 {
0115     Q_D(Engine);
0116     d->m_defaultLibraries.removeAll(libName);
0117 }
0118 
0119 void Engine::loadDefaultLibraries()
0120 {
0121     Q_D(Engine);
0122 
0123 #ifdef QT_QML_LIB
0124     // Make sure we can load default scriptable libraries if we're supposed to.
0125     if (d->m_defaultLibraries.contains(QLatin1String(s_scriptableLibName)) && !d->m_scriptableTagLibrary) {
0126         d->m_scriptableTagLibrary = new ScriptableTagLibrary(this);
0127 
0128 // It would be better to load this as a plugin, but that is not currently
0129 // possible with webkit/javascriptcore
0130 // so we new the library directly.
0131 // https://bugs.webkit.org/show_bug.cgi?id=38193
0132 #if 0
0133     d->loadCppLibrary( s_scriptableLibName );
0134     PluginPointer<TagLibraryInterface> library = d->loadCppLibrary( s_scriptableLibName );
0135     if ( !library )
0136       throw KTextTemplate::Exception( TagSyntaxError, QStringLiteral("Could not load scriptable tags library") );
0137 #endif
0138     }
0139 #endif
0140 
0141     for (const QString &libName : std::as_const(d->m_defaultLibraries)) {
0142         if (libName == QLatin1String(s_scriptableLibName))
0143             continue;
0144 
0145         // already loaded by the engine.
0146         if (d->m_libraries.contains(libName))
0147             continue;
0148 
0149 #ifdef QT_QML_LIB
0150         // Although we don't use scripted libaries here, we need to
0151         // recognize them being first in the search path and not load a
0152         // c++ plugin of the same name in that case.
0153         auto scriptableLibrary = d->loadScriptableLibrary(libName);
0154         if (scriptableLibrary) {
0155             scriptableLibrary->clear();
0156             break;
0157         }
0158 #endif
0159 
0160         auto library = d->loadCppLibrary(libName);
0161         if (library) {
0162             break;
0163         }
0164     }
0165 }
0166 
0167 TagLibraryInterface *Engine::loadLibrary(const QString &name)
0168 {
0169     Q_D(Engine);
0170 
0171 #ifdef QT_QML_LIB
0172     if (name == QLatin1String(s_scriptableLibName))
0173         return nullptr;
0174 #endif
0175 
0176     // already loaded by the engine.
0177     if (d->m_libraries.contains(name))
0178         return d->m_libraries.value(name).data();
0179 
0180     auto library = d->loadLibrary(name);
0181     if (library) {
0182         return library;
0183     }
0184     throw KTextTemplate::Exception(TagSyntaxError, QStringLiteral("Plugin library '%1' not found.").arg(name));
0185     return nullptr;
0186 }
0187 
0188 TagLibraryInterface *EnginePrivate::loadLibrary(const QString &name)
0189 {
0190 #ifdef QT_QML_LIB
0191     auto scriptableLibrary = loadScriptableLibrary(name);
0192     if (scriptableLibrary)
0193         return scriptableLibrary;
0194 
0195 // else this is not a scriptable library.
0196 #endif
0197 
0198     return loadCppLibrary(name).data();
0199 }
0200 
0201 EnginePrivate::EnginePrivate(Engine *engine)
0202     : q_ptr(engine)
0203 #ifdef QT_QML_LIB
0204     , m_scriptableTagLibrary(nullptr)
0205 #endif
0206     , m_smartTrimEnabled(false)
0207 {
0208 }
0209 
0210 QString EnginePrivate::getScriptLibraryName(const QString &name) const
0211 {
0212     auto pluginIndex = 0;
0213     const QString prefix = QStringLiteral("/kf6/ktexttemplate/");
0214     while (m_pluginDirs.size() > pluginIndex) {
0215         const auto nextDir = m_pluginDirs.at(pluginIndex++);
0216         const QString libFileName = nextDir + prefix + name + QStringLiteral(".qs");
0217 
0218         const QFile file(libFileName);
0219         if (!file.exists())
0220             continue;
0221         return libFileName;
0222     }
0223     auto it = m_loaders.constBegin();
0224     const auto end = m_loaders.constEnd();
0225     for (; it != end; ++it) {
0226         const auto pair = (*it)->getMediaUri(prefix + name + QStringLiteral(".qs"));
0227 
0228         if (!pair.first.isEmpty() && !pair.second.isEmpty()) {
0229             return pair.first + pair.second;
0230         }
0231     }
0232     return {};
0233 }
0234 
0235 #ifdef QT_QML_LIB
0236 ScriptableLibraryContainer *EnginePrivate::loadScriptableLibrary(const QString &name)
0237 {
0238     if (!m_scriptableTagLibrary)
0239         return nullptr;
0240 
0241 #if 0
0242   if ( !m_libraries.contains( s_scriptableLibName ) )
0243     return 0;
0244 #endif
0245 
0246     const auto libFileName = getScriptLibraryName(name);
0247 
0248     if (libFileName.isEmpty())
0249         return nullptr;
0250 
0251     const auto it = m_scriptableLibraries.constFind(libFileName);
0252     if (it != m_scriptableLibraries.constEnd()) {
0253         auto library = it.value();
0254         library->setNodeFactories(m_scriptableTagLibrary->nodeFactories(libFileName));
0255         library->setFilters(m_scriptableTagLibrary->filters(libFileName));
0256         return library;
0257     }
0258 #if 0
0259   PluginPointer<TagLibraryInterface> scriptableTagLibrary = m_libraries.value( s_scriptableLibName );
0260 #endif
0261 
0262     const auto factories = m_scriptableTagLibrary->nodeFactories(libFileName);
0263     const auto filters = m_scriptableTagLibrary->filters(libFileName);
0264 
0265     auto library = new ScriptableLibraryContainer(factories, filters);
0266     m_scriptableLibraries.insert(libFileName, library);
0267     return library;
0268 }
0269 #endif
0270 
0271 PluginPointer<TagLibraryInterface> EnginePrivate::loadCppLibrary(const QString &name)
0272 {
0273     auto pluginIndex = 0;
0274 
0275     while (m_pluginDirs.size() > pluginIndex) {
0276         const auto nextDir = m_pluginDirs.at(pluginIndex++);
0277         const QString pluginDirString = nextDir + QStringLiteral("/kf6/ktexttemplate/");
0278 
0279         const QDir pluginDir(pluginDirString);
0280 
0281         if (!pluginDir.exists())
0282             continue;
0283 
0284         auto list = pluginDir.entryList(QStringList(name
0285 #if PLUGINS_PREFER_DEBUG_POSTFIX
0286                                                     + QLatin1Char('d')
0287 #endif
0288                                                     + QLatin1Char('*')));
0289 
0290         if (list.isEmpty()) {
0291 #if PLUGINS_PREFER_DEBUG_POSTFIX
0292             list = pluginDir.entryList(QStringList(name + QLatin1Char('*')));
0293             if (list.isEmpty())
0294                 continue;
0295 #else
0296             continue;
0297 #endif
0298         }
0299 
0300         auto pluginPath = pluginDir.absoluteFilePath(list.first());
0301         auto plugin = PluginPointer<TagLibraryInterface>(pluginPath);
0302 
0303         if (plugin) {
0304             m_libraries.insert(name, plugin);
0305             return plugin;
0306         }
0307     }
0308     return nullptr;
0309 }
0310 
0311 Template Engine::loadByName(const QString &name) const
0312 {
0313     Q_D(const Engine);
0314 
0315     for (auto &loader : d->m_loaders) {
0316         if (!loader->canLoadTemplate(name))
0317             continue;
0318 
0319         const auto t = loader->loadByName(name, this);
0320 
0321         if (t) {
0322             return t;
0323         }
0324     }
0325     auto t = Template(new TemplateImpl(this));
0326     t->setObjectName(name);
0327     t->d_ptr->m_error = TagSyntaxError;
0328     t->d_ptr->m_errorString = QStringLiteral("Template not found, %1").arg(name);
0329     return t;
0330 }
0331 
0332 Template Engine::newTemplate(const QString &content, const QString &name) const
0333 {
0334     Q_D(const Engine);
0335     auto t = Template(new TemplateImpl(this, d->m_smartTrimEnabled));
0336     t->setObjectName(name);
0337     t->setContent(content);
0338     return t;
0339 }
0340 
0341 void Engine::setSmartTrimEnabled(bool enabled)
0342 {
0343     Q_D(Engine);
0344     d->m_smartTrimEnabled = enabled;
0345 }
0346 
0347 bool Engine::smartTrimEnabled() const
0348 {
0349     Q_D(const Engine);
0350     return d->m_smartTrimEnabled;
0351 }
0352 
0353 #include "moc_engine.cpp"