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 
44 Q_DECLARE_METATYPE(Token)
45 
46 using namespace Cutelee;
47 
48 QJSValue 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 
68 QJSValue 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 
80 QJSValue
81 ScriptableHelperFunctions::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 
104 QJSValue 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 
119 QJSValue 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 
132 ScriptableTagLibrary::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 
183 bool 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 
205 QHash<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 
219 QHash<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 
232 QHash<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 
252 QHash<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 
268 void ScriptableTagLibrary::addFactory(const QString &factoryName,
269  const QString &tagName)
270 {
271  m_factoryNames.insert(tagName, factoryName);
272 }
273 
274 void 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.