• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenLightingProject / ola / 16900099157

12 Aug 2025 05:43AM UTC coverage: 45.72% (-0.02%) from 45.742%
16900099157

Pull #2016

github

web-flow
Merge c368ef6ae into eaf937e80
Pull Request #2016: Bump actions/checkout from 4 to 5

7586 of 17462 branches covered (43.44%)

22424 of 49046 relevant lines covered (45.72%)

51.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.96
/common/web/SchemaParseContext.cpp
1
/*
2
 * This library is free software; you can redistribute it and/or
3
 * modify it under the terms of the GNU Lesser General Public
4
 * License as published by the Free Software Foundation; either
5
 * version 2.1 of the License, or (at your option) any later version.
6
 *
7
 * This library is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10
 * Lesser General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU Lesser General Public
13
 * License along with this library; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
 *
16
 * SchemaParseContext.cpp
17
 * Stores the state required as we walk the JSON schema document.
18
 * Copyright (C) 2014 Simon Newton
19
 */
20
#include "common/web/SchemaParseContext.h"
21

22
#include <limits>
23
#include <map>
24
#include <memory>
25
#include <set>
26
#include <string>
27
#include <utility>
28
#include <vector>
29

30
#include "ola/Logging.h"
31

32
#include "ola/StringUtils.h"
33
#include "ola/stl/STLUtils.h"
34
#include "ola/web/Json.h"
35
#include "ola/web/JsonTypes.h"
36

37
namespace ola {
38
namespace web {
39

40
namespace {
41

42
/*
43
 * A cool hack to determine if a type is positive.
44
 * We do this to avoid:
45
 *   comparison of unsigned expression >= 0 is always true
46
 * warnings. See http://gcc.gnu.org/ml/gcc-help/2010-08/msg00286.html
47
 */
48
template<typename T, bool sign>
49
struct positive {
50
  bool operator()(const T &x) { return x >= 0; }
51
};
52

53
template<typename T>
54
struct positive<T, false>{
55
  bool operator()(const T &) {
56
    return true;
57
  }
58
};
59
}  // namespace
60

61
using std::auto_ptr;
62
using std::pair;
63
using std::set;
64
using std::string;
65
using std::vector;
66

67
// DefinitionsParseContext
68
// Used for parsing an object with key : json schema pairs, within 'definitions'
69
SchemaParseContextInterface* DefinitionsParseContext::OpenObject(
10✔
70
    OLA_UNUSED SchemaErrorLogger *logger) {
71
  m_current_schema.reset(new SchemaParseContext(m_schema_defs));
10✔
72
  return m_current_schema.get();
10✔
73
}
74

75
void DefinitionsParseContext::CloseObject(SchemaErrorLogger *logger) {
10✔
76
  string key = TakeKeyword();
10✔
77

78
  ValidatorInterface *schema = m_current_schema->GetValidator(logger);
10✔
79
  m_schema_defs->Add(key, schema);
10✔
80
  m_current_schema.reset();
10✔
81
}
10✔
82

83
// StrictTypedParseContext
84
// Used as a parent class for many of the other contexts.
85
void StrictTypedParseContext::String(SchemaErrorLogger *logger,
2✔
86
                                     const string &value) {
87
  ReportErrorForType(logger, TypeFromValue(value));
2✔
88
}
2✔
89

90
void StrictTypedParseContext::Number(SchemaErrorLogger *logger,
3✔
91
                                     uint32_t value) {
92
  ReportErrorForType(logger, TypeFromValue(value));
3✔
93
}
3✔
94

95
void StrictTypedParseContext::Number(SchemaErrorLogger *logger, int32_t value) {
2✔
96
  ReportErrorForType(logger, TypeFromValue(value));
2✔
97
}
2✔
98

99
void StrictTypedParseContext::Number(SchemaErrorLogger *logger,
×
100
                                     uint64_t value) {
101
  ReportErrorForType(logger, TypeFromValue(value));
×
102
}
×
103

104
void StrictTypedParseContext::Number(SchemaErrorLogger *logger, int64_t value) {
×
105
  ReportErrorForType(logger, TypeFromValue(value));
×
106
}
×
107

108
void StrictTypedParseContext::Number(SchemaErrorLogger *logger, double value) {
1✔
109
  ReportErrorForType(logger, TypeFromValue(value));
1✔
110
}
1✔
111

112
void StrictTypedParseContext::Bool(SchemaErrorLogger *logger, bool value) {
1✔
113
  ReportErrorForType(logger, TypeFromValue(value));
1✔
114
}
1✔
115

116
void StrictTypedParseContext::Null(SchemaErrorLogger *logger) {
2✔
117
  ReportErrorForType(logger, JSON_NULL);
2✔
118
}
2✔
119

120
SchemaParseContextInterface* StrictTypedParseContext::OpenArray(
2✔
121
    SchemaErrorLogger *logger) {
122
  ReportErrorForType(logger, JSON_ARRAY);
2✔
123
  return NULL;
2✔
124
}
125

126
void StrictTypedParseContext::CloseArray(
×
127
    OLA_UNUSED SchemaErrorLogger *logger) {
128
}
×
129

130
SchemaParseContextInterface* StrictTypedParseContext::OpenObject(
×
131
    SchemaErrorLogger *logger) {
132
  ReportErrorForType(logger, JSON_ARRAY);
×
133
  return NULL;
×
134
}
135

136
void StrictTypedParseContext::CloseObject(
64✔
137
    OLA_UNUSED SchemaErrorLogger *logger) {
138
}
64✔
139

140
void StrictTypedParseContext::ReportErrorForType(
13✔
141
    SchemaErrorLogger *logger,
142
    JsonType type) {
143
  logger->Error() << "Invalid type '" << JsonTypeToString(type)
26✔
144
                  << "' in 'required', elements must be strings";
26✔
145
}
13✔
146

147
// SchemaParseContext
148
// Used for parsing an object that describes a JSON schema.
149
ValidatorInterface* SchemaParseContext::GetValidator(
197✔
150
    SchemaErrorLogger *logger) {
151
  if (m_ref_schema.IsSet()) {
197✔
152
    return new ReferenceValidator(m_schema_defs, m_ref_schema.Value());
27✔
153
  }
154

155
  BaseValidator *validator = NULL;
170✔
156
  auto_ptr<IntegerValidator> int_validator;
170✔
157
  switch (m_type) {
170✔
158
    case JSON_UNDEFINED:
159
      break;
160
    case JSON_ARRAY:
30✔
161
      validator = BuildArrayValidator(logger);
30✔
162
      break;
163
    case JSON_BOOLEAN:
10✔
164
      validator = new BoolValidator();
10✔
165
      break;
166
    case JSON_INTEGER:
20✔
167
      int_validator.reset(new IntegerValidator());
20✔
168
      break;
169
    case JSON_NULL:
2✔
170
      validator = new NullValidator();
2✔
171
      break;
172
    case JSON_NUMBER:
8✔
173
      int_validator.reset(new NumberValidator());
8✔
174
      break;
175
    case JSON_OBJECT:
24✔
176
      validator = BuildObjectValidator(logger);
24✔
177
      break;
178
    case JSON_STRING:
22✔
179
      validator = BuildStringValidator(logger);
22✔
180
      break;
181
    default:
170✔
182
      {}
170✔
183
  }
184

185
  if (int_validator.get()) {
170✔
186
    if (!AddNumberConstraints(int_validator.get(), logger)) {
28✔
187
      return NULL;
188
    }
189
    validator = int_validator.release();
26✔
190
  }
191

192
  if (!validator && m_allof_context.get()) {
168✔
193
    ValidatorInterface::ValidatorList all_of_validators;
4✔
194
    m_allof_context->GetValidators(logger, &all_of_validators);
4✔
195
    if (all_of_validators.empty()) {
4✔
196
      logger->Error() << "allOf must contain at least one schema";
1✔
197
      return NULL;
1✔
198
    }
199
    validator = new AllOfValidator(&all_of_validators);
3✔
200
  }
4✔
201

202
  if (!validator && m_anyof_context.get()) {
167✔
203
    ValidatorInterface::ValidatorList any_of_validators;
7✔
204
    m_anyof_context->GetValidators(logger, &any_of_validators);
7✔
205
    if (any_of_validators.empty()) {
7✔
206
      logger->Error() << "anyOf must contain at least one schema";
1✔
207
      return NULL;
1✔
208
    }
209
    validator = new AnyOfValidator(&any_of_validators);
6✔
210
  }
7✔
211

212
  if (!validator && m_oneof_context.get()) {
166✔
213
    ValidatorInterface::ValidatorList one_of_validators;
2✔
214
    m_oneof_context->GetValidators(logger, &one_of_validators);
2✔
215
    if (one_of_validators.empty()) {
2✔
216
      logger->Error() << "oneOf must contain at least one schema";
1✔
217
      return NULL;
1✔
218
    }
219
    validator = new OneOfValidator(&one_of_validators);
1✔
220
  }
2✔
221

222
  if (!validator && m_not_context.get()) {
165✔
223
    validator = new NotValidator(m_not_context->GetValidator(logger));
1✔
224
  }
225

226
  if (validator == NULL) {
165✔
227
    if (m_type == JSON_UNDEFINED) {
40✔
228
      validator = new WildcardValidator();
40✔
229
    } else {
230
      logger->Error() << "Unknown type: " << JsonTypeToString(m_type);
×
231
      return NULL;
×
232
    }
233
  }
234

235
  if (m_schema.IsSet()) {
165✔
236
    validator->SetSchema(m_schema.Value());
9✔
237
    m_schema.Reset();
9✔
238
  }
239
  if (m_id.IsSet()) {
165✔
240
    validator->SetId(m_id.Value());
9✔
241
    m_id.Reset();
9✔
242
  }
243
  if (m_title.IsSet()) {
165✔
244
    validator->SetTitle(m_title.Value());
9✔
245
    m_title.Reset();
9✔
246
  }
247
  if (m_description.IsSet()) {
165✔
248
    validator->SetDescription(m_description.Value());
10✔
249
    m_description.Reset();
10✔
250
  }
251
  if (m_default_value.get()) {
165✔
252
    validator->SetDefaultValue(m_default_value.release());
24✔
253
  }
254

255
  if (m_enum_context.get()) {
165✔
256
    m_enum_context->AddEnumsToValidator(validator);
6✔
257
  }
258

259
  return validator;
260
}
170✔
261

262

263
void SchemaParseContext::ObjectKey(OLA_UNUSED SchemaErrorLogger *logger,
582✔
264
                                   const string &keyword) {
265
  m_keyword = LookupKeyword(keyword);
582✔
266
}
582✔
267

268
void SchemaParseContext::String(SchemaErrorLogger *logger,
237✔
269
                                const string &value) {
270
  if (!ValidTypeForKeyword(logger, m_keyword, TypeFromValue(value))) {
237✔
271
    return;
272
  }
273

274
  switch (m_keyword) {
215✔
275
    case SCHEMA_REF:
27✔
276
      m_ref_schema.Set(value);
27✔
277
      break;
27✔
278
    case SCHEMA_SCHEMA:
9✔
279
      m_schema.Set(value);
9✔
280
      break;
9✔
281
    case SCHEMA_DESCRIPTION:
10✔
282
      m_description.Set(value);
10✔
283
      break;
10✔
284
    case SCHEMA_DEFAULT:
2✔
285
      m_default_value.reset(new JsonString(value));
2✔
286
      break;
287
    case SCHEMA_FORMAT:
×
288
      m_format.Set(value);
×
289
      break;
×
290
    case SCHEMA_ID:
9✔
291
      m_id.Set(value);
9✔
292
      break;
9✔
293
    case SCHEMA_TITLE:
9✔
294
      m_title.Set(value);
9✔
295
      break;
9✔
296
    case SCHEMA_TYPE:
148✔
297
      m_type = StringToJsonType(value);
148✔
298
      if (m_type == JSON_UNDEFINED) {
148✔
299
        logger->Error() <<  "Invalid type: " << value;
1✔
300
      }
301
      break;
302
    default:
237✔
303
      // Nothing, we ignore keywords we don't support.
304
      {}
237✔
305
  }
306
}
307

308
void SchemaParseContext::Number(SchemaErrorLogger *logger, uint32_t value) {
54✔
309
  ProcessInt(logger, value);
54✔
310
}
54✔
311

312
void SchemaParseContext::Number(SchemaErrorLogger *logger, int32_t value) {
27✔
313
  ProcessInt(logger, value);
27✔
314
}
27✔
315

316
void SchemaParseContext::Number(SchemaErrorLogger *logger, uint64_t value) {
×
317
  ProcessInt(logger, value);
×
318
}
×
319

320
void SchemaParseContext::Number(SchemaErrorLogger *logger, int64_t value) {
×
321
  ProcessInt(logger, value);
×
322
}
×
323

324
void SchemaParseContext::Number(SchemaErrorLogger *logger, double value) {
28✔
325
  ValidTypeForKeyword(logger, m_keyword, TypeFromValue(value));
28✔
326

327
  switch (m_keyword) {
28✔
328
    case SCHEMA_DEFAULT:
1✔
329
      m_default_value.reset(new JsonDouble(value));
1✔
330
      break;
331
    case SCHEMA_MAXIMUM:
1✔
332
      m_maximum.reset(JsonValue::NewNumberValue(value));
1✔
333
      break;
1✔
334
    case SCHEMA_MINIMUM:
1✔
335
      m_minimum.reset(JsonValue::NewNumberValue(value));
1✔
336
      break;
1✔
337
    case SCHEMA_MULTIPLEOF:
2✔
338
      if (value <= 0) {
2✔
339
        logger->Error() << KeywordToString(m_keyword) << " can't be negative";
3✔
340
      } else {
341
        m_multiple_of.reset(JsonValue::NewNumberValue(value));
1✔
342
      }
343
      return;
344
    default:
28✔
345
      {}
28✔
346
  }
347
}
348

349
void SchemaParseContext::Bool(SchemaErrorLogger *logger, bool value) {
46✔
350
  if (!ValidTypeForKeyword(logger, m_keyword, TypeFromValue(value))) {
46✔
351
    OLA_INFO << "type was not valid";
20✔
352
    return;
20✔
353
  }
354

355
  switch (m_keyword) {
26✔
356
    case SCHEMA_DEFAULT:
4✔
357
      m_default_value.reset(new JsonBool(value));
4✔
358
      break;
359
    case SCHEMA_EXCLUSIVE_MAXIMUM:
3✔
360
      m_exclusive_maximum.Set(value);
3✔
361
      break;
3✔
362
    case SCHEMA_EXCLUSIVE_MINIMUM:
4✔
363
      m_exclusive_minimum.Set(value);
4✔
364
      break;
4✔
365
    case SCHEMA_UNIQUE_ITEMS:
6✔
366
      m_unique_items.Set(value);
6✔
367
      break;
6✔
368
    case SCHEMA_ADDITIONAL_ITEMS:
7✔
369
      m_additional_items.Set(value);
7✔
370
      break;
7✔
371
    case SCHEMA_ADDITIONAL_PROPERTIES:
2✔
372
      m_additional_properties.Set(value);
2✔
373
    default:
46✔
374
      {}
46✔
375
  }
376
}
377

378
void SchemaParseContext::Null(SchemaErrorLogger *logger) {
29✔
379
  ValidTypeForKeyword(logger, m_keyword, JSON_NULL);
29✔
380

381
  switch (m_keyword) {
29✔
382
    case SCHEMA_DEFAULT:
1✔
383
      m_default_value.reset(new JsonNull());
1✔
384
      break;
385
    default:
29✔
386
      {}
29✔
387
  }
388
}
29✔
389

390
SchemaParseContextInterface* SchemaParseContext::OpenArray(
63✔
391
    SchemaErrorLogger *logger) {
392
  if (!ValidTypeForKeyword(logger, m_keyword, JSON_ARRAY)) {
63✔
393
    return NULL;
394
  }
395

396
  switch (m_keyword) {
42✔
397
    case SCHEMA_DEFAULT:
3✔
398
      m_default_value_context.reset(new JsonValueContext());
3✔
399
      m_default_value_context->OpenArray(logger);
3✔
400
      return m_default_value_context.get();
3✔
401
    case SCHEMA_ITEMS:
13✔
402
      m_items_context_array.reset(new ArrayOfSchemaContext(m_schema_defs));
13✔
403
      return m_items_context_array.get();
13✔
404
    case SCHEMA_REQUIRED:
4✔
405
      m_required_items.reset(new ArrayOfStringsContext());
4✔
406
      return m_required_items.get();
4✔
407
    case SCHEMA_ENUM:
9✔
408
      m_enum_context.reset(new ArrayOfJsonValuesContext());
9✔
409
      return m_enum_context.get();
9✔
410
    case SCHEMA_ALL_OF:
4✔
411
      m_allof_context.reset(new ArrayOfSchemaContext(m_schema_defs));
4✔
412
      return m_allof_context.get();
4✔
413
    case SCHEMA_ANY_OF:
7✔
414
      m_anyof_context.reset(new ArrayOfSchemaContext(m_schema_defs));
7✔
415
      return m_anyof_context.get();
7✔
416
    case SCHEMA_ONE_OF:
2✔
417
      m_oneof_context.reset(new ArrayOfSchemaContext(m_schema_defs));
2✔
418
      return m_oneof_context.get();
2✔
419
    default:
420
      {}
421
  }
422
  return NULL;
423
}
424

425
void SchemaParseContext::CloseArray(SchemaErrorLogger *logger) {
31✔
426
  if (m_default_value_context.get()) {
31✔
427
    m_default_value_context->CloseArray(logger);
2✔
428
    m_default_value.reset(m_default_value_context->ClaimValue(logger));
2✔
429
    m_default_value_context.reset();
2✔
430
  }
431

432
  if (m_keyword == SCHEMA_ENUM) {
31✔
433
    if (m_enum_context->Empty()) {
7✔
434
      logger->Error() << "enum must contain at least one value";
1✔
435
    }
436
  }
437
}
31✔
438

439
SchemaParseContextInterface* SchemaParseContext::OpenObject(
97✔
440
    SchemaErrorLogger *logger) {
441
  if (!ValidTypeForKeyword(logger, m_keyword, JSON_OBJECT)) {
97✔
442
    return NULL;
443
  }
444

445
  switch (m_keyword) {
75✔
446
    case SCHEMA_DEFAULT:
12✔
447
      m_default_value_context.reset(new JsonValueContext());
12✔
448
      m_default_value_context->OpenObject(logger);
12✔
449
      return m_default_value_context.get();
12✔
450
    case SCHEMA_DEFINITIONS:
2✔
451
      m_definitions_context.reset(new DefinitionsParseContext(m_schema_defs));
2✔
452
      return m_definitions_context.get();
2✔
453
    case SCHEMA_PROPERTIES:
9✔
454
      m_properties_context.reset(new PropertiesParseContext(m_schema_defs));
9✔
455
      return m_properties_context.get();
9✔
456
    case SCHEMA_ADDITIONAL_PROPERTIES:
6✔
457
      m_additional_properties_context.reset(
6✔
458
          new SchemaParseContext(m_schema_defs));
6✔
459
      return m_additional_properties_context.get();
6✔
460
    case SCHEMA_ITEMS:
34✔
461
      m_items_single_context.reset(new SchemaParseContext(m_schema_defs));
34✔
462
      return m_items_single_context.get();
34✔
463
    case SCHEMA_ADDITIONAL_ITEMS:
4✔
464
      m_additional_items_context.reset(new SchemaParseContext(m_schema_defs));
4✔
465
      return m_additional_items_context.get();
4✔
466
    case SCHEMA_DEPENDENCIES:
7✔
467
      m_dependency_context.reset(new DependencyParseContext(m_schema_defs));
7✔
468
      return m_dependency_context.get();
7✔
469
    case SCHEMA_NOT:
1✔
470
      m_not_context.reset(new SchemaParseContext(m_schema_defs));
1✔
471
      return m_not_context.get();
1✔
472
    default:
473
      {}
474
  }
475
  return NULL;
476
}
477

478
void SchemaParseContext::CloseObject(SchemaErrorLogger *logger) {
65✔
479
  if (m_default_value_context.get()) {
65✔
480
    m_default_value_context->CloseObject(logger);
10✔
481
    m_default_value.reset(m_default_value_context->ClaimValue(logger));
10✔
482
    m_default_value_context.reset();
10✔
483
  }
484
}
65✔
485

486
void SchemaParseContext::ProcessPositiveInt(
23✔
487
    OLA_UNUSED SchemaErrorLogger *logger,
488
    uint64_t value) {
489
  switch (m_keyword) {
23✔
490
    case SCHEMA_MULTIPLEOF:
1✔
491
      m_multiple_of.reset(JsonValue::NewNumberValue(value));
1✔
492
      return;
1✔
493
    case SCHEMA_MIN_ITEMS:
9✔
494
      m_min_items.Set(value);
9✔
495
      break;
9✔
496
    case SCHEMA_MAX_ITEMS:
2✔
497
      m_max_items.Set(value);
2✔
498
      break;
2✔
499
    case SCHEMA_MAX_LENGTH:
3✔
500
      m_max_length.Set(value);
3✔
501
      break;
3✔
502
    case SCHEMA_MIN_LENGTH:
3✔
503
      m_min_length.Set(value);
3✔
504
      break;
3✔
505
    case SCHEMA_MAX_PROPERTIES:
3✔
506
      m_max_properties.Set(value);
3✔
507
      break;
3✔
508
    case SCHEMA_MIN_PROPERTIES:
2✔
509
      m_min_properties.Set(value);
2✔
510
      break;
2✔
511
    default:
23✔
512
      {}
23✔
513
  }
514
}
515

516
template <typename T>
517
void SchemaParseContext::ProcessInt(SchemaErrorLogger *logger, T value) {
81✔
518
  if (!ValidTypeForKeyword(logger, m_keyword, TypeFromValue(value))) {
81✔
519
    return;
520
  }
521

522
  switch (m_keyword) {
45✔
523
    case SCHEMA_DEFAULT:
4✔
524
      m_default_value.reset(JsonValue::NewValue(value));
4✔
525
      return;
4✔
526
    case SCHEMA_MAXIMUM:
4✔
527
      m_maximum.reset(JsonValue::NewNumberValue(value));
4✔
528
      return;
4✔
529
    case SCHEMA_MINIMUM:
7✔
530
      m_minimum.reset(JsonValue::NewNumberValue(value));
7✔
531
      return;
7✔
532
    default:
30✔
533
      {}
534
  }
535

536
  if (positive<T, std::numeric_limits<T>::is_signed>()(value)) {
7✔
537
    ProcessPositiveInt(logger, static_cast<uint64_t>(value));
23✔
538
    return;
23✔
539
  }
540

541
  logger->Error() << KeywordToString(m_keyword) << " can't be negative";
21✔
542
}
543

544
bool SchemaParseContext::AddNumberConstraints(IntegerValidator *validator,
28✔
545
                                              SchemaErrorLogger *logger) {
546
  if (m_exclusive_maximum.IsSet() && !m_maximum.get()) {
28✔
547
    logger->Error() << "exclusiveMaximum requires maximum to be defined";
1✔
548
    return false;
1✔
549
  }
550

551
  if (m_maximum.get()) {
27✔
552
    if (m_exclusive_maximum.IsSet()) {
5✔
553
      validator->AddConstraint(new MaximumConstraint(
2✔
554
            m_maximum.release(), m_exclusive_maximum.Value()));
2✔
555
    } else {
556
      validator->AddConstraint(new MaximumConstraint(m_maximum.release()));
3✔
557
    }
558
  }
559

560
  if (m_exclusive_minimum.IsSet() && !m_minimum.get()) {
27✔
561
    logger->Error() << "exclusiveMinimum requires minimum to be defined";
1✔
562
    return false;
1✔
563
  }
564

565
  if (m_minimum.get()) {
26✔
566
    if (m_exclusive_minimum.IsSet()) {
8✔
567
      validator->AddConstraint(new MinimumConstraint(
3✔
568
            m_minimum.release(), m_exclusive_minimum.Value()));
3✔
569
    } else {
570
      validator->AddConstraint(new MinimumConstraint(m_minimum.release()));
5✔
571
    }
572
  }
573

574
  if (m_multiple_of.get()) {
26✔
575
    validator->AddConstraint(new MultipleOfConstraint(m_multiple_of.release()));
2✔
576
  }
577
  return true;
578
}
579

580
BaseValidator* SchemaParseContext::BuildArrayValidator(
30✔
581
    SchemaErrorLogger *logger) {
582
  ArrayValidator::Options options;
30✔
583
  if (m_min_items.IsSet()) {
30✔
584
    options.min_items = m_min_items.Value();
9✔
585
  }
586

587
  if (m_max_items.IsSet()) {
30✔
588
    options.max_items = m_max_items.Value();
2✔
589
  }
590

591
  if (m_unique_items.IsSet()) {
30✔
592
    options.unique_items = m_unique_items.Value();
6✔
593
  }
594

595
  auto_ptr<ArrayValidator::Items> items;
30✔
596
  auto_ptr<ArrayValidator::AdditionalItems> additional_items;
30✔
597

598
  // items
599
  if (m_items_single_context.get() && m_items_context_array.get()) {
30✔
600
    logger->Error() << "'items' is somehow both a schema and an array!";
×
601
    return NULL;
602
  } else if (m_items_single_context.get()) {
30✔
603
    // 8.2.3.1
604
    items.reset(new ArrayValidator::Items(
15✔
605
          m_items_single_context->GetValidator(logger)));
15✔
606
  } else if (m_items_context_array.get()) {
15✔
607
    // 8.2.3.2
608
    ValidatorInterface::ValidatorList item_validators;
7✔
609
    m_items_context_array->GetValidators(logger, &item_validators);
7✔
610
    items.reset(new ArrayValidator::Items(&item_validators));
7✔
611
  }
7✔
612

613
  // additionalItems
614
  if (m_additional_items_context.get()) {
30✔
615
    additional_items.reset(new ArrayValidator::AdditionalItems(
4✔
616
        m_additional_items_context->GetValidator(logger)));
4✔
617
  } else if (m_additional_items.IsSet()) {
26✔
618
    additional_items.reset(
7✔
619
        new ArrayValidator::AdditionalItems(m_additional_items.Value()));
7✔
620
  }
621

622
  return new ArrayValidator(items.release(), additional_items.release(),
30✔
623
                            options);
30✔
624
}
30✔
625

626
BaseValidator* SchemaParseContext::BuildObjectValidator(
24✔
627
    SchemaErrorLogger* logger) {
628
  ObjectValidator::Options options;
24✔
629
  if (m_max_properties.IsSet()) {
24✔
630
    options.max_properties = m_max_properties.Value();
3✔
631
  }
632

633
  if (m_min_properties.IsSet()) {
24✔
634
    options.min_properties = m_min_properties.Value();
2✔
635
  }
636

637
  if (m_required_items.get()) {
24✔
638
    set<string> required_properties;
2✔
639
    m_required_items->GetStringSet(&required_properties);
2✔
640
    options.SetRequiredProperties(required_properties);
2✔
641
  }
2✔
642

643
  if (m_additional_properties.IsSet()) {
24✔
644
    options.SetAdditionalProperties(m_additional_properties.Value());
2✔
645
  }
646

647
  ObjectValidator *object_validator = new ObjectValidator(options);
24✔
648

649
  if (m_additional_properties_context.get()) {
24✔
650
    object_validator->SetAdditionalValidator(
6✔
651
        m_additional_properties_context->GetValidator(logger));
652
  }
653

654
  if (m_properties_context.get()) {
24✔
655
    m_properties_context->AddPropertyValidators(object_validator, logger);
3✔
656
  }
657

658
  if (m_dependency_context.get()) {
24✔
659
    m_dependency_context->AddDependenciesToValidator(object_validator);
5✔
660
  }
661
  return object_validator;
24✔
662
}
24✔
663

664
BaseValidator* SchemaParseContext::BuildStringValidator(
22✔
665
    OLA_UNUSED SchemaErrorLogger *logger) {
666
  StringValidator::Options options;
22✔
667

668
  if (m_max_length.IsSet()) {
22✔
669
    options.max_length = m_max_length.Value();
3✔
670
  }
671

672
  if (m_min_length.IsSet()) {
22✔
673
    options.min_length = m_min_length.Value();
3✔
674
  }
675

676
  return new StringValidator(options);
22✔
677
}
678

679
/*
680
 * Verify the type is valid for the given keyword.
681
 * If the type isn't valid, an error is logged.
682
 * @returns false if the type isn't valid or if the keyword is SCHEMA_UNKNOWN.
683
 */
684
bool SchemaParseContext::ValidTypeForKeyword(SchemaErrorLogger *logger,
581✔
685
                                             SchemaKeyword keyword,
686
                                             JsonType type) {
687
  switch (keyword) {
581✔
688
    case SCHEMA_UNKNOWN:
689
      return false;
690
    case SCHEMA_ID:
16✔
691
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING);
16✔
692
    case SCHEMA_SCHEMA:
16✔
693
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING);
16✔
694
    case SCHEMA_REF:
27✔
695
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING);
27✔
696
    case SCHEMA_TITLE:
16✔
697
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING);
16✔
698
    case SCHEMA_DESCRIPTION:
17✔
699
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING);
17✔
700
    case SCHEMA_DEFAULT:
27✔
701
      return true;
27✔
702
    case SCHEMA_MULTIPLEOF:
9✔
703
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER, JSON_NUMBER);
9✔
704
    case SCHEMA_MAXIMUM:
10✔
705
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER, JSON_NUMBER);
10✔
706
    case SCHEMA_EXCLUSIVE_MAXIMUM:
9✔
707
      return CheckTypeAndLog(logger, keyword, type, JSON_BOOLEAN);
9✔
708
    case SCHEMA_MINIMUM:
13✔
709
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER, JSON_NUMBER);
13✔
710
    case SCHEMA_EXCLUSIVE_MINIMUM:
9✔
711
      return CheckTypeAndLog(logger, keyword, type, JSON_BOOLEAN);
9✔
712
    case SCHEMA_MAX_LENGTH:
9✔
713
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER);
9✔
714
    case SCHEMA_MIN_LENGTH:
9✔
715
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER);
9✔
716
    case SCHEMA_PATTERN:
1✔
717
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING);
1✔
718
    case SCHEMA_ADDITIONAL_ITEMS:
11✔
719
      return CheckTypeAndLog(logger, keyword, type, JSON_BOOLEAN, JSON_OBJECT);
11✔
720
    case SCHEMA_ITEMS:
53✔
721
      return CheckTypeAndLog(logger, keyword, type, JSON_ARRAY, JSON_OBJECT);
53✔
722
    case SCHEMA_MAX_ITEMS:
8✔
723
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER);
8✔
724
    case SCHEMA_MIN_ITEMS:
15✔
725
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER);
15✔
726
    case SCHEMA_UNIQUE_ITEMS:
13✔
727
      return CheckTypeAndLog(logger, keyword, type, JSON_BOOLEAN);
13✔
728
    case SCHEMA_MAX_PROPERTIES:
10✔
729
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER);
10✔
730
    case SCHEMA_MIN_PROPERTIES:
9✔
731
      return CheckTypeAndLog(logger, keyword, type, JSON_INTEGER);
9✔
732
    case SCHEMA_REQUIRED:
11✔
733
      return CheckTypeAndLog(logger, keyword, type, JSON_ARRAY);
11✔
734
    case SCHEMA_ADDITIONAL_PROPERTIES:
14✔
735
      return CheckTypeAndLog(logger, keyword, type, JSON_BOOLEAN, JSON_OBJECT);
14✔
736
    case SCHEMA_DEFINITIONS:
9✔
737
      return CheckTypeAndLog(logger, keyword, type, JSON_OBJECT);
9✔
738
    case SCHEMA_PROPERTIES:
15✔
739
      return CheckTypeAndLog(logger, keyword, type, JSON_OBJECT);
15✔
740
    case SCHEMA_PATTERN_PROPERTIES:
×
741
      return CheckTypeAndLog(logger, keyword, type, JSON_OBJECT);
×
742
    case SCHEMA_DEPENDENCIES:
13✔
743
      return CheckTypeAndLog(logger, keyword, type, JSON_OBJECT);
13✔
744
    case SCHEMA_ENUM:
16✔
745
      return CheckTypeAndLog(logger, keyword, type, JSON_ARRAY);
16✔
746
    case SCHEMA_TYPE:
154✔
747
      return CheckTypeAndLog(logger, keyword, type, JSON_STRING, JSON_ARRAY);
154✔
748
    case SCHEMA_ALL_OF:
11✔
749
      return CheckTypeAndLog(logger, keyword, type, JSON_ARRAY);
11✔
750
    case SCHEMA_ANY_OF:
14✔
751
      return CheckTypeAndLog(logger, keyword, type, JSON_ARRAY);
14✔
752
    case SCHEMA_ONE_OF:
9✔
753
      return CheckTypeAndLog(logger, keyword, type, JSON_ARRAY);
9✔
754
    case SCHEMA_NOT:
8✔
755
      return CheckTypeAndLog(logger, keyword, type, JSON_OBJECT);
8✔
756
    default:
757
      return false;
758
  }
759
}
760

761
bool SchemaParseContext::CheckTypeAndLog(
290✔
762
    SchemaErrorLogger *logger, SchemaKeyword keyword,
763
    JsonType type, JsonType expected_type) {
764
  if (type == expected_type) {
290✔
765
    return true;
766
  } else {
767
    logger->Error() << "Invalid type for " << KeywordToString(keyword)
278✔
768
        << ", got " << JsonTypeToString(type) << ", expected "
278✔
769
        << JsonTypeToString(expected_type);
556✔
770
    return false;
139✔
771
  }
772
}
773

774
bool SchemaParseContext::CheckTypeAndLog(
264✔
775
    SchemaErrorLogger *logger, SchemaKeyword keyword,
776
    JsonType type, JsonType expected_type1, JsonType expected_type2) {
777
  if (type == expected_type1 || type == expected_type2) {
264✔
778
    return true;
779
  } else {
780
    logger->Error() << "Invalid type for " << KeywordToString(keyword)
66✔
781
        << ", got " << JsonTypeToString(type) << ", expected "
66✔
782
        << JsonTypeToString(expected_type1) << " or "
66✔
783
        << JsonTypeToString(expected_type2);
165✔
784
    return false;
33✔
785
  }
786
}
787

788

789
// PropertiesParseContext
790
// Used for parsing an object with key : json schema pairs, within 'properties'
791
void PropertiesParseContext::AddPropertyValidators(
3✔
792
    ObjectValidator *object_validator,
793
    SchemaErrorLogger *logger) {
794
  SchemaMap::iterator iter = m_property_contexts.begin();
3✔
795
  for (; iter != m_property_contexts.end(); ++iter) {
38✔
796
    ValidatorInterface *validator = iter->second->GetValidator(logger);
35✔
797
    if (validator) {
35✔
798
      object_validator->AddValidator(iter->first, validator);
35✔
799
    }
800
  }
801
}
3✔
802

803
PropertiesParseContext::~PropertiesParseContext() {
18✔
804
  STLDeleteValues(&m_property_contexts);
9✔
805
}
18✔
806

807
SchemaParseContextInterface* PropertiesParseContext::OpenObject(
35✔
808
    SchemaErrorLogger *logger) {
809
  const string key = TakeKeyword();
35✔
810
  pair<SchemaMap::iterator, bool> r = m_property_contexts.insert(
35✔
811
      pair<string, SchemaParseContext*>(key, NULL));
35✔
812

813
  if (r.second) {
35✔
814
    r.first->second = new SchemaParseContext(m_schema_defs);
35✔
815
  } else {
816
    logger->Error() << "Duplicate key " << key;
×
817
  }
818
  return r.first->second;
35✔
819
}
35✔
820

821
// ArrayOfSchemaContext
822
// Used for parsing an array of JSON schema within 'items'
823
ArrayOfSchemaContext::~ArrayOfSchemaContext() {
52✔
824
  STLDeleteElements(&m_item_schemas);
26✔
825
}
66✔
826

827
void ArrayOfSchemaContext::GetValidators(
20✔
828
    SchemaErrorLogger *logger,
829
    ValidatorInterface::ValidatorList *validators) {
830
  ItemSchemas::iterator iter = m_item_schemas.begin();
20✔
831
  for (; iter != m_item_schemas.end(); ++iter) {
49✔
832
    validators->push_back((*iter)->GetValidator(logger));
29✔
833
  }
834
}
20✔
835

836
SchemaParseContextInterface* ArrayOfSchemaContext::OpenObject(
29✔
837
    OLA_UNUSED SchemaErrorLogger *logger) {
838
  m_item_schemas.push_back(new SchemaParseContext(m_schema_defs));
29✔
839
  return m_item_schemas.back();
29✔
840
}
841

842
// ArrayOfStringsContext
843
// Used for parsing an array of strings.
844
void ArrayOfStringsContext::GetStringSet(StringSet *items) {
7✔
845
  *items = m_items;
7✔
846
}
7✔
847

848
void ArrayOfStringsContext::String(SchemaErrorLogger *logger,
10✔
849
                                   const string &value) {
850
  if (!m_items.insert(value).second) {
10✔
851
    logger->Error() << value << " appeared more than once in the array";
2✔
852
  }
853
}
10✔
854

855
// JsonValueContext
856
// Used for parsing a default value.
857
JsonValueContext::JsonValueContext() : SchemaParseContextInterface() {
15✔
858
  m_parser.Begin();
15✔
859
}
15✔
860

861
const JsonValue* JsonValueContext::ClaimValue(SchemaErrorLogger *logger) {
12✔
862
  m_parser.End();
12✔
863
  const JsonValue *value = m_parser.ClaimRoot();
12✔
864
  if (!value) {
12✔
865
    logger->Error() << " is invalid: " << m_parser.GetError();
×
866
  }
867
  return value;
12✔
868
}
869

870
void JsonValueContext::String(SchemaErrorLogger *, const string &value) {
×
871
  m_parser.String(value);
×
872
}
×
873

874
void JsonValueContext::Number(SchemaErrorLogger *, uint32_t value) {
7✔
875
  m_parser.Number(value);
7✔
876
}
7✔
877

878
void JsonValueContext::Number(SchemaErrorLogger *, int32_t value) {
×
879
  m_parser.Number(value);
×
880
}
×
881

882
void JsonValueContext::Number(SchemaErrorLogger *, uint64_t value) {
×
883
  m_parser.Number(value);
×
884
}
×
885

886
void JsonValueContext::Number(SchemaErrorLogger *, int64_t value) {
×
887
  m_parser.Number(value);
×
888
}
×
889

890
void JsonValueContext::Number(SchemaErrorLogger *, double value) {
×
891
  m_parser.Number(value);
×
892
}
×
893

894
void JsonValueContext::Bool(SchemaErrorLogger *, bool value) {
×
895
  m_parser.Bool(value);
×
896
}
×
897

898
void JsonValueContext::Null(OLA_UNUSED SchemaErrorLogger *logger) {
2✔
899
  m_parser.Null();
2✔
900
}
2✔
901

902
SchemaParseContextInterface* JsonValueContext::OpenArray(
5✔
903
    SchemaErrorLogger *) {
904
  m_parser.OpenArray();
5✔
905
  return this;
5✔
906
}
907

908
void JsonValueContext::CloseArray(OLA_UNUSED SchemaErrorLogger *logger) {
3✔
909
  m_parser.CloseArray();
3✔
910
}
3✔
911

912
SchemaParseContextInterface* JsonValueContext::OpenObject(
14✔
913
    SchemaErrorLogger *) {
914
  m_parser.OpenObject();
14✔
915
  return this;
14✔
916
}
917

918
void JsonValueContext::ObjectKey(SchemaErrorLogger *, const string &key) {
4✔
919
  m_parser.ObjectKey(key);
4✔
920
}
4✔
921

922
void JsonValueContext::CloseObject(OLA_UNUSED SchemaErrorLogger *logger) {
11✔
923
  m_parser.CloseObject();
11✔
924
}
11✔
925

926
// ArrayOfJsonValuesContext
927
// Used for parsing a list of enums.
928
ArrayOfJsonValuesContext::~ArrayOfJsonValuesContext() {
18✔
929
  STLDeleteElements(&m_enums);
9✔
930
}
18✔
931

932
void ArrayOfJsonValuesContext::AddEnumsToValidator(BaseValidator *validator) {
6✔
933
  vector<const JsonValue*>::const_iterator iter = m_enums.begin();
6✔
934
  for (; iter != m_enums.end(); ++iter) {
30✔
935
    validator->AddEnumValue(*iter);
24✔
936
  }
937
  m_enums.clear();
6✔
938
}
6✔
939

940
void ArrayOfJsonValuesContext::String(SchemaErrorLogger *logger,
21✔
941
                                      const string &value) {
942
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
21✔
943
}
21✔
944

945
void ArrayOfJsonValuesContext::Number(SchemaErrorLogger *logger,
5✔
946
                                     uint32_t value) {
947
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
5✔
948
}
5✔
949

950
void ArrayOfJsonValuesContext::Number(SchemaErrorLogger *logger,
×
951
                                      int32_t value) {
952
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
×
953
}
×
954

955
void ArrayOfJsonValuesContext::Number(SchemaErrorLogger *logger,
×
956
                                      uint64_t value) {
957
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
×
958
}
×
959

960
void ArrayOfJsonValuesContext::Number(SchemaErrorLogger *logger,
×
961
                                      int64_t value) {
962
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
×
963
}
×
964

965
void ArrayOfJsonValuesContext::Number(SchemaErrorLogger *logger,
×
966
                                      double value) {
967
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
×
968
}
×
969

970
void ArrayOfJsonValuesContext::Bool(SchemaErrorLogger *logger, bool value) {
2✔
971
  CheckForDuplicateAndAdd(logger, JsonValue::NewValue(value));
2✔
972
}
2✔
973

974
void ArrayOfJsonValuesContext::Null(SchemaErrorLogger *logger) {
×
975
  CheckForDuplicateAndAdd(logger, new JsonNull());
×
976
}
×
977

978
SchemaParseContextInterface* ArrayOfJsonValuesContext::OpenArray(
×
979
    OLA_UNUSED SchemaErrorLogger *logger) {
980
  m_value_context.reset(new JsonValueContext());
×
981
  return m_value_context.get();
×
982
}
983

984
void ArrayOfJsonValuesContext::CloseArray(SchemaErrorLogger *logger) {
×
985
  CheckForDuplicateAndAdd(logger, m_value_context->ClaimValue(logger));
×
986
}
×
987

988
SchemaParseContextInterface* ArrayOfJsonValuesContext::OpenObject(
×
989
    OLA_UNUSED SchemaErrorLogger *logger) {
990
  m_value_context.reset(new JsonValueContext());
×
991
  return m_value_context.get();
×
992
}
993

994
void ArrayOfJsonValuesContext::CloseObject(SchemaErrorLogger *logger) {
×
995
  CheckForDuplicateAndAdd(logger, m_value_context->ClaimValue(logger));
×
996
}
×
997

998
void ArrayOfJsonValuesContext::CheckForDuplicateAndAdd(
28✔
999
    SchemaErrorLogger *logger,
1000
    const JsonValue *value) {
1001
  vector<const JsonValue*>::const_iterator iter = m_enums.begin();
28✔
1002
  for (; iter != m_enums.end(); ++iter) {
78✔
1003
    if (**iter == *value) {
52✔
1004
      logger->Error() << "Duplicate entries in enum array: " << value;
2✔
1005
      delete value;
2✔
1006
      return;
2✔
1007
    }
1008
  }
1009
  m_enums.push_back(value);
26✔
1010
}
1011

1012
// ArrayOfJsonValuesContext
1013
// Used for parsing a list of enums.
1014
DependencyParseContext::~DependencyParseContext() {
14✔
1015
  STLDeleteValues(&m_schema_dependencies);
7✔
1016
}
14✔
1017

1018
void DependencyParseContext::AddDependenciesToValidator(
5✔
1019
    ObjectValidator *validator) {
1020

1021
  PropertyDependencies::const_iterator prop_iter =
5✔
1022
    m_property_dependencies.begin();
5✔
1023
  for (; prop_iter != m_property_dependencies.end(); ++prop_iter) {
9✔
1024
    validator->AddPropertyDependency(prop_iter->first, prop_iter->second);
4✔
1025
  }
1026

1027
  // Check Schema Dependencies
1028
  SchemaDependencies::const_iterator schema_iter =
5✔
1029
    m_schema_dependencies.begin();
5✔
1030
  for (; schema_iter != m_schema_dependencies.end(); ++schema_iter) {
7✔
1031
    validator->AddSchemaDependency(schema_iter->first, schema_iter->second);
2✔
1032
  }
1033
  m_schema_dependencies.clear();
5✔
1034
}
5✔
1035

1036
SchemaParseContextInterface* DependencyParseContext::OpenArray(
6✔
1037
    OLA_UNUSED SchemaErrorLogger *logger) {
1038
  m_property_context.reset(new ArrayOfStringsContext());
6✔
1039
  return m_property_context.get();
6✔
1040
}
1041

1042
void DependencyParseContext::CloseArray(SchemaErrorLogger *logger) {
5✔
1043
  StringSet &properties = m_property_dependencies[Keyword()];
5✔
1044
  m_property_context->GetStringSet(&properties);
5✔
1045

1046
  if (properties.empty()) {
5✔
1047
    logger->Error()
1✔
1048
      << " property dependency lists must contain at least one item";
1✔
1049
  }
1050
  m_property_context.reset();
5✔
1051
}
5✔
1052

1053
SchemaParseContextInterface* DependencyParseContext::OpenObject(
2✔
1054
    OLA_UNUSED SchemaErrorLogger *logger) {
1055
  m_schema_context.reset(new SchemaParseContext(m_schema_defs));
2✔
1056
  return m_schema_context.get();
2✔
1057
}
1058

1059
void DependencyParseContext::CloseObject(SchemaErrorLogger *logger) {
2✔
1060
  STLReplaceAndDelete(&m_schema_dependencies, Keyword(),
4✔
1061
                      m_schema_context->GetValidator(logger));
2✔
1062
  m_schema_context.reset();
2✔
1063
}
2✔
1064
}  // namespace web
1065
}  // namespace ola
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc