File: src\Components\Endpoints\src\FormMapping\FormDataMapperOptions.cs
Web Access
Project: src\src\Http\Http.Extensions\src\Microsoft.AspNetCore.Http.Extensions.csproj (Microsoft.AspNetCore.Http.Extensions)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
 
namespace Microsoft.AspNetCore.Components.Endpoints.FormMapping;
 
internal sealed class FormDataMapperOptions
{
    private readonly ConcurrentDictionary<Type, FormDataConverter> _converters = new();
    private readonly List<IFormDataConverterFactory> _factories = new();
 
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    public FormDataMapperOptions() : this(NullLoggerFactory.Instance)
    {        
    }
 
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    public FormDataMapperOptions(ILoggerFactory loggerFactory)
    {
        _converters = new(WellKnownConverters.Converters);
        _factories.Add(new ParsableConverterFactory());
        _factories.Add(new FileConverterFactory());
        _factories.Add(new EnumConverterFactory());
        _factories.Add(new NullableConverterFactory());
        _factories.Add(new DictionaryConverterFactory());
        _factories.Add(new CollectionConverterFactory());
        _factories.Add(new ComplexTypeConverterFactory(this, loggerFactory));
        Logger = loggerFactory.CreateLogger<FormDataMapperOptions>();
    }
 
    // For testing purposes only.
    internal List<IFormDataConverterFactory> Factories => _factories;
 
    internal ILogger Logger { get; }
 
    internal int MaxCollectionSize { get; set; } = FormReader.DefaultValueCountLimit;
 
    internal int MaxRecursionDepth { get; set; } = 64;
 
    internal int MaxErrorCount { get; set; } = 200;
 
    internal int MaxKeyBufferSize { get; set; } = FormReader.DefaultKeyLengthLimit;
 
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    internal FormDataConverter<T>? ResolveConverter<T>()
    {
        return (FormDataConverter<T>)_converters.GetOrAdd(typeof(T), CreateConverter, this);
    }
 
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    private static FormDataConverter CreateConverter(Type type, FormDataMapperOptions options)
    {
        foreach (var factory in options._factories)
        {
            if (factory.CanConvert(type, options))
            {
                return factory.CreateConverter(type, options);
            }
        }
 
        throw new InvalidOperationException($"No converter registered for type '{type.FullName}'.");
    }
 
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    internal FormDataConverter ResolveConverter(Type type)
    {
        return _converters.GetOrAdd(type, CreateConverter, this);
    }
 
    // For testing
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    internal void AddConverter<T>(FormDataConverter<T> converter)
    {
        _converters[typeof(T)] = converter;
    }
 
    [RequiresDynamicCode(FormMappingHelpers.RequiresDynamicCodeMessage)]
    [RequiresUnreferencedCode(FormMappingHelpers.RequiresUnreferencedCodeMessage)]
    internal bool CanConvert(Type type)
    {
        if (_converters.ContainsKey(type))
        {
            return true;
        }
 
        foreach (var factory in _factories)
        {
            if (factory.CanConvert(type, this))
            {
                return true;
            }
        }
 
        return false;
    }
}