~ubuntu-branches/ubuntu/trusty/smuxi/trusty-proposed

« back to all changes in this revision

Viewing changes to lib/Newtonsoft.Json/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs

  • Committer: Package Import Robot
  • Author(s): Mirco Bauer
  • Date: 2013-05-25 22:11:31 UTC
  • mfrom: (1.2.12)
  • Revision ID: package-import@ubuntu.com-20130525221131-nd2mc0kzubuwyx20
Tags: 0.8.11-1
* [22d13d5] Imported Upstream version 0.8.11
* [6d2b95a] Refreshed patches
* [89eb66e] Added ServiceStack libraries to smuxi-engine package
* [848ab10] Enable Campfire engine
* [c6dbdc7] Always build db4o for predictable build result
* [13ec489] Exclude OS X specific libraries from dh_clideps

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.Linq;
28
 
using System.Globalization;
29
 
using System.ComponentModel;
30
 
using System.Collections.Generic;
31
 
using Newtonsoft.Json.Linq;
32
 
using Newtonsoft.Json.Utilities;
33
 
using Newtonsoft.Json.Serialization;
34
 
 
35
 
namespace Newtonsoft.Json.Schema
36
 
{
37
 
  /// <summary>
38
 
  /// Generates a <see cref="JsonSchema"/> from a specified <see cref="Type"/>.
39
 
  /// </summary>
40
 
  public class JsonSchemaGenerator
41
 
  {
42
 
    /// <summary>
43
 
    /// Gets or sets how undefined schemas are handled by the serializer.
44
 
    /// </summary>
45
 
    public UndefinedSchemaIdHandling UndefinedSchemaIdHandling { get; set; }
46
 
 
47
 
    private IContractResolver _contractResolver;
48
 
    /// <summary>
49
 
    /// Gets or sets the contract resolver.
50
 
    /// </summary>
51
 
    /// <value>The contract resolver.</value>
52
 
    public IContractResolver ContractResolver
53
 
    {
54
 
      get
55
 
      {
56
 
        if (_contractResolver == null)
57
 
          return DefaultContractResolver.Instance;
58
 
 
59
 
        return _contractResolver;
60
 
      }
61
 
      set { _contractResolver = value; }
62
 
    }
63
 
 
64
 
    private class TypeSchema
65
 
    {
66
 
      public Type Type { get; private set; }
67
 
      public JsonSchema Schema { get; private set;}
68
 
 
69
 
      public TypeSchema(Type type, JsonSchema schema)
70
 
      {
71
 
        ValidationUtils.ArgumentNotNull(type, "type");
72
 
        ValidationUtils.ArgumentNotNull(schema, "schema");
73
 
 
74
 
        Type = type;
75
 
        Schema = schema;
76
 
      }
77
 
    }
78
 
 
79
 
    private JsonSchemaResolver _resolver;
80
 
    private IList<TypeSchema> _stack = new List<TypeSchema>();
81
 
    private JsonSchema _currentSchema;
82
 
 
83
 
    private JsonSchema CurrentSchema
84
 
    {
85
 
      get { return _currentSchema; }
86
 
    }
87
 
 
88
 
    private void Push(TypeSchema typeSchema)
89
 
    {
90
 
      _currentSchema = typeSchema.Schema;
91
 
      _stack.Add(typeSchema);
92
 
      _resolver.LoadedSchemas.Add(typeSchema.Schema);
93
 
    }
94
 
 
95
 
    private TypeSchema Pop()
96
 
    {
97
 
      TypeSchema popped = _stack[_stack.Count - 1];
98
 
      _stack.RemoveAt(_stack.Count - 1);
99
 
      TypeSchema newValue = _stack.LastOrDefault();
100
 
      if (newValue != null)
101
 
      {
102
 
        _currentSchema = newValue.Schema;
103
 
      }
104
 
      else
105
 
      {
106
 
        _currentSchema = null;
107
 
      }
108
 
 
109
 
      return popped;
110
 
    }
111
 
 
112
 
    /// <summary>
113
 
    /// Generate a <see cref="JsonSchema"/> from the specified type.
114
 
    /// </summary>
115
 
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
116
 
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
117
 
    public JsonSchema Generate(Type type)
118
 
    {
119
 
      return Generate(type, new JsonSchemaResolver(), false);
120
 
    }
121
 
 
122
 
    /// <summary>
123
 
    /// Generate a <see cref="JsonSchema"/> from the specified type.
124
 
    /// </summary>
125
 
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
126
 
    /// <param name="resolver">The <see cref="JsonSchemaResolver"/> used to resolve schema references.</param>
127
 
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
128
 
    public JsonSchema Generate(Type type, JsonSchemaResolver resolver)
129
 
    {
130
 
      return Generate(type, resolver, false);
131
 
    }
132
 
 
133
 
    /// <summary>
134
 
    /// Generate a <see cref="JsonSchema"/> from the specified type.
135
 
    /// </summary>
136
 
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
137
 
    /// <param name="rootSchemaNullable">Specify whether the generated root <see cref="JsonSchema"/> will be nullable.</param>
138
 
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
139
 
    public JsonSchema Generate(Type type, bool rootSchemaNullable)
140
 
    {
141
 
      return Generate(type, new JsonSchemaResolver(), rootSchemaNullable);
142
 
    }
143
 
 
144
 
    /// <summary>
145
 
    /// Generate a <see cref="JsonSchema"/> from the specified type.
146
 
    /// </summary>
147
 
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
148
 
    /// <param name="resolver">The <see cref="JsonSchemaResolver"/> used to resolve schema references.</param>
149
 
    /// <param name="rootSchemaNullable">Specify whether the generated root <see cref="JsonSchema"/> will be nullable.</param>
150
 
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
151
 
    public JsonSchema Generate(Type type, JsonSchemaResolver resolver, bool rootSchemaNullable)
152
 
    {
153
 
      ValidationUtils.ArgumentNotNull(type, "type");
154
 
      ValidationUtils.ArgumentNotNull(resolver, "resolver");
155
 
 
156
 
      _resolver = resolver;
157
 
 
158
 
      return GenerateInternal(type, (!rootSchemaNullable) ? Required.Always : Required.Default, false);
159
 
    }
160
 
 
161
 
    private string GetTitle(Type type)
162
 
    {
163
 
      JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type);
164
 
 
165
 
      if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Title))
166
 
        return containerAttribute.Title;
167
 
 
168
 
      return null;
169
 
    }
170
 
 
171
 
    private string GetDescription(Type type)
172
 
    {
173
 
      JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type);
174
 
 
175
 
      if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Description))
176
 
        return containerAttribute.Description;
177
 
 
178
 
#if !PocketPC
179
 
      DescriptionAttribute descriptionAttribute = ReflectionUtils.GetAttribute<DescriptionAttribute>(type);
180
 
      if (descriptionAttribute != null)
181
 
        return descriptionAttribute.Description;
182
 
#endif
183
 
 
184
 
      return null;
185
 
    }
186
 
 
187
 
    private string GetTypeId(Type type, bool explicitOnly)
188
 
    {
189
 
      JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type);
190
 
 
191
 
      if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Id))
192
 
        return containerAttribute.Id;
193
 
 
194
 
      if (explicitOnly)
195
 
        return null;
196
 
 
197
 
      switch (UndefinedSchemaIdHandling)
198
 
      {
199
 
        case UndefinedSchemaIdHandling.UseTypeName:
200
 
          return type.FullName;
201
 
        case UndefinedSchemaIdHandling.UseAssemblyQualifiedName:
202
 
          return type.AssemblyQualifiedName;
203
 
        default:
204
 
          return null;
205
 
      }
206
 
    }
207
 
 
208
 
    private JsonSchema GenerateInternal(Type type, Required valueRequired, bool optional)
209
 
    {
210
 
      ValidationUtils.ArgumentNotNull(type, "type");
211
 
 
212
 
      string resolvedId = GetTypeId(type, false);
213
 
      string explicitId = GetTypeId(type, true);
214
 
 
215
 
      if (!string.IsNullOrEmpty(resolvedId))
216
 
      {
217
 
        JsonSchema resolvedSchema = _resolver.GetSchema(resolvedId);
218
 
        if (resolvedSchema != null)
219
 
        {
220
 
          // resolved schema is not null but referencing member allows nulls
221
 
          // change resolved schema to allow nulls. hacky but what are ya gonna do?
222
 
          if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null))
223
 
            resolvedSchema.Type |= JsonSchemaType.Null;
224
 
          if (optional && resolvedSchema.Optional != true)
225
 
            resolvedSchema.Optional = true;
226
 
 
227
 
          return resolvedSchema;
228
 
        }
229
 
      }
230
 
 
231
 
      // test for unresolved circular reference
232
 
      if (_stack.Any(tc => tc.Type == type))
233
 
      {
234
 
        throw new Exception("Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.".FormatWith(CultureInfo.InvariantCulture, type));
235
 
      }
236
 
 
237
 
      JsonContract contract = ContractResolver.ResolveContract(type);
238
 
      JsonConverter converter;
239
 
      if ((converter = contract.Converter) != null || (converter = contract.InternalConverter) != null)
240
 
      {
241
 
        JsonSchema converterSchema = converter.GetSchema();
242
 
        if (converterSchema != null)
243
 
          return converterSchema;
244
 
      }
245
 
 
246
 
      Push(new TypeSchema(type, new JsonSchema()));
247
 
 
248
 
      if (explicitId != null)
249
 
        CurrentSchema.Id = explicitId;
250
 
 
251
 
      if (optional)
252
 
        CurrentSchema.Optional = true;
253
 
      CurrentSchema.Title = GetTitle(type);
254
 
      CurrentSchema.Description = GetDescription(type);
255
 
 
256
 
      if (converter != null)
257
 
      {
258
 
        // todo: Add GetSchema to JsonConverter and use here?
259
 
        CurrentSchema.Type = JsonSchemaType.Any;
260
 
      }
261
 
      else if (contract is JsonDictionaryContract)
262
 
      {
263
 
        CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
264
 
 
265
 
        Type keyType;
266
 
        Type valueType;
267
 
        ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType);
268
 
 
269
 
        if (keyType != null)
270
 
        {
271
 
          // can be converted to a string
272
 
          if (typeof (IConvertible).IsAssignableFrom(keyType))
273
 
          {
274
 
            CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false);
275
 
          }
276
 
        }
277
 
      }
278
 
      else if (contract is JsonArrayContract)
279
 
      {
280
 
        CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired);
281
 
 
282
 
        CurrentSchema.Id = GetTypeId(type, false);
283
 
 
284
 
        JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute;
285
 
        bool allowNullItem = (arrayAttribute != null) ? arrayAttribute.AllowNullItems : true;
286
 
 
287
 
        Type collectionItemType = ReflectionUtils.GetCollectionItemType(type);
288
 
        if (collectionItemType != null)
289
 
        {
290
 
          CurrentSchema.Items = new List<JsonSchema>();
291
 
          CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default, false));
292
 
        }
293
 
      }
294
 
      else if (contract is JsonPrimitiveContract)
295
 
      {
296
 
        CurrentSchema.Type = GetJsonSchemaType(type, valueRequired);
297
 
 
298
 
        if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof(FlagsAttribute), true))
299
 
        {
300
 
          CurrentSchema.Enum = new List<JToken>();
301
 
          CurrentSchema.Options = new Dictionary<JToken, string>();
302
 
 
303
 
          EnumValues<long> enumValues = EnumUtils.GetNamesAndValues<long>(type);
304
 
          foreach (EnumValue<long> enumValue in enumValues)
305
 
          {
306
 
            JToken value = JToken.FromObject(enumValue.Value);
307
 
 
308
 
            CurrentSchema.Enum.Add(value);
309
 
            CurrentSchema.Options.Add(value, enumValue.Name);
310
 
          }
311
 
        }
312
 
      }
313
 
      else if (contract is JsonObjectContract)
314
 
      {
315
 
        CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
316
 
        CurrentSchema.Id = GetTypeId(type, false);
317
 
        GenerateObjectSchema(type, (JsonObjectContract)contract);
318
 
      }
319
 
#if !SILVERLIGHT && !PocketPC
320
 
      else if (contract is JsonISerializableContract)
321
 
      {
322
 
        CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
323
 
        CurrentSchema.Id = GetTypeId(type, false);
324
 
        GenerateISerializableContract(type, (JsonISerializableContract) contract);
325
 
      }
326
 
#endif
327
 
      else if (contract is JsonStringContract)
328
 
      {
329
 
        JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType))
330
 
                                      ? JsonSchemaType.String
331
 
                                      : AddNullType(JsonSchemaType.String, valueRequired);
332
 
 
333
 
        CurrentSchema.Type = schemaType;
334
 
      }
335
 
      else if (contract is JsonLinqContract)
336
 
      {
337
 
        CurrentSchema.Type = JsonSchemaType.Any;
338
 
      }
339
 
      else
340
 
      {
341
 
        throw new Exception("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract));
342
 
      }
343
 
 
344
 
      return Pop().Schema;
345
 
    }
346
 
 
347
 
    private JsonSchemaType AddNullType(JsonSchemaType type, Required valueRequired)
348
 
    {
349
 
      if (valueRequired != Required.Always)
350
 
        return type | JsonSchemaType.Null;
351
 
 
352
 
      return type;
353
 
    }
354
 
 
355
 
    private void GenerateObjectSchema(Type type, JsonObjectContract contract)
356
 
    {
357
 
      CurrentSchema.Properties = new Dictionary<string, JsonSchema>();
358
 
      foreach (JsonProperty property in contract.Properties)
359
 
      {
360
 
        if (!property.Ignored)
361
 
        {
362
 
          bool optional = property.NullValueHandling == NullValueHandling.Ignore ||
363
 
                          property.DefaultValueHandling == DefaultValueHandling.Ignore ||
364
 
                          property.ShouldSerialize != null;
365
 
 
366
 
          JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required, optional);
367
 
 
368
 
          if (property.DefaultValue != null)
369
 
            propertySchema.Default = JToken.FromObject(property.DefaultValue);
370
 
 
371
 
          CurrentSchema.Properties.Add(property.PropertyName, propertySchema);
372
 
        }
373
 
      }
374
 
 
375
 
      if (type.IsSealed)
376
 
        CurrentSchema.AllowAdditionalProperties = false;
377
 
    }
378
 
 
379
 
#if !SILVERLIGHT && !PocketPC
380
 
    private void GenerateISerializableContract(Type type, JsonISerializableContract contract)
381
 
    {
382
 
      CurrentSchema.AllowAdditionalProperties = true;
383
 
    }
384
 
#endif
385
 
 
386
 
    internal static bool HasFlag(JsonSchemaType? value, JsonSchemaType flag)
387
 
    {
388
 
      // default value is Any
389
 
      if (value == null)
390
 
        return true;
391
 
 
392
 
      return ((value & flag) == flag);
393
 
    }
394
 
 
395
 
    private JsonSchemaType GetJsonSchemaType(Type type, Required valueRequired)
396
 
    {
397
 
      JsonSchemaType schemaType = JsonSchemaType.None;
398
 
      if (valueRequired != Required.Always && ReflectionUtils.IsNullable(type))
399
 
      {
400
 
        schemaType = JsonSchemaType.Null;
401
 
        if (ReflectionUtils.IsNullableType(type))
402
 
          type = Nullable.GetUnderlyingType(type);
403
 
      }
404
 
 
405
 
      TypeCode typeCode = Type.GetTypeCode(type);
406
 
 
407
 
      switch (typeCode)
408
 
      {
409
 
        case TypeCode.Empty:
410
 
        case TypeCode.Object:
411
 
          return schemaType | JsonSchemaType.String;
412
 
        case TypeCode.DBNull:
413
 
          return schemaType | JsonSchemaType.Null;
414
 
        case TypeCode.Boolean:
415
 
          return schemaType | JsonSchemaType.Boolean;
416
 
        case TypeCode.Char:
417
 
          return schemaType | JsonSchemaType.String;
418
 
        case TypeCode.SByte:
419
 
        case TypeCode.Byte:
420
 
        case TypeCode.Int16:
421
 
        case TypeCode.UInt16:
422
 
        case TypeCode.Int32:
423
 
        case TypeCode.UInt32:
424
 
        case TypeCode.Int64:
425
 
        case TypeCode.UInt64:
426
 
          return schemaType | JsonSchemaType.Integer;
427
 
        case TypeCode.Single:
428
 
        case TypeCode.Double:
429
 
        case TypeCode.Decimal:
430
 
          return schemaType | JsonSchemaType.Float;
431
 
        // convert to string?
432
 
        case TypeCode.DateTime:
433
 
          return schemaType | JsonSchemaType.String;
434
 
        case TypeCode.String:
435
 
          return schemaType | JsonSchemaType.String;
436
 
        default:
437
 
          throw new Exception("Unexpected type code '{0}' for type '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeCode, type));
438
 
      }
439
 
    }
440
 
  }
441
 
}
 
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.Globalization;
 
28
using System.ComponentModel;
 
29
using System.Collections.Generic;
 
30
using Newtonsoft.Json.Linq;
 
31
using Newtonsoft.Json.Utilities;
 
32
using Newtonsoft.Json.Serialization;
 
33
#if NETFX_CORE
 
34
using IConvertible = Newtonsoft.Json.Utilities.Convertible;
 
35
#endif
 
36
#if NET20
 
37
using Newtonsoft.Json.Utilities.LinqBridge;
 
38
#else
 
39
using System.Linq;
 
40
#endif
 
41
 
 
42
namespace Newtonsoft.Json.Schema
 
43
{
 
44
  /// <summary>
 
45
  /// Generates a <see cref="JsonSchema"/> from a specified <see cref="Type"/>.
 
46
  /// </summary>
 
47
  public class JsonSchemaGenerator
 
48
  {
 
49
    /// <summary>
 
50
    /// Gets or sets how undefined schemas are handled by the serializer.
 
51
    /// </summary>
 
52
    public UndefinedSchemaIdHandling UndefinedSchemaIdHandling { get; set; }
 
53
 
 
54
    private IContractResolver _contractResolver;
 
55
    /// <summary>
 
56
    /// Gets or sets the contract resolver.
 
57
    /// </summary>
 
58
    /// <value>The contract resolver.</value>
 
59
    public IContractResolver ContractResolver
 
60
    {
 
61
      get
 
62
      {
 
63
        if (_contractResolver == null)
 
64
          return DefaultContractResolver.Instance;
 
65
 
 
66
        return _contractResolver;
 
67
      }
 
68
      set { _contractResolver = value; }
 
69
    }
 
70
 
 
71
    private class TypeSchema
 
72
    {
 
73
      public Type Type { get; private set; }
 
74
      public JsonSchema Schema { get; private set;}
 
75
 
 
76
      public TypeSchema(Type type, JsonSchema schema)
 
77
      {
 
78
        ValidationUtils.ArgumentNotNull(type, "type");
 
79
        ValidationUtils.ArgumentNotNull(schema, "schema");
 
80
 
 
81
        Type = type;
 
82
        Schema = schema;
 
83
      }
 
84
    }
 
85
 
 
86
    private JsonSchemaResolver _resolver;
 
87
    private readonly IList<TypeSchema> _stack = new List<TypeSchema>();
 
88
    private JsonSchema _currentSchema;
 
89
 
 
90
    private JsonSchema CurrentSchema
 
91
    {
 
92
      get { return _currentSchema; }
 
93
    }
 
94
 
 
95
    private void Push(TypeSchema typeSchema)
 
96
    {
 
97
      _currentSchema = typeSchema.Schema;
 
98
      _stack.Add(typeSchema);
 
99
      _resolver.LoadedSchemas.Add(typeSchema.Schema);
 
100
    }
 
101
 
 
102
    private TypeSchema Pop()
 
103
    {
 
104
      TypeSchema popped = _stack[_stack.Count - 1];
 
105
      _stack.RemoveAt(_stack.Count - 1);
 
106
      TypeSchema newValue = _stack.LastOrDefault();
 
107
      if (newValue != null)
 
108
      {
 
109
        _currentSchema = newValue.Schema;
 
110
      }
 
111
      else
 
112
      {
 
113
        _currentSchema = null;
 
114
      }
 
115
 
 
116
      return popped;
 
117
    }
 
118
 
 
119
    /// <summary>
 
120
    /// Generate a <see cref="JsonSchema"/> from the specified type.
 
121
    /// </summary>
 
122
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
 
123
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
 
124
    public JsonSchema Generate(Type type)
 
125
    {
 
126
      return Generate(type, new JsonSchemaResolver(), false);
 
127
    }
 
128
 
 
129
    /// <summary>
 
130
    /// Generate a <see cref="JsonSchema"/> from the specified type.
 
131
    /// </summary>
 
132
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
 
133
    /// <param name="resolver">The <see cref="JsonSchemaResolver"/> used to resolve schema references.</param>
 
134
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
 
135
    public JsonSchema Generate(Type type, JsonSchemaResolver resolver)
 
136
    {
 
137
      return Generate(type, resolver, false);
 
138
    }
 
139
 
 
140
    /// <summary>
 
141
    /// Generate a <see cref="JsonSchema"/> from the specified type.
 
142
    /// </summary>
 
143
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
 
144
    /// <param name="rootSchemaNullable">Specify whether the generated root <see cref="JsonSchema"/> will be nullable.</param>
 
145
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
 
146
    public JsonSchema Generate(Type type, bool rootSchemaNullable)
 
147
    {
 
148
      return Generate(type, new JsonSchemaResolver(), rootSchemaNullable);
 
149
    }
 
150
 
 
151
    /// <summary>
 
152
    /// Generate a <see cref="JsonSchema"/> from the specified type.
 
153
    /// </summary>
 
154
    /// <param name="type">The type to generate a <see cref="JsonSchema"/> from.</param>
 
155
    /// <param name="resolver">The <see cref="JsonSchemaResolver"/> used to resolve schema references.</param>
 
156
    /// <param name="rootSchemaNullable">Specify whether the generated root <see cref="JsonSchema"/> will be nullable.</param>
 
157
    /// <returns>A <see cref="JsonSchema"/> generated from the specified type.</returns>
 
158
    public JsonSchema Generate(Type type, JsonSchemaResolver resolver, bool rootSchemaNullable)
 
159
    {
 
160
      ValidationUtils.ArgumentNotNull(type, "type");
 
161
      ValidationUtils.ArgumentNotNull(resolver, "resolver");
 
162
 
 
163
      _resolver = resolver;
 
164
 
 
165
      return GenerateInternal(type, (!rootSchemaNullable) ? Required.Always : Required.Default, false);
 
166
    }
 
167
 
 
168
    private string GetTitle(Type type)
 
169
    {
 
170
      JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type);
 
171
 
 
172
      if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Title))
 
173
        return containerAttribute.Title;
 
174
 
 
175
      return null;
 
176
    }
 
177
 
 
178
    private string GetDescription(Type type)
 
179
    {
 
180
      JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type);
 
181
 
 
182
      if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Description))
 
183
        return containerAttribute.Description;
 
184
 
 
185
#if !(NETFX_CORE || PORTABLE)
 
186
      DescriptionAttribute descriptionAttribute = ReflectionUtils.GetAttribute<DescriptionAttribute>(type);
 
187
      if (descriptionAttribute != null)
 
188
        return descriptionAttribute.Description;
 
189
#endif
 
190
 
 
191
      return null;
 
192
    }
 
193
 
 
194
    private string GetTypeId(Type type, bool explicitOnly)
 
195
    {
 
196
      JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type);
 
197
 
 
198
      if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Id))
 
199
        return containerAttribute.Id;
 
200
 
 
201
      if (explicitOnly)
 
202
        return null;
 
203
 
 
204
      switch (UndefinedSchemaIdHandling)
 
205
      {
 
206
        case UndefinedSchemaIdHandling.UseTypeName:
 
207
          return type.FullName;
 
208
        case UndefinedSchemaIdHandling.UseAssemblyQualifiedName:
 
209
          return type.AssemblyQualifiedName;
 
210
        default:
 
211
          return null;
 
212
      }
 
213
    }
 
214
 
 
215
    private JsonSchema GenerateInternal(Type type, Required valueRequired, bool required)
 
216
    {
 
217
      ValidationUtils.ArgumentNotNull(type, "type");
 
218
 
 
219
      string resolvedId = GetTypeId(type, false);
 
220
      string explicitId = GetTypeId(type, true);
 
221
 
 
222
      if (!string.IsNullOrEmpty(resolvedId))
 
223
      {
 
224
        JsonSchema resolvedSchema = _resolver.GetSchema(resolvedId);
 
225
        if (resolvedSchema != null)
 
226
        {
 
227
          // resolved schema is not null but referencing member allows nulls
 
228
          // change resolved schema to allow nulls. hacky but what are ya gonna do?
 
229
          if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null))
 
230
            resolvedSchema.Type |= JsonSchemaType.Null;
 
231
          if (required && resolvedSchema.Required != true)
 
232
            resolvedSchema.Required = true;
 
233
 
 
234
          return resolvedSchema;
 
235
        }
 
236
      }
 
237
 
 
238
      // test for unresolved circular reference
 
239
      if (_stack.Any(tc => tc.Type == type))
 
240
      {
 
241
        throw new JsonException("Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.".FormatWith(CultureInfo.InvariantCulture, type));
 
242
      }
 
243
 
 
244
      JsonContract contract = ContractResolver.ResolveContract(type);
 
245
      JsonConverter converter;
 
246
      if ((converter = contract.Converter) != null || (converter = contract.InternalConverter) != null)
 
247
      {
 
248
        JsonSchema converterSchema = converter.GetSchema();
 
249
        if (converterSchema != null)
 
250
          return converterSchema;
 
251
      }
 
252
 
 
253
      Push(new TypeSchema(type, new JsonSchema()));
 
254
 
 
255
      if (explicitId != null)
 
256
        CurrentSchema.Id = explicitId;
 
257
 
 
258
      if (required)
 
259
        CurrentSchema.Required = true;
 
260
      CurrentSchema.Title = GetTitle(type);
 
261
      CurrentSchema.Description = GetDescription(type);
 
262
 
 
263
      if (converter != null)
 
264
      {
 
265
        // todo: Add GetSchema to JsonConverter and use here?
 
266
        CurrentSchema.Type = JsonSchemaType.Any;
 
267
      }
 
268
      else
 
269
      {
 
270
        switch (contract.ContractType)
 
271
        {
 
272
          case JsonContractType.Object:
 
273
            CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
 
274
            CurrentSchema.Id = GetTypeId(type, false);
 
275
            GenerateObjectSchema(type, (JsonObjectContract) contract);
 
276
            break;
 
277
          case JsonContractType.Array:
 
278
            CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired);
 
279
 
 
280
            CurrentSchema.Id = GetTypeId(type, false);
 
281
 
 
282
            JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute;
 
283
            bool allowNullItem = (arrayAttribute == null || arrayAttribute.AllowNullItems);
 
284
 
 
285
            Type collectionItemType = ReflectionUtils.GetCollectionItemType(type);
 
286
            if (collectionItemType != null)
 
287
            {
 
288
              CurrentSchema.Items = new List<JsonSchema>();
 
289
              CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default, false));
 
290
            }
 
291
            break;
 
292
          case JsonContractType.Primitive:
 
293
            CurrentSchema.Type = GetJsonSchemaType(type, valueRequired);
 
294
 
 
295
            if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum() && !type.IsDefined(typeof (FlagsAttribute), true))
 
296
            {
 
297
              CurrentSchema.Enum = new List<JToken>();
 
298
              CurrentSchema.Options = new Dictionary<JToken, string>();
 
299
 
 
300
              EnumValues<long> enumValues = EnumUtils.GetNamesAndValues<long>(type);
 
301
              foreach (EnumValue<long> enumValue in enumValues)
 
302
              {
 
303
                JToken value = JToken.FromObject(enumValue.Value);
 
304
 
 
305
                CurrentSchema.Enum.Add(value);
 
306
                CurrentSchema.Options.Add(value, enumValue.Name);
 
307
              }
 
308
            }
 
309
            break;
 
310
          case JsonContractType.String:
 
311
            JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType))
 
312
                                          ? JsonSchemaType.String
 
313
                                          : AddNullType(JsonSchemaType.String, valueRequired);
 
314
 
 
315
            CurrentSchema.Type = schemaType;
 
316
            break;
 
317
          case JsonContractType.Dictionary:
 
318
            CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
 
319
 
 
320
            Type keyType;
 
321
            Type valueType;
 
322
            ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType);
 
323
 
 
324
            if (keyType != null)
 
325
            {
 
326
              // can be converted to a string
 
327
              if (ConvertUtils.IsConvertible(keyType))
 
328
              {
 
329
                CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false);
 
330
              }
 
331
            }
 
332
            break;
 
333
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
 
334
          case JsonContractType.Serializable:
 
335
            CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired);
 
336
            CurrentSchema.Id = GetTypeId(type, false);
 
337
            GenerateISerializableContract(type, (JsonISerializableContract) contract);
 
338
            break;
 
339
#endif
 
340
#if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE)
 
341
          case JsonContractType.Dynamic:
 
342
#endif
 
343
          case JsonContractType.Linq:
 
344
            CurrentSchema.Type = JsonSchemaType.Any;
 
345
            break;
 
346
          default:
 
347
            throw new JsonException("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract));
 
348
        }
 
349
      }
 
350
 
 
351
      return Pop().Schema;
 
352
    }
 
353
 
 
354
    private JsonSchemaType AddNullType(JsonSchemaType type, Required valueRequired)
 
355
    {
 
356
      if (valueRequired != Required.Always)
 
357
        return type | JsonSchemaType.Null;
 
358
 
 
359
      return type;
 
360
    }
 
361
 
 
362
    private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag)
 
363
    {
 
364
      return ((value & flag) == flag);
 
365
    }
 
366
 
 
367
    private void GenerateObjectSchema(Type type, JsonObjectContract contract)
 
368
    {
 
369
      CurrentSchema.Properties = new Dictionary<string, JsonSchema>();
 
370
      foreach (JsonProperty property in contract.Properties)
 
371
      {
 
372
        if (!property.Ignored)
 
373
        {
 
374
          bool optional = property.NullValueHandling == NullValueHandling.Ignore ||
 
375
                          HasFlag(property.DefaultValueHandling.GetValueOrDefault(), DefaultValueHandling.Ignore) ||
 
376
                          property.ShouldSerialize != null ||
 
377
                          property.GetIsSpecified != null;
 
378
 
 
379
          JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required, !optional);
 
380
 
 
381
          if (property.DefaultValue != null)
 
382
            propertySchema.Default = JToken.FromObject(property.DefaultValue);
 
383
 
 
384
          CurrentSchema.Properties.Add(property.PropertyName, propertySchema);
 
385
        }
 
386
      }
 
387
 
 
388
      if (type.IsSealed())
 
389
        CurrentSchema.AllowAdditionalProperties = false;
 
390
    }
 
391
 
 
392
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
 
393
    private void GenerateISerializableContract(Type type, JsonISerializableContract contract)
 
394
    {
 
395
      CurrentSchema.AllowAdditionalProperties = true;
 
396
    }
 
397
#endif
 
398
 
 
399
    internal static bool HasFlag(JsonSchemaType? value, JsonSchemaType flag)
 
400
    {
 
401
      // default value is Any
 
402
      if (value == null)
 
403
        return true;
 
404
 
 
405
      bool match = ((value & flag) == flag);
 
406
      if (match)
 
407
        return true;
 
408
 
 
409
      // integer is a subset of float
 
410
      if (value == JsonSchemaType.Float && flag == JsonSchemaType.Integer)
 
411
        return true;
 
412
 
 
413
      return false;
 
414
    }
 
415
 
 
416
    private JsonSchemaType GetJsonSchemaType(Type type, Required valueRequired)
 
417
    {
 
418
      JsonSchemaType schemaType = JsonSchemaType.None;
 
419
      if (valueRequired != Required.Always && ReflectionUtils.IsNullable(type))
 
420
      {
 
421
        schemaType = JsonSchemaType.Null;
 
422
        if (ReflectionUtils.IsNullableType(type))
 
423
          type = Nullable.GetUnderlyingType(type);
 
424
      }
 
425
 
 
426
      TypeCode typeCode = ConvertUtils.GetTypeCode(type);
 
427
 
 
428
      switch (typeCode)
 
429
      {
 
430
        case TypeCode.Empty:
 
431
        case TypeCode.Object:
 
432
          return schemaType | JsonSchemaType.String;
 
433
#if !(NETFX_CORE || PORTABLE)
 
434
        case TypeCode.DBNull:
 
435
          return schemaType | JsonSchemaType.Null;
 
436
#endif
 
437
        case TypeCode.Boolean:
 
438
          return schemaType | JsonSchemaType.Boolean;
 
439
        case TypeCode.Char:
 
440
          return schemaType | JsonSchemaType.String;
 
441
        case TypeCode.SByte:
 
442
        case TypeCode.Byte:
 
443
        case TypeCode.Int16:
 
444
        case TypeCode.UInt16:
 
445
        case TypeCode.Int32:
 
446
        case TypeCode.UInt32:
 
447
        case TypeCode.Int64:
 
448
        case TypeCode.UInt64:
 
449
          return schemaType | JsonSchemaType.Integer;
 
450
        case TypeCode.Single:
 
451
        case TypeCode.Double:
 
452
        case TypeCode.Decimal:
 
453
          return schemaType | JsonSchemaType.Float;
 
454
        // convert to string?
 
455
        case TypeCode.DateTime:
 
456
          return schemaType | JsonSchemaType.String;
 
457
        case TypeCode.String:
 
458
          return schemaType | JsonSchemaType.String;
 
459
        default:
 
460
          throw new JsonException("Unexpected type code '{0}' for type '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeCode, type));
 
461
      }
 
462
    }
 
463
  }
 
464
}