// Copyright 2009-2022 Josh Close // This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0. // See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0. // https://github.com/JoshClose/CsvHelper using CsvHelper.Configuration.Attributes; using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Numerics; using System.Reflection; namespace CsvHelper.TypeConversion { /// /// Caches s for a given type. /// public class TypeConverterCache { private readonly Dictionary typeConverters = new Dictionary(); private readonly List typeConverterFactories = new List(); private readonly Dictionary typeConverterFactoryCache = new Dictionary(); /// /// Initializes the class. /// public TypeConverterCache() { CreateDefaultConverters(); } /// /// Determines if there is a converter registered for the given type. /// /// The type to check. /// true if the converter is registered, otherwise false. public bool Contains(Type type) { return typeConverters.ContainsKey(type); } /// /// Adds the . /// Factories are queried in order of being added and first factory that handles the type is used for creating the . /// /// Type converter factory public void AddConverterFactory(ITypeConverterFactory typeConverterFactory) { if (typeConverterFactory == null) { throw new ArgumentNullException(nameof(typeConverterFactory)); } typeConverterFactories.Add(typeConverterFactory); } /// /// Adds the for the given . /// /// The type the converter converts. /// The type converter that converts the type. public void AddConverter(Type type, ITypeConverter typeConverter) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (typeConverter == null) { throw new ArgumentNullException(nameof(typeConverter)); } typeConverters[type] = typeConverter; } /// /// Adds the for the given . /// /// The type the converter converts. /// The type converter that converts the type. public void AddConverter(ITypeConverter typeConverter) { if (typeConverter == null) { throw new ArgumentNullException(nameof(typeConverter)); } typeConverters[typeof(T)] = typeConverter; } /// /// Adds the given to all registered types. /// /// The type converter. public void AddConverter(ITypeConverter typeConverter) { foreach (var type in typeConverters.Keys) { typeConverters[type] = typeConverter; } } /// /// Removes the for the given . /// /// The type to remove the converter for. public void RemoveConverter(Type type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } typeConverters.Remove(type); } /// /// Removes the for the given . /// /// The type to remove the converter for. public void RemoveConverter() { RemoveConverter(typeof(T)); } /// /// Removes the ITypeConverterFactory. /// /// The ITypeConverterFactory to remove. public void RemoveConverterFactory(ITypeConverterFactory typeConverterFactory) { typeConverterFactories.Remove(typeConverterFactory); var toRemove = typeConverterFactoryCache.Where(pair => pair.Value == typeConverterFactory); foreach (var pair in toRemove) { typeConverterFactoryCache.Remove(pair.Key); } } /// /// Gets the converter for the given . /// /// The type to get the converter for. /// The for the given . public ITypeConverter GetConverter(Type type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (typeConverters.TryGetValue(type, out ITypeConverter typeConverter)) { return typeConverter; } if (!typeConverterFactoryCache.TryGetValue(type, out var factory)) { factory = typeConverterFactories.FirstOrDefault(f => f.CanCreate(type)); if (factory != null) { typeConverterFactoryCache[type] = factory; } } if (factory != null) { if (factory.Create(type, this, out typeConverter)) { AddConverter(type, typeConverter); } return typeConverter; } return new DefaultTypeConverter(); } /// /// Gets the converter for the given member. If an attribute is /// found on the member, that will be used, otherwise the cache /// will be used. /// /// The member to get the converter for. public ITypeConverter GetConverter(MemberInfo member) { var typeConverterAttribute = member.GetCustomAttribute(); if (typeConverterAttribute != null) { return typeConverterAttribute.TypeConverter; } return GetConverter(member.MemberType()); } /// /// Gets the converter for the given . /// /// The type to get the converter for. /// The for the given . public ITypeConverter GetConverter() { return GetConverter(typeof(T)); } private void CreateDefaultConverters() { AddConverter(typeof(BigInteger), new BigIntegerConverter()); AddConverter(typeof(bool), new BooleanConverter()); AddConverter(typeof(byte), new ByteConverter()); AddConverter(typeof(byte[]), new ByteArrayConverter()); AddConverter(typeof(char), new CharConverter()); AddConverter(typeof(DateTime), new DateTimeConverter()); AddConverter(typeof(DateTimeOffset), new DateTimeOffsetConverter()); AddConverter(typeof(decimal), new DecimalConverter()); AddConverter(typeof(double), new DoubleConverter()); AddConverter(typeof(float), new SingleConverter()); AddConverter(typeof(Guid), new GuidConverter()); AddConverter(typeof(short), new Int16Converter()); AddConverter(typeof(int), new Int32Converter()); AddConverter(typeof(long), new Int64Converter()); AddConverter(typeof(sbyte), new SByteConverter()); AddConverter(typeof(string), new StringConverter()); AddConverter(typeof(TimeSpan), new TimeSpanConverter()); AddConverter(typeof(Type), new TypeConverter()); AddConverter(typeof(ushort), new UInt16Converter()); AddConverter(typeof(uint), new UInt32Converter()); AddConverter(typeof(ulong), new UInt64Converter()); AddConverter(typeof(Uri), new UriConverter()); #if NET6_0 AddConverter(typeof(DateOnly), new DateOnlyConverter()); AddConverter(typeof(TimeOnly), new TimeOnlyConverter()); #endif AddConverterFactory(new EnumConverterFactory()); AddConverterFactory(new NullableConverterFactory()); AddConverterFactory(new CollectionConverterFactory()); } } }