Cutelee 6.1.0
lists.cpp
1/*
2 This file is part of the Cutelee template system.
3
4 Copyright (c) 2009,2010,2011 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 "lists.h"
22
23#include "metatype.h"
24#include "util.h"
25#include "variable.h"
26
27#include <QSequentialIterable>
28#include <QtCore/QDateTime>
29
30QVariant JoinFilter::doFilter(const QVariant &input, const QVariant &argument,
31 bool autoescape) const
32{
33 if (!input.canConvert<QVariantList>())
34 return QVariant();
35
36 auto iter = input.value<QSequentialIterable>();
37
38 QString ret;
39 for (auto it = iter.begin(); it != iter.end(); ++it) {
40 const auto var = *it;
41 auto s = getSafeString(var);
42 if (autoescape)
43 s = conditionalEscape(s);
44
45 ret.append(s);
46 if ((it + 1) != iter.end()) {
47 auto argString = getSafeString(argument);
48 ret.append(conditionalEscape(argString));
49 }
50 }
51 return markSafe(ret);
52}
53
54QVariant LengthFilter::doFilter(const QVariant &input, const QVariant &argument,
55 bool autoescape) const
56{
57 Q_UNUSED(autoescape)
58 Q_UNUSED(argument)
59 if (input.canConvert<QVariantList>())
60 return input.value<QSequentialIterable>().size();
61
62 if (input.userType() == qMetaTypeId<SafeString>()
63 || input.userType() == qMetaTypeId<QString>())
64 return getSafeString(input).get().size();
65
66 return QVariant();
67}
68
70 const QVariant &argument,
71 bool autoescape) const
72{
73 Q_UNUSED(autoescape)
74 if (!input.isValid() || (input.userType() == qMetaTypeId<int>())
75 || (input.userType() == qMetaTypeId<QDateTime>()))
76 return QVariant();
77
78 auto size = 0;
79 if (input.canConvert<QVariantList>())
80 size = input.value<QSequentialIterable>().size();
81 else if (input.userType() == qMetaTypeId<SafeString>()
82 || input.userType() == qMetaTypeId<QString>())
83 size = getSafeString(input).get().size();
84
85 bool ok;
86 auto argInt = getSafeString(argument).get().toInt(&ok);
87
88 if (!ok)
89 return QVariant();
90
91 return size == argInt;
92}
93
94QVariant FirstFilter::doFilter(const QVariant &input, const QVariant &argument,
95 bool autoescape) const
96{
97 Q_UNUSED(autoescape)
98 Q_UNUSED(argument)
99
100 if (!input.canConvert<QVariantList>())
101 return QVariant();
102
103 auto iter = input.value<QSequentialIterable>();
104
105 if (iter.size() == 0)
106 return QString();
107
108 return *iter.begin();
109}
110
111QVariant LastFilter::doFilter(const QVariant &input, const QVariant &argument,
112 bool autoescape) const
113{
114 Q_UNUSED(autoescape)
115 Q_UNUSED(argument)
116
117 if (!input.canConvert<QVariantList>())
118 return QVariant();
119
120 auto iter = input.value<QSequentialIterable>();
121
122 if (iter.size() == 0)
123 return QString();
124
125 return *(iter.end() - 1);
126}
127
128QVariant RandomFilter::doFilter(const QVariant &input, const QVariant &argument,
129 bool autoescape) const
130{
131 Q_UNUSED(autoescape)
132 Q_UNUSED(argument)
133
134 if (!input.canConvert<QVariantList>())
135 return QVariant();
136
137 auto varList = input.value<QVariantList>();
138
139 if (varList.isEmpty())
140 return QVariant();
141
142 srand(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch());
143 auto rnd = rand() % varList.size();
144 return varList.at(rnd);
145}
146
147QVariant SliceFilter::doFilter(const QVariant &input, const QVariant &argument,
148 bool autoescape) const
149{
150 Q_UNUSED(autoescape)
151 auto argString = getSafeString(argument);
152 auto splitterIndex = argString.get().indexOf(QLatin1Char(':'));
153 QString inputString = getSafeString(input);
154 if (inputString.isEmpty())
155 return QVariant();
156
157 if (splitterIndex >= 0) {
158 auto left = argString.get().left(splitterIndex).get().toInt();
159 auto right = argString.get().right(splitterIndex).get().toInt();
160 if (right < 0) {
161 right = inputString.size() + right;
162 }
163 return inputString.mid(left, right);
164 } else {
165 return QString(inputString.at(argument.value<int>()));
166 }
167}
168
170 const QVariant &argument,
171 bool autoescape) const
172{
173 Q_UNUSED(autoescape)
174 Q_UNUSED(argument)
175 if (_input.userType() == qMetaTypeId<QVariantList>())
176 return _input;
177 if (_input.canConvert<QVariantList>())
178 return _input.value<QVariantList>();
179
180 auto input = _input;
181
182 if (input.userType() == qMetaTypeId<int>()) {
183#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
184 input.convert(QMetaType::QString);
185#else
186 input.convert(QMetaType(QMetaType::QString));
187#endif
188 }
189
190 if (input.userType() == qMetaTypeId<SafeString>()
191 || input.userType() == qMetaTypeId<QString>()) {
192 QVariantList list;
193#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
194 const auto parts = getSafeString(input).get().split(QString(), QString::SkipEmptyParts);
195#else
196 const auto parts = getSafeString(input).get().split(QString(), Qt::SkipEmptyParts);
197#endif
198 for (const auto &var : parts) {
199 list << var;
200 }
201 return list;
202 }
203 return QVariant();
204}
205
207 const QVariant &argument,
208 bool autoescape) const
209{
210 Q_UNUSED(argument)
211
212 if (!input.canConvert<QVariantList>())
213 return QVariant();
214
215 return markSafe(processList(input.value<QVariantList>(), 1, autoescape));
216}
217
218SafeString UnorderedListFilter::processList(const QVariantList &list, int tabs,
219 bool autoescape) const
220{
221 QString indent;
222 for (auto i = 0; i < tabs; ++i)
223 indent.append(QLatin1Char('\t'));
224 QStringList output;
225
226 auto i = 0;
227 auto listSize = list.size();
228 while (i < listSize) {
229 auto titleObject = list.at(i);
230 auto title = getSafeString(titleObject);
231 QString sublist;
232 QVariant sublistItem;
233
234 if (titleObject.userType() == qMetaTypeId<QVariantList>()) {
235 sublistItem = titleObject;
236 title.get().clear();
237 } else if (i < listSize - 1) {
238 auto nextItem = list.at(i + 1);
239 if (nextItem.userType() == qMetaTypeId<QVariantList>()) {
240 sublistItem = nextItem;
241 }
242 ++i;
243 }
244 if (sublistItem.isValid()) {
245 sublist = processList(sublistItem.value<QVariantList>(), tabs + 1,
246 autoescape);
247 sublist = QStringLiteral("\n%1<ul>\n%2\n%3</ul>\n%4")
248 .arg(indent, sublist, indent, indent);
249 }
250 output.append(QStringLiteral("%1<li>%2%3</li>")
251 .arg(indent,
252 autoescape ? conditionalEscape(title) : title,
253 sublist));
254 ++i;
255 }
256
257 // Should be QLatin1Char() ?
258 return output.join(QChar::fromLatin1('\n'));
259}
260
262 bool operator()(const std::pair<QVariant, QVariant> &lp,
263 const std::pair<QVariant, QVariant> &rp) const
264 {
265 const auto l = lp.first;
266 const auto r = rp.first;
267 switch (l.userType()) {
268 case QMetaType::UnknownType:
269 return (r.isValid());
270 case QMetaType::Int:
271 return l.value<int>() < r.value<int>();
272 case QMetaType::UInt:
273 return l.value<uint>() < r.value<uint>();
274 case QMetaType::LongLong:
275 return l.value<long long>() < r.value<long long>();
276 case QMetaType::ULongLong:
277 return l.value<unsigned long long>() < r.value<unsigned long long>();
278 case QMetaType::Float:
279 return l.value<float>() < r.value<float>();
280 case QMetaType::Double:
281 return l.value<double>() < r.value<double>();
282 case QMetaType::Char:
283 return l.toChar() < r.toChar();
284 case QMetaType::QDate:
285 return l.toDate() < r.toDate();
286 case QMetaType::QTime:
287 return l.toTime() < r.toTime();
288 case QMetaType::QDateTime:
289 return l.toDateTime() < r.toDateTime();
290 case QMetaType::QObjectStar:
291 return l.value<QObject *>() < r.value<QObject *>();
292 }
293 if (l.userType() == qMetaTypeId<Cutelee::SafeString>()) {
294 if (r.userType() == qMetaTypeId<Cutelee::SafeString>()) {
295 return l.value<Cutelee::SafeString>().get()
296 < r.value<Cutelee::SafeString>().get();
297 } else if (r.userType() == qMetaTypeId<QString>()) {
298 return l.value<Cutelee::SafeString>().get() < r.toString();
299 }
300 } else if (r.userType() == qMetaTypeId<Cutelee::SafeString>()) {
301 if (l.userType() == qMetaTypeId<QString>()) {
302 return l.toString() < r.value<Cutelee::SafeString>().get();
303 }
304 } else if (l.userType() == qMetaTypeId<QString>()) {
305 if (r.userType() == qMetaTypeId<QString>()) {
306 return l.toString() < r.toString();
307 }
308 }
309 return false;
310 }
311};
312
314 const QVariant &argument,
315 bool autoescape) const
316{
317 Q_UNUSED(autoescape)
318
319 if (!input.canConvert<QVariantList>())
320 return QVariant();
321
322 QList<std::pair<QVariant, QVariant>> keyList;
323 const auto inList = input.value<QSequentialIterable>();
324 for (const QVariant &item : inList) {
325 auto var = item;
326
327 const Variable v(getSafeString(argument));
328
329 if (v.literal().isValid()) {
330 var = MetaType::lookup(var, v.literal().toString());
331 } else {
332 const auto lookups = v.lookups();
333 Q_FOREACH (const QString &lookup, lookups) {
334 var = MetaType::lookup(var, lookup);
335 }
336 }
337 keyList.push_back({var, item});
338 }
339
341 std::stable_sort(keyList.begin(), keyList.end(), lt);
342
343 QVariantList outList;
344 auto it = keyList.constBegin();
345 const auto end = keyList.constEnd();
346 for (; it != end; ++it) {
347 outList << it->second;
348 }
349 return outList;
350}
SafeString conditionalEscape(const SafeString &input) const
Definition filter.cpp:22
A QString wrapper class for containing whether a string is safe or needs to be escaped.
Definition safestring.h:92
const NestedString & get() const
Definition safestring.h:340
A container for static variables defined in Templates.
Definition variable.h:53
QStringList lookups() const
Definition variable.cpp:159
QVariant literal() const
Definition variable.cpp:153
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:313
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:94
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:30
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:111
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:54
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:69
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:169
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:128
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:147
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
Definition lists.cpp:206
Cutelee::SafeString getSafeString(const QVariant &input)
Definition util.cpp:108
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition util.cpp:90
Utility functions used throughout Cutelee.