cutelyst  3.9.1
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatorfilesize.cpp
1 /*
2  * SPDX-FileCopyrightText: (C) 2018-2022 Matthias Fehring <mf@huessenbergnetz.de>
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 #include "validatorfilesize_p.h"
7 
8 #include <cmath>
9 #include <limits>
10 
11 using namespace Cutelyst;
12 
14  Option option,
15  const QVariant &min,
16  const QVariant &max,
17  const ValidatorMessages &messages,
18  const QString &defValKey)
19  : ValidatorRule(*new ValidatorFileSizePrivate(field, option, min, max, messages, defValKey))
20 {
21 }
22 
24 {
25 }
26 
28  double min,
29  double max,
31  const QLocale &locale,
32  double *fileSize)
33 {
34  bool valid = true;
35 
36  QString digitPart;
37  QString symbolPart;
38  bool decimalPointFound = false;
39  const QString decimalPoint(locale.decimalPoint());
40  int multiplier = 0;
41  bool binary = false;
42  bool byteSignFound = false;
43  qint8 startsWith = 0; // 0 not set, -1 digit part, 1 symbol part
44 
45  for (const QChar &ch : value) {
46  if (valid) {
47  const ushort &uc = ch.unicode();
48  if (((uc > 47) && (uc < 58)) || (ch == decimalPoint)) {
49  if (startsWith == 0) {
50  startsWith = -1;
51  }
52  if (ch == decimalPoint) {
53  if (decimalPointFound) {
54  valid = false;
55  break;
56  } else {
57  decimalPointFound = true;
58  }
59  }
60  if ((symbolPart.isEmpty() && (startsWith < 0)) ||
61  (!symbolPart.isEmpty() && (startsWith > 0))) {
62  digitPart.append(ch);
63  } else {
64  valid = false;
65  break;
66  }
67  } else if ((uc != 9) &&
68  (uc != 32)) { // not a digit or decimal point and not a space or tab
69  if (startsWith == 0) {
70  startsWith = 1;
71  }
72  if ((digitPart.isEmpty() && (startsWith > 0)) ||
73  (!digitPart.isEmpty() && (startsWith < 0))) {
74  switch (uc) {
75  case 75: // K
76  case 107: // k
77  {
78  if (multiplier > 0) {
79  valid = false;
80  } else {
81  multiplier = 1;
82  symbolPart.append(ch);
83  }
84  } break;
85  case 77: // M
86  case 109: // m
87  {
88  if (multiplier > 0) {
89  valid = false;
90  } else {
91  multiplier = 2;
92  symbolPart.append(ch);
93  }
94  } break;
95  case 71: // G
96  case 103: // g
97  {
98  if (multiplier > 0) {
99  valid = false;
100  } else {
101  multiplier = 3;
102  symbolPart.append(ch);
103  }
104  } break;
105  case 84: // T
106  case 116: // t
107  {
108  if (multiplier > 0) {
109  valid = false;
110  } else {
111  multiplier = 4;
112  symbolPart.append(ch);
113  }
114  } break;
115  case 80: // P
116  case 112: // p
117  {
118  if (multiplier > 0) {
119  valid = false;
120  } else {
121  multiplier = 5;
122  symbolPart.append(ch);
123  }
124  } break;
125  case 69: // E
126  case 101: // e
127  {
128  if (multiplier > 0) {
129  valid = false;
130  } else {
131  multiplier = 6;
132  symbolPart.append(ch);
133  }
134  } break;
135  case 90: // Z
136  case 122: // z
137  {
138  if (multiplier > 0) {
139  valid = false;
140  } else {
141  multiplier = 7;
142  symbolPart.append(ch);
143  }
144  } break;
145  case 89: // Y
146  case 121: // y
147  {
148  if (multiplier > 0) {
149  valid = false;
150  } else {
151  multiplier = 8;
152  symbolPart.append(ch);
153  }
154  } break;
155  case 73: // I
156  case 105: // i
157  {
158  if ((multiplier == 0) || binary) {
159  valid = false;
160  } else {
161  binary = true;
162  symbolPart.append(ch);
163  }
164  } break;
165  case 66: // B
166  case 98: // b
167  {
168  if (byteSignFound) {
169  valid = false;
170  } else {
171  byteSignFound = true;
172  symbolPart.append(ch);
173  }
174  } break;
175  case 9: // horizontal tab
176  case 32: // space
177  break;
178  default:
179  valid = false;
180  break;
181  }
182  } else {
183  valid = false;
184  break;
185  }
186  }
187  } else {
188  break;
189  }
190  }
191 
192  if ((option == OnlyBinary) && !binary) {
193  valid = false;
194  } else if ((option == OnlyDecimal) && binary) {
195  valid = false;
196  } else if (option == ForceBinary) {
197  binary = true;
198  } else if (option == ForceDecimal) {
199  binary = false;
200  }
201 
202  if (valid) {
203  bool ok = false;
204  double size = locale.toDouble(digitPart, &ok);
205  if (!ok) {
206  valid = false;
207  } else {
208  if (multiplier > 0) {
209  const double _mult =
210  binary ? std::exp2(multiplier * 10) : std::pow(10.0, multiplier * 3);
211  size *= _mult;
212  }
213  if ((min >= 1.0) && (size < min)) {
214  valid = false;
215  }
216  if ((max >= 1.0) && (size > max)) {
217  valid = false;
218  }
219  if (valid && fileSize) {
220  *fileSize = size;
221  }
222  }
223  }
224 
225  return valid;
226 }
227 
229 {
230  ValidatorReturnType result;
231 
232  Q_D(const ValidatorFileSize);
233 
234  const QString v = value(params);
235 
236  if (!v.isEmpty()) {
237 
238  double min = -1;
239  double max = -1;
240  bool ok = true;
241  if (d->min.isValid()) {
242  min = d->extractDouble(c, params, d->min, &ok);
243  if (!ok) {
244  result.errorMessage = validationDataError(c, 0);
245  }
246  }
247 
248  if (ok && d->max.isValid()) {
249  max = d->extractDouble(c, params, d->max, &ok);
250  if (!ok) {
251  result.errorMessage = validationDataError(c, 1);
252  }
253  }
254 
255  if (ok) {
256  double size = 0;
257  if (ValidatorFileSize::validate(v, min, max, d->option, c->locale(), &size)) {
258  if (size < static_cast<double>(std::numeric_limits<qulonglong>::max())) {
259  result.value.setValue<qulonglong>(static_cast<qulonglong>(size + 0.5));
260  } else {
261  result.value.setValue(size);
262  }
263  } else {
264  result.errorMessage = validationError(c);
265  }
266  }
267 
268  } else {
269  defaultValue(c, &result, "ValidatorFileSize");
270  }
271 
272  return result;
273 }
274 
276 {
277  QString error;
278  Q_D(const ValidatorFileSize);
279  Q_UNUSED(errorData)
280  const QString _label = label(c);
281  if (d->min.isValid() || d->max.isValid()) {
282  if (_label.isEmpty()) {
283  error = c->translate("Cutelyst::ValidatorFileSize",
284  "Invalid file size or file size not within the allowed limits.");
285  } else {
286  error = c->translate("Cutelyst::ValidatorFileSize",
287  "The value in the “%1” field is either not a valid file size or "
288  "not within the allowed limits.")
289  .arg(_label);
290  }
291  } else {
292  if (_label.isEmpty()) {
293  error = c->translate("Cutelyst::ValidatorFileSize", "Invalid file size.");
294  } else {
295  error = c->translate("Cutelyst::ValidatorFileSize",
296  "The “%1” field does not contain a valid file size.")
297  .arg(_label);
298  }
299  }
300 
301  return error;
302 }
303 
305 {
306  QString error;
307 
308  const QString _label = label(c);
309 
310  const int sizeType = errorData.toInt();
311 
312  if (sizeType == 0) { // minimum file size
313  if (_label.isEmpty()) {
314  error = c->translate("Cutelyst::ValidatorFileSize",
315  "The minimum file size comparison value is not valid.");
316  } else {
317  error = c->translate(
318  "Cutelyst::ValidatorFileSize",
319  "The minimum file size comparison value for the “%1” field is not valid.")
320  .arg(_label);
321  }
322  } else {
323  if (_label.isEmpty()) {
324  error = c->translate("Cutelyst::ValidatorFileSize",
325  "The maximum file size comparison value is not valid.");
326  } else {
327  error = c->translate(
328  "Cutelyst::ValidatorFileSize",
329  "The maximum file size comparison value for the “%1” field is not valid.")
330  .arg(_label);
331  }
332  }
333 
334  return error;
335 }
336 
338 {
339  Q_ASSERT(c);
340  c->setStash(stashKey,
342  ? QStringLiteral("^\\d+[,.٫]?\\d*\\s*[KkMmGgTt]?[Ii]?[Bb]?")
343  : QStringLiteral("[KkMmGgTt]?[Ii]?[Bb]?\\s*\\d+[,.٫]?\\d*"));
344 }
QString & append(QChar ch)
QString validationError(Context *c, const QVariant &errorData=QVariant()) const
Returns a descriptive error message if validation failed.
QString genericValidationDataError(Context *c, const QVariant &errorData) const override
Returns a generic error messages if validation data is missing or invalid.
Stores custom error messages and the input field label.
void setStash(const QString &key, const QVariant &value)
Definition: context.cpp:217
ValidatorFileSize(const QString &field, Option option=NoOption, const QVariant &min=QVariant(), const QVariant &max=QVariant(), const ValidatorMessages &messages=ValidatorMessages(), const QString &defValKey=QString())
Constructs a new file size validator.
QChar decimalPoint() const const
LeftToRight
double toDouble(const QString &s, bool *ok) const const
Option
Options for ValidatorFileSize.
static void inputPattern(Context *c, const QString &stashKey=QStringLiteral("fileSizePattern"))
Puts an HTML input pattern for file sizes into the stash.
The Cutelyst Context.
Definition: context.h:38
int toInt(bool *ok) const const
bool isEmpty() const const
Qt::LayoutDirection textDirection() const const
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:490
QString validationDataError(Context *c, const QVariant &errorData=QVariant()) const
Returns an error message if any validation data is missing or invalid.
The Cutelyst namespace holds all public Cutelyst API.
Definition: Mainpage.dox:7
Base class for all validator rules.
QLocale locale() const noexcept
Definition: context.cpp:466
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
Checks if the input field contains a valid file size string like 1.5 GB.
void setValue(const T &value)
QString value(const ParamsMultiMap &params) const
Returns the value of the field from the input params.
const QChar * unicode() const const
static bool validate(const QString &value, double min=-1, double max=-1, Option option=NoOption, const QLocale &locale=QLocale(), double *fileSize=nullptr)
Returns true if value is a valid file size string.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
Contains the result of a single input parameter validation.
Definition: validatorrule.h:49
~ValidatorFileSize() override
Deconstructs the file size validator.
void defaultValue(Context *c, ValidatorReturnType *result, const char *validatorName) const
I a defValKey has been set in the constructor, this will try to get the default value from the stash ...
QString genericValidationError(Context *c, const QVariant &errorData=QVariant()) const override
Returns a generic error message if validation failed.