8#include <QJsonParseError>
9#include <QRegularExpression>
11#if defined USE_CUTELEE
12#include <cutelee/cachingloaderdecorator.h>
13#include <cutelee/templateloader.h>
14#elif defined USE_GRANTLEE
15#include <grantlee/cachingloaderdecorator.h>
16#include <grantlee/templateloader.h>
17#elif defined USE_KTEXTTEMPLATE
18#include <KTextTemplate/CachingLoaderDecorator>
19#include <KTextTemplate/TemplateLoader>
22#define QSL(str) QStringLiteral(str)
26 engine.setSmartTrimEnabled(
true);
29bool Renderer::loadTemplates(
const QString &dir)
31 qCInfo(lc).noquote() << tr(
"Loading Textlee templates from %1").arg(dir);
34 #if defined USE_CUTELEE
35 auto loader = std::make_shared<Textlee::FileSystemTemplateLoader>();
36 auto cachedLoader = std::make_shared<Textlee::CachingLoaderDecorator>(loader);
37 #elif defined USE_GRANTLEE or defined USE_KTEXTTEMPLATE
38 auto loader = QSharedPointer<Textlee::FileSystemTemplateLoader>::create();
39 auto cachedLoader = QSharedPointer<Textlee::CachingLoaderDecorator>::create(loader);
42 loader->setTemplateDirs(QStringList{dir});
43 engine.addTemplateLoader(cachedLoader);
46 QDirIterator iter{QDir::cleanPath(dir), QDir::Files, QDirIterator::Subdirectories};
47 while (iter.hasNext()) {
48 const QString name = iter.next().mid(iter.path().size()+1);
49 qCDebug(lc).noquote() << tr(
"Loading template: %1").arg(name);
50 const Textlee::Template tmplate = engine.loadByName(name);
51 if (tmplate->error()) {
52 qCritical().noquote() << tr(
"Error loading template %1: %2")
53 .arg(name, tmplate->errorString());
56 templates.append(name);
58 qDebug(lc).noquote() << tr(
"Loaded %n template/s from %1",
nullptr, templates.size()).arg(dir);
62QStringList Renderer::templatesNames()
const
68class NoEscapeStream :
public Textlee::OutputStream {
70 explicit NoEscapeStream(QTextStream * stream) : Textlee::OutputStream(stream) { }
73 virtual QString escape(
const QString &input)
const {
return input; }
76 #if defined USE_CUTELEE
77 virtual std::shared_ptr<OutputStream> clone(QTextStream *stream)
const {
78 return std::shared_ptr<OutputStream>(
new NoEscapeStream(stream));
80 #elif defined USE_GRANTLEE
81 virtual QSharedPointer<OutputStream> clone(QTextStream *stream)
const {
82 return QSharedPointer<OutputStream>(
new NoEscapeStream(stream));
87void Renderer::push(
const QVariantHash &context)
90 for (
auto iter = context.constBegin(); iter != context.constEnd(); ++iter) {
91 this->context.insert(sanitise(iter.key()), sanitise(iter.value()));
100bool Renderer::render(
const QString &templateName,
const QString &outputPathName,
101 const QVariantHash &additionalContext)
103 qCDebug(lc).noquote() << tr(
"Rendering %1 to %2").arg(templateName, outputPathName);
104 if (!templates.contains(templateName)) {
105 qCCritical(lc).noquote() << tr(
"Template %1 has not been loaded").arg(templateName);
109 const QDir dir = QFileInfo(outputPathName).dir();
110 if (!dir.mkpath(QSL(
"./"))) {
111 qCCritical(lc).noquote() << tr(
"Failed to create directory path %1").arg(dir.path());
115 QFile file(outputPathName);
116 if (!file.open(QFile::WriteOnly)) {
117 qCCritical(lc).noquote() << tr(
"Failed to open %1 for writing: %2")
118 .arg(outputPathName, file.errorString());
122 push(additionalContext);
123 QTextStream textStream(&file);
124 NoEscapeStream noEscapeStream(&textStream);
125 Textlee::Template tmplate = engine.loadByName(templateName);
127 qCCritical(lc).noquote() << tr(
"Failed to fetch template %1").arg(outputPathName);
131 tmplate->render(&noEscapeStream, &context);
132 if (tmplate->error()) {
133 qCCritical(lc).noquote() << tr(
"Failed to render %1: %2").arg(outputPathName, tmplate->errorString());
141QString Renderer::sanitise(
const QString &key)
143 QString newKey = key;
145 while ((pos = newKey.indexOf(QLatin1Char(
'.'))) >= 0) {
146 newKey = newKey.mid(0, pos) + newKey.mid(pos+1,1).toUpper() + newKey.mid(pos+2);
148 newKey.replace(QRegularExpression{QSL(
"[^a-zA-Z0-9_]")}, QSL(
"_"));
149 newKey.replace(QRegularExpression{QSL(
"^[0-9_]")}, QLatin1String());
153QVariant Renderer::sanitise(
const QVariant &variant)
155 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
156 const int typeId = variant.typeId();
158 const int typeId = variant.type();
160 if (typeId == QMetaType::QVariantHash) {
161 return sanitise(variant.toHash());
162 }
else if (typeId == QMetaType::QVariantMap) {
163 return sanitise(variant.toMap());
168QVariantHash Renderer::sanitise(
const QVariantHash &hash)
170 QVariantHash newHash;
171 for (
auto iter = hash.begin(); iter != hash.end(); ++iter) {
172 const QString saneKey = sanitise(iter.key());
173 QVariant saneValue = iter.value();
174 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
175 const int typeId = iter->typeId();
177 const int typeId = iter->type();
179 if (typeId == QMetaType::QVariantHash) {
180 saneValue = sanitise(iter->toHash());
181 }
else if (typeId == QMetaType::QVariantMap) {
182 saneValue = sanitise(iter->toMap());
184 newHash.insert(saneKey, saneValue);
186 Q_ASSERT(hash.size() == newHash.size());
190QVariantMap Renderer::sanitise(
const QVariantMap &map)
193 for (
auto iter = map.begin(); iter != map.end(); ++iter) {
194 const QString saneKey = sanitise(iter.key());
195 QVariant saneValue = iter.value();
196 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
197 const int typeId = iter->typeId();
199 const int typeId = iter->type();
201 if (typeId == QMetaType::QVariantHash) {
202 saneValue = sanitise(iter->toHash());
203 }
else if (typeId == QMetaType::QVariantMap) {
204 saneValue = sanitise(iter->toMap());
206 newMap.insert(saneKey, saneValue);
208 Q_ASSERT(map.size() == newMap.size());