Coverage Report

Created: 2023-06-07 06:59

/src/valijson/include/valijson/constraints/concrete_constraints.hpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 *
4
 * @brief   Class definitions to support JSON Schema constraints
5
 *
6
 * This file contains class definitions for all of the constraints required to
7
 * support JSON Schema. These classes all inherit from the BasicConstraint
8
 * template class, which implements the common parts of the Constraint
9
 * interface.
10
 *
11
 * @see BasicConstraint
12
 * @see Constraint
13
 */
14
15
#pragma once
16
17
#include <limits>
18
#include <map>
19
#include <set>
20
#include <string>
21
#include <vector>
22
23
#include <valijson/constraints/basic_constraint.hpp>
24
#include <valijson/internal/custom_allocator.hpp>
25
#include <valijson/internal/frozen_value.hpp>
26
#include <valijson/schema.hpp>
27
#include <valijson/exceptions.hpp>
28
29
#ifdef _MSC_VER
30
#pragma warning( push )
31
#pragma warning( disable : 4702 )
32
#endif
33
34
namespace valijson {
35
36
class ValidationResults;
37
38
namespace constraints {
39
40
/**
41
 * @brief  Represents an 'allOf' constraint.
42
 *
43
 * An allOf constraint provides a collection of sub-schemas that a value must
44
 * validate against. If a value fails to validate against any of these sub-
45
 * schemas, then validation fails.
46
 */
47
class AllOfConstraint: public BasicConstraint<AllOfConstraint>
48
{
49
public:
50
    AllOfConstraint()
51
0
      : m_subschemas(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
52
53
    AllOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
54
      : BasicConstraint(allocFn, freeFn),
55
0
        m_subschemas(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
56
57
    void addSubschema(const Subschema *subschema)
58
0
    {
59
0
        m_subschemas.push_back(subschema);
60
0
    }
61
62
    template<typename FunctorType>
63
    void applyToSubschemas(const FunctorType &fn) const
64
    {
65
        unsigned int index = 0;
66
        for (const Subschema *subschema : m_subschemas) {
67
            if (!fn(index, subschema)) {
68
                return;
69
            }
70
71
            index++;
72
        }
73
    }
74
75
private:
76
    typedef std::vector<const Subschema *, internal::CustomAllocator<const Subschema *>> Subschemas;
77
78
    /// Collection of sub-schemas, all of which must be satisfied
79
    Subschemas m_subschemas;
80
};
81
82
/**
83
 * @brief  Represents an 'anyOf' constraint
84
 *
85
 * An anyOf constraint provides a collection of sub-schemas that a value can
86
 * validate against. If a value validates against one of these sub-schemas,
87
 * then the validation passes.
88
 */
89
class AnyOfConstraint: public BasicConstraint<AnyOfConstraint>
90
{
91
public:
92
    AnyOfConstraint()
93
0
      : m_subschemas(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
94
95
    AnyOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
96
      : BasicConstraint(allocFn, freeFn),
97
0
        m_subschemas(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
98
99
    void addSubschema(const Subschema *subschema)
100
0
    {
101
0
        m_subschemas.push_back(subschema);
102
0
    }
103
104
    template<typename FunctorType>
105
    void applyToSubschemas(const FunctorType &fn) const
106
    {
107
        unsigned int index = 0;
108
        for (const Subschema *subschema : m_subschemas) {
109
            if (!fn(index, subschema)) {
110
                return;
111
            }
112
113
            index++;
114
        }
115
    }
116
117
private:
118
    typedef std::vector<const Subschema *, internal::CustomAllocator<const Subschema *>> Subschemas;
119
120
    /// Collection of sub-schemas, at least one of which must be satisfied
121
    Subschemas m_subschemas;
122
};
123
124
/**
125
 * @brief  Represents a combination 'if', 'then' and 'else' constraints
126
 *
127
 * The schema provided by an 'if' constraint is used as the expression for a conditional. When the
128
 * target validates against that schema, the 'then' subschema will be also be tested. Otherwise,
129
 * the 'else' subschema will be tested.
130
 */
131
class ConditionalConstraint: public BasicConstraint<ConditionalConstraint>
132
{
133
public:
134
    ConditionalConstraint()
135
      : m_ifSubschema(nullptr),
136
        m_thenSubschema(nullptr),
137
0
        m_elseSubschema(nullptr) { }
138
139
    ConditionalConstraint(CustomAlloc allocFn, CustomFree freeFn)
140
      : BasicConstraint(allocFn, freeFn),
141
        m_ifSubschema(nullptr),
142
        m_thenSubschema(nullptr),
143
0
        m_elseSubschema(nullptr) { }
144
145
    const Subschema * getIfSubschema() const
146
0
    {
147
0
        return m_ifSubschema;
148
0
    }
149
150
    const Subschema * getThenSubschema() const
151
0
    {
152
0
        return m_thenSubschema;
153
0
    }
154
155
    const Subschema * getElseSubschema() const
156
0
    {
157
0
        return m_elseSubschema;
158
0
    }
159
160
    void setIfSubschema(const Subschema *subschema)
161
0
    {
162
0
        m_ifSubschema = subschema;
163
0
    }
164
165
    void setThenSubschema(const Subschema *subschema)
166
0
    {
167
0
        m_thenSubschema = subschema;
168
0
    }
169
170
    void setElseSubschema(const Subschema *subschema)
171
0
    {
172
0
        m_elseSubschema = subschema;
173
0
    }
174
175
private:
176
    const Subschema *m_ifSubschema;
177
    const Subschema *m_thenSubschema;
178
    const Subschema *m_elseSubschema;
179
};
180
181
class ConstConstraint: public BasicConstraint<ConstConstraint>
182
{
183
public:
184
    ConstConstraint()
185
0
      : m_value(nullptr) { }
186
187
    ConstConstraint(CustomAlloc allocFn, CustomFree freeFn)
188
      : BasicConstraint(allocFn, freeFn),
189
0
        m_value(nullptr) { }
190
191
    ConstConstraint(const ConstConstraint &other)
192
      : BasicConstraint(other),
193
0
        m_value(other.m_value->clone()) { }
194
195
    adapters::FrozenValue * getValue() const
196
0
    {
197
0
        return m_value.get();
198
0
    }
199
200
    void setValue(const adapters::Adapter &value)
201
0
    {
202
0
        m_value = std::unique_ptr<adapters::FrozenValue>(value.freeze());
203
0
    }
204
205
private:
206
    std::unique_ptr<adapters::FrozenValue> m_value;
207
};
208
209
/**
210
 * @brief  Represents a 'contains' constraint
211
 *
212
 * A 'contains' constraint specifies a schema that must be satisfied by at least one
213
 * of the values in an array.
214
 */
215
class ContainsConstraint: public BasicConstraint<ContainsConstraint>
216
{
217
public:
218
    ContainsConstraint()
219
0
      : m_subschema(nullptr) { }
220
221
    ContainsConstraint(CustomAlloc allocFn, CustomFree freeFn)
222
      : BasicConstraint(allocFn, freeFn),
223
0
        m_subschema(nullptr) { }
224
225
    const Subschema * getSubschema() const
226
0
    {
227
0
        return m_subschema;
228
0
    }
229
230
    void setSubschema(const Subschema *subschema)
231
0
    {
232
0
        m_subschema = subschema;
233
0
    }
234
235
private:
236
    const Subschema *m_subschema;
237
};
238
239
/**
240
 * @brief  Represents a 'dependencies' constraint.
241
 *
242
 * A dependency constraint ensures that a given property is valid only if the
243
 * properties that it depends on are present.
244
 */
245
class DependenciesConstraint: public BasicConstraint<DependenciesConstraint>
246
{
247
public:
248
    DependenciesConstraint()
249
      : m_propertyDependencies(std::less<String>(), m_allocator),
250
        m_schemaDependencies(std::less<String>(), m_allocator)
251
0
    { }
252
253
    DependenciesConstraint(CustomAlloc allocFn, CustomFree freeFn)
254
      : BasicConstraint(allocFn, freeFn),
255
        m_propertyDependencies(std::less<String>(), m_allocator),
256
        m_schemaDependencies(std::less<String>(), m_allocator)
257
0
    { }
258
259
    template<typename StringType>
260
    DependenciesConstraint & addPropertyDependency(
261
            const StringType &propertyName,
262
            const StringType &dependencyName)
263
0
    {
264
0
        const String key(propertyName.c_str(), m_allocator);
265
0
        auto itr = m_propertyDependencies.find(key);
266
0
        if (itr == m_propertyDependencies.end()) {
267
0
            itr = m_propertyDependencies.insert(PropertyDependencies::value_type(
268
0
                    key, PropertySet(std::less<String>(), m_allocator))).first;
269
0
        }
270
271
0
        itr->second.insert(String(dependencyName.c_str(), m_allocator));
272
273
0
        return *this;
274
0
    }
275
276
    template<typename StringType, typename ContainerType>
277
    DependenciesConstraint & addPropertyDependencies(
278
            const StringType &propertyName,
279
            const ContainerType &dependencyNames)
280
0
    {
281
0
        const String key(propertyName.c_str(), m_allocator);
282
0
        auto itr = m_propertyDependencies.find(key);
283
0
        if (itr == m_propertyDependencies.end()) {
284
0
            itr = m_propertyDependencies.insert(PropertyDependencies::value_type(
285
0
                    key, PropertySet(std::less<String>(), m_allocator))).first;
286
0
        }
287
288
0
        typedef typename ContainerType::value_type ValueType;
289
0
        for (const ValueType &dependencyName : dependencyNames) {
290
0
            itr->second.insert(String(dependencyName.c_str(), m_allocator));
291
0
        }
292
293
0
        return *this;
294
0
    }
295
296
    template<typename StringType>
297
    DependenciesConstraint & addSchemaDependency(const StringType &propertyName, const Subschema *schemaDependency)
298
0
    {
299
0
        if (m_schemaDependencies.insert(SchemaDependencies::value_type(
300
0
                String(propertyName.c_str(), m_allocator),
301
0
                schemaDependency)).second) {
302
0
            return *this;
303
0
        }
304
305
0
        throwRuntimeError("Dependencies constraint already contains a dependent "
306
0
                "schema for the property '" + propertyName + "'");
307
0
    }
308
309
    template<typename FunctorType>
310
    void applyToPropertyDependencies(const FunctorType &fn) const
311
    {
312
        for (const PropertyDependencies::value_type &v : m_propertyDependencies) {
313
            if (!fn(v.first, v.second)) {
314
                return;
315
            }
316
        }
317
    }
318
319
    template<typename FunctorType>
320
    void applyToSchemaDependencies(const FunctorType &fn) const
321
    {
322
        for (const SchemaDependencies::value_type &v : m_schemaDependencies) {
323
            if (!fn(v.first, v.second)) {
324
                return;
325
            }
326
        }
327
    }
328
329
private:
330
    typedef std::set<String, std::less<String>, internal::CustomAllocator<String>> PropertySet;
331
332
    typedef std::map<String, PropertySet, std::less<String>,
333
            internal::CustomAllocator<std::pair<const String, PropertySet>>> PropertyDependencies;
334
335
    typedef std::map<String, const Subschema *, std::less<String>,
336
            internal::CustomAllocator<std::pair<const String, const Subschema *>>> SchemaDependencies;
337
338
    /// Mapping from property names to their property-based dependencies
339
    PropertyDependencies m_propertyDependencies;
340
341
    /// Mapping from property names to their schema-based dependencies
342
    SchemaDependencies m_schemaDependencies;
343
};
344
345
/**
346
 * @brief  Represents an 'enum' constraint
347
 *
348
 * An enum constraint provides a collection of permissible values for a JSON
349
 * node. The node will only validate against this constraint if it matches one
350
 * or more of the values in the collection.
351
 */
352
class EnumConstraint: public BasicConstraint<EnumConstraint>
353
{
354
public:
355
    EnumConstraint()
356
0
      : m_enumValues(Allocator::rebind<const EnumValue *>::other(m_allocator)) { }
357
358
    EnumConstraint(CustomAlloc allocFn, CustomFree freeFn)
359
      : BasicConstraint(allocFn, freeFn),
360
0
        m_enumValues(Allocator::rebind<const EnumValue *>::other(m_allocator)) { }
361
362
    EnumConstraint(const EnumConstraint &other)
363
      : BasicConstraint(other),
364
        m_enumValues(Allocator::rebind<const EnumValue *>::other(m_allocator))
365
0
    {
366
0
#if VALIJSON_USE_EXCEPTIONS
367
0
        try {
368
0
#endif
369
            // Clone individual enum values
370
0
            for (const EnumValue *otherValue : other.m_enumValues) {
371
0
                const EnumValue *value = otherValue->clone();
372
0
#if VALIJSON_USE_EXCEPTIONS
373
0
                try {
374
0
#endif
375
0
                    m_enumValues.push_back(value);
376
0
#if VALIJSON_USE_EXCEPTIONS
377
0
                } catch (...) {
378
0
                    delete value;
379
0
                    value = nullptr;
380
0
                    throw;
381
0
                }
382
0
            }
383
0
        } catch (...) {
384
            // Delete values already added to constraint
385
0
            for (const EnumValue *value : m_enumValues) {
386
0
                delete value;
387
0
            }
388
0
            throw;
389
0
#endif
390
0
        }
391
0
    }
392
393
    ~EnumConstraint() override
394
0
    {
395
0
        for (const EnumValue *value : m_enumValues) {
396
0
            delete value;
397
0
        }
398
0
    }
399
400
    void addValue(const adapters::Adapter &value)
401
0
    {
402
        // TODO: Freeze value using custom alloc/free functions
403
0
        m_enumValues.push_back(value.freeze());
404
0
    }
405
406
    void addValue(const adapters::FrozenValue &value)
407
0
    {
408
0
        // TODO: Clone using custom alloc/free functions
409
0
        m_enumValues.push_back(value.clone());
410
0
    }
411
412
    template<typename FunctorType>
413
    void applyToValues(const FunctorType &fn) const
414
    {
415
        for (const EnumValue *value : m_enumValues) {
416
            if (!fn(*value)) {
417
                return;
418
            }
419
        }
420
    }
421
422
private:
423
    typedef adapters::FrozenValue EnumValue;
424
425
    typedef std::vector<const EnumValue *, internal::CustomAllocator<const EnumValue *>> EnumValues;
426
427
    EnumValues m_enumValues;
428
};
429
430
/**
431
 * @brief  Represent a 'format' constraint
432
 *
433
 * A format constraint restricts the content of string values, as defined by a set of commonly used formats.
434
 *
435
 * As this is an optional feature in JSON Schema, unrecognised formats will be treated as valid for any string value.
436
 */
437
class FormatConstraint: public BasicConstraint<FormatConstraint>
438
{
439
public:
440
    FormatConstraint()
441
0
        : m_format() { }
442
443
    const std::string & getFormat() const
444
0
    {
445
0
        return m_format;
446
0
    }
447
448
    void setFormat(const std::string & format)
449
0
    {
450
0
        m_format = format;
451
0
    }
452
453
private:
454
    std::string m_format;
455
};
456
457
/**
458
 * @brief  Represents non-singular 'items' and 'additionalItems' constraints
459
 *
460
 * Unlike the SingularItemsConstraint class, this class represents an 'items'
461
 * constraint that specifies an array of sub-schemas, which should be used to
462
 * validate each item in an array, in sequence. It also represents an optional
463
 * 'additionalItems' sub-schema that should be used when an array contains
464
 * more values than there are sub-schemas in the 'items' constraint.
465
 *
466
 * The prefix 'Linear' comes from the fact that this class contains a list of
467
 * sub-schemas that corresponding array items must be validated against, and
468
 * this validation is performed linearly (i.e. in sequence).
469
 */
470
class LinearItemsConstraint: public BasicConstraint<LinearItemsConstraint>
471
{
472
public:
473
    LinearItemsConstraint()
474
      : m_itemSubschemas(Allocator::rebind<const Subschema *>::other(m_allocator)),
475
0
        m_additionalItemsSubschema(nullptr) { }
476
477
    LinearItemsConstraint(CustomAlloc allocFn, CustomFree freeFn)
478
      : BasicConstraint(allocFn, freeFn),
479
        m_itemSubschemas(Allocator::rebind<const Subschema *>::other(m_allocator)),
480
0
        m_additionalItemsSubschema(nullptr) { }
481
482
    void addItemSubschema(const Subschema *subschema)
483
0
    {
484
0
        m_itemSubschemas.push_back(subschema);
485
0
    }
486
487
    template<typename FunctorType>
488
    void applyToItemSubschemas(const FunctorType &fn) const
489
    {
490
        unsigned int index = 0;
491
        for (const Subschema *subschema : m_itemSubschemas) {
492
            if (!fn(index, subschema)) {
493
                return;
494
            }
495
496
            index++;
497
        }
498
    }
499
500
    const Subschema * getAdditionalItemsSubschema() const
501
0
    {
502
0
        return m_additionalItemsSubschema;
503
0
    }
504
505
    size_t getItemSubschemaCount() const
506
0
    {
507
0
        return m_itemSubschemas.size();
508
0
    }
509
510
    void setAdditionalItemsSubschema(const Subschema *subschema)
511
0
    {
512
0
        m_additionalItemsSubschema = subschema;
513
0
    }
514
515
private:
516
    typedef std::vector<const Subschema *, internal::CustomAllocator<const Subschema *>> Subschemas;
517
518
    Subschemas m_itemSubschemas;
519
520
    const Subschema* m_additionalItemsSubschema;
521
};
522
523
/**
524
 * @brief   Represents 'maximum' and 'exclusiveMaximum' constraints
525
 */
526
class MaximumConstraint: public BasicConstraint<MaximumConstraint>
527
{
528
public:
529
    MaximumConstraint()
530
      : m_maximum(std::numeric_limits<double>::infinity()),
531
0
        m_exclusiveMaximum(false) { }
532
533
    MaximumConstraint(CustomAlloc allocFn, CustomFree freeFn)
534
      : BasicConstraint(allocFn, freeFn),
535
        m_maximum(std::numeric_limits<double>::infinity()),
536
0
        m_exclusiveMaximum(false) { }
537
538
    bool getExclusiveMaximum() const
539
0
    {
540
0
        return m_exclusiveMaximum;
541
0
    }
542
543
    void setExclusiveMaximum(bool newExclusiveMaximum)
544
0
    {
545
0
        m_exclusiveMaximum = newExclusiveMaximum;
546
0
    }
547
548
    double getMaximum() const
549
0
    {
550
0
        return m_maximum;
551
0
    }
552
553
    void setMaximum(double newMaximum)
554
0
    {
555
0
        m_maximum = newMaximum;
556
0
    }
557
558
private:
559
    double m_maximum;
560
    bool m_exclusiveMaximum;
561
};
562
563
/**
564
 * @brief   Represents a 'maxItems' constraint
565
 */
566
class MaxItemsConstraint: public BasicConstraint<MaxItemsConstraint>
567
{
568
public:
569
    MaxItemsConstraint()
570
0
      : m_maxItems(std::numeric_limits<uint64_t>::max()) { }
571
572
    MaxItemsConstraint(CustomAlloc allocFn, CustomFree freeFn)
573
      : BasicConstraint(allocFn, freeFn),
574
0
        m_maxItems(std::numeric_limits<uint64_t>::max()) { }
575
576
    uint64_t getMaxItems() const
577
0
    {
578
0
        return m_maxItems;
579
0
    }
580
581
    void setMaxItems(uint64_t newMaxItems)
582
0
    {
583
0
        m_maxItems = newMaxItems;
584
0
    }
585
586
private:
587
    uint64_t m_maxItems;
588
};
589
590
/**
591
 * @brief   Represents a 'maxLength' constraint
592
 */
593
class MaxLengthConstraint: public BasicConstraint<MaxLengthConstraint>
594
{
595
public:
596
    MaxLengthConstraint()
597
0
      : m_maxLength(std::numeric_limits<uint64_t>::max()) { }
598
599
    MaxLengthConstraint(CustomAlloc allocFn, CustomFree freeFn)
600
      : BasicConstraint(allocFn, freeFn),
601
0
        m_maxLength(std::numeric_limits<uint64_t>::max()) { }
602
603
    uint64_t getMaxLength() const
604
0
    {
605
0
        return m_maxLength;
606
0
    }
607
608
    void setMaxLength(uint64_t newMaxLength)
609
0
    {
610
0
        m_maxLength = newMaxLength;
611
0
    }
612
613
private:
614
    uint64_t m_maxLength;
615
};
616
617
/**
618
 * @brief   Represents a 'maxProperties' constraint
619
 */
620
class MaxPropertiesConstraint: public BasicConstraint<MaxPropertiesConstraint>
621
{
622
public:
623
    MaxPropertiesConstraint()
624
0
      : m_maxProperties(std::numeric_limits<uint64_t>::max()) { }
625
626
    MaxPropertiesConstraint(CustomAlloc allocFn, CustomFree freeFn)
627
      : BasicConstraint(allocFn, freeFn),
628
0
        m_maxProperties(std::numeric_limits<uint64_t>::max()) { }
629
630
    uint64_t getMaxProperties() const
631
0
    {
632
0
        return m_maxProperties;
633
0
    }
634
635
    void setMaxProperties(uint64_t newMaxProperties)
636
0
    {
637
0
        m_maxProperties = newMaxProperties;
638
0
    }
639
640
private:
641
    uint64_t m_maxProperties;
642
};
643
644
/**
645
 * @brief   Represents 'minimum' and 'exclusiveMinimum' constraints
646
 */
647
class MinimumConstraint: public BasicConstraint<MinimumConstraint>
648
{
649
public:
650
    MinimumConstraint()
651
      : m_minimum(-std::numeric_limits<double>::infinity()),
652
0
        m_exclusiveMinimum(false) { }
653
654
    MinimumConstraint(CustomAlloc allocFn, CustomFree freeFn)
655
      : BasicConstraint(allocFn, freeFn),
656
        m_minimum(-std::numeric_limits<double>::infinity()),
657
0
        m_exclusiveMinimum(false) { }
658
659
    bool getExclusiveMinimum() const
660
0
    {
661
0
        return m_exclusiveMinimum;
662
0
    }
663
664
    void setExclusiveMinimum(bool newExclusiveMinimum)
665
0
    {
666
0
        m_exclusiveMinimum = newExclusiveMinimum;
667
0
    }
668
669
    double getMinimum() const
670
0
    {
671
0
        return m_minimum;
672
0
    }
673
674
    void setMinimum(double newMinimum)
675
0
    {
676
0
        m_minimum = newMinimum;
677
0
    }
678
679
private:
680
    double m_minimum;
681
    bool m_exclusiveMinimum;
682
};
683
684
/**
685
 * @brief   Represents a 'minItems' constraint
686
 */
687
class MinItemsConstraint: public BasicConstraint<MinItemsConstraint>
688
{
689
public:
690
    MinItemsConstraint()
691
0
      : m_minItems(0) { }
692
693
    MinItemsConstraint(CustomAlloc allocFn, CustomFree freeFn)
694
      : BasicConstraint(allocFn, freeFn),
695
0
        m_minItems(0) { }
696
697
    uint64_t getMinItems() const
698
0
    {
699
0
        return m_minItems;
700
0
    }
701
702
    void setMinItems(uint64_t newMinItems)
703
0
    {
704
0
        m_minItems = newMinItems;
705
0
    }
706
707
private:
708
    uint64_t m_minItems;
709
};
710
711
/**
712
 * @brief   Represents a 'minLength' constraint
713
 */
714
class MinLengthConstraint: public BasicConstraint<MinLengthConstraint>
715
{
716
public:
717
    MinLengthConstraint()
718
0
      : m_minLength(0) { }
719
720
    MinLengthConstraint(CustomAlloc allocFn, CustomFree freeFn)
721
      : BasicConstraint(allocFn, freeFn),
722
0
        m_minLength(0) { }
723
724
    uint64_t getMinLength() const
725
0
    {
726
0
        return m_minLength;
727
0
    }
728
729
    void setMinLength(uint64_t newMinLength)
730
0
    {
731
0
        m_minLength = newMinLength;
732
0
    }
733
734
private:
735
    uint64_t m_minLength;
736
};
737
738
/**
739
 * @brief   Represents a 'minProperties' constraint
740
 */
741
class MinPropertiesConstraint: public BasicConstraint<MinPropertiesConstraint>
742
{
743
public:
744
    MinPropertiesConstraint()
745
0
      : m_minProperties(0) { }
746
747
    MinPropertiesConstraint(CustomAlloc allocFn, CustomFree freeFn)
748
      : BasicConstraint(allocFn, freeFn),
749
0
        m_minProperties(0) { }
750
751
    uint64_t getMinProperties() const
752
0
    {
753
0
        return m_minProperties;
754
0
    }
755
756
    void setMinProperties(uint64_t newMinProperties)
757
0
    {
758
0
        m_minProperties = newMinProperties;
759
0
    }
760
761
private:
762
    uint64_t m_minProperties;
763
};
764
765
/**
766
 * @brief  Represents either 'multipleOf' or 'divisibleBy' constraints where
767
 *         the divisor is a floating point number
768
 */
769
class MultipleOfDoubleConstraint:
770
        public BasicConstraint<MultipleOfDoubleConstraint>
771
{
772
public:
773
    MultipleOfDoubleConstraint()
774
0
      : m_value(1.) { }
775
776
    MultipleOfDoubleConstraint(CustomAlloc allocFn, CustomFree freeFn)
777
      : BasicConstraint(allocFn, freeFn),
778
0
        m_value(1.) { }
779
780
    double getDivisor() const
781
0
    {
782
0
        return m_value;
783
0
    }
784
785
    void setDivisor(double newValue)
786
0
    {
787
0
        m_value = newValue;
788
0
    }
789
790
private:
791
    double m_value;
792
};
793
794
/**
795
 * @brief  Represents either 'multipleOf' or 'divisibleBy' constraints where
796
 *         the divisor is of integer type
797
 */
798
class MultipleOfIntConstraint:
799
        public BasicConstraint<MultipleOfIntConstraint>
800
{
801
public:
802
    MultipleOfIntConstraint()
803
0
      : m_value(1) { }
804
805
    MultipleOfIntConstraint(CustomAlloc allocFn, CustomFree freeFn)
806
      : BasicConstraint(allocFn, freeFn),
807
0
        m_value(1) { }
808
809
    int64_t getDivisor() const
810
0
    {
811
0
        return m_value;
812
0
    }
813
814
    void setDivisor(int64_t newValue)
815
0
    {
816
0
        m_value = newValue;
817
0
    }
818
819
private:
820
    int64_t m_value;
821
};
822
823
/**
824
 * @brief   Represents a 'not' constraint
825
 */
826
class NotConstraint: public BasicConstraint<NotConstraint>
827
{
828
public:
829
    NotConstraint()
830
0
      : m_subschema(nullptr) { }
831
832
    NotConstraint(CustomAlloc allocFn, CustomFree freeFn)
833
      : BasicConstraint(allocFn, freeFn),
834
0
        m_subschema(nullptr) { }
835
836
    const Subschema * getSubschema() const
837
0
    {
838
0
        return m_subschema;
839
0
    }
840
841
    void setSubschema(const Subschema *subschema)
842
0
    {
843
0
        m_subschema = subschema;
844
0
    }
845
846
private:
847
    const Subschema *m_subschema;
848
};
849
850
/**
851
 * @brief   Represents a 'oneOf' constraint.
852
 */
853
class OneOfConstraint: public BasicConstraint<OneOfConstraint>
854
{
855
public:
856
    OneOfConstraint()
857
0
      : m_subschemas(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
858
859
    OneOfConstraint(CustomAlloc allocFn, CustomFree freeFn)
860
      : BasicConstraint(allocFn, freeFn),
861
0
        m_subschemas(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
862
863
    void addSubschema(const Subschema *subschema)
864
0
    {
865
0
        m_subschemas.push_back(subschema);
866
0
    }
867
868
    template<typename FunctorType>
869
    void applyToSubschemas(const FunctorType &fn) const
870
    {
871
        unsigned int index = 0;
872
        for (const Subschema *subschema : m_subschemas) {
873
            if (!fn(index, subschema)) {
874
                return;
875
            }
876
877
            index++;
878
        }
879
    }
880
881
private:
882
    typedef std::vector<const Subschema *, internal::CustomAllocator<const Subschema *>> Subschemas;
883
884
    /// Collection of sub-schemas, exactly one of which must be satisfied
885
    Subschemas m_subschemas;
886
};
887
888
/**
889
 * @brief   Represents a 'pattern' constraint
890
 */
891
class PatternConstraint: public BasicConstraint<PatternConstraint>
892
{
893
public:
894
    PatternConstraint()
895
0
      : m_pattern(Allocator::rebind<char>::other(m_allocator)) { }
896
897
    PatternConstraint(CustomAlloc allocFn, CustomFree freeFn)
898
      : BasicConstraint(allocFn, freeFn),
899
0
        m_pattern(Allocator::rebind<char>::other(m_allocator)) { }
900
901
    template<typename AllocatorType>
902
    bool getPattern(std::basic_string<char, std::char_traits<char>, AllocatorType> &result) const
903
    {
904
        result.assign(m_pattern.c_str());
905
        return true;
906
    }
907
908
    template<typename AllocatorType>
909
    std::basic_string<char, std::char_traits<char>, AllocatorType> getPattern(
910
            const AllocatorType &alloc = AllocatorType()) const
911
    {
912
        return std::basic_string<char, std::char_traits<char>, AllocatorType>(m_pattern.c_str(), alloc);
913
    }
914
915
    template<typename AllocatorType>
916
    void setPattern(const std::basic_string<char, std::char_traits<char>, AllocatorType> &pattern)
917
0
    {
918
0
        m_pattern.assign(pattern.c_str());
919
0
    }
920
921
private:
922
    String m_pattern;
923
};
924
925
class PolyConstraint : public Constraint
926
{
927
public:
928
    bool accept(ConstraintVisitor &visitor) const override
929
0
    {
930
0
        return visitor.visit(*static_cast<const PolyConstraint*>(this));
931
0
    }
932
933
    OwningPointer clone(CustomAlloc allocFn, CustomFree freeFn) const override
934
0
    {
935
0
        // smart pointer to automatically free raw memory on exception
936
0
        typedef std::unique_ptr<Constraint, CustomFree> RawOwningPointer;
937
0
        auto ptr = RawOwningPointer(static_cast<Constraint*>(allocFn(sizeOf())), freeFn);
938
0
        if (!ptr) {
939
0
            throwRuntimeError("Failed to allocate memory for cloned constraint");
940
0
        }
941
0
942
0
        // constructor might throw but the memory will be taken care of anyways
943
0
        (void)cloneInto(ptr.get());
944
0
945
0
        // implicitly convert to smart pointer that will also destroy object instance
946
0
        return ptr;
947
0
    }
948
949
    virtual bool validate(const adapters::Adapter &target,
950
            const std::vector<std::string>& context,
951
            valijson::ValidationResults *results) const = 0;
952
953
private:
954
    virtual Constraint * cloneInto(void *) const = 0;
955
956
    virtual size_t sizeOf() const = 0;
957
};
958
959
/**
960
 * @brief   Represents a combination of 'properties', 'patternProperties' and
961
 *          'additionalProperties' constraints
962
 */
963
class PropertiesConstraint: public BasicConstraint<PropertiesConstraint>
964
{
965
public:
966
    PropertiesConstraint()
967
      : m_properties(std::less<String>(), m_allocator),
968
        m_patternProperties(std::less<String>(), m_allocator),
969
0
        m_additionalProperties(nullptr) { }
970
971
    PropertiesConstraint(CustomAlloc allocFn, CustomFree freeFn)
972
      : BasicConstraint(allocFn, freeFn),
973
        m_properties(std::less<String>(), m_allocator),
974
        m_patternProperties(std::less<String>(), m_allocator),
975
0
        m_additionalProperties(nullptr) { }
976
977
    bool addPatternPropertySubschema(const char *patternProperty, const Subschema *subschema)
978
0
    {
979
0
        return m_patternProperties.insert(PropertySchemaMap::value_type(
980
0
                String(patternProperty, m_allocator), subschema)).second;
981
0
    }
982
983
    template<typename AllocatorType>
984
    bool addPatternPropertySubschema(const std::basic_string<char,
985
            std::char_traits<char>, AllocatorType> &patternProperty,
986
            const Subschema *subschema)
987
0
    {
988
0
        return addPatternPropertySubschema(patternProperty.c_str(), subschema);
989
0
    }
990
991
    bool addPropertySubschema(const char *propertyName,
992
            const Subschema *subschema)
993
0
    {
994
0
        return m_properties.insert(PropertySchemaMap::value_type(
995
0
                String(propertyName, m_allocator), subschema)).second;
996
0
    }
997
998
    template<typename AllocatorType>
999
    bool addPropertySubschema(const std::basic_string<char,
1000
            std::char_traits<char>, AllocatorType> &propertyName,
1001
            const Subschema *subschema)
1002
0
    {
1003
0
        return addPropertySubschema(propertyName.c_str(), subschema);
1004
0
    }
1005
1006
    template<typename FunctorType>
1007
    void applyToPatternProperties(const FunctorType &fn) const
1008
    {
1009
        typedef typename PropertySchemaMap::value_type ValueType;
1010
        for (const ValueType &value : m_patternProperties) {
1011
            if (!fn(value.first, value.second)) {
1012
                return;
1013
            }
1014
        }
1015
    }
1016
1017
    template<typename FunctorType>
1018
    void applyToProperties(const FunctorType &fn) const
1019
    {
1020
        typedef typename PropertySchemaMap::value_type ValueType;
1021
        for (const ValueType &value : m_properties) {
1022
            if (!fn(value.first, value.second)) {
1023
                return;
1024
            }
1025
        }
1026
    }
1027
1028
    const Subschema * getAdditionalPropertiesSubschema() const
1029
0
    {
1030
0
        return m_additionalProperties;
1031
0
    }
1032
1033
    void setAdditionalPropertiesSubschema(const Subschema *subschema)
1034
0
    {
1035
0
        m_additionalProperties = subschema;
1036
0
    }
1037
1038
private:
1039
    typedef std::map<
1040
            String,
1041
            const Subschema *,
1042
            std::less<String>,
1043
            internal::CustomAllocator<std::pair<const String, const Subschema *>>
1044
        > PropertySchemaMap;
1045
1046
    PropertySchemaMap m_properties;
1047
    PropertySchemaMap m_patternProperties;
1048
1049
    const Subschema *m_additionalProperties;
1050
};
1051
1052
class PropertyNamesConstraint: public BasicConstraint<PropertyNamesConstraint>
1053
{
1054
public:
1055
    PropertyNamesConstraint()
1056
0
      : m_subschema(nullptr) { }
1057
1058
    PropertyNamesConstraint(CustomAlloc allocFn, CustomFree freeFn)
1059
      : BasicConstraint(allocFn, freeFn),
1060
0
        m_subschema(nullptr) { }
1061
1062
    const Subschema * getSubschema() const
1063
0
    {
1064
0
        return m_subschema;
1065
0
    }
1066
1067
    void setSubschema(const Subschema *subschema)
1068
0
    {
1069
0
        m_subschema = subschema;
1070
0
    }
1071
1072
private:
1073
    const Subschema *m_subschema;
1074
};
1075
1076
/**
1077
 * @brief   Represents a 'required' constraint
1078
 */
1079
class RequiredConstraint: public BasicConstraint<RequiredConstraint>
1080
{
1081
public:
1082
    RequiredConstraint()
1083
0
      : m_requiredProperties(std::less<String>(), m_allocator) { }
1084
1085
    RequiredConstraint(CustomAlloc allocFn, CustomFree freeFn)
1086
      : BasicConstraint(allocFn, freeFn),
1087
0
        m_requiredProperties(std::less<String>(), m_allocator) { }
1088
1089
    bool addRequiredProperty(const char *propertyName)
1090
0
    {
1091
0
        return m_requiredProperties.insert(String(propertyName,
1092
0
                Allocator::rebind<char>::other(m_allocator))).second;
1093
0
    }
1094
1095
    template<typename AllocatorType>
1096
    bool addRequiredProperty(const std::basic_string<char, std::char_traits<char>, AllocatorType> &propertyName)
1097
0
    {
1098
0
        return addRequiredProperty(propertyName.c_str());
1099
0
    }
1100
1101
    template<typename FunctorType>
1102
    void applyToRequiredProperties(const FunctorType &fn) const
1103
    {
1104
        for (const String &propertyName : m_requiredProperties) {
1105
            if (!fn(propertyName)) {
1106
                return;
1107
            }
1108
        }
1109
    }
1110
1111
private:
1112
    typedef std::set<String, std::less<String>,
1113
            internal::CustomAllocator<String>> RequiredProperties;
1114
1115
    RequiredProperties m_requiredProperties;
1116
};
1117
1118
/**
1119
 * @brief  Represents an 'items' constraint that specifies one sub-schema
1120
 *
1121
 * A value is considered valid against this constraint if it is an array, and
1122
 * each item in the array validates against the sub-schema specified by this
1123
 * constraint.
1124
 *
1125
 * The prefix 'Singular' comes from the fact that array items must validate
1126
 * against exactly one sub-schema.
1127
 */
1128
class SingularItemsConstraint: public BasicConstraint<SingularItemsConstraint>
1129
{
1130
public:
1131
    SingularItemsConstraint()
1132
0
      : m_itemsSubschema(nullptr) { }
1133
1134
    SingularItemsConstraint(CustomAlloc allocFn, CustomFree freeFn)
1135
      : BasicConstraint(allocFn, freeFn),
1136
0
        m_itemsSubschema(nullptr) { }
1137
1138
    const Subschema * getItemsSubschema() const
1139
0
    {
1140
0
        return m_itemsSubschema;
1141
0
    }
1142
1143
    void setItemsSubschema(const Subschema *subschema)
1144
0
    {
1145
0
        m_itemsSubschema = subschema;
1146
0
    }
1147
1148
private:
1149
    const Subschema *m_itemsSubschema;
1150
};
1151
1152
/**
1153
 * @brief   Represents a 'type' constraint.
1154
 */
1155
class TypeConstraint: public BasicConstraint<TypeConstraint>
1156
{
1157
public:
1158
    enum JsonType {
1159
        kAny,
1160
        kArray,
1161
        kBoolean,
1162
        kInteger,
1163
        kNull,
1164
        kNumber,
1165
        kObject,
1166
        kString
1167
    };
1168
1169
    TypeConstraint()
1170
      : m_namedTypes(std::less<JsonType>(), m_allocator),
1171
0
        m_schemaTypes(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
1172
1173
    TypeConstraint(CustomAlloc allocFn, CustomFree freeFn)
1174
      : BasicConstraint(allocFn, freeFn),
1175
        m_namedTypes(std::less<JsonType>(), m_allocator),
1176
0
        m_schemaTypes(Allocator::rebind<const Subschema *>::other(m_allocator)) { }
1177
1178
    void addNamedType(JsonType type)
1179
0
    {
1180
0
        m_namedTypes.insert(type);
1181
0
    }
1182
1183
    void addSchemaType(const Subschema *subschema)
1184
0
    {
1185
0
        m_schemaTypes.push_back(subschema);
1186
0
    }
1187
1188
    template<typename FunctorType>
1189
    void applyToNamedTypes(const FunctorType &fn) const
1190
    {
1191
        for (const JsonType namedType : m_namedTypes) {
1192
            if (!fn(namedType)) {
1193
                return;
1194
            }
1195
        }
1196
    }
1197
1198
    template<typename FunctorType>
1199
    void applyToSchemaTypes(const FunctorType &fn) const
1200
    {
1201
        unsigned int index = 0;
1202
        for (const Subschema *subschema : m_schemaTypes) {
1203
            if (!fn(index, subschema)) {
1204
                return;
1205
            }
1206
1207
            index++;
1208
        }
1209
    }
1210
1211
    template<typename AllocatorType>
1212
    static JsonType jsonTypeFromString(const std::basic_string<char,
1213
            std::char_traits<char>, AllocatorType> &typeName)
1214
0
    {
1215
0
        if (typeName.compare("any") == 0) {
1216
0
            return kAny;
1217
0
        } else if (typeName.compare("array") == 0) {
1218
0
            return kArray;
1219
0
        } else if (typeName.compare("boolean") == 0) {
1220
0
            return kBoolean;
1221
0
        } else if (typeName.compare("integer") == 0) {
1222
0
            return kInteger;
1223
0
        } else if (typeName.compare("null") == 0) {
1224
0
            return kNull;
1225
0
        } else if (typeName.compare("number") == 0) {
1226
0
            return kNumber;
1227
0
        } else if (typeName.compare("object") == 0) {
1228
0
            return kObject;
1229
0
        } else if (typeName.compare("string") == 0) {
1230
0
            return kString;
1231
0
        }
1232
1233
0
        throwRuntimeError("Unrecognised JSON type name '" +
1234
0
                std::string(typeName.c_str()) + "'");
1235
0
        abort();
1236
0
    }
1237
1238
private:
1239
    typedef std::set<JsonType, std::less<JsonType>, internal::CustomAllocator<JsonType>> NamedTypes;
1240
1241
    typedef std::vector<const Subschema *,
1242
            Allocator::rebind<const Subschema *>::other> SchemaTypes;
1243
1244
    /// Set of named JSON types that serve as valid types
1245
    NamedTypes m_namedTypes;
1246
1247
    /// Set of sub-schemas that serve as valid types
1248
    SchemaTypes m_schemaTypes;
1249
};
1250
1251
/**
1252
 * @brief   Represents a 'uniqueItems' constraint
1253
 */
1254
class UniqueItemsConstraint: public BasicConstraint<UniqueItemsConstraint>
1255
{
1256
public:
1257
0
    UniqueItemsConstraint() = default;
1258
1259
    UniqueItemsConstraint(CustomAlloc allocFn, CustomFree freeFn)
1260
0
      : BasicConstraint(allocFn, freeFn) { }
1261
};
1262
1263
} // namespace constraints
1264
} // namespace valijson
1265
1266
#ifdef _MSC_VER
1267
#pragma warning( pop )
1268
#endif