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