Cutelyst  2.3.0
validatorfilesize.cpp
1 /*
2  * Copyright (C) 2018 Matthias Fehring <kontakt@buschmann23.de>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "validatorfilesize_p.h"
20 #include <cmath>
21 #include <limits>
22 
23 using namespace Cutelyst;
24 
25 ValidatorFileSize::ValidatorFileSize(const QString &field, Option option, const QVariant &min, const QVariant &max, const ValidatorMessages &messages, const QString &defValKey) :
26  ValidatorRule(*new ValidatorFileSizePrivate(field, option, min, max, messages, defValKey))
27 {
28 
29 }
30 
32 {
33 
34 }
35 
36 bool ValidatorFileSize::validate(const QString &value, double min, double max, Cutelyst::ValidatorFileSize::Option option, const QLocale &locale, double *fileSize)
37 {
38  bool valid = true;
39 
40  QString digitPart;
41  QString symbolPart;
42  bool decimalPointFound = false;
43  const QChar decimalPoint = locale.decimalPoint();
44  int multiplier = 0;
45  bool binary = false;
46  bool byteSignFound = false;
47  qint8 startsWith = 0; // 0 not set, -1 digit part, 1 symbol part
48 
49  for (const QChar ch : value) {
50  if (valid) {
51  const ushort &uc = ch.unicode();
52  if (((uc > 47) && (uc < 58)) || (ch == decimalPoint)) {
53  if (startsWith == 0) {
54  startsWith = -1;
55  }
56  if (ch == decimalPoint) {
57  if (decimalPointFound) {
58  valid = false;
59  break;
60  } else {
61  decimalPointFound = true;
62  }
63  }
64  if ((symbolPart.isEmpty() && (startsWith < 0)) || (!symbolPart.isEmpty() && (startsWith > 0))) {
65  digitPart.append(ch);
66  } else {
67  valid = false;
68  break;
69  }
70  } else if ((uc != 9) && (uc != 32)) { // not a digit or decimal point and not a space or tab
71  if (startsWith == 0) {
72  startsWith = 1;
73  }
74  if ((digitPart.isEmpty() && (startsWith > 0)) || (!digitPart.isEmpty() && (startsWith < 0))) {
75  switch (uc) {
76  case 75: // K
77  case 107: // k
78  {
79  if (multiplier > 0) {
80  valid = false;
81  } else {
82  multiplier = 1;
83  symbolPart.append(ch);
84  }
85  }
86  break;
87  case 77: // M
88  case 109: // m
89  {
90  if (multiplier > 0) {
91  valid = false;
92  } else {
93  multiplier = 2;
94  symbolPart.append(ch);
95  }
96  }
97  break;
98  case 71: // G
99  case 103: // g
100  {
101  if (multiplier > 0) {
102  valid = false;
103  } else {
104  multiplier = 3;
105  symbolPart.append(ch);
106  }
107  }
108  break;
109  case 84: // T
110  case 116: // t
111  {
112  if (multiplier > 0) {
113  valid = false;
114  } else {
115  multiplier = 4;
116  symbolPart.append(ch);
117  }
118  }
119  break;
120  case 80: // P
121  case 112: // p
122  {
123  if (multiplier > 0) {
124  valid = false;
125  } else {
126  multiplier = 5;
127  symbolPart.append(ch);
128  }
129  }
130  break;
131  case 69: // E
132  case 101: // e
133  {
134  if (multiplier > 0) {
135  valid = false;
136  } else {
137  multiplier = 6;
138  symbolPart.append(ch);
139  }
140  }
141  break;
142  case 90: // Z
143  case 122: // z
144  {
145  if (multiplier > 0) {
146  valid = false;
147  } else {
148  multiplier = 7;
149  symbolPart.append(ch);
150  }
151  }
152  break;
153  case 89: // Y
154  case 121: // y
155  {
156  if (multiplier > 0) {
157  valid = false;
158  } else {
159  multiplier = 8;
160  symbolPart.append(ch);
161  }
162  }
163  break;
164  case 73: // I
165  case 105: // i
166  {
167  if ((multiplier == 0) || binary) {
168  valid = false;
169  } else {
170  binary = true;
171  symbolPart.append(ch);
172  }
173  }
174  break;
175  case 66: // B
176  case 98: // b
177  {
178  if (byteSignFound) {
179  valid = false;
180  } else {
181  byteSignFound = true;
182  symbolPart.append(ch);
183  }
184  }
185  break;
186  case 9: // horizontal tab
187  case 32: // space
188  break;
189  default:
190  valid = false;
191  break;
192  }
193  } else {
194  valid = false;
195  break;
196  }
197  }
198  } else {
199  break;
200  }
201  }
202 
203  if ((option == OnlyBinary) && !binary) {
204  valid = false;
205  } else if ((option == OnlyDecimal) && binary) {
206  valid = false;
207  } else if (option == ForceBinary) {
208  binary = true;
209  } else if (option == ForceDecimal) {
210  binary = false;
211  }
212 
213  if (valid) {
214  bool ok = false;
215  double size = locale.toDouble(digitPart, &ok);
216  if (!ok) {
217  valid = false;
218  } else {
219  if (multiplier > 0) {
220  const double _mult = binary ? std::exp2(multiplier * 10) : std::pow(10.0, multiplier * 3);
221  size *= _mult;
222  }
223  if ((min >= 1.0) && (size < min)) {
224  valid = false;
225  }
226  if ((max >= 1.0) && (size > max)) {
227  valid = false;
228  }
229  if (valid && fileSize) {
230  *fileSize = size;
231  }
232  }
233  }
234 
235  return valid;
236 }
237 
239 {
240  ValidatorReturnType result;
241 
242  Q_D(const ValidatorFileSize);
243 
244  const QString v = value(params);
245 
246  if (!v.isEmpty()) {
247 
248  double min = -1;
249  double max = -1;
250  bool ok = true;
251  if (d->min.isValid()) {
252  min = d->extractDouble(c, params, d->min, &ok);
253  if (!ok) {
254  result.errorMessage = validationDataError(c, 0);
255  }
256  }
257 
258  if (ok && d->max.isValid()) {
259  max = d->extractDouble(c, params, d->max, &ok);
260  if (!ok) {
261  result.errorMessage = validationDataError(c, 1);
262  }
263  }
264 
265  if (ok) {
266  double size = 0;
267  if (ValidatorFileSize::validate(v, min, max, d->option, c->locale(), &size)) {
268  if (size < static_cast<double>(std::numeric_limits<qulonglong>::max())) {
269  result.value.setValue<qulonglong>(static_cast<qulonglong>(size + 0.5));
270  } else {
271  result.value.setValue<double>(size);
272  }
273  } else {
274  result.errorMessage = validationError(c);
275  }
276  }
277 
278  } else {
279  defaultValue(c, &result, "ValidatorFileSize");
280  }
281 
282  return result;
283 }
284 
285 QString ValidatorFileSize::genericValidationError(Context *c, const QVariant &errorData) const
286 {
287  QString error;
288  Q_D(const ValidatorFileSize);
289  Q_UNUSED(errorData)
290  const QString _label = label(c);
291  if (d->min.isValid() || d->max.isValid()) {
292  if (_label.isEmpty()) {
293  error = c->translate("Cutelyst::ValidatorFileSize", "Invalid file size or file size not within the allowed limits.");
294  } else {
295  error = c->translate("Cutelyst::ValidatorFileSize", "The value in the “%1” field is either not a valid file size or not within the allowed limits.").arg(_label);
296  }
297  } else {
298  if (_label.isEmpty()) {
299  error = c->translate("Cutelyst::ValidatorFileSize", "Invalid file size.");
300  } else {
301  error = c->translate("Cutelyst::ValidatorFileSize", "The “%1” field does not contain a valid file size.").arg(_label);
302  }
303  }
304 
305  return error;
306 }
307 
308 QString ValidatorFileSize::genericValidationDataError(Context *c, const QVariant &errorData) const
309 {
310  QString error;
311 
312  const QString _label = label(c);
313 
314  const int sizeType = errorData.toInt();
315 
316  if (sizeType == 0) { // minimum file size
317  if (_label.isEmpty()) {
318  error = c->translate("Cutelyst::ValidatorFileSize", "The minimum file size comparison value is not valid.");
319  } else {
320  error = c->translate("Cutelyst::ValidatorFileSize", "The minimum file size comparison value for the “%1” field is not valid.").arg(_label);
321  }
322  } else {
323  if (_label.isEmpty()) {
324  error = c->translate("Cutelyst::ValidatorFileSize", "The maximum file size comparison value is not valid.");
325  } else {
326  error = c->translate("Cutelyst::ValidatorFileSize", "The maximum file size comparison value for the “%1” field is not valid.").arg(_label);
327  }
328  }
329 
330  return error;
331 }
332 
333 void ValidatorFileSize::inputPattern(Context *c, const QString &stashKey)
334 {
335  Q_ASSERT(c);
336  c->setStash(stashKey, c->locale().textDirection() == Qt::LeftToRight ? QStringLiteral("^\\d+[,.٫]?\\d*\\s*[KkMmGgTt]?[Ii]?[Bb]?") : QStringLiteral("[KkMmGgTt]?[Ii]?[Bb]?\\s*\\d+[,.٫]?\\d*"));
337 }
QMap< QString, QString > ParamsMultiMap
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:207
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.
QLocale locale() const
Definition: context.cpp:390
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:50
QString translate(const char *context, const char *sourceText, const char *disambiguation=nullptr, int n=-1) const
Definition: context.cpp:414
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.
QString label(Context *c) const
Returns the human readable field label used for generic error messages.
~ValidatorFileSize()
Deconstructs the file size validator.
Checks if the input field contains a valid file size string like 1.5 GB.
QString value(const ParamsMultiMap &params) const
Returns the value of the field from the input params.
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.
Contains the result of a single input parameter validation.
Definition: validatorrule.h:62
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.