JsonSchemaBuilder
                    class JsonSchemaBuilder
                
                using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Newtonsoft.Json.Schema
{
    [Obsolete("JSON Schema validation has been moved to its own package. See https://www.newtonsoft.com/jsonschema for more details.")]
    internal class JsonSchemaBuilder
    {
        private readonly IList<JsonSchema> _stack;
        private readonly JsonSchemaResolver _resolver;
        private readonly IDictionary<string, JsonSchema> _documentSchemas;
        private JsonSchema _currentSchema;
        private JObject _rootSchema;
        private JsonSchema CurrentSchema => _currentSchema;
        public JsonSchemaBuilder(JsonSchemaResolver resolver)
        {
            _stack = new List<JsonSchema>();
            _documentSchemas = new Dictionary<string, JsonSchema>();
            _resolver = resolver;
        }
        private void Push(JsonSchema value)
        {
            _currentSchema = value;
            _stack.Add(value);
            _resolver.LoadedSchemas.Add(value);
            _documentSchemas.Add(value.Location, value);
        }
        private JsonSchema Pop()
        {
            JsonSchema currentSchema = _currentSchema;
            _stack.RemoveAt(_stack.Count - 1);
            _currentSchema = _stack.LastOrDefault();
            return currentSchema;
        }
        internal JsonSchema Read(JsonReader reader)
        {
            JToken jToken = JToken.ReadFrom(reader);
            _rootSchema = (jToken as JObject);
            JsonSchema jsonSchema = BuildSchema(jToken);
            ResolveReferences(jsonSchema);
            return jsonSchema;
        }
        private string UnescapeReference(string reference)
        {
            return Uri.UnescapeDataString(reference).Replace("~1", "/").Replace("~0", "~");
        }
        private JsonSchema ResolveReferences(JsonSchema schema)
        {
            if (schema.DeferredReference != null) {
                string text = schema.DeferredReference;
                bool flag = text.StartsWith("#", StringComparison.Ordinal);
                if (flag)
                    text = UnescapeReference(text);
                JsonSchema jsonSchema = _resolver.GetSchema(text);
                if (jsonSchema == null) {
                    if (flag) {
                        string[] array = schema.DeferredReference.TrimStart(new char[1] {
                            '#'
                        }).Split(new char[1] {
                            '/'
                        }, StringSplitOptions.RemoveEmptyEntries);
                        JToken jToken = _rootSchema;
                        string[] array2 = array;
                        foreach (string reference in array2) {
                            string text2 = UnescapeReference(reference);
                            if (jToken.Type == JTokenType.Object)
                                jToken = jToken[text2];
                            else if (jToken.Type == JTokenType.Array || jToken.Type == JTokenType.Constructor) {
                                int result;
                                jToken = ((!int.TryParse(text2, out result) || result < 0 || result >= jToken.Count()) ? null : jToken[result]);
                            }
                            if (jToken == null)
                                break;
                        }
                        if (jToken != null)
                            jsonSchema = BuildSchema(jToken);
                    }
                    if (jsonSchema == null)
                        throw new JsonException("Could not resolve schema reference '{0}'.".FormatWith(CultureInfo.InvariantCulture, schema.DeferredReference));
                }
                schema = jsonSchema;
            }
            if (schema.ReferencesResolved)
                return schema;
            schema.ReferencesResolved = true;
            if (schema.Extends != null) {
                for (int j = 0; j < schema.Extends.Count; j++) {
                    schema.Extends[j] = ResolveReferences(schema.Extends[j]);
                }
            }
            if (schema.Items != null) {
                for (int k = 0; k < schema.Items.Count; k++) {
                    schema.Items[k] = ResolveReferences(schema.Items[k]);
                }
            }
            if (schema.AdditionalItems != null)
                schema.AdditionalItems = ResolveReferences(schema.AdditionalItems);
            List<KeyValuePair<string, JsonSchema>>.Enumerator enumerator;
            if (schema.PatternProperties != null) {
                enumerator = schema.PatternProperties.ToList().GetEnumerator();
                try {
                    while (enumerator.MoveNext()) {
                        KeyValuePair<string, JsonSchema> current = enumerator.Current;
                        schema.PatternProperties[current.Key] = ResolveReferences(current.Value);
                    }
                } finally {
                    ((IDisposable)enumerator).Dispose();
                }
            }
            if (schema.Properties != null) {
                enumerator = schema.Properties.ToList().GetEnumerator();
                try {
                    while (enumerator.MoveNext()) {
                        KeyValuePair<string, JsonSchema> current2 = enumerator.Current;
                        schema.Properties[current2.Key] = ResolveReferences(current2.Value);
                    }
                } finally {
                    ((IDisposable)enumerator).Dispose();
                }
            }
            if (schema.AdditionalProperties != null)
                schema.AdditionalProperties = ResolveReferences(schema.AdditionalProperties);
            return schema;
        }
        private JsonSchema BuildSchema(JToken token)
        {
            JObject jObject = token as JObject;
            if (jObject == null)
                throw JsonException.Create(token, token.Path, "Expected object while parsing schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type));
            if (jObject.TryGetValue("$ref", out JToken value))
                return new JsonSchema {
                    DeferredReference = (string)value
                };
            string text = token.Path.Replace(".", "/").Replace("[", "/").Replace("]", string.Empty);
            if (!StringUtils.IsNullOrEmpty(text))
                text = "/" + text;
            text = "#" + text;
            if (_documentSchemas.TryGetValue(text, out JsonSchema value2))
                return value2;
            Push(new JsonSchema {
                Location = text
            });
            ProcessSchemaProperties(jObject);
            return Pop();
        }
        private void ProcessSchemaProperties(JObject schemaObject)
        {
            foreach (KeyValuePair<string, JToken> item in schemaObject) {
                string key = item.Key;
                if (key != null) {
                    switch (key) {
                    case "type":
                        CurrentSchema.Type = ProcessType(item.Value);
                        break;
                    case "id":
                        CurrentSchema.Id = (string)item.Value;
                        break;
                    case "title":
                        CurrentSchema.Title = (string)item.Value;
                        break;
                    case "description":
                        CurrentSchema.Description = (string)item.Value;
                        break;
                    case "properties":
                        CurrentSchema.Properties = ProcessProperties(item.Value);
                        break;
                    case "items":
                        ProcessItems(item.Value);
                        break;
                    case "additionalProperties":
                        ProcessAdditionalProperties(item.Value);
                        break;
                    case "additionalItems":
                        ProcessAdditionalItems(item.Value);
                        break;
                    case "patternProperties":
                        CurrentSchema.PatternProperties = ProcessProperties(item.Value);
                        break;
                    case "required":
                        CurrentSchema.Required = (bool)item.Value;
                        break;
                    case "requires":
                        CurrentSchema.Requires = (string)item.Value;
                        break;
                    case "minimum":
                        CurrentSchema.Minimum = (double)item.Value;
                        break;
                    case "maximum":
                        CurrentSchema.Maximum = (double)item.Value;
                        break;
                    case "exclusiveMinimum":
                        CurrentSchema.ExclusiveMinimum = (bool)item.Value;
                        break;
                    case "exclusiveMaximum":
                        CurrentSchema.ExclusiveMaximum = (bool)item.Value;
                        break;
                    case "maxLength":
                        CurrentSchema.MaximumLength = (int)item.Value;
                        break;
                    case "minLength":
                        CurrentSchema.MinimumLength = (int)item.Value;
                        break;
                    case "maxItems":
                        CurrentSchema.MaximumItems = (int)item.Value;
                        break;
                    case "minItems":
                        CurrentSchema.MinimumItems = (int)item.Value;
                        break;
                    case "divisibleBy":
                        CurrentSchema.DivisibleBy = (double)item.Value;
                        break;
                    case "disallow":
                        CurrentSchema.Disallow = ProcessType(item.Value);
                        break;
                    case "default":
                        CurrentSchema.Default = item.Value.DeepClone();
                        break;
                    case "hidden":
                        CurrentSchema.Hidden = (bool)item.Value;
                        break;
                    case "readonly":
                        CurrentSchema.ReadOnly = (bool)item.Value;
                        break;
                    case "format":
                        CurrentSchema.Format = (string)item.Value;
                        break;
                    case "pattern":
                        CurrentSchema.Pattern = (string)item.Value;
                        break;
                    case "enum":
                        ProcessEnum(item.Value);
                        break;
                    case "extends":
                        ProcessExtends(item.Value);
                        break;
                    case "uniqueItems":
                        CurrentSchema.UniqueItems = (bool)item.Value;
                        break;
                    }
                }
            }
        }
        private void ProcessExtends(JToken token)
        {
            IList<JsonSchema> list = new List<JsonSchema>();
            if (token.Type == JTokenType.Array) {
                foreach (JToken item in (IEnumerable<JToken>)token) {
                    list.Add(BuildSchema(item));
                }
            } else {
                JsonSchema jsonSchema = BuildSchema(token);
                if (jsonSchema != null)
                    list.Add(jsonSchema);
            }
            if (list.Count > 0)
                CurrentSchema.Extends = list;
        }
        private void ProcessEnum(JToken token)
        {
            if (token.Type != JTokenType.Array)
                throw JsonException.Create(token, token.Path, "Expected Array token while parsing enum values, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type));
            CurrentSchema.Enum = new List<JToken>();
            foreach (JToken item in (IEnumerable<JToken>)token) {
                CurrentSchema.Enum.Add(item.DeepClone());
            }
        }
        private void ProcessAdditionalProperties(JToken token)
        {
            if (token.Type == JTokenType.Boolean)
                CurrentSchema.AllowAdditionalProperties = (bool)token;
            else
                CurrentSchema.AdditionalProperties = BuildSchema(token);
        }
        private void ProcessAdditionalItems(JToken token)
        {
            if (token.Type == JTokenType.Boolean)
                CurrentSchema.AllowAdditionalItems = (bool)token;
            else
                CurrentSchema.AdditionalItems = BuildSchema(token);
        }
        private IDictionary<string, JsonSchema> ProcessProperties(JToken token)
        {
            IDictionary<string, JsonSchema> dictionary = new Dictionary<string, JsonSchema>();
            if (token.Type != JTokenType.Object)
                throw JsonException.Create(token, token.Path, "Expected Object token while parsing schema properties, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type));
            foreach (JProperty item in (IEnumerable<JToken>)token) {
                if (dictionary.ContainsKey(item.Name))
                    throw new JsonException("Property {0} has already been defined in schema.".FormatWith(CultureInfo.InvariantCulture, item.Name));
                dictionary.Add(item.Name, BuildSchema(item.Value));
            }
            return dictionary;
        }
        private void ProcessItems(JToken token)
        {
            CurrentSchema.Items = new List<JsonSchema>();
            switch (token.Type) {
            case JTokenType.Object:
                CurrentSchema.Items.Add(BuildSchema(token));
                CurrentSchema.PositionalItemsValidation = false;
                break;
            case JTokenType.Array:
                CurrentSchema.PositionalItemsValidation = true;
                foreach (JToken item in (IEnumerable<JToken>)token) {
                    CurrentSchema.Items.Add(BuildSchema(item));
                }
                break;
            default:
                throw JsonException.Create(token, token.Path, "Expected array or JSON schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type));
            }
        }
        private JsonSchemaType? ProcessType(JToken token)
        {
            switch (token.Type) {
            case JTokenType.Array: {
                JsonSchemaType? nullable = JsonSchemaType.None;
                {
                    foreach (JToken item in (IEnumerable<JToken>)token) {
                        if (item.Type != JTokenType.String)
                            throw JsonException.Create(item, item.Path, "Expected JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type));
                        nullable |= MapType((string)item);
                    }
                    return nullable;
                }
            }
            case JTokenType.String:
                return MapType((string)token);
            default:
                throw JsonException.Create(token, token.Path, "Expected array or JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, token.Type));
            }
        }
        internal static JsonSchemaType MapType(string type)
        {
            if (!JsonSchemaConstants.JsonSchemaTypeMapping.TryGetValue(type, out JsonSchemaType value))
                throw new JsonException("Invalid JSON schema type: {0}".FormatWith(CultureInfo.InvariantCulture, type));
            return value;
        }
        internal static string MapType(JsonSchemaType type)
        {
            return JsonSchemaConstants.JsonSchemaTypeMapping.Single((KeyValuePair<string, JsonSchemaType> kv) => kv.Value == type).Key;
        }
    }
}