Cutelee 6.1.0
engine.cpp
1/*
2 This file is part of the Cutelee template system.
3
4 Copyright (c) 2009,2010 Stephen Kelly <steveire@gmail.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either version
9 2.1 of the Licence, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library. If not, see <http://www.gnu.org/licenses/>.
18
19*/
20
21#include "engine.h"
22#include "engine_p.h"
23
24#include "exception.h"
25#include "cutelee_config_p.h"
26#include "cutelee_version.h"
27#ifdef QT_QML_LIB
28#include "scriptabletags.h"
29#endif
30#include "template_p.h"
31#include "templateloader.h"
32
33#include <QtCore/QCoreApplication>
34#include <QtCore/QDir>
35#include <QtCore/QPluginLoader>
36#include <QtCore/QTextStream>
37
38using namespace Cutelee;
39
40static const char __scriptableLibName[] = "cutelee_scriptabletags";
41
42Engine::Engine(QObject *parent)
43 : QObject(parent), d_ptr(new EnginePrivate(this))
44{
45 d_ptr->m_defaultLibraries << QStringLiteral("cutelee_defaulttags")
46 << QStringLiteral("cutelee_loadertags")
47 << QStringLiteral("cutelee_defaultfilters");
48
49 d_ptr->m_pluginDirs = QCoreApplication::instance()->libraryPaths();
50 d_ptr->m_pluginDirs << QString::fromLocal8Bit(CUTELEE_PLUGIN_PATH);
51}
52
54{
55#ifdef QT_QML_LIB
56 qDeleteAll(d_ptr->m_scriptableLibraries);
57#endif
58 d_ptr->m_libraries.clear();
59 delete d_ptr;
60}
61
62QList<std::shared_ptr<AbstractTemplateLoader>> Engine::templateLoaders()
63{
64 Q_D(Engine);
65 return d->m_loaders;
66}
67
68void Engine::addTemplateLoader(std::shared_ptr<AbstractTemplateLoader> loader)
69{
70 Q_D(Engine);
71 d->m_loaders << loader;
72}
73
74std::pair<QString, QString> Engine::mediaUri(const QString &fileName) const
75{
76 Q_D(const Engine);
77
78 std::pair<QString, QString> uri;
79 for (const auto &loader : d->m_loaders) {
80 uri = loader->getMediaUri(fileName);
81 if (!uri.second.isEmpty())
82 break;
83 }
84 return uri;
85}
86
87void Engine::setPluginPaths(const QStringList &dirs)
88{
89 Q_D(Engine);
90 d->m_pluginDirs = dirs;
91}
92
94{
95 Q_D(Engine);
96 QStringList temp;
97 temp << dir;
98 temp << d->m_pluginDirs;
99 d->m_pluginDirs = temp;
100}
101
103{
104 Q_D(Engine);
105 d->m_pluginDirs.removeAll(dir);
106}
107
108QStringList Engine::pluginPaths() const
109{
110 Q_D(const Engine);
111 return d->m_pluginDirs;
112}
113
114QStringList Engine::defaultLibraries() const
115{
116 Q_D(const Engine);
117 return d->m_defaultLibraries;
118}
119
121{
122 Q_D(Engine);
123 d->m_defaultLibraries << libName;
124}
125
127{
128 Q_D(Engine);
129 d->m_defaultLibraries.removeAll(libName);
130}
131
132template <uint v> bool acceptableVersion(uint minorVersion)
133{
134 return minorVersion >= v;
135}
136
137template <> bool acceptableVersion<0>(uint) { return true; }
138
139void Engine::loadDefaultLibraries()
140{
141 Q_D(Engine);
142
143#ifdef QT_QML_LIB
144 // Make sure we can load default scriptable libraries if we're supposed to.
145 if (d->m_defaultLibraries.contains(QLatin1String(__scriptableLibName))
146 && !d->m_scriptableTagLibrary) {
147 d->m_scriptableTagLibrary = new ScriptableTagLibrary(this);
148
149// It would be better to load this as a plugin, but that is not currently
150// possible with webkit/javascriptcore
151// so we new the library directly.
152// https://bugs.webkit.org/show_bug.cgi?id=38193
153#if 0
154 d->loadCppLibrary( __scriptableLibName, CUTELEE_VERSION_MINOR );
155 PluginPointer<TagLibraryInterface> library = d->loadCppLibrary( __scriptableLibName, CUTELEE_VERSION_MINOR );
156 if ( !library )
157 throw Cutelee::Exception( TagSyntaxError, QStringLiteral("Could not load scriptable tags library") );
158#endif
159 }
160#endif
161
162 const QStringList defaultLibraries = d->m_defaultLibraries;
163 for (const QString &libName : defaultLibraries) {
164 if (libName == QLatin1String(__scriptableLibName))
165 continue;
166
167 // already loaded by the engine.
168 if (d->m_libraries.contains(libName))
169 continue;
170
171 uint minorVersion = CUTELEE_VERSION_MINOR;
172 while (acceptableVersion<CUTELEE_MIN_PLUGIN_VERSION>(minorVersion)) {
173#ifdef QT_QML_LIB
174 // Although we don't use scripted libaries here, we need to
175 // recognize them being first in the search path and not load a
176 // c++ plugin of the same name in that case.
177 auto scriptableLibrary = d->loadScriptableLibrary(libName, minorVersion);
178 if (scriptableLibrary) {
179 scriptableLibrary->clear();
180 break;
181 }
182#endif
183
184 auto library = d->loadCppLibrary(libName, minorVersion);
185 if (minorVersion == 0)
186 break;
187 minorVersion--;
188 if (library)
189 break;
190 }
191 }
192}
193
194TagLibraryInterface *Engine::loadLibrary(const QString &name)
195{
196 Q_D(Engine);
197
198#ifdef QT_QML_LIB
199 if (name == QLatin1String(__scriptableLibName))
200 return 0;
201#endif
202
203 // already loaded by the engine.
204 const auto it = d->m_libraries.constFind(name);
205 if (it != d->m_libraries.constEnd()) {
206 return it->data();
207 }
208
209 uint minorVersion = CUTELEE_VERSION_MINOR;
210 while (acceptableVersion<CUTELEE_MIN_PLUGIN_VERSION>(minorVersion)) {
211 auto library = d->loadLibrary(name, minorVersion);
212 if (library)
213 return library;
214 if (minorVersion == 0)
215 break;
216 minorVersion--;
217 }
218 throw Cutelee::Exception(
219 TagSyntaxError,
220 QStringLiteral("Plugin library '%1' not found.").arg(name));
221 return 0;
222}
223
225{
226 Q_D(Engine);
227 auto ptr = PluginPointer<TagLibraryInterface>(lib);
228 d->m_libraries.insert(name, ptr);
229}
230
232{
233 Q_D(Engine);
234 insertLibrary(name, lib);
235 d->m_defaultLibraries << name;
236}
237
238TagLibraryInterface *EnginePrivate::loadLibrary(const QString &name,
239 uint minorVersion)
240{
241#ifdef QT_QML_LIB
242 auto scriptableLibrary = loadScriptableLibrary(name, minorVersion);
243 if (scriptableLibrary)
244 return scriptableLibrary;
245
246// else this is not a scriptable library.
247#endif
248
249 return loadCppLibrary(name, minorVersion).data();
250}
251
252EnginePrivate::EnginePrivate(Engine *engine)
253 : q_ptr(engine)
254#ifdef QT_QML_LIB
255 ,
256 m_scriptableTagLibrary(0)
257#endif
258 ,
259 m_smartTrimEnabled(false)
260{
261}
262
263QString EnginePrivate::getScriptLibraryName(const QString &name,
264 uint minorVersion) const
265{
266 auto pluginIndex = 0;
267 const QString prefix
268 = QStringLiteral("/cutelee-qt") + QString::number(QT_VERSION_MAJOR) + QLatin1Char('/')
269 + QString::number(CUTELEE_VERSION_MAJOR) + QLatin1Char('.') + QString::number(minorVersion) + QLatin1Char('/');
270 while (m_pluginDirs.size() > pluginIndex) {
271 const auto nextDir = m_pluginDirs.at(pluginIndex++);
272 const QString libFileName = nextDir + prefix + name + QStringLiteral(".qs");
273
274 const QFile file(libFileName);
275 if (!file.exists())
276 continue;
277 return libFileName;
278 }
279
280 for (const auto &loader : m_loaders) {
281 const auto pair = loader->getMediaUri(prefix + name + QStringLiteral(".qs"));
282
283 if (!pair.first.isEmpty() && !pair.second.isEmpty()) {
284 return pair.first + pair.second;
285 }
286 }
287
288 return QString();
289}
290
291#ifdef QT_QML_LIB
292ScriptableLibraryContainer *
293EnginePrivate::loadScriptableLibrary(const QString &name, uint minorVersion)
294{
295 if (!m_scriptableTagLibrary)
296 return 0;
297
298 const auto libFileName = getScriptLibraryName(name, minorVersion);
299
300 if (libFileName.isEmpty())
301 return 0;
302
303 const auto it = m_scriptableLibraries.constFind(libFileName);
304 if (it != m_scriptableLibraries.constEnd()) {
305 auto library = it.value();
306 library->setNodeFactories(m_scriptableTagLibrary->nodeFactories(libFileName));
307 library->setFilters(m_scriptableTagLibrary->filters(libFileName));
308 return library;
309 }
310#if 0
311 PluginPointer<TagLibraryInterface> scriptableTagLibrary = m_libraries.value( __scriptableLibName );
312#endif
313
314 const auto factories = m_scriptableTagLibrary->nodeFactories(libFileName);
315 const auto filters = m_scriptableTagLibrary->filters(libFileName);
316
317 auto library = new ScriptableLibraryContainer(factories, filters);
318 m_scriptableLibraries.insert(libFileName, library);
319 return library;
320}
321#endif
322
323PluginPointer<TagLibraryInterface>
324EnginePrivate::loadCppLibrary(const QString &name, uint minorVersion)
325{
326 auto pluginIndex = 0;
327
328 while (m_pluginDirs.size() > pluginIndex) {
329 const auto nextDir = m_pluginDirs.at(pluginIndex++);
330 const QString pluginDirString
331 = nextDir + QStringLiteral("/cutelee-qt") + QString::number(QT_VERSION_MAJOR) + QLatin1Char('/')
332 + QString::number(CUTELEE_VERSION_MAJOR) + QLatin1Char('.')
333 + QString::number(minorVersion) + QLatin1Char('/');
334
335 const QDir pluginDir(pluginDirString);
336
337 if (!pluginDir.exists())
338 continue;
339
340 auto list = pluginDir.entryList(QStringList(name
341#if PLUGINS_PREFER_DEBUG_POSTFIX
342 + QLatin1Char('d')
343#endif
344 + QLatin1Char('*')));
345
346 if (list.isEmpty()) {
347#if PLUGINS_PREFER_DEBUG_POSTFIX
348 list = pluginDir.entryList(QStringList(name + QLatin1Char('*')));
349 if (list.isEmpty())
350 continue;
351#else
352 continue;
353#endif
354 }
355
356 auto pluginPath = pluginDir.absoluteFilePath(list.first());
357 auto plugin = PluginPointer<TagLibraryInterface>(pluginPath);
358
359 if (plugin) {
360#ifdef __COVERAGESCANNER__
361 __coveragescanner_register_library(pluginPath.toLatin1().data());
362#endif
363 m_libraries.insert(name, plugin);
364 return plugin;
365 }
366 }
367 return 0;
368}
369
371{
372 Q_D(const Engine);
373
374 for (auto &loader : d->m_loaders) {
375 if (!loader->canLoadTemplate(name))
376 continue;
377
378 const auto t = loader->loadByName(name, this);
379
380 if (t) {
381 return t;
382 }
383 }
384 auto t = Template(new TemplateImpl(this));
385 t->setObjectName(name);
386 t->d_ptr->m_error = TagSyntaxError;
387 t->d_ptr->m_errorString = QStringLiteral("Template not found, %1").arg(name);
388 return t;
389}
390
391Template Engine::newTemplate(const QString &content, const QString &name) const
392{
393 Q_D(const Engine);
394 auto t = Template(new TemplateImpl(this, d->m_smartTrimEnabled));
395 t->setObjectName(name);
396 t->setContent(content);
397 return t;
398}
399
401{
402 Q_D(Engine);
403 d->m_smartTrimEnabled = enabled;
404}
405
407{
408 Q_D(const Engine);
409 return d->m_smartTrimEnabled;
410}
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition engine.h:121
QList< std::shared_ptr< AbstractTemplateLoader > > templateLoaders()
Definition engine.cpp:62
QStringList pluginPaths() const
Definition engine.cpp:108
~Engine() override
Definition engine.cpp:53
std::pair< QString, QString > mediaUri(const QString &fileName) const
Definition engine.cpp:74
void insertDefaultLibrary(const QString &name, TagLibraryInterface *lib)
Definition engine.cpp:231
Engine(QObject *parent=nullptr)
Definition engine.cpp:42
Template loadByName(const QString &name) const
Definition engine.cpp:370
void setPluginPaths(const QStringList &dirs)
Definition engine.cpp:87
void addPluginPath(const QString &dir)
Definition engine.cpp:93
void setSmartTrimEnabled(bool enabled)
Definition engine.cpp:400
void removeDefaultLibrary(const QString &libName)
Definition engine.cpp:126
QStringList defaultLibraries() const
Definition engine.cpp:114
Template newTemplate(const QString &content, const QString &name) const
Definition engine.cpp:391
bool smartTrimEnabled() const
Definition engine.cpp:406
void removePluginPath(const QString &dir)
Definition engine.cpp:102
void addTemplateLoader(std::shared_ptr< AbstractTemplateLoader > loader)
Definition engine.cpp:68
void insertLibrary(const QString &name, TagLibraryInterface *lib)
Definition engine.cpp:224
void addDefaultLibrary(const QString &libName)
Definition engine.cpp:120
An exception for use when implementing template tags.
Definition exception.h:85
The TagLibraryInterface returns available tags and filters from libraries.
The Template class is a tree of nodes which may be rendered.
Definition template.h:95
The Cutelee namespace holds all public Cutelee API.
Definition Mainpage.dox:8