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 
30 QVariant 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 
54 QVariant 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 
94 QVariant 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 
111 QVariant 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 
128 QVariant 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 
147 QVariant 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 
218 SafeString 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 
340  DictSortLessThan lt;
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.