~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/Newtonsoft.Json/Src/Newtonsoft.Json/JsonValidatingReader.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
ļ»æ#region License
 
2
// Copyright (c) 2007 James Newton-King
 
3
//
 
4
// Permission is hereby granted, free of charge, to any person
 
5
// obtaining a copy of this software and associated documentation
 
6
// files (the "Software"), to deal in the Software without
 
7
// restriction, including without limitation the rights to use,
 
8
// copy, modify, merge, publish, distribute, sublicense, and/or sell
 
9
// copies of the Software, and to permit persons to whom the
 
10
// Software is furnished to do so, subject to the following
 
11
// conditions:
 
12
//
 
13
// The above copyright notice and this permission notice shall be
 
14
// included in all copies or substantial portions of the Software.
 
15
//
 
16
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
17
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 
18
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
19
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 
20
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 
21
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
22
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 
23
// OTHER DEALINGS IN THE SOFTWARE.
 
24
#endregion
 
25
 
 
26
using System;
 
27
using System.Collections.Generic;
 
28
using Newtonsoft.Json.Linq;
 
29
using Newtonsoft.Json.Schema;
 
30
using Newtonsoft.Json.Utilities;
 
31
using System.Globalization;
 
32
using System.Text.RegularExpressions;
 
33
using System.IO;
 
34
#if NET20
 
35
using Newtonsoft.Json.Utilities.LinqBridge;
 
36
#else
 
37
using System.Linq;
 
38
#endif
 
39
 
 
40
namespace Newtonsoft.Json
 
41
{
 
42
  /// <summary>
 
43
  /// Represents a reader that provides <see cref="JsonSchema"/> validation.
 
44
  /// </summary>
 
45
  public class JsonValidatingReader : JsonReader, IJsonLineInfo
 
46
  {
 
47
    private class SchemaScope
 
48
    {
 
49
      private readonly JTokenType _tokenType;
 
50
      private readonly IList<JsonSchemaModel> _schemas;
 
51
      private readonly Dictionary<string, bool> _requiredProperties;
 
52
 
 
53
      public string CurrentPropertyName { get; set; }
 
54
      public int ArrayItemCount { get; set; }
 
55
 
 
56
      public IList<JsonSchemaModel> Schemas
 
57
      {
 
58
        get { return _schemas; }
 
59
      }
 
60
 
 
61
      public Dictionary<string, bool> RequiredProperties
 
62
      {
 
63
        get { return _requiredProperties; }
 
64
      }
 
65
 
 
66
      public JTokenType TokenType
 
67
      {
 
68
        get { return _tokenType; }
 
69
      }
 
70
 
 
71
      public SchemaScope(JTokenType tokenType, IList<JsonSchemaModel> schemas)
 
72
      {
 
73
        _tokenType = tokenType;
 
74
        _schemas = schemas;
 
75
 
 
76
        _requiredProperties = schemas.SelectMany<JsonSchemaModel, string>(GetRequiredProperties).Distinct().ToDictionary(p => p, p => false);
 
77
      }
 
78
 
 
79
      private IEnumerable<string> GetRequiredProperties(JsonSchemaModel schema)
 
80
      {
 
81
        if (schema == null || schema.Properties == null)
 
82
          return Enumerable.Empty<string>();
 
83
 
 
84
        return schema.Properties.Where(p => p.Value.Required).Select(p => p.Key);
 
85
      }
 
86
    }
 
87
 
 
88
    private readonly JsonReader _reader;
 
89
    private readonly Stack<SchemaScope> _stack;
 
90
    private JsonSchema _schema;
 
91
    private JsonSchemaModel _model;
 
92
    private SchemaScope _currentScope;
 
93
 
 
94
    /// <summary>
 
95
    /// Sets an event handler for receiving schema validation errors.
 
96
    /// </summary>
 
97
    public event ValidationEventHandler ValidationEventHandler;
 
98
 
 
99
    /// <summary>
 
100
    /// Gets the text value of the current Json token.
 
101
    /// </summary>
 
102
    /// <value></value>
 
103
    public override object Value
 
104
    {
 
105
      get { return _reader.Value; }
 
106
    }
 
107
 
 
108
    /// <summary>
 
109
    /// Gets the depth of the current token in the JSON document.
 
110
    /// </summary>
 
111
    /// <value>The depth of the current token in the JSON document.</value>
 
112
    public override int Depth
 
113
    {
 
114
      get { return _reader.Depth; }
 
115
    }
 
116
 
 
117
    /// <summary>
 
118
    /// Gets the path of the current JSON token. 
 
119
    /// </summary>
 
120
    public override string Path
 
121
    {
 
122
      get { return _reader.Path; }
 
123
    }
 
124
 
 
125
    /// <summary>
 
126
    /// Gets the quotation mark character used to enclose the value of a string.
 
127
    /// </summary>
 
128
    /// <value></value>
 
129
    public override char QuoteChar
 
130
    {
 
131
      get { return _reader.QuoteChar; }
 
132
      protected internal set { }
 
133
    }
 
134
 
 
135
    /// <summary>
 
136
    /// Gets the type of the current Json token.
 
137
    /// </summary>
 
138
    /// <value></value>
 
139
    public override JsonToken TokenType
 
140
    {
 
141
      get { return _reader.TokenType; }
 
142
    }
 
143
 
 
144
    /// <summary>
 
145
    /// Gets the Common Language Runtime (CLR) type for the current Json token.
 
146
    /// </summary>
 
147
    /// <value></value>
 
148
    public override Type ValueType
 
149
    {
 
150
      get { return _reader.ValueType; }
 
151
    }
 
152
 
 
153
    private void Push(SchemaScope scope)
 
154
    {
 
155
      _stack.Push(scope);
 
156
      _currentScope = scope;
 
157
    }
 
158
 
 
159
    private SchemaScope Pop()
 
160
    {
 
161
      SchemaScope poppedScope = _stack.Pop();
 
162
      _currentScope = (_stack.Count != 0)
 
163
        ? _stack.Peek()
 
164
        : null;
 
165
 
 
166
      return poppedScope;
 
167
    }
 
168
 
 
169
    private IEnumerable<JsonSchemaModel> CurrentSchemas
 
170
    {
 
171
      get { return _currentScope.Schemas; }
 
172
    }
 
173
 
 
174
    private IEnumerable<JsonSchemaModel> CurrentMemberSchemas
 
175
    {
 
176
      get
 
177
      {
 
178
        if (_currentScope == null)
 
179
          return new List<JsonSchemaModel>(new [] { _model });
 
180
 
 
181
        if (_currentScope.Schemas == null || _currentScope.Schemas.Count == 0)
 
182
          return Enumerable.Empty<JsonSchemaModel>();
 
183
 
 
184
        switch (_currentScope.TokenType)
 
185
        {
 
186
          case JTokenType.None:
 
187
            return _currentScope.Schemas;
 
188
          case JTokenType.Object:
 
189
            {
 
190
              if (_currentScope.CurrentPropertyName == null)
 
191
                throw new JsonReaderException("CurrentPropertyName has not been set on scope.");
 
192
 
 
193
              IList<JsonSchemaModel> schemas = new List<JsonSchemaModel>();
 
194
 
 
195
              foreach (JsonSchemaModel schema in CurrentSchemas)
 
196
              {
 
197
                JsonSchemaModel propertySchema;
 
198
                if (schema.Properties != null && schema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema))
 
199
                {
 
200
                  schemas.Add(propertySchema);
 
201
                }
 
202
                if (schema.PatternProperties != null)
 
203
                {
 
204
                  foreach (KeyValuePair<string, JsonSchemaModel> patternProperty in schema.PatternProperties)
 
205
                  {
 
206
                    if (Regex.IsMatch(_currentScope.CurrentPropertyName, patternProperty.Key))
 
207
                    {
 
208
                      schemas.Add(patternProperty.Value);
 
209
                    }
 
210
                  }
 
211
                }
 
212
 
 
213
                if (schemas.Count == 0 && schema.AllowAdditionalProperties && schema.AdditionalProperties != null)
 
214
                  schemas.Add(schema.AdditionalProperties);
 
215
              }
 
216
 
 
217
              return schemas;
 
218
            }
 
219
          case JTokenType.Array:
 
220
            {
 
221
              IList<JsonSchemaModel> schemas = new List<JsonSchemaModel>();
 
222
              
 
223
              foreach (JsonSchemaModel schema in CurrentSchemas)
 
224
              {
 
225
                if (!CollectionUtils.IsNullOrEmpty(schema.Items))
 
226
                {
 
227
                  if (schema.Items.Count == 1)
 
228
                  {
 
229
                    schemas.Add(schema.Items[0]);
 
230
                  }
 
231
                  else
 
232
                  {
 
233
                    if (schema.Items.Count > (_currentScope.ArrayItemCount - 1))
 
234
                      schemas.Add(schema.Items[_currentScope.ArrayItemCount - 1]);
 
235
                  }
 
236
                }
 
237
 
 
238
                if (schema.AllowAdditionalProperties && schema.AdditionalProperties != null)
 
239
                  schemas.Add(schema.AdditionalProperties);
 
240
              }
 
241
 
 
242
              return schemas;
 
243
            }
 
244
          case JTokenType.Constructor:
 
245
            return Enumerable.Empty<JsonSchemaModel>();
 
246
          default:
 
247
            throw new ArgumentOutOfRangeException("TokenType", "Unexpected token type: {0}".FormatWith(CultureInfo.InvariantCulture, _currentScope.TokenType));
 
248
        }
 
249
      }
 
250
    }
 
251
 
 
252
    private void RaiseError(string message, JsonSchemaModel schema)
 
253
    {
 
254
      IJsonLineInfo lineInfo = this;
 
255
 
 
256
      string exceptionMessage = (lineInfo.HasLineInfo())
 
257
                                  ? message + " Line {0}, position {1}.".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition)
 
258
                                  : message;
 
259
 
 
260
      OnValidationEvent(new JsonSchemaException(exceptionMessage, null, Path, lineInfo.LineNumber, lineInfo.LinePosition));
 
261
    }
 
262
 
 
263
    private void OnValidationEvent(JsonSchemaException exception)
 
264
    {
 
265
      ValidationEventHandler handler = ValidationEventHandler;
 
266
      if (handler != null)
 
267
        handler(this, new ValidationEventArgs(exception));
 
268
      else
 
269
        throw exception;
 
270
    }
 
271
 
 
272
    /// <summary>
 
273
    /// Initializes a new instance of the <see cref="JsonValidatingReader"/> class that
 
274
    /// validates the content returned from the given <see cref="JsonReader"/>.
 
275
    /// </summary>
 
276
    /// <param name="reader">The <see cref="JsonReader"/> to read from while validating.</param>
 
277
    public JsonValidatingReader(JsonReader reader)
 
278
    {
 
279
      ValidationUtils.ArgumentNotNull(reader, "reader");
 
280
      _reader = reader;
 
281
      _stack = new Stack<SchemaScope>();
 
282
    }
 
283
 
 
284
    /// <summary>
 
285
    /// Gets or sets the schema.
 
286
    /// </summary>
 
287
    /// <value>The schema.</value>
 
288
    public JsonSchema Schema
 
289
    {
 
290
      get { return _schema; }
 
291
      set
 
292
      {
 
293
        if (TokenType != JsonToken.None)
 
294
          throw new InvalidOperationException("Cannot change schema while validating JSON.");
 
295
 
 
296
        _schema = value;
 
297
        _model = null;
 
298
      }
 
299
    }
 
300
 
 
301
    /// <summary>
 
302
    /// Gets the <see cref="JsonReader"/> used to construct this <see cref="JsonValidatingReader"/>.
 
303
    /// </summary>
 
304
    /// <value>The <see cref="JsonReader"/> specified in the constructor.</value>
 
305
    public JsonReader Reader
 
306
    {
 
307
      get { return _reader; }
 
308
    }
 
309
 
 
310
    private void ValidateInEnumAndNotDisallowed(JsonSchemaModel schema)
 
311
    {
 
312
      if (schema == null)
 
313
        return;
 
314
 
 
315
      JToken value = new JValue(_reader.Value);
 
316
 
 
317
      if (schema.Enum != null)
 
318
      {
 
319
        StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
 
320
        value.WriteTo(new JsonTextWriter(sw));
 
321
 
 
322
        if (!schema.Enum.ContainsValue(value, new JTokenEqualityComparer()))
 
323
          RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()),
 
324
                     schema);
 
325
      }
 
326
 
 
327
      JsonSchemaType? currentNodeType = GetCurrentNodeSchemaType();
 
328
      if (currentNodeType != null)
 
329
      {
 
330
        if (JsonSchemaGenerator.HasFlag(schema.Disallow, currentNodeType.Value))
 
331
          RaiseError("Type {0} is disallowed.".FormatWith(CultureInfo.InvariantCulture, currentNodeType), schema);
 
332
      }
 
333
    }
 
334
 
 
335
    private JsonSchemaType? GetCurrentNodeSchemaType()
 
336
    {
 
337
      switch (_reader.TokenType)
 
338
      {
 
339
        case JsonToken.StartObject:
 
340
          return JsonSchemaType.Object;
 
341
        case JsonToken.StartArray:
 
342
          return JsonSchemaType.Array;
 
343
        case JsonToken.Integer:
 
344
          return JsonSchemaType.Integer;
 
345
        case JsonToken.Float:
 
346
          return JsonSchemaType.Float;
 
347
        case JsonToken.String:
 
348
          return JsonSchemaType.String;
 
349
        case JsonToken.Boolean:
 
350
          return JsonSchemaType.Boolean;
 
351
        case JsonToken.Null:
 
352
          return JsonSchemaType.Null;
 
353
        default:
 
354
          return null;
 
355
      }
 
356
    }
 
357
 
 
358
    /// <summary>
 
359
    /// Reads the next JSON token from the stream as a <see cref="Nullable{Int32}"/>.
 
360
    /// </summary>
 
361
    /// <returns>A <see cref="Nullable{Int32}"/>.</returns>
 
362
    public override int? ReadAsInt32()
 
363
    {
 
364
      int? i = _reader.ReadAsInt32();
 
365
 
 
366
      ValidateCurrentToken();
 
367
      return i;
 
368
    }
 
369
 
 
370
    /// <summary>
 
371
    /// Reads the next JSON token from the stream as a <see cref="T:Byte[]"/>.
 
372
    /// </summary>
 
373
    /// <returns>
 
374
    /// A <see cref="T:Byte[]"/> or a null reference if the next JSON token is null.
 
375
    /// </returns>
 
376
    public override byte[] ReadAsBytes()
 
377
    {
 
378
      byte[] data = _reader.ReadAsBytes();
 
379
 
 
380
      ValidateCurrentToken();
 
381
      return data;
 
382
    }
 
383
 
 
384
    /// <summary>
 
385
    /// Reads the next JSON token from the stream as a <see cref="Nullable{Decimal}"/>.
 
386
    /// </summary>
 
387
    /// <returns>A <see cref="Nullable{Decimal}"/>.</returns>
 
388
    public override decimal? ReadAsDecimal()
 
389
    {
 
390
      decimal? d = _reader.ReadAsDecimal();
 
391
 
 
392
      ValidateCurrentToken();
 
393
      return d;
 
394
    }
 
395
 
 
396
    /// <summary>
 
397
    /// Reads the next JSON token from the stream as a <see cref="String"/>.
 
398
    /// </summary>
 
399
    /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
 
400
    public override string ReadAsString()
 
401
    {
 
402
      string s = _reader.ReadAsString();
 
403
 
 
404
      ValidateCurrentToken();
 
405
      return s;
 
406
    }
 
407
 
 
408
    /// <summary>
 
409
    /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTime}"/>.
 
410
    /// </summary>
 
411
    /// <returns>A <see cref="String"/>. This method will return <c>null</c> at the end of an array.</returns>
 
412
    public override DateTime? ReadAsDateTime()
 
413
    {
 
414
      DateTime? dateTime = _reader.ReadAsDateTime();
 
415
 
 
416
      ValidateCurrentToken();
 
417
      return dateTime;
 
418
    }
 
419
 
 
420
#if !NET20
 
421
    /// <summary>
 
422
    /// Reads the next JSON token from the stream as a <see cref="Nullable{DateTimeOffset}"/>.
 
423
    /// </summary>
 
424
    /// <returns>A <see cref="Nullable{DateTimeOffset}"/>.</returns>
 
425
    public override DateTimeOffset? ReadAsDateTimeOffset()
 
426
    {
 
427
      DateTimeOffset? dateTimeOffset = _reader.ReadAsDateTimeOffset();
 
428
 
 
429
      ValidateCurrentToken();
 
430
      return dateTimeOffset;
 
431
    }
 
432
#endif
 
433
 
 
434
    /// <summary>
 
435
    /// Reads the next JSON token from the stream.
 
436
    /// </summary>
 
437
    /// <returns>
 
438
    /// true if the next token was read successfully; false if there are no more tokens to read.
 
439
    /// </returns>
 
440
    public override bool Read()
 
441
    {
 
442
      if (!_reader.Read())
 
443
        return false;
 
444
 
 
445
      if (_reader.TokenType == JsonToken.Comment)
 
446
        return true;
 
447
 
 
448
      ValidateCurrentToken();
 
449
      return true;
 
450
    }
 
451
 
 
452
    private void ValidateCurrentToken()
 
453
    {
 
454
      // first time validate has been called. build model
 
455
      if (_model == null)
 
456
      {
 
457
        JsonSchemaModelBuilder builder = new JsonSchemaModelBuilder();
 
458
        _model = builder.Build(_schema);
 
459
      }
 
460
 
 
461
      switch (_reader.TokenType)
 
462
      {
 
463
        case JsonToken.StartObject:
 
464
          ProcessValue();
 
465
          IList<JsonSchemaModel> objectSchemas = CurrentMemberSchemas.Where(ValidateObject).ToList();
 
466
          Push(new SchemaScope(JTokenType.Object, objectSchemas));
 
467
          break;
 
468
        case JsonToken.StartArray:
 
469
          ProcessValue();
 
470
          IList<JsonSchemaModel> arraySchemas = CurrentMemberSchemas.Where(ValidateArray).ToList();
 
471
          Push(new SchemaScope(JTokenType.Array, arraySchemas));
 
472
          break;
 
473
        case JsonToken.StartConstructor:
 
474
          Push(new SchemaScope(JTokenType.Constructor, null));
 
475
          break;
 
476
        case JsonToken.PropertyName:
 
477
          foreach (JsonSchemaModel schema in CurrentSchemas)
 
478
          {
 
479
            ValidatePropertyName(schema);
 
480
          }
 
481
          break;
 
482
        case JsonToken.Raw:
 
483
          break;
 
484
        case JsonToken.Integer:
 
485
          ProcessValue();
 
486
          foreach (JsonSchemaModel schema in CurrentMemberSchemas)
 
487
          {
 
488
            ValidateInteger(schema);
 
489
          }
 
490
          break;
 
491
        case JsonToken.Float:
 
492
          ProcessValue();
 
493
          foreach (JsonSchemaModel schema in CurrentMemberSchemas)
 
494
          {
 
495
            ValidateFloat(schema);
 
496
          }
 
497
          break;
 
498
        case JsonToken.String:
 
499
          ProcessValue();
 
500
          foreach (JsonSchemaModel schema in CurrentMemberSchemas)
 
501
          {
 
502
            ValidateString(schema);
 
503
          }
 
504
          break;
 
505
        case JsonToken.Boolean:
 
506
          ProcessValue();
 
507
          foreach (JsonSchemaModel schema in CurrentMemberSchemas)
 
508
          {
 
509
            ValidateBoolean(schema);
 
510
          }
 
511
          break;
 
512
        case JsonToken.Null:
 
513
          ProcessValue();
 
514
          foreach (JsonSchemaModel schema in CurrentMemberSchemas)
 
515
          {
 
516
            ValidateNull(schema);
 
517
          }
 
518
          break;
 
519
        case JsonToken.Undefined:
 
520
          break;
 
521
        case JsonToken.EndObject:
 
522
          foreach (JsonSchemaModel schema in CurrentSchemas)
 
523
          {
 
524
            ValidateEndObject(schema);
 
525
          }
 
526
          Pop();
 
527
          break;
 
528
        case JsonToken.EndArray:
 
529
          foreach (JsonSchemaModel schema in CurrentSchemas)
 
530
          {
 
531
            ValidateEndArray(schema);
 
532
          }
 
533
          Pop();
 
534
          break;
 
535
        case JsonToken.EndConstructor:
 
536
          Pop();
 
537
          break;
 
538
        case JsonToken.Date:
 
539
        case JsonToken.Bytes:
 
540
          // these have no equivalent in JSON schema
 
541
          break;
 
542
        case JsonToken.None:
 
543
          // no content, do nothing
 
544
          break;
 
545
        default:
 
546
          throw new ArgumentOutOfRangeException();
 
547
      }
 
548
    }
 
549
 
 
550
    private void ValidateEndObject(JsonSchemaModel schema)
 
551
    {
 
552
      if (schema == null)
 
553
        return;
 
554
 
 
555
      Dictionary<string, bool> requiredProperties = _currentScope.RequiredProperties;
 
556
 
 
557
      if (requiredProperties != null)
 
558
      {
 
559
        List<string> unmatchedRequiredProperties =
 
560
          requiredProperties.Where(kv => !kv.Value).Select(kv => kv.Key).ToList();
 
561
 
 
562
        if (unmatchedRequiredProperties.Count > 0)
 
563
          RaiseError("Required properties are missing from object: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", unmatchedRequiredProperties.ToArray())), schema);
 
564
      }
 
565
    }
 
566
 
 
567
    private void ValidateEndArray(JsonSchemaModel schema)
 
568
    {
 
569
      if (schema == null)
 
570
        return;
 
571
 
 
572
      int arrayItemCount = _currentScope.ArrayItemCount;
 
573
 
 
574
      if (schema.MaximumItems != null && arrayItemCount > schema.MaximumItems)
 
575
        RaiseError("Array item count {0} exceeds maximum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MaximumItems), schema);
 
576
 
 
577
      if (schema.MinimumItems != null && arrayItemCount < schema.MinimumItems)
 
578
        RaiseError("Array item count {0} is less than minimum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MinimumItems), schema);
 
579
    }
 
580
 
 
581
    private void ValidateNull(JsonSchemaModel schema)
 
582
    {
 
583
      if (schema == null)
 
584
        return;
 
585
 
 
586
      if (!TestType(schema, JsonSchemaType.Null))
 
587
        return;
 
588
 
 
589
      ValidateInEnumAndNotDisallowed(schema);
 
590
    }
 
591
 
 
592
    private void ValidateBoolean(JsonSchemaModel schema)
 
593
    {
 
594
      if (schema == null)
 
595
        return;
 
596
 
 
597
      if (!TestType(schema, JsonSchemaType.Boolean))
 
598
        return;
 
599
 
 
600
      ValidateInEnumAndNotDisallowed(schema);
 
601
    }
 
602
 
 
603
    private void ValidateString(JsonSchemaModel schema)
 
604
    {
 
605
      if (schema == null)
 
606
        return;
 
607
 
 
608
      if (!TestType(schema, JsonSchemaType.String))
 
609
        return;
 
610
 
 
611
      ValidateInEnumAndNotDisallowed(schema);
 
612
 
 
613
      string value = _reader.Value.ToString();
 
614
 
 
615
      if (schema.MaximumLength != null && value.Length > schema.MaximumLength)
 
616
        RaiseError("String '{0}' exceeds maximum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MaximumLength), schema);
 
617
 
 
618
      if (schema.MinimumLength != null && value.Length < schema.MinimumLength)
 
619
        RaiseError("String '{0}' is less than minimum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MinimumLength), schema);
 
620
 
 
621
      if (schema.Patterns != null)
 
622
      {
 
623
        foreach (string pattern in schema.Patterns)
 
624
        {
 
625
          if (!Regex.IsMatch(value, pattern))
 
626
            RaiseError("String '{0}' does not match regex pattern '{1}'.".FormatWith(CultureInfo.InvariantCulture, value, pattern), schema);
 
627
        }
 
628
      }
 
629
    }
 
630
 
 
631
    private void ValidateInteger(JsonSchemaModel schema)
 
632
    {
 
633
      if (schema == null)
 
634
        return;
 
635
 
 
636
      if (!TestType(schema, JsonSchemaType.Integer))
 
637
        return;
 
638
 
 
639
      ValidateInEnumAndNotDisallowed(schema);
 
640
      
 
641
      long value = Convert.ToInt64(_reader.Value, CultureInfo.InvariantCulture);
 
642
 
 
643
      if (schema.Maximum != null)
 
644
      {
 
645
        if (value > schema.Maximum)
 
646
          RaiseError("Integer {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema);
 
647
        if (schema.ExclusiveMaximum && value == schema.Maximum)
 
648
          RaiseError("Integer {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema);
 
649
      }
 
650
 
 
651
      if (schema.Minimum != null)
 
652
      {
 
653
        if (value < schema.Minimum)
 
654
          RaiseError("Integer {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema);
 
655
        if (schema.ExclusiveMinimum && value == schema.Minimum)
 
656
          RaiseError("Integer {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema);
 
657
      }
 
658
 
 
659
      if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value))
 
660
        RaiseError("Integer {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema);
 
661
    }
 
662
 
 
663
    private void ProcessValue()
 
664
    {
 
665
      if (_currentScope != null && _currentScope.TokenType == JTokenType.Array)
 
666
      {
 
667
        _currentScope.ArrayItemCount++;
 
668
 
 
669
        foreach (JsonSchemaModel currentSchema in CurrentSchemas)
 
670
        {
 
671
          if (currentSchema != null && currentSchema.Items != null && currentSchema.Items.Count > 1 && _currentScope.ArrayItemCount >= currentSchema.Items.Count)
 
672
            RaiseError("Index {0} has not been defined and the schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), currentSchema);
 
673
        }
 
674
      }
 
675
    }
 
676
 
 
677
    private void ValidateFloat(JsonSchemaModel schema)
 
678
    {
 
679
      if (schema == null)
 
680
        return;
 
681
 
 
682
      if (!TestType(schema, JsonSchemaType.Float))
 
683
        return;
 
684
 
 
685
      ValidateInEnumAndNotDisallowed(schema);
 
686
      
 
687
      double value = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture);
 
688
 
 
689
      if (schema.Maximum != null)
 
690
      {
 
691
        if (value > schema.Maximum)
 
692
          RaiseError("Float {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema);
 
693
        if (schema.ExclusiveMaximum && value == schema.Maximum)
 
694
          RaiseError("Float {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema);
 
695
      }
 
696
 
 
697
      if (schema.Minimum != null)
 
698
      {
 
699
        if (value < schema.Minimum)
 
700
          RaiseError("Float {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema);
 
701
        if (schema.ExclusiveMinimum && value == schema.Minimum)
 
702
          RaiseError("Float {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema);
 
703
      }
 
704
 
 
705
      if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value))
 
706
        RaiseError("Float {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema);
 
707
    }
 
708
 
 
709
    private static bool IsZero(double value)
 
710
    {
 
711
      const double epsilon = 2.2204460492503131e-016;
 
712
 
 
713
      return Math.Abs(value) < 10.0 * epsilon;
 
714
    }
 
715
 
 
716
    private void ValidatePropertyName(JsonSchemaModel schema)
 
717
    {
 
718
      if (schema == null)
 
719
        return;
 
720
 
 
721
      string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture);
 
722
 
 
723
      if (_currentScope.RequiredProperties.ContainsKey(propertyName))
 
724
        _currentScope.RequiredProperties[propertyName] = true;
 
725
 
 
726
      if (!schema.AllowAdditionalProperties)
 
727
      {
 
728
        bool propertyDefinied = IsPropertyDefinied(schema, propertyName);
 
729
 
 
730
        if (!propertyDefinied)
 
731
          RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema);
 
732
      }
 
733
 
 
734
      _currentScope.CurrentPropertyName = propertyName;
 
735
    }
 
736
 
 
737
    private bool IsPropertyDefinied(JsonSchemaModel schema, string propertyName)
 
738
    {
 
739
      if (schema.Properties != null && schema.Properties.ContainsKey(propertyName))
 
740
        return true;
 
741
 
 
742
      if (schema.PatternProperties != null)
 
743
      {
 
744
        foreach (string pattern in schema.PatternProperties.Keys)
 
745
        {
 
746
          if (Regex.IsMatch(propertyName, pattern))
 
747
            return true;
 
748
        }
 
749
      }
 
750
 
 
751
      return false;
 
752
    }
 
753
 
 
754
    private bool ValidateArray(JsonSchemaModel schema)
 
755
    {
 
756
      if (schema == null)
 
757
        return true;
 
758
 
 
759
      return (TestType(schema, JsonSchemaType.Array));
 
760
    }
 
761
 
 
762
    private bool ValidateObject(JsonSchemaModel schema)
 
763
    {
 
764
      if (schema == null)
 
765
        return true;
 
766
 
 
767
      return (TestType(schema, JsonSchemaType.Object));
 
768
    }
 
769
 
 
770
    private bool TestType(JsonSchemaModel currentSchema, JsonSchemaType currentType)
 
771
    {
 
772
      if (!JsonSchemaGenerator.HasFlag(currentSchema.Type, currentType))
 
773
      {
 
774
        RaiseError("Invalid type. Expected {0} but got {1}.".FormatWith(CultureInfo.InvariantCulture, currentSchema.Type, currentType), currentSchema);
 
775
        return false;
 
776
      }
 
777
 
 
778
      return true;
 
779
    }
 
780
 
 
781
    bool IJsonLineInfo.HasLineInfo()
 
782
    {
 
783
      IJsonLineInfo lineInfo = _reader as IJsonLineInfo;
 
784
      return lineInfo != null && lineInfo.HasLineInfo();
 
785
    }
 
786
 
 
787
    int IJsonLineInfo.LineNumber
 
788
    {
 
789
      get
 
790
      {
 
791
        IJsonLineInfo lineInfo = _reader as IJsonLineInfo;
 
792
        return (lineInfo != null) ? lineInfo.LineNumber : 0;
 
793
      }
 
794
    }
 
795
 
 
796
    int IJsonLineInfo.LinePosition
 
797
    {
 
798
      get
 
799
      {
 
800
        IJsonLineInfo lineInfo = _reader as IJsonLineInfo;
 
801
        return (lineInfo != null) ? lineInfo.LinePosition : 0;
 
802
      }
 
803
    }
 
804
  }
 
805
}
 
 
b'\\ No newline at end of file'