// 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());
}
}
}