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"