Line data Source code
1 : /**
2 : Copyright (c) 2016-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPDbField.h"
25 : #include "SPMemUuid.h"
26 : #include "SPString.h"
27 : #include "SPValid.h"
28 : #include "SPCrypto.h"
29 : #include "SPDbScheme.h"
30 :
31 : #ifdef _MSC_VER
32 : #define strncasecmp _strnicmp
33 : #define strcasecmp _stricmp
34 : #endif
35 :
36 : namespace STAPPLER_VERSIONIZED stappler::db {
37 :
38 75 : AutoFieldScheme::AutoFieldScheme(const Scheme &s, ReqVec &&vec, ViewLinkageFn &&fn, ReqVec &&lvec)
39 75 : : scheme(s), requiresForAuto(std::move(vec)), linkage(std::move(fn)), requiresForLinking(std::move(lvec)) { }
40 :
41 0 : AutoFieldScheme::AutoFieldScheme(const Scheme &s, ReqVec &&vec, ReqVec &&lvec)
42 0 : : scheme(s), requiresForAuto(std::move(vec)), linkage(nullptr), requiresForLinking(std::move(lvec)) { }
43 :
44 37375 : bool Field::Slot::isProtected() const {
45 37375 : return hasFlag(Flags::Protected) || hasFlag(Flags::Admin);
46 : }
47 :
48 975 : bool Field::isReference() const {
49 975 : if (slot->type == Type::Object || slot->type == Type::Set) {
50 975 : auto ref = static_cast<const FieldObject *>(slot);
51 975 : return ref->onRemove == RemovePolicy::Reference || ref->onRemove == RemovePolicy::StrongReference;
52 : }
53 0 : return false;
54 : }
55 :
56 28625 : const Scheme * Field::getForeignScheme() const {
57 28625 : if (slot->type == Type::Object || slot->type == Type::Set) {
58 675 : auto ref = static_cast<const FieldObject *>(slot);
59 675 : return ref->scheme;
60 27950 : } else if (slot->type == Type::View) {
61 150 : auto ref = static_cast<const FieldView *>(slot);
62 150 : return ref->scheme;
63 : }
64 27800 : return nullptr;
65 : }
66 :
67 475 : bool Field::transform(const Scheme &scheme, int64_t id, Value &val, bool isCreate) const {
68 475 : return transform(scheme, Value(id), val, isCreate);
69 : }
70 :
71 44275 : bool Field::transform(const Scheme &scheme, const Value &obj, Value &val, bool isCreate) const {
72 44275 : if (slot->writeFilterFn) {
73 25 : if (!slot->writeFilterFn(scheme, obj, val, isCreate)) {
74 0 : return false;
75 : }
76 : }
77 44275 : if (slot->transformValue(scheme, obj, val, isCreate)) {
78 41775 : return true;
79 : }
80 2500 : return false;
81 : }
82 :
83 3300 : Value Field::getTypeDesc() const {
84 3300 : Value ret;
85 3300 : if (slot->flags != Flags::None) {
86 1050 : auto &f = ret.emplace("flags");
87 1050 : if ((slot->flags & Flags::Required) != Flags::None) { f.addString("required"); }
88 1050 : if ((slot->flags & Flags::Protected) != Flags::None) { f.addString("protected"); }
89 1050 : if ((slot->flags & Flags::ReadOnly) != Flags::None) { f.addString("readonly"); }
90 1050 : if ((slot->flags & Flags::Reference) != Flags::None) { f.addString("reference"); }
91 1050 : if ((slot->flags & Flags::Unique) != Flags::None) { f.addString("unique"); }
92 1050 : if ((slot->flags & Flags::AutoCTime) != Flags::None) { f.addString("auto-ctime"); }
93 1050 : if ((slot->flags & Flags::AutoMTime) != Flags::None) { f.addString("auto-mtime"); }
94 1050 : if ((slot->flags & Flags::AutoUser) != Flags::None) { f.addString("auto-user"); }
95 1050 : if ((slot->flags & Flags::Indexed) != Flags::None) { f.addString("indexed"); }
96 1050 : if ((slot->flags & Flags::Admin) != Flags::None) { f.addString("admin"); }
97 1050 : if ((slot->flags & Flags::ForceInclude) != Flags::None) { f.addString("forceinclude"); }
98 : }
99 :
100 3300 : if (slot->transform != Transform::None) {
101 400 : switch (slot->transform) {
102 0 : case Transform::Text: ret.setString("text", "transform"); break;
103 25 : case Transform::Identifier: ret.setString("identifier", "transform"); break;
104 125 : case Transform::Alias: ret.setString("alias", "transform"); break;
105 25 : case Transform::Url: ret.setString("url", "transform"); break;
106 100 : case Transform::Email: ret.setString("email", "transform"); break;
107 0 : case Transform::Number: ret.setString("number", "transform"); break;
108 0 : case Transform::Hexadecimial: ret.setString("hexadecimal", "transform"); break;
109 0 : case Transform::Base64: ret.setString("base64", "transform"); break;
110 0 : case Transform::Uuid: ret.setString("uuid", "transform"); break;
111 50 : case Transform::PublicKey: ret.setString("publickey", "transform"); break;
112 50 : case Transform::Password: ret.setString("password", "transform"); break;
113 25 : default: break;
114 : }
115 : }
116 :
117 3300 : if (slot->defaultFn) {
118 25 : ret.setString("(functional)", "default");
119 3275 : } else if (slot->def) {
120 100 : ret.setValue(slot->def, "default");
121 : }
122 :
123 3300 : switch (slot->type) {
124 0 : case Type::None: ret.setString("none", "type"); break;
125 700 : case Type::Integer: ret.setString("integer", "type"); break;
126 25 : case Type::Float: ret.setString("float", "type"); break;
127 150 : case Type::Boolean: ret.setString("boolean", "type"); break;
128 900 : case Type::Text:
129 900 : ret.setString("text", "type");
130 900 : if (auto t = static_cast<const FieldText *>(slot)) {
131 900 : ret.setInteger(t->minLength, "min");
132 900 : ret.setInteger(t->maxLength, "max");
133 : }
134 900 : break;
135 150 : case Type::Bytes:
136 150 : ret.setString("bytes", "type");
137 150 : if (auto t = static_cast<const FieldText *>(slot)) {
138 150 : ret.setInteger(t->minLength, "min");
139 150 : ret.setInteger(t->maxLength, "max");
140 : }
141 150 : break;
142 250 : case Type::Data: ret.setString("data", "type"); break;
143 225 : case Type::Extra:
144 225 : ret.setString("extra", "type");
145 225 : if (auto e = static_cast<const FieldExtra *>(slot)) {
146 225 : auto &f = ret.emplace("fields");
147 775 : for (auto &it : e->fields) {
148 550 : f.setValue(it.second.getTypeDesc(), it.first);
149 : }
150 : }
151 225 : break;
152 175 : case Type::Object:
153 175 : ret.setString("object", "type");
154 175 : if (auto o = static_cast<const FieldObject *>(slot)) {
155 175 : if (o->scheme) {
156 175 : ret.setString(o->scheme->getName(), "scheme");
157 : }
158 175 : switch (o->onRemove) {
159 25 : case RemovePolicy::Cascade: ret.setString("cascade", "onRemove"); break;
160 0 : case RemovePolicy::Restrict: ret.setString("cascade", "onRemove"); break;
161 0 : case RemovePolicy::Reference: ret.setString("reference", "onRemove"); break;
162 25 : case RemovePolicy::StrongReference: ret.setString("strongReference", "onRemove"); break;
163 125 : case RemovePolicy::Null: ret.setString("null", "onRemove"); break;
164 : }
165 :
166 175 : switch (o->linkage) {
167 150 : case Linkage::Auto: ret.setString("auto", "linkage"); break;
168 25 : case Linkage::Manual: ret.setString("manual", "linkage"); break;
169 0 : case Linkage::None: ret.setString("none", "linkage"); break;
170 : }
171 : }
172 175 : break;
173 150 : case Type::Set:
174 150 : ret.setString("set", "type");
175 150 : if (auto o = static_cast<const FieldObject *>(slot)) {
176 150 : if (o->scheme) {
177 150 : ret.setString(o->scheme->getName(), "scheme");
178 : }
179 150 : switch (o->onRemove) {
180 0 : case RemovePolicy::Cascade: ret.setString("cascade", "onRemove"); break;
181 0 : case RemovePolicy::Restrict: ret.setString("cascade", "onRemove"); break;
182 25 : case RemovePolicy::Reference: ret.setString("reference", "onRemove"); break;
183 50 : case RemovePolicy::StrongReference: ret.setString("strongReference", "onRemove"); break;
184 75 : case RemovePolicy::Null: ret.setString("null", "onRemove"); break;
185 : }
186 :
187 150 : switch (o->linkage) {
188 125 : case Linkage::Auto: ret.setString("auto", "linkage"); break;
189 25 : case Linkage::Manual: ret.setString("manual", "linkage"); break;
190 0 : case Linkage::None: ret.setString("none", "linkage"); break;
191 : }
192 : }
193 150 : break;
194 100 : case Type::Array:
195 100 : ret.setString("array", "type");
196 100 : if (auto a = static_cast<const FieldArray *>(slot)) {
197 100 : ret.setValue(a->tfield.getTypeDesc(), "field");
198 : }
199 100 : break;
200 75 : case Type::File:
201 75 : ret.setString("file", "type");
202 75 : if (auto f = static_cast<const FieldFile *>(slot)) {
203 75 : ret.setInteger(f->maxSize, "maxFileSize");
204 75 : if (!f->allowedTypes.empty()) {
205 0 : auto &t = ret.emplace("allowed");
206 0 : for (auto &it : f->allowedTypes) {
207 0 : t.addString(it);
208 : }
209 : }
210 : }
211 75 : break;
212 200 : case Type::Image:
213 200 : if (auto f = static_cast<const FieldImage *>(slot)) {
214 200 : if (f->primary) {
215 50 : ret.setString("image", "type");
216 50 : ret.setInteger(f->maxSize, "maxFileSize");
217 50 : if (!f->allowedTypes.empty()) {
218 0 : auto &t = ret.emplace("allowed");
219 0 : for (auto &it : f->allowedTypes) {
220 0 : t.addString(it);
221 : }
222 : }
223 :
224 50 : auto &min = ret.emplace("minImageSize");
225 50 : min.setInteger(f->minImageSize.width, "width");
226 50 : min.setInteger(f->minImageSize.width, "height");
227 50 : switch (f->minImageSize.policy) {
228 0 : case ImagePolicy::Resize: min.setString("resize", "policy"); break;
229 50 : case ImagePolicy::Reject: min.setString("resize", "policy"); break;
230 : }
231 :
232 50 : auto &max = ret.emplace("maxImageSize");
233 50 : max.setInteger(f->maxImageSize.width, "width");
234 50 : max.setInteger(f->maxImageSize.width, "height");
235 50 : switch (f->maxImageSize.policy) {
236 50 : case ImagePolicy::Resize: max.setString("resize", "policy"); break;
237 0 : case ImagePolicy::Reject: max.setString("resize", "policy"); break;
238 : }
239 :
240 50 : if (!f->thumbnails.empty()) {
241 50 : auto &t = ret.emplace("thumbnails");
242 200 : for (auto &it : f->thumbnails) {
243 150 : auto &tb = t.emplace(it.name);
244 150 : tb.setInteger(it.width, "width");
245 150 : tb.setInteger(it.height, "height");
246 : }
247 : }
248 : }
249 : }
250 200 : break;
251 50 : case Type::View:
252 50 : ret.setString("view", "type");
253 50 : if (auto v = static_cast<const FieldView *>(slot)) {
254 50 : if (v->scheme) {
255 50 : ret.setString(v->scheme->getName(), "scheme");
256 : }
257 :
258 50 : if (!v->requireFields.empty()) {
259 25 : auto &f = ret.emplace("requires");
260 50 : for (auto &it : v->requireFields) {
261 25 : f.addString(it);
262 : }
263 : }
264 :
265 50 : if (v->delta) {
266 25 : ret.setBool(true, "delta");
267 : }
268 : }
269 50 : break;
270 150 : default: break;
271 : }
272 :
273 3300 : if (!slot->documentation.empty()) {
274 0 : ret.setString(slot->documentation, "documentation");
275 : }
276 :
277 3300 : return ret;
278 0 : }
279 :
280 42200 : bool Field::Slot::hasDefault() const {
281 42200 : return defaultFn || !def.isNull() || (transform == Transform::Uuid && type == Type::Bytes);
282 : }
283 :
284 250 : Value Field::Slot::getDefault(const Value &patch) const {
285 250 : if (defaultFn) {
286 75 : return defaultFn(patch);
287 175 : } else if (transform == Transform::Uuid && type == Type::Bytes) {
288 0 : return Value(stappler::memory::uuid::generate().bytes());
289 : } else {
290 175 : return def;
291 : }
292 : }
293 :
294 20575 : bool Field::Slot::transformValue(const Scheme &scheme, const Value &obj, Value &val, bool isCreate) const {
295 20575 : if (!val.isBasicType() && type != Type::Data) {
296 0 : return false;
297 : }
298 :
299 20575 : switch (type) {
300 4575 : case Type::Data: break;
301 10625 : case Type::Integer: val.setInteger(val.asInteger()); break;
302 2500 : case Type::Float: val.setDouble(val.asDouble()); break;
303 2875 : case Type::Boolean:
304 2875 : if (val.isString()) {
305 0 : auto &str = val.getString();
306 0 : if (str == "1" || str == "on" || str == "true") {
307 0 : val.setBool(true);
308 : } else {
309 0 : val.setBool(false);
310 : }
311 : } else {
312 2875 : val.setBool(val.asBool());
313 : }
314 2875 : break;
315 0 : case Type::Object:
316 0 : if (val.isBasicType()) {
317 0 : val.setInteger(val.asInteger());
318 : } else {
319 0 : return false;
320 : }
321 0 : break;
322 0 : case Type::Set:
323 0 : if (!val.isArray()) {
324 0 : return false;
325 : }
326 0 : break;
327 0 : case Type::File:
328 : case Type::Image:
329 0 : if (val.isInteger()) {
330 0 : return true;
331 : }
332 0 : break;
333 0 : default:
334 0 : return false;
335 : break;
336 : }
337 :
338 20575 : return true;
339 : }
340 :
341 0 : void Field::Slot::hash(StringStream &stream, ValidationLevel l) const {
342 0 : if (l == ValidationLevel::NamesAndTypes) {
343 0 : stream << name << stappler::toInt(type);
344 : } else {
345 0 : stream << name << stappler::toInt(flags) << stappler::toInt(type) << stappler::toInt(transform);
346 : }
347 0 : }
348 :
349 10325 : bool FieldText::transformValue(const Scheme &scheme, const Value &obj, Value &val, bool isCreate) const {
350 10325 : switch (type) {
351 7850 : case Type::Text: {
352 7850 : if (!val.isBasicType()) {
353 0 : return false;
354 : }
355 7850 : if (!val.isString()) {
356 100 : if (val.isBool()) {
357 0 : val.setValue(Value());
358 0 : return true;
359 : } else {
360 100 : val.setString(val.asString());
361 : }
362 : }
363 7850 : if (val.isString()) {
364 7850 : auto &str = val.getString();
365 7850 : if (str.size() < minLength || str.size() > maxLength) {
366 75 : if (str.size() == 0) {
367 50 : val.setValue(Value());
368 50 : return true;
369 : }
370 25 : return false;
371 : }
372 : }
373 7775 : auto &str = val.getString();
374 7775 : if (str.empty()) {
375 0 : if (minLength == 0) {
376 0 : return true;
377 : } else {
378 0 : val.setValue(Value());
379 0 : return true;
380 : }
381 : }
382 7775 : switch (transform) {
383 5100 : case Transform::None:
384 : case Transform::Text:
385 5100 : if ( !stappler::valid::validateText(str)) {
386 0 : return false;
387 : }
388 5100 : break;
389 2650 : case Transform::Identifier:
390 : case Transform::Alias:
391 2650 : if (!stappler::valid::validateIdentifier(str)) {
392 0 : return false;
393 : }
394 2650 : break;
395 25 : case Transform::Url:
396 25 : if (!stappler::valid::validateUrl(str)) {
397 0 : return false;
398 : }
399 25 : break;
400 0 : case Transform::Email:
401 0 : if (!stappler::valid::validateEmail(str)) {
402 0 : return false;
403 : }
404 0 : break;
405 0 : case Transform::Number:
406 0 : if (!stappler::valid::validateNumber(str)) {
407 0 : return false;
408 : }
409 0 : break;
410 0 : case Transform::Hexadecimial:
411 0 : if (!stappler::valid::validateHexadecimial(str)) {
412 0 : return false;
413 : }
414 0 : break;
415 0 : case Transform::Base64:
416 0 : if (!stappler::valid::validateBase64(str)) {
417 0 : return false;
418 : }
419 0 : break;
420 0 : default:
421 0 : break;
422 : }
423 7775 : break;
424 : }
425 2475 : case Type::Bytes:
426 2475 : if (val.isString()) {
427 0 : auto &str = val.getString();
428 0 : if (str.size() > 4 && strncasecmp(str.data(), "hex:", 4) == 0) {
429 0 : auto len = (str.size() - 4) / 2;
430 0 : if (len < minLength || len > maxLength) {
431 0 : return false;
432 : }
433 :
434 0 : val.setBytes(stappler::base16::decode<Interface>(StringView(str.data() + 4, str.size() - 4)));
435 0 : } else if (str.size() > 7 && strncasecmp(str.data(), "base64:", 7) == 0) {
436 0 : auto len = stappler::base64::decodeSize(str.size() - 7);
437 0 : if (len < minLength || len > maxLength) {
438 0 : return false;
439 : }
440 :
441 0 : val.setBytes(stappler::base64::decode<Interface>(StringView(str.data() + 7, str.size() - 7)));
442 0 : } else if (transform == Transform::Uuid) {
443 0 : auto b = stappler::memory::uuid(str).bytes();
444 0 : if (b.empty()) {
445 0 : return false;
446 : }
447 :
448 0 : val.setBytes(std::move(b));
449 0 : } else if (transform == Transform::PublicKey) {
450 0 : if (StringView(str).starts_with("ssh-")) {
451 0 : crypto::PublicKey key;
452 0 : if (key.importOpenSSH(str)) {
453 0 : return key.exportDer([&] (BytesView bytes) {
454 0 : val.setBytes(bytes);
455 0 : });
456 : }
457 0 : return false;
458 0 : }
459 :
460 0 : stappler::crypto::PublicKey pk(BytesView((const uint8_t *)str.data(), str.size()));
461 0 : if (!pk) {
462 0 : return false;
463 : }
464 :
465 0 : return pk.exportDer([&] (BytesView bytes) {
466 0 : val.setBytes(bytes);
467 0 : });
468 0 : }
469 2475 : } else if (val.isBytes()) {
470 2475 : auto &bytes = val.getBytes();
471 2475 : if (transform == Transform::PublicKey) {
472 0 : stappler::crypto::PublicKey pk(bytes);
473 0 : if (!pk) {
474 0 : return false;
475 : }
476 :
477 0 : return pk.exportDer([&] (BytesView bytes) {
478 0 : val.setBytes(bytes);
479 0 : });
480 0 : }
481 :
482 2475 : if (bytes.size() < minLength || bytes.size() > maxLength) {
483 25 : return false;
484 : }
485 : }
486 2450 : break;
487 0 : default:
488 0 : return false;
489 : break;
490 : }
491 10225 : return true;
492 : }
493 :
494 0 : void FieldText::hash(StringStream &stream, ValidationLevel l) const {
495 0 : Slot::hash(stream, l);
496 0 : if (l == ValidationLevel::Full) {
497 0 : stream << minLength << maxLength;
498 : }
499 0 : }
500 :
501 :
502 100 : bool FieldPassword::transformValue(const Scheme &scheme, const Value &, Value &val, bool isCreate) const {
503 100 : if (!val.isString() && !val.isBytes()) {
504 0 : return false;
505 : }
506 :
507 100 : if (val.isString()) {
508 100 : auto &str = val.getString();
509 100 : if (str.size() < minLength || str.size() > maxLength) {
510 0 : return false;
511 : }
512 100 : val.setBytes(stappler::valid::makePassword<Interface>(str, salt));
513 : }
514 :
515 100 : return true;
516 : }
517 :
518 0 : void FieldPassword::hash(StringStream &stream, ValidationLevel l) const {
519 0 : Slot::hash(stream, l);
520 0 : if (l == ValidationLevel::Full) {
521 0 : stream << minLength << maxLength << salt;
522 : }
523 0 : }
524 :
525 :
526 3325 : bool FieldExtra::hasDefault() const {
527 12625 : for (auto & it : fields) {
528 9300 : if (it.second.hasDefault()) {
529 0 : return true;
530 : }
531 : }
532 3325 : return false;
533 : }
534 :
535 0 : Value FieldExtra::getDefault(const Value &patch) const {
536 0 : if (def) {
537 0 : return def;
538 0 : } else if (defaultFn) {
539 0 : return defaultFn(patch);
540 : }
541 :
542 0 : Value ret;
543 0 : for (auto & it : fields) {
544 0 : if (it.second.hasDefault()) {
545 0 : ret.setValue(it.second.getDefault(patch), it.first);
546 : }
547 : }
548 0 : return ret;
549 0 : }
550 :
551 2500 : bool FieldExtra::transformValue(const Scheme &scheme, const Value &obj, Value &v, bool isCreate) const {
552 16000 : auto processValue = [&, this] (Value &val) -> bool {
553 2500 : if (!val.isDictionary()) {
554 0 : return false;
555 : }
556 2500 : auto &dict = val.asDict();
557 2500 : auto it = dict.begin();
558 9250 : while (it != dict.end()) {
559 6750 : auto f_it = fields.find(it->first);
560 6750 : if (f_it != fields.end()) {
561 6750 : if (it->second.isNull()) {
562 0 : it ++;
563 6750 : } else if (!f_it->second.transform(scheme, obj, it->second, isCreate)) {
564 50 : it = val.getDict().erase(it);
565 : } else {
566 6700 : it ++;
567 : }
568 : } else {
569 0 : it = val.getDict().erase(it);
570 : }
571 : }
572 :
573 2500 : if (!val.empty()) {
574 2500 : return true;
575 : }
576 :
577 0 : return false;
578 2500 : };
579 :
580 2500 : if (transform == Transform::Array) {
581 0 : if (!v.isArray()) {
582 0 : return false;
583 : }
584 :
585 0 : auto &arr = v.asArray();
586 :
587 0 : auto it = arr.begin();
588 0 : while (it != arr.end()) {
589 0 : if (processValue(*it)) {
590 0 : ++ it;
591 : } else {
592 0 : it = arr.erase(it);
593 : }
594 : }
595 :
596 0 : if (!v.empty()) {
597 0 : return true;
598 : }
599 :
600 0 : return false;
601 : } else {
602 2500 : return processValue(v);
603 : }
604 : }
605 :
606 0 : void FieldExtra::hash(StringStream &stream, ValidationLevel l) const {
607 0 : Slot::hash(stream, l);
608 0 : if (l == ValidationLevel::Full) {
609 0 : for (auto &it : fields) {
610 0 : it.second.hash(stream, l);
611 : }
612 : }
613 0 : }
614 :
615 0 : void FieldFile::hash(StringStream &stream, ValidationLevel l) const {
616 0 : Slot::hash(stream, l);
617 0 : if (l == ValidationLevel::Full) {
618 0 : stream << maxSize;
619 0 : for (auto &it : allowedTypes) {
620 0 : stream << it;
621 : }
622 : }
623 0 : }
624 :
625 0 : void FieldImage::hash(StringStream &stream, ValidationLevel l) const {
626 0 : Slot::hash(stream, l);
627 0 : if (l == ValidationLevel::Full) {
628 0 : stream << maxSize << primary
629 0 : << maxImageSize.width << maxImageSize.height << stappler::toInt(maxImageSize.policy)
630 0 : << minImageSize.width << minImageSize.height << stappler::toInt(minImageSize.policy);
631 0 : for (auto &it : allowedTypes) {
632 0 : stream << it;
633 : }
634 : }
635 0 : }
636 :
637 450 : bool FieldObject::transformValue(const Scheme &scheme, const Value &obj, Value &val, bool isCreate) const {
638 450 : switch (type) {
639 325 : case Type::Object:
640 325 : if (val.isBasicType()) {
641 325 : val.setInteger(val.asInteger());
642 325 : return true;
643 0 : } else if (val.isDictionary()) {
644 0 : return true; // pass to object's scheme
645 : }
646 0 : break;
647 125 : case Type::Set:
648 125 : if (val.isArray()) {
649 125 : auto &arr = val.asArray();
650 125 : auto it = arr.begin();
651 350 : while (it != arr.end()) {
652 225 : if (it->isBasicType()) {
653 225 : auto tmp = it->asInteger();
654 225 : if (tmp) {
655 225 : it->setInteger(tmp);
656 : }
657 0 : } else if (it->isArray()) {
658 0 : it = arr.erase(it);
659 0 : continue;
660 : }
661 225 : ++ it;
662 : }
663 125 : return true;
664 0 : } else if (val.isInteger()) {
665 0 : return true;
666 : }
667 0 : break;
668 0 : default:
669 0 : return false;
670 : break;
671 : }
672 0 : return false;
673 : }
674 :
675 0 : void FieldObject::hash(StringStream &stream, ValidationLevel l) const {
676 0 : Slot::hash(stream, l);
677 0 : if (l == ValidationLevel::Full) {
678 0 : if (scheme) {
679 0 : stream << scheme->getName();
680 : }
681 0 : stream << stappler::toInt(onRemove) << stappler::toInt(linkage) << link;
682 : }
683 0 : }
684 :
685 400 : bool FieldArray::transformValue(const Scheme &scheme, const Value &obj, Value &val, bool isCreate) const {
686 400 : if (val.isArray()) {
687 400 : if (tfield) {
688 400 : auto &arr = val.asArray();
689 400 : auto it = arr.begin();
690 1000 : while (it != arr.end()) {
691 600 : if (!tfield.transform(scheme, obj, *it, isCreate)) {
692 0 : it = arr.erase(it);
693 : } else {
694 600 : ++ it;
695 : }
696 : }
697 : }
698 400 : return true;
699 : }
700 0 : return false;
701 : }
702 :
703 0 : void FieldArray::hash(StringStream &stream, ValidationLevel l) const {
704 0 : Slot::hash(stream, l);
705 0 : if (l == ValidationLevel::Full) {
706 0 : tfield.hash(stream, l);
707 : }
708 0 : }
709 :
710 250 : FullTextQuery FieldFullTextView::parseQuery(const Value &data) const {
711 250 : if (queryFn) {
712 0 : return queryFn(data);
713 250 : } else if (data.isString() && searchConfiguration) {
714 250 : return searchConfiguration->parseQuery(data.getString());
715 : }
716 0 : return FullTextQuery();
717 : }
718 :
719 25 : bool FieldVirtual::transformValue(const Scheme &scheme, const Value &obj, Value &value, bool isCreate) const {
720 25 : if (!writeFn) {
721 0 : log::error("FieldVirtual", "Fail to write into virtual fields", data::EncodeFormat::Pretty, Value({
722 0 : stappler::pair("object", Value(obj)),
723 0 : stappler::pair("value", Value(value)),
724 0 : }));
725 0 : return false;
726 : }
727 :
728 25 : return true;
729 : }
730 :
731 : }
|