From 2a1cd4fda8a4a8e649910d16b4dfa1ce7ae63543 Mon Sep 17 00:00:00 2001 From: chai <215380520@qq.com> Date: Fri, 12 May 2023 09:24:40 +0800 Subject: *misc --- .../src/CsvHelper/Configuration/ClassMap.cs | 648 +++++++++++++++++++++ 1 file changed, 648 insertions(+) create mode 100644 ThirdParty/CsvHelper-master/src/CsvHelper/Configuration/ClassMap.cs (limited to 'ThirdParty/CsvHelper-master/src/CsvHelper/Configuration/ClassMap.cs') diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Configuration/ClassMap.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Configuration/ClassMap.cs new file mode 100644 index 0000000..b000693 --- /dev/null +++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Configuration/ClassMap.cs @@ -0,0 +1,648 @@ +// 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 CsvHelper.TypeConversion; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace CsvHelper.Configuration +{ + /// + /// Maps class members to CSV fields. + /// + public abstract class ClassMap + { + private static readonly List enumerableConverters = new List + { + typeof(ArrayConverter), + typeof(CollectionGenericConverter), + typeof(EnumerableConverter), + typeof(IDictionaryConverter), + typeof(IDictionaryGenericConverter), + typeof(IEnumerableConverter), + typeof(IEnumerableGenericConverter) + }; + + /// + /// The type of the class this map is for. + /// + public virtual Type ClassType { get; private set; } + + /// + /// The class constructor parameter mappings. + /// + public virtual List ParameterMaps { get; } = new List(); + + /// + /// The class member mappings. + /// + public virtual MemberMapCollection MemberMaps { get; } = new MemberMapCollection(); + + /// + /// The class member reference mappings. + /// + public virtual MemberReferenceMapCollection ReferenceMaps { get; } = new MemberReferenceMapCollection(); + + /// + /// Allow only internal creation of CsvClassMap. + /// + /// The type of the class this map is for. + internal ClassMap(Type classType) + { + ClassType = classType; + } + + /// + /// Maps a member to a CSV field. + /// + /// The type of the class this map is for. This may not be the same type + /// as the member.DeclaringType or the current ClassType due to nested member mappings. + /// The member to map. + /// If true, an existing map will be used if available. + /// If false, a new map is created for the same member. + /// The member mapping. + public MemberMap Map(Type classType, MemberInfo member, bool useExistingMap = true) + { + if (useExistingMap) + { + var existingMap = MemberMaps.Find(member); + if (existingMap != null) + { + return existingMap; + } + } + + var memberMap = MemberMap.CreateGeneric(classType, member); + memberMap.Data.Index = GetMaxIndex() + 1; + MemberMaps.Add(memberMap); + + return memberMap; + } + + /// + /// Maps a non-member to a CSV field. This allows for writing + /// data that isn't mapped to a class member. + /// + /// The member mapping. + public virtual MemberMap Map() + { + var memberMap = new MemberMap(null); + memberMap.Data.Index = GetMaxIndex() + 1; + MemberMaps.Add(memberMap); + + return memberMap; + } + + /// + /// Maps a member to another class map. + /// + /// The type of the class map. + /// The member. + /// Constructor arguments used to create the reference map. + /// The reference mapping for the member. + public virtual MemberReferenceMap References(Type classMapType, MemberInfo member, params object[] constructorArgs) + { + if (!typeof(ClassMap).IsAssignableFrom(classMapType)) + { + throw new InvalidOperationException($"Argument {nameof(classMapType)} is not a CsvClassMap."); + } + + var existingMap = ReferenceMaps.Find(member); + + if (existingMap != null) + { + return existingMap; + } + + var map = (ClassMap)ObjectResolver.Current.Resolve(classMapType, constructorArgs); + map.ReIndex(GetMaxIndex() + 1); + var reference = new MemberReferenceMap(member, map); + ReferenceMaps.Add(reference); + + return reference; + } + + /// + /// Maps a constructor parameter to a CSV field. + /// + /// The name of the constructor parameter. + public virtual ParameterMap Parameter(string name) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + + var args = new GetConstructorArgs(ClassType); + + return Parameter(() => ConfigurationFunctions.GetConstructor(args), name); + } + + /// + /// Maps a constructor parameter to a CSV field. + /// + /// A function that returns the for the constructor. + /// The name of the constructor parameter. + public virtual ParameterMap Parameter(Func getConstructor, string name) + { + if (getConstructor == null) throw new ArgumentNullException(nameof(getConstructor)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + + var constructor = getConstructor(); + var parameters = constructor.GetParameters(); + var parameter = parameters.SingleOrDefault(p => p.Name == name); + if (parameter == null) + { + throw new ConfigurationException($"Constructor {constructor.GetDefinition()} doesn't contain a paramter with name '{name}'."); + } + + return Parameter(constructor, parameter); + } + + /// + /// Maps a constructor parameter to a CSV field. + /// + /// The for the constructor. + /// The for the constructor parameter. + public virtual ParameterMap Parameter(ConstructorInfo constructor, ParameterInfo parameter) + { + if (constructor == null) throw new ArgumentNullException(nameof(constructor)); + if (parameter == null) throw new ArgumentNullException(nameof(parameter)); + + if (!constructor.GetParameters().Contains(parameter)) + { + throw new ConfigurationException($"Constructor {constructor.GetDefinition()} doesn't contain parameter '{parameter.GetDefinition()}'."); + } + + var parameterMap = new ParameterMap(parameter); + parameterMap.Data.Index = GetMaxIndex(isParameter: true) + 1; + ParameterMaps.Add(parameterMap); + + return parameterMap; + } + + /// + /// Auto maps all members for the given type. If a member + /// is mapped again it will override the existing map. + /// + /// The culture. + public virtual void AutoMap(CultureInfo culture) + { + AutoMap(new CsvConfiguration(culture)); + } + + /// + /// Auto maps all members for the given type. If a member + /// is mapped again it will override the existing map. + /// + /// The configuration. + public virtual void AutoMap(CsvConfiguration configuration) + { + AutoMap(new CsvContext(configuration)); + } + + /// + /// Auto maps all members for the given type. If a member + /// is mapped again it will override the existing map. + /// + /// The context. + public virtual void AutoMap(CsvContext context) + { + var type = GetGenericType(); + if (typeof(IEnumerable).IsAssignableFrom(type)) + { + throw new ConfigurationException("Types that inherit IEnumerable cannot be auto mapped. " + + "Did you accidentally call GetRecord or WriteRecord which " + + "acts on a single record instead of calling GetRecords or " + + "WriteRecords which acts on a list of records?"); + } + + var mapParents = new LinkedList(); + var args = new ShouldUseConstructorParametersArgs(type); + if (context.Configuration.ShouldUseConstructorParameters(args)) + { + // This type doesn't have a parameterless constructor so we can't create an + // instance and set it's member. Constructor parameters need to be created + // instead. Writing only uses getters, so members will also be mapped + // for writing purposes. + AutoMapConstructorParameters(this, context, mapParents); + } + + AutoMapMembers(this, context, mapParents); + } + + /// + /// Get the largest index for the + /// members and references. + /// + /// The max index. + public virtual int GetMaxIndex(bool isParameter = false) + { + if (isParameter) + { + return ParameterMaps.Select(parameterMap => parameterMap.GetMaxIndex()).DefaultIfEmpty(-1).Max(); + } + + if (MemberMaps.Count == 0 && ReferenceMaps.Count == 0) + { + return -1; + } + + var indexes = new List(); + if (MemberMaps.Count > 0) + { + indexes.Add(MemberMaps.Max(pm => pm.Data.Index)); + } + + if (ReferenceMaps.Count > 0) + { + indexes.AddRange(ReferenceMaps.Select(referenceMap => referenceMap.GetMaxIndex())); + } + + return indexes.Max(); + } + + /// + /// Resets the indexes based on the given start index. + /// + /// The index start. + /// The last index + 1. + public virtual int ReIndex(int indexStart = 0) + { + foreach (var parameterMap in ParameterMaps) + { + parameterMap.Data.Index = indexStart + parameterMap.Data.Index; + } + + foreach (var memberMap in MemberMaps) + { + if (!memberMap.Data.IsIndexSet) + { + memberMap.Data.Index = indexStart + memberMap.Data.Index; + } + } + + foreach (var referenceMap in ReferenceMaps) + { + indexStart = referenceMap.Data.Mapping.ReIndex(indexStart); + } + + return indexStart; + } + + /// + /// Auto maps the given map and checks for circular references as it goes. + /// + /// The map to auto map. + /// The context. + /// The list of parents for the map. + /// The index starting point. + protected virtual void AutoMapMembers(ClassMap map, CsvContext context, LinkedList mapParents, int indexStart = 0) + { + var type = map.GetGenericType(); + + var flags = BindingFlags.Instance | BindingFlags.Public; + if (context.Configuration.IncludePrivateMembers) + { + flags = flags | BindingFlags.NonPublic; + } + + var members = new List(); + if ((context.Configuration.MemberTypes & MemberTypes.Properties) == MemberTypes.Properties) + { + // We need to go up the declaration tree and find the actual type the property + // exists on and use that PropertyInfo instead. This is so we can get the private + // set method for the property. + var properties = new List(); + foreach (var property in ReflectionHelper.GetUniqueProperties(type, flags)) + { + if (properties.Any(p => p.Name == property.Name)) + { + // Multiple properties could have the same name if a child class property + // is hiding a parent class property by using `new`. It's possible that + // the order of the properties returned + continue; + } + + properties.Add(ReflectionHelper.GetDeclaringProperty(type, property, flags)); + } + + members.AddRange(properties); + } + + if ((context.Configuration.MemberTypes & MemberTypes.Fields) == MemberTypes.Fields) + { + // We need to go up the declaration tree and find the actual type the field + // exists on and use that FieldInfo instead. + var fields = new List(); + foreach (var field in ReflectionHelper.GetUniqueFields(type, flags)) + { + if (fields.Any(p => p.Name == field.Name)) + { + // Multiple fields could have the same name if a child class field + // is hiding a parent class field by using `new`. It's possible that + // the order of the fields returned + continue; + } + + if (!field.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) + { + fields.Add(ReflectionHelper.GetDeclaringField(type, field, flags)); + } + } + + members.AddRange(fields); + } + + foreach (var member in members) + { + if (member.GetCustomAttribute() != null) + { + // Ignore this member including its tree if it's a reference. + continue; + } + + var typeConverterType = context.TypeConverterCache.GetConverter(member).GetType(); + + if (context.Configuration.HasHeaderRecord && enumerableConverters.Contains(typeConverterType)) + { + // Enumerable converters can't write the header properly, so skip it. + continue; + } + + var memberTypeInfo = member.MemberType().GetTypeInfo(); + var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter); + if (isDefaultConverter) + { + // If the type is not one covered by our type converters + // and it has a parameterless constructor, create a + // reference map for it. + + if (context.Configuration.IgnoreReferences) + { + continue; + } + + if (CheckForCircularReference(member.MemberType(), mapParents)) + { + continue; + } + + mapParents.AddLast(type); + var refMapType = typeof(DefaultClassMap<>).MakeGenericType(member.MemberType()); + var refMap = (ClassMap)ObjectResolver.Current.Resolve(refMapType); + + if (memberTypeInfo.HasConstructor() && !memberTypeInfo.HasParameterlessConstructor() && !memberTypeInfo.IsUserDefinedStruct()) + { + AutoMapConstructorParameters(refMap, context, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); + } + + // Need to use Max here for nested types. + AutoMapMembers(refMap, context, mapParents, Math.Max(map.GetMaxIndex() + 1, indexStart)); + mapParents.Drop(mapParents.Find(type)); + + if (refMap.MemberMaps.Count > 0 || refMap.ReferenceMaps.Count > 0) + { + var referenceMap = new MemberReferenceMap(member, refMap); + if (context.Configuration.ReferenceHeaderPrefix != null) + { + var args = new ReferenceHeaderPrefixArgs(member.MemberType(), member.Name); + referenceMap.Data.Prefix = context.Configuration.ReferenceHeaderPrefix(args); + } + + ApplyAttributes(referenceMap); + + map.ReferenceMaps.Add(referenceMap); + } + } + else + { + // Only add the member map if it can be converted later on. + // If the member will use the default converter, don't add it because + // we don't want the .ToString() value to be used when auto mapping. + + // Use the top of the map tree. This will maps that have been auto mapped + // to later on get a reference to a map by doing map.Map( m => m.A.B.C.Id ) + // and it will return the correct parent map type of A instead of C. + var classType = mapParents.First?.Value ?? map.ClassType; + var memberMap = MemberMap.CreateGeneric(classType, member); + + // Use global values as the starting point. + memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), context.TypeConverterOptionsCache.GetOptions(member.MemberType()), memberMap.Data.TypeConverterOptions); + memberMap.Data.Index = map.GetMaxIndex() + 1; + + ApplyAttributes(memberMap); + + map.MemberMaps.Add(memberMap); + } + } + + map.ReIndex(indexStart); + } + + /// + /// Auto maps the given map using constructor parameters. + /// + /// The map. + /// The context. + /// The list of parents for the map. + /// The index starting point. + protected virtual void AutoMapConstructorParameters(ClassMap map, CsvContext context, LinkedList mapParents, int indexStart = 0) + { + var type = map.GetGenericType(); + var args = new GetConstructorArgs(map.ClassType); + var constructor = context.Configuration.GetConstructor(args); + var parameters = constructor.GetParameters(); + + foreach (var parameter in parameters) + { + var parameterMap = new ParameterMap(parameter); + + if (parameter.GetCustomAttributes(true).Any() || parameter.GetCustomAttributes(true).Any()) + { + // If there is an IgnoreAttribute or ConstantAttribute, we still need to add a map because a constructor requires + // all parameters to be present. A default value will be used later on. + + ApplyAttributes(parameterMap); + map.ParameterMaps.Add(parameterMap); + continue; + } + + var typeConverterType = context.TypeConverterCache.GetConverter(parameter.ParameterType).GetType(); + var memberTypeInfo = parameter.ParameterType.GetTypeInfo(); + var isDefaultConverter = typeConverterType == typeof(DefaultTypeConverter); + if (isDefaultConverter && (memberTypeInfo.HasParameterlessConstructor() || memberTypeInfo.IsUserDefinedStruct())) + { + // If the type is not one covered by our type converters + // and it has a parameterless constructor, create a + // reference map for it. + + if (context.Configuration.IgnoreReferences) + { + throw new InvalidOperationException($"Configuration '{nameof(CsvConfiguration.IgnoreReferences)}' can't be true " + + "when using types without a default constructor. Constructor parameters " + + "are used and all members including references must be used."); + } + + if (CheckForCircularReference(parameter.ParameterType, mapParents)) + { + throw new InvalidOperationException($"A circular reference was detected in constructor paramter '{parameter.Name}'." + + "Since all parameters must be supplied for a constructor, this parameter can't be skipped."); + } + + mapParents.AddLast(type); + var refMapType = typeof(DefaultClassMap<>).MakeGenericType(parameter.ParameterType); + var refMap = (ClassMap)ObjectResolver.Current.Resolve(refMapType); + AutoMapMembers(refMap, context, mapParents, Math.Max(map.GetMaxIndex(isParameter: true) + 1, indexStart)); + mapParents.Drop(mapParents.Find(type)); + + var referenceMap = new ParameterReferenceMap(parameter, refMap); + if (context.Configuration.ReferenceHeaderPrefix != null) + { + var referenceHeaderPrefix = new ReferenceHeaderPrefixArgs(memberTypeInfo.MemberType(), memberTypeInfo.Name); + referenceMap.Data.Prefix = context.Configuration.ReferenceHeaderPrefix(referenceHeaderPrefix); + } + + ApplyAttributes(referenceMap); + + parameterMap.ReferenceMap = referenceMap; + } + else if (isDefaultConverter && context.Configuration.ShouldUseConstructorParameters(new ShouldUseConstructorParametersArgs(parameter.ParameterType))) + { + // If the type is not one covered by our type converters + // and it should use contructor parameters, create a + // constructor map for it. + + mapParents.AddLast(type); + var constructorMapType = typeof(DefaultClassMap<>).MakeGenericType(parameter.ParameterType); + var constructorMap = (ClassMap)ObjectResolver.Current.Resolve(constructorMapType); + // Need to use Max here for nested types. + AutoMapConstructorParameters(constructorMap, context, mapParents, Math.Max(map.GetMaxIndex(isParameter: true) + 1, indexStart)); + mapParents.Drop(mapParents.Find(type)); + + parameterMap.ConstructorTypeMap = constructorMap; + } + else + { + parameterMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), context.TypeConverterOptionsCache.GetOptions(parameter.ParameterType), parameterMap.Data.TypeConverterOptions); + parameterMap.Data.Index = map.GetMaxIndex(isParameter: true) + 1; + + ApplyAttributes(parameterMap); + } + + map.ParameterMaps.Add(parameterMap); + } + + map.ReIndex(indexStart); + } + + /// + /// Checks for circular references. + /// + /// The type to check for. + /// The list of parents to check against. + /// A value indicating if a circular reference was found. + /// True if a circular reference was found, otherwise false. + protected virtual bool CheckForCircularReference(Type type, LinkedList mapParents) + { + if (mapParents.Count == 0) + { + return false; + } + + var node = mapParents.Last; + while (true) + { + if (node?.Value == type) + { + return true; + } + + node = node?.Previous; + if (node == null) + { + break; + } + } + + return false; + } + + /// + /// Gets the generic type for this class map. + /// + protected virtual Type GetGenericType() + { + return GetType().GetTypeInfo().BaseType?.GetGenericArguments()[0] ?? throw new ConfigurationException(); + } + + /// + /// Applies attribute configurations to the map. + /// + /// The parameter map. + protected virtual void ApplyAttributes(ParameterMap parameterMap) + { + var parameter = parameterMap.Data.Parameter; + var attributes = parameter.GetCustomAttributes().OfType(); + + foreach (var attribute in attributes) + { + attribute.ApplyTo(parameterMap); + } + } + + /// + /// Applies attribute configurations to the map. + /// + /// The parameter reference map. + protected virtual void ApplyAttributes(ParameterReferenceMap referenceMap) + { + var parameter = referenceMap.Data.Parameter; + var attributes = parameter.GetCustomAttributes().OfType(); + + foreach (var attribute in attributes) + { + attribute.ApplyTo(referenceMap); + } + } + + /// + /// Applies attribute configurations to the map. + /// + /// The member map. + protected virtual void ApplyAttributes(MemberMap memberMap) + { + if (memberMap.Data.Member == null) + { + return; + } + + var member = memberMap.Data.Member; + var attributes = member.GetCustomAttributes().OfType(); + + foreach (var attribute in attributes) + { + attribute.ApplyTo(memberMap); + } + } + + /// + /// Applies attribute configurations to the map. + /// + /// The member reference map. + protected virtual void ApplyAttributes(MemberReferenceMap referenceMap) + { + var member = referenceMap.Data.Member; + var attributes = member.GetCustomAttributes().OfType(); + + foreach (var attribute in attributes) + { + attribute.ApplyTo(referenceMap); + } + } + } +} -- cgit v1.1-26-g67d0