Cutelee 6.1.0
stringfilters.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 "stringfilters.h"
22
23#include "util.h"
24
25#include <QtCore/QRegularExpression>
26#include <QtCore/QVariant>
27
29 const QVariant &argument,
30 bool autoescape) const
31{
32 Q_UNUSED(argument)
33 Q_UNUSED(autoescape)
34 auto safeString = getSafeString(input);
35 safeString.get()
36 .replace(QLatin1Char('\\'), QStringLiteral("\\\\"))
37 .get()
38 .replace(QLatin1Char('\"'), QStringLiteral("\\\""))
39 .get()
40 .replace(QLatin1Char('\''), QStringLiteral("\\\'"));
41 return safeString;
42}
43
45 const QVariant &argument,
46 bool autoescape) const
47{
48 Q_UNUSED(argument)
49 Q_UNUSED(autoescape)
50 auto safeString = getSafeString(input);
51 if (safeString.get().isEmpty())
52 return QString();
53
54 return QVariant(safeString.get().at(0).toUpper()
55 + static_cast<QString>(
56 safeString.get().right(safeString.get().size() - 1)));
57}
58
59EscapeJsFilter::EscapeJsFilter() {}
60
61static QList<std::pair<QString, QString>> getJsEscapes()
62{
64 jsEscapes << std::pair<QString, QString>(QChar::fromLatin1('\\'),
65 QStringLiteral("\\u005C"))
66 << std::pair<QString, QString>(QChar::fromLatin1('\''),
67 QStringLiteral("\\u0027"))
68 << std::pair<QString, QString>(QChar::fromLatin1('\"'),
69 QStringLiteral("\\u0022"))
70 << std::pair<QString, QString>(QChar::fromLatin1('>'),
71 QStringLiteral("\\u003E"))
72 << std::pair<QString, QString>(QChar::fromLatin1('<'),
73 QStringLiteral("\\u003C"))
74 << std::pair<QString, QString>(QChar::fromLatin1('&'),
75 QStringLiteral("\\u0026"))
76 << std::pair<QString, QString>(QChar::fromLatin1('='),
77 QStringLiteral("\\u003D"))
78 << std::pair<QString, QString>(QChar::fromLatin1('-'),
79 QStringLiteral("\\u002D"))
80 << std::pair<QString, QString>(QChar::fromLatin1(';'),
81 QStringLiteral("\\u003B"))
82 << std::pair<QString, QString>(QChar(0x2028), QStringLiteral("\\u2028"))
83 << std::pair<QString, QString>(QChar(0x2029),
84 QStringLiteral("\\u2029"));
85
86 for (auto i = 0; i < 32; ++i) {
87 jsEscapes << std::pair<QString, QString>(
88 QChar(i),
89 QStringLiteral("\\u00")
90 + QStringLiteral("%1").arg(i, 2, 16, QLatin1Char('0')).toUpper());
91 }
92 return jsEscapes;
93}
94
96 const QVariant &argument,
97 bool autoescape) const
98{
99 Q_UNUSED(argument)
100 Q_UNUSED(autoescape)
101 QString retString = getSafeString(input);
102
103 static const auto jsEscapes = getJsEscapes();
104
105 for (auto &escape : jsEscapes) {
106 retString = retString.replace(escape.first, escape.second);
107 }
108 return retString;
109}
110
112 const QVariant &argument,
113 bool autoescape) const
114{
115 Q_UNUSED(argument)
116 Q_UNUSED(autoescape)
117 auto safeString = getSafeString(input);
118
119 const QRegularExpression fixAmpersandsRegexp(
120 QStringLiteral("&(?!(\\w+|#\\d+);)"));
121
122 safeString.get().replace(fixAmpersandsRegexp, QStringLiteral("&amp;"));
123
124 return safeString;
125}
126
127QVariant CutFilter::doFilter(const QVariant &input, const QVariant &argument,
128 bool autoescape) const
129{
130 Q_UNUSED(autoescape)
131 auto retString = getSafeString(input);
132 auto argString = getSafeString(argument);
133
134 auto inputSafe = retString.isSafe();
135
136 retString.get().remove(argString);
137
138 if (inputSafe && argString.get() != QChar::fromLatin1(';'))
139 return SafeString(retString, true);
140 else
141 return retString;
142}
143
144QVariant SafeFilter::doFilter(const QVariant &input, const QVariant &argument,
145 bool autoescape) const
146{
147 Q_UNUSED(argument)
148 Q_UNUSED(autoescape)
149 return markSafe(getSafeString(input));
150}
151
153 const QVariant &argument,
154 bool autoescape) const
155{
156 Q_UNUSED(argument)
157 auto safeString = getSafeString(input);
158 auto lines = safeString.get().split(QLatin1Char('\n'));
159 auto width = QString::number(lines.size()).size();
160
161 const auto shouldEscape = (autoescape && !safeString.isSafe());
162 for (auto i = 0; i < lines.size(); ++i) {
163 lines[i]
164 = QStringLiteral("%1. %2")
165 .arg(i + 1, width)
166 .arg(shouldEscape ? QString(escape(lines.at(i))) : lines.at(i));
167 }
168
169 return SafeString(lines.join(QChar::fromLatin1('\n')), true);
170}
171
172QVariant LowerFilter::doFilter(const QVariant &input, const QVariant &argument,
173 bool autoescape) const
174{
175 Q_UNUSED(argument)
176 Q_UNUSED(autoescape)
177 return getSafeString(input).get().toLower();
178}
179
181 const QVariant &argument,
182 bool autoescape) const
183{
184 Q_UNUSED(autoescape)
185 SafeString a;
186 if (isSafeString(input))
187 a = getSafeString(input);
188 else if (input.userType() == qMetaTypeId<QVariantList>()) {
189 a = toString(input.value<QVariantList>());
190 }
191
192 return SafeString(getSafeString(argument).get().arg(a),
193 getSafeString(input).isSafe());
194}
195
196QVariant TitleFilter::doFilter(const QVariant &input, const QVariant &argument,
197 bool autoescape) const
198{
199 Q_UNUSED(argument)
200 Q_UNUSED(autoescape)
201
202 QString str = getSafeString(input);
203
204 auto it = str.begin();
205 const auto end = str.end();
206
207 auto toUpper = true;
208 for (; it != end; ++it) {
209 if (toUpper)
210 *it = it->toUpper();
211 else
212 *it = it->toLower();
213 toUpper = it->isSpace();
214 }
215
216 return str;
217}
218
220 const QVariant &argument,
221 bool autoescape) const
222{
223 Q_UNUSED(autoescape)
224 auto s = getSafeString(argument);
225
226 bool ok;
227 auto numWords = s.get().toInt(&ok);
228
229 if (!ok) {
230 return input.toString();
231 }
232
233 QString inputString = getSafeString(input);
234#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
235 auto words = inputString.split(QLatin1Char(' '), QString::SkipEmptyParts);
236#else
237 auto words = inputString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
238#endif
239
240 if (words.size() > numWords) {
241 words = words.mid(0, numWords);
242 if (!words.at(words.size() - 1).endsWith(QStringLiteral("..."))) {
243 words << QStringLiteral("...");
244 }
245 }
246 return words.join(QChar::fromLatin1(' '));
247}
248
249QVariant UpperFilter::doFilter(const QVariant &input, const QVariant &argument,
250 bool autoescape) const
251{
252 Q_UNUSED(argument)
253 Q_UNUSED(autoescape)
254 return getSafeString(input).get().toUpper();
255}
256
258 const QVariant &argument,
259 bool autoescape) const
260{
261 Q_UNUSED(argument)
262 Q_UNUSED(autoescape)
263 return QString::number(
264 getSafeString(input).get().split(QLatin1Char(' ')).size());
265}
266
267QVariant LJustFilter::doFilter(const QVariant &input, const QVariant &argument,
268 bool autoescape) const
269{
270 Q_UNUSED(autoescape)
271 return getSafeString(input).get().leftJustified(
272 getSafeString(argument).get().toInt());
273}
274
275QVariant RJustFilter::doFilter(const QVariant &input, const QVariant &argument,
276 bool autoescape) const
277{
278 Q_UNUSED(autoescape)
279 return getSafeString(input).get().rightJustified(
280 getSafeString(argument).get().toInt());
281}
282
283QVariant CenterFilter::doFilter(const QVariant &input, const QVariant &argument,
284 bool autoescape) const
285{
286 Q_UNUSED(autoescape)
287 QString value = getSafeString(input);
288 const auto valueWidth = value.size();
289 const auto width = getSafeString(argument).get().toInt();
290 const auto totalPadding = width - valueWidth;
291 const auto rightPadding = totalPadding >> 1;
292
293 return value.leftJustified(valueWidth + rightPadding).rightJustified(width);
294}
295
296QVariant EscapeFilter::doFilter(const QVariant &input, const QVariant &argument,
297 bool autoescape) const
298{
299 Q_UNUSED(argument)
300 Q_UNUSED(autoescape)
301 return markForEscaping(getSafeString(input));
302}
303
305 const QVariant &argument,
306 bool autoescape) const
307{
308 Q_UNUSED(argument)
309 Q_UNUSED(autoescape)
310 return markSafe(escape(getSafeString(input)));
311}
312
314 const QVariant &argument,
315 bool autoescape) const
316{
317 Q_UNUSED(autoescape)
318 const auto tags = getSafeString(argument).get().split(QLatin1Char(' '));
319 const auto tagRe
320 = QStringLiteral("(%1)").arg(tags.join(QChar::fromLatin1('|')));
321 const QRegularExpression startTag(
322 QStringLiteral("<%1(/?>|(\\s+[^>]*>))").arg(tagRe));
323 const QRegularExpression endTag(QStringLiteral("</%1>").arg(tagRe));
324
325 auto value = getSafeString(input);
326 const auto safeInput = value.isSafe();
327 value.get().remove(startTag);
328 value.get().remove(endTag);
329 if (safeInput)
330 return markSafe(value);
331 return value;
332}
333
335 const QVariant &argument,
336 bool autoescape) const
337{
338 Q_UNUSED(argument)
339 Q_UNUSED(autoescape)
340 static QRegularExpression tagRe(QStringLiteral("<[^>]*>"),
342
343 QString value = getSafeString(input);
344 value.remove(tagRe);
345 return value;
346}
347
349 const QVariant &argument,
350 bool autoescape) const
351{
352 Q_UNUSED(autoescape)
353 QString _input = getSafeString(input);
354 auto width = argument.value<int>();
355#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
356 auto partList = _input.split(QLatin1Char(' '), QString::SkipEmptyParts);
357#else
358 auto partList = _input.split(QLatin1Char(' '), Qt::SkipEmptyParts);
359#endif
360 if (partList.isEmpty())
361 return QVariant();
362 auto output = partList.takeFirst();
363 auto pos = output.size() - output.lastIndexOf(QLatin1Char('\n')) - 1;
364 Q_FOREACH (const QString &part, partList) {
365 QStringList lines;
366 if (part.contains(QLatin1Char('\n'))) {
367 lines = part.split(QLatin1Char('\n'));
368 } else {
369 lines.append(part);
370 }
371 pos += lines.first().size() + 1;
372 if (pos > width) {
373 output.append(QLatin1Char('\n'));
374 pos += lines.last().size();
375 } else {
376 output.append(QLatin1Char(' '));
377 if (lines.size() > 1)
378 pos += lines.last().size();
379 }
380 output.append(part);
381 }
382 return output;
383}
384
386 const QVariant &argument,
387 bool autoescape) const
388{
389 Q_UNUSED(autoescape)
390 double inputDouble;
391 switch (input.userType()) {
392 case QMetaType::Int:
393 case QMetaType::UInt:
397 inputDouble = input.toDouble();
398 break;
399 default:
400 inputDouble = getSafeString(input).get().toDouble();
401 }
402
403 int precision;
404 if (argument.isValid())
405 precision = getSafeString(argument).get().toInt();
406 else
407 precision = 1;
408
409 return QString::number(inputDouble, 'f', precision);
410}
411
413 const QVariant &argument,
414 bool autoescape) const
415{
416 Q_UNUSED(argument)
417 Q_UNUSED(autoescape)
418 QVariantList list;
419 if (input.userType() == qMetaTypeId<QVariantList>()) {
420 const auto inputList = input.value<QVariantList>();
421 for (const auto &item : inputList) {
422 list << markSafe(getSafeString(item));
423 }
424 }
425 return list;
426}
427
429 const QVariant &argument,
430 bool autoescape) const
431{
432 Q_UNUSED(argument)
433 auto inputString = getSafeString(input);
434 static const QRegularExpression re(QStringLiteral("\n{2,}"));
435 QStringList output;
436
437 Q_FOREACH (const QString &bit, inputString.get().split(re)) {
438 auto _bit = SafeString(bit, inputString.isSafe());
439 if (autoescape)
440 _bit = conditionalEscape(_bit);
441 _bit.get().replace(QLatin1Char('\n'), QStringLiteral("<br />"));
442 output.append(QStringLiteral("<p>%1</p>").arg(_bit));
443 }
444 return markSafe(output.join(QStringLiteral("\n\n")));
445}
446
448 const QVariant &argument,
449 bool autoescape) const
450{
451 Q_UNUSED(argument)
452 auto inputString = getSafeString(input);
453 if (autoescape && isSafeString(input)) {
454 inputString = conditionalEscape(inputString);
455 }
456 return markSafe(
457 inputString.get().replace(QLatin1Char('\n'), QStringLiteral("<br />")));
458}
459
460static QString nofailStringToAscii(const QString &input)
461{
462 QString output;
463 output.reserve(input.size());
464
465 auto it = input.constBegin();
466 const auto end = input.constEnd();
467 static const QChar asciiEndPoint(128);
468 for (; it != end; ++it)
469 if (*it < asciiEndPoint)
470 output.append(*it);
471
472 return output;
473}
474
476 const QVariant &argument,
477 bool autoescape) const
478{
479 Q_UNUSED(argument)
480 Q_UNUSED(autoescape)
481 QString inputString = getSafeString(input);
482 inputString = inputString.normalized(QString::NormalizationForm_KD);
483 inputString = nofailStringToAscii(inputString);
484 inputString = inputString.trimmed()
485 .toLower()
486 .remove(QRegularExpression(QStringLiteral("[^\\w\\s-]")))
487 .replace(QRegularExpression(QStringLiteral("[-\\s]+")), QStringLiteral("-"));
488 return SafeString(inputString, true);
489}
490
492 const QVariant &argument,
493 bool autoescape) const
494{
495 QVariant ret;
496
497 Q_UNUSED(autoescape)
498 const auto arg = getSafeString(argument);
499 bool numberConvert = true;
500
501 qreal size = 0.0f;
502 if (input.canConvert<qreal>()) {
503 size = input.toReal(&numberConvert);
504 if (!numberConvert) {
505 qWarning("%s", "Failed to convert input file size into floating point value.");
506 }
507 } else {
508 size = getSafeString(input).get().toDouble(&numberConvert);
509 if (!numberConvert) {
510 qWarning("%s", "Failed to convert input file size into floating point value.");
511 }
512 }
513
514 int unitSystem = 10;
515 int precision = 2;
516 qreal multiplier = 1.0f;
517
518 if (!arg.get().isEmpty()) {
519#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
520 const auto argList = arg.get().split(QLatin1Char(','), QString::SkipEmptyParts);
521#else
522 const auto argList = arg.get().split(QLatin1Char(','), Qt::SkipEmptyParts);
523#endif
524 const auto numArgs = argList.size();
525 if (numArgs > 0) {
526 unitSystem = argList.at(0).toInt(&numberConvert);
527 if (!numberConvert) {
528 qWarning("%s", "Failed to convert filse size format unit system into integer. Falling back to default 10.");
529 unitSystem = 10;
530 }
531 }
532
533 if (numArgs > 1) {
534 precision = argList.at(1).toInt(&numberConvert);
535 if (!numberConvert) {
536 qWarning("%s", "Failed to convert file size format decimal precision into integer. Falling back to default 2.");
537 precision = 2;
538 }
539 }
540
541 if (numArgs > 2) {
542 multiplier = argList.at(2).toDouble(&numberConvert);
543 if (!numberConvert) {
544 qWarning("%s", "Failed to convert file size format multiplier into double value. Falling back to default 1.0");
545 multiplier = 1.0f;
546 } else {
547 if (multiplier == 0.0f) {
548 qWarning("%s", "It makes no sense to multiply the file size by zero. Using default value 1.0.");
549 multiplier = 1.0f;
550 }
551 }
552 }
553 }
554
555 const double sizeMult = size * multiplier;
556
557 if (unitSystem == 10) {
558 if ((sizeMult > -1000) && (sizeMult < 1000)) {
559 precision = 0;
560 }
561 } else if (unitSystem == 2) {
562 if ((sizeMult > - 1024) && (sizeMult < 1024)) {
563 precision = 0;
564 }
565 }
566
567 const std::pair<qreal,QString> sizePair = calcFileSize(size, unitSystem, multiplier);
568
569 const QString retString = QString::number(sizePair.first, 'f', precision) + QLatin1Char(' ') + sizePair.second;
570
571 ret.setValue(retString);
572
573 return ret;
574}
575
576QVariant TruncateCharsFilter::doFilter(const QVariant &input, const QVariant &argument, bool autoescape) const
577{
578 Q_UNUSED(autoescape)
579 QString retString = getSafeString(input);
580 int count = getSafeString(argument).get().toInt();
581
582 if(retString.length() < count) return retString;
583 retString.truncate(count);
584 retString.append(QStringLiteral("..."));
585 return retString;
586}
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
SafeString escape(const QString &input) const
Definition filter.cpp:10
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
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument=QVariant(), bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
bool isSafe() const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument=QVariant(), bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
QVariant doFilter(const QVariant &input, const QVariant &argument={}, bool autoescape={}) const override
std::pair< qreal, QString > calcFileSize(qreal size, int unitSystem=10, qreal multiplier=1.0)
Definition util.cpp:215
bool isSafeString(const QVariant &input)
Definition util.cpp:117
Cutelee::SafeString getSafeString(const QVariant &input)
Definition util.cpp:108
Cutelee::SafeString markSafe(const Cutelee::SafeString &input)
Definition util.cpp:90
Cutelee::SafeString markForEscaping(const Cutelee::SafeString &input)
Definition util.cpp:98
QChar fromLatin1(char c)
void append(const T &value)
T & first()
T & last()
int size() const const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const const
NormalizationForm_KD
QString & append(QChar ch)
const QChar at(int position) const const
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
iterator end()
QString leftJustified(int width, QChar fill, bool truncate) const const
int length() const const
QString normalized(NormalizationForm mode, QChar::UnicodeVersion version) const const
QString number(int n, int base)
QString & remove(int position, int n)
QString & replace(int position, int n, QChar after)
void reserve(int size)
QString rightJustified(int width, QChar fill, bool truncate) const const
int size() const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
QString toLower() const const
QString toUpper() const const
QString trimmed() const const
void truncate(int position)
QString join(const QString &separator) const const
SkipEmptyParts
bool canConvert(int targetTypeId) const const
bool isValid() const const
void setValue(const T &value)
double toDouble(bool *ok) const const
qreal toReal(bool *ok) const const
QString toString() const const
int userType() const const
T value() const const
Utility functions used throughout Cutelee.