private JSchemaGenerationProvider ResolveTypeProvider(Type nonNullableType, JsonProperty memberProperty)
        {
            JSchemaGenerationProviderAttribute providerAttribute = null;

            if (memberProperty != null && memberProperty.AttributeProvider != null)
            {
                providerAttribute = (JSchemaGenerationProviderAttribute)memberProperty.AttributeProvider.GetAttributes(typeof(JSchemaGenerationProviderAttribute), true).SingleOrDefault();
            }

            if (providerAttribute == null)
            {
                providerAttribute = ReflectionUtils.GetAttribute <JSchemaGenerationProviderAttribute>(nonNullableType, true);

                if (providerAttribute == null)
                {
                    return(null);
                }
            }

            JSchemaGenerationProvider provider = (JSchemaGenerationProvider)Activator.CreateInstance(providerAttribute.ProviderType, providerAttribute.ProviderParameters);

            return(provider);
        }
        private JSchema GenerateInternal(Type type, Required valueRequired, JsonProperty memberProperty, JsonContainerContract container)
        {
            ValidationUtils.ArgumentNotNull(type, "type");

            Type nonNullableType = ReflectionUtils.IsNullableType(type) ? Nullable.GetUnderlyingType(type) : type;

            Uri resolvedId = GetTypeId(nonNullableType, false);
            Uri explicitId = GetTypeId(nonNullableType, true);

            if (resolvedId != null)
            {
                JSchema resolvedSchema = _resolver.GetSchema(resolvedId);
                if (resolvedSchema != null)
                {
                    // resolved schema is not null but referencing member allows nulls
                    // change resolved schema to allow nulls. hacky but what are ya gonna do?
                    if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JSchemaType.Null))
                    {
                        resolvedSchema.Type |= JSchemaType.Null;
                    }

                    return(resolvedSchema);
                }
            }

            JsonContract contract = ContractResolver.ResolveContract(type);

            var key = CreateKey(valueRequired, memberProperty, contract);

            switch (contract.ContractType)
            {
            case JsonContractType.Object:
            case JsonContractType.Array:
            case JsonContractType.Dictionary:
                TypeSchema typeSchema = _typeSchemas.SingleOrDefault(s => s.Key.Equals(key));
                if (typeSchema != null)
                {
                    return(typeSchema.Schema);
                }
                break;
            }

            JSchema schema = null;

            JSchemaGenerationProvider provider = ResolveTypeProvider(nonNullableType, memberProperty);

            if (provider != null)
            {
                JSchemaTypeGenerationContext context = new JSchemaTypeGenerationContext(type, valueRequired, memberProperty, container, this);

                schema = provider.GetSchema(context);

                if (schema == null)
                {
                    throw new JsonException("Could not get schema for type '{0}' from provider '{1}'.".FormatWith(CultureInfo.InvariantCulture, type.FullName, provider.GetType().FullName));
                }
            }

            if (_generationProviders != null)
            {
                JSchemaTypeGenerationContext context = new JSchemaTypeGenerationContext(type, valueRequired, memberProperty, container, this);

                foreach (JSchemaGenerationProvider generationProvider in _generationProviders)
                {
                    schema = generationProvider.GetSchema(context);
                }
            }

            if (schema != null)
            {
                _typeSchemas.Add(new TypeSchema(key, schema));
                return(schema);
            }

            schema = new JSchema();
            if (explicitId != null)
            {
                schema.Id = explicitId;
            }

            switch (contract.ContractType)
            {
            case JsonContractType.Object:
            case JsonContractType.Array:
            case JsonContractType.Dictionary:
                _typeSchemas.Add(new TypeSchema(key, schema));
                break;
            }

            return(PopulateSchema(schema, contract, memberProperty, valueRequired));
        }