Cutelee 6.1.0
scriptabletags.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 "scriptabletags.h"
22
23#include <QtCore/QFile>
24#include <QtPlugin>
25
26#include <QtQml/QJSEngine>
27#include <QtQml/QJSValueIterator>
28
29#include "nodebuiltins_p.h"
30
31#include "engine.h"
32#include "exception.h"
33#include "parser.h"
34#include "scriptablefilter.h"
35#include "scriptablefilterexpression.h"
36#include "scriptablenode.h"
37#include "scriptablesafestring.h"
38#include "scriptabletemplate.h"
39#include "scriptablevariable.h"
40
41#include "token.h"
42#include "util.h"
43
44Q_DECLARE_METATYPE(Token)
45
46using namespace Cutelee;
47
48QJSValue ScriptableHelperFunctions::markSafeFunction(QJSValue inputValue)
49{
50 if (inputValue.isQObject()) {
51 auto obj = inputValue.toQObject();
52 auto ssObj = qobject_cast<ScriptableSafeString *>(obj);
53 if (!ssObj)
54 return QJSValue::NullValue;
55
56 ssObj->setSafety(true);
57 return m_scriptEngine->newQObject(ssObj);
58
59 } else if (inputValue.isString()) {
60 auto str = inputValue.toString();
61 auto ssObj = new ScriptableSafeString(m_scriptEngine);
62 ssObj->setContent(markSafe(str));
63 return m_scriptEngine->newQObject(ssObj);
64 }
65 return QJSValue::NullValue;
66}
67
68QJSValue ScriptableHelperFunctions::ScriptableFilterExpressionConstructor(
69 QString name, QObject *parserObj)
70{
71 auto object = new ScriptableFilterExpression(m_scriptEngine);
72
73 auto p = qobject_cast<Parser *>(parserObj);
74
75 object->init(name, p);
76
77 return m_scriptEngine->newQObject(object);
78}
79
80QJSValue
81ScriptableHelperFunctions::ScriptableNodeConstructor(QJSValue callContext)
82{
83 QJSValueIterator it(callContext);
84 it.next();
85 auto scriptableNodeName = it.value().toString();
86 auto concreteNode
87 = m_scriptEngine->globalObject().property(scriptableNodeName);
88
89 QJSValueList args;
90 while (it.next())
91 args << it.value();
92
93 concreteNode = concreteNode.callAsConstructor(args);
94
95 auto renderMethod = concreteNode.property(QStringLiteral("render"));
96
97 auto object = new ScriptableNode(m_scriptEngine);
98 object->setObjectName(scriptableNodeName);
99 object->setScriptEngine(m_scriptEngine);
100 object->init(concreteNode, renderMethod);
101 return m_scriptEngine->newQObject(object);
102}
103
104QJSValue ScriptableHelperFunctions::ScriptableTemplateConstructor(
105 QString content, QString name, QObject *parent)
106{
107 auto templateEngine
108 = m_scriptEngine->property("templateEngine").value<Engine *>();
109
110 if (!templateEngine)
111 return QJSValue();
112
113 auto t = templateEngine->newTemplate(content, name);
114
115 auto object = new ScriptableTemplate(t, parent);
116 return m_scriptEngine->newQObject(object);
117}
118
119QJSValue ScriptableHelperFunctions::ScriptableVariableConstructor(QString name)
120{
121 // TODO: Decide what the parent should be;
122 // It should be the owning scriptableNode. I think I can get that from the
123 // scriptContext.
124
125 QObject *parent = 0;
126 auto object = new ScriptableVariable(m_scriptEngine, parent);
127 object->setContent(name);
128
129 return m_scriptEngine->newQObject(object);
130}
131
132ScriptableTagLibrary::ScriptableTagLibrary(QObject *parent)
133 : QObject(parent), m_scriptEngine(new QJSEngine(this)),
134 m_functions(m_scriptEngine->newQObject(
135 new ScriptableHelperFunctions(m_scriptEngine)))
136{
137 m_scriptEngine->globalObject().setProperty(
138 QStringLiteral("internalCuteleeFunctions"), m_functions);
139
140 // Make Node new-able
141 m_scriptEngine->globalObject().setProperty(
142 QStringLiteral("Node"),
143 m_scriptEngine->evaluate(QStringLiteral(R"javascript(
144 (function() {
145 return internalCuteleeFunctions.ScriptableNodeConstructor(
146 Array.prototype.slice.call(arguments));
147 })
148 )javascript")));
149
150 // Make Variable new-able
151 m_scriptEngine->globalObject().setProperty(
152 QStringLiteral("Variable"),
153 m_functions.property(QStringLiteral("ScriptableVariableConstructor")));
154
155 // Make FilterExpression new-able
156 m_scriptEngine->globalObject().setProperty(
157 QStringLiteral("FilterExpression"),
158 m_functions.property(
159 QStringLiteral("ScriptableFilterExpressionConstructor")));
160
161 // Make Template new-able
162 m_scriptEngine->globalObject().setProperty(
163 QStringLiteral("Template"),
164 m_functions.property(QStringLiteral("ScriptableTemplateConstructor")));
165
166 // Create a global Library object
167 auto libraryObject = m_scriptEngine->newQObject(this);
168 m_scriptEngine->globalObject().setProperty(QStringLiteral("Library"),
169 libraryObject);
170
171 // Create a global AbstractNodeFactory object to make smartSplit available.
172 auto nodeFactory = new ScriptableNodeFactory(this);
173 auto nodeFactoryObject = m_scriptEngine->newQObject(nodeFactory);
174 m_scriptEngine->globalObject().setProperty(
175 QStringLiteral("AbstractNodeFactory"), nodeFactoryObject);
176
177 // Make mark_safe a globally available object.
178 m_scriptEngine->globalObject().setProperty(
179 QStringLiteral("mark_safe"),
180 m_functions.property(QStringLiteral("markSafeFunction")));
181}
182
183bool ScriptableTagLibrary::evaluateScript(const QString &name)
184{
185 QFile scriptFile(name);
186
187 if (!scriptFile.exists()
188 || !scriptFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
189 return false;
190 }
191
192 QTextStream fstream(&scriptFile);
193 fstream.setCodec("UTF-8");
194 const auto fileContent = fstream.readAll();
195
196 scriptFile.close();
197
198 QJSValue result = m_scriptEngine->evaluate(fileContent);
199 if (result.isError())
200 throw Cutelee::Exception(TagSyntaxError, result.toString());
201
202 return true;
203}
204
205QHash<QString, AbstractNodeFactory *>
207{
208 m_factoryNames.clear();
209 m_nodeFactories.clear();
210 QHash<QString, AbstractNodeFactory *> h;
211
212 if (!evaluateScript(name)) {
213 return h;
214 }
215
216 return getFactories();
217}
218
219QHash<QString, Filter *> ScriptableTagLibrary::filters(const QString &name)
220{
221 m_filterNames.clear();
222 m_filters.clear();
223 QHash<QString, Filter *> filters;
224
225 if (!evaluateScript(name)) {
226 return filters;
227 }
228
229 return getFilters();
230}
231
232QHash<QString, AbstractNodeFactory *> ScriptableTagLibrary::getFactories()
233{
234 QHash<QString, AbstractNodeFactory *> factories;
235 for (auto it = m_factoryNames.begin(), end = m_factoryNames.end(); it != end;
236 ++it) {
237 auto factoryName = it.value();
238 auto tagName = it.key();
239
240 auto factoryObject = m_scriptEngine->globalObject().property(factoryName);
241
242 auto snf = new ScriptableNodeFactory();
243 snf->setScriptEngine(m_scriptEngine);
244 snf->setFactory(factoryObject);
245
246 factories.insert(tagName, snf);
247 }
248
249 return factories;
250}
251
252QHash<QString, Filter *> ScriptableTagLibrary::getFilters()
253{
254 QHash<QString, Filter *> filters;
255
256 for (auto &filterNameString : m_filterNames) {
257 auto filterObject
258 = m_scriptEngine->globalObject().property(filterNameString);
259 auto filterName
260 = filterObject.property(QStringLiteral("filterName")).toString();
261 auto filter = new ScriptableFilter(filterObject, m_scriptEngine);
262 filters.insert(filterName, filter);
263 }
264
265 return filters;
266}
267
268void ScriptableTagLibrary::addFactory(const QString &factoryName,
269 const QString &tagName)
270{
271 m_factoryNames.insert(tagName, factoryName);
272}
273
274void ScriptableTagLibrary::addFilter(const QString &filterName)
275{
276 m_filterNames << filterName;
277}
Cutelee::Engine is the main entry point for creating Cutelee Templates.
Definition engine.h:121
An exception for use when implementing template tags.
Definition exception.h:85
QHash< QString, AbstractNodeFactory * > nodeFactories(const QString &name={}) override
QHash< QString, Filter * > filters(const QString &name={}) override
The Cutelee namespace holds all public Cutelee API.
Definition Mainpage.dox:8
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition util.cpp:90
Utility functions used throughout Cutelee.