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 
38 using namespace Cutelee;
39 
40 static const char __scriptableLibName[] = "cutelee_scriptabletags";
41 
42 Engine::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 
62 QList<std::shared_ptr<AbstractTemplateLoader>> Engine::templateLoaders()
63 {
64  Q_D(Engine);
65  return d->m_loaders;
66 }
67 
68 void Engine::addTemplateLoader(std::shared_ptr<AbstractTemplateLoader> loader)
69 {
70  Q_D(Engine);
71  d->m_loaders << loader;
72 }
73 
74 std::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 
87 void 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 
108 QStringList Engine::pluginPaths() const
109 {
110  Q_D(const Engine);
111  return d->m_pluginDirs;
112 }
113 
114 QStringList Engine::defaultLibraries() const
115 {
116  Q_D(const Engine);
117  return d->m_defaultLibraries;
118 }
119 
120 void Engine::addDefaultLibrary(const QString &libName)
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 
132 template <uint v> bool acceptableVersion(uint minorVersion)
133 {
134  return minorVersion >= v;
135 }
136 
137 template <> bool acceptableVersion<0>(uint) { return true; }
138 
139 void 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 
194 TagLibraryInterface *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 
238 TagLibraryInterface *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 
252 EnginePrivate::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 
263 QString 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
292 ScriptableLibraryContainer *
293 EnginePrivate::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 
323 PluginPointer<TagLibraryInterface>
324 EnginePrivate::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 
391 Template 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 
400 void Engine::setSmartTrimEnabled(bool enabled)
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