summaryrefslogtreecommitdiff
path: root/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions
diff options
context:
space:
mode:
Diffstat (limited to 'ThirdParty/CsvHelper-master/src/CsvHelper/Expressions')
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordCreator.cs61
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordWriter.cs75
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpandoObjectRecordWriter.cs49
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpressionManager.cs490
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordCreator.cs66
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordWriter.cs124
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordCreator.cs49
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordWriter.cs57
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreator.cs107
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreatorFactory.cs51
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordHydrator.cs126
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordManager.cs80
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriter.cs109
-rw-r--r--ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriterFactory.cs61
14 files changed, 1505 insertions, 0 deletions
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordCreator.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordCreator.cs
new file mode 100644
index 0000000..b93390f
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordCreator.cs
@@ -0,0 +1,61 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Dynamic;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Creates dynamic records.
+ /// </summary>
+ public class DynamicRecordCreator : RecordCreator
+ {
+ /// <summary>
+ /// Initializes a new instance.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ public DynamicRecordCreator(CsvReader reader) : base(reader) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Func{T}"/>
+ /// that will create a record of the given type using the current
+ /// reader row.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ protected override Delegate CreateCreateRecordDelegate(Type recordType) => (Func<dynamic>)CreateDynamicRecord;
+
+ /// <summary>
+ /// Creates a dynamic record of the current reader row.
+ /// </summary>
+ protected virtual dynamic CreateDynamicRecord()
+ {
+ var obj = new ExpandoObject();
+ var dict = obj as IDictionary<string, object>;
+ if (Reader.HeaderRecord != null)
+ {
+ for (var i = 0; i < Reader.HeaderRecord.Length; i++)
+ {
+ var args = new GetDynamicPropertyNameArgs(i, Reader.Context);
+ var propertyName = Reader.Configuration.GetDynamicPropertyName(args);
+ Reader.TryGetField(i, out string field);
+ dict.Add(propertyName, field);
+ }
+ }
+ else
+ {
+ for (var i = 0; i < Reader.Parser.Count; i++)
+ {
+ var args = new GetDynamicPropertyNameArgs(i, Reader.Context);
+ var propertyName = Reader.Configuration.GetDynamicPropertyName(args);
+ var field = Reader.GetField(i);
+ dict.Add(propertyName, field);
+ }
+ }
+
+ return obj;
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordWriter.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordWriter.cs
new file mode 100644
index 0000000..a9ee401
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/DynamicRecordWriter.cs
@@ -0,0 +1,75 @@
+// 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 Microsoft.CSharp.RuntimeBinder;
+using System;
+using System.Collections;
+using System.Dynamic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.CompilerServices;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Write dynamic records.
+ /// </summary>
+ public class DynamicRecordWriter : RecordWriter
+ {
+ private readonly Hashtable getters = new Hashtable();
+
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public DynamicRecordWriter(CsvWriter writer) : base(writer) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Action{T}"/>
+ /// that will write the given record using the current writer row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ protected override Action<T> CreateWriteDelegate<T>(T record)
+ {
+ // http://stackoverflow.com/a/14011692/68499
+
+ Action<T> action = r =>
+ {
+ var provider = (IDynamicMetaObjectProvider)r;
+ var type = provider.GetType();
+
+ var parameterExpression = Expression.Parameter(typeof(T), "record");
+ var metaObject = provider.GetMetaObject(parameterExpression);
+ var memberNames = metaObject.GetDynamicMemberNames();
+ if (Writer.Configuration.DynamicPropertySort != null)
+ {
+ memberNames = memberNames.OrderBy(name => name, Writer.Configuration.DynamicPropertySort);
+ }
+
+ foreach (var name in memberNames)
+ {
+ var value = GetValue(name, provider);
+ Writer.WriteField(value);
+ }
+ };
+
+ return action;
+ }
+
+ private object GetValue(string name, IDynamicMetaObjectProvider target)
+ {
+ // https://stackoverflow.com/a/30757547/68499
+
+ var callSite = (CallSite<Func<CallSite, IDynamicMetaObjectProvider, object>>)getters[name];
+ if (callSite == null)
+ {
+ var getMemberBinder = Binder.GetMember(CSharpBinderFlags.None, name, typeof(DynamicRecordWriter), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) });
+ getters[name] = callSite = CallSite<Func<CallSite, IDynamicMetaObjectProvider, object>>.Create(getMemberBinder);
+ }
+
+ return callSite.Target(callSite, target);
+ }
+ }
+} \ No newline at end of file
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpandoObjectRecordWriter.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpandoObjectRecordWriter.cs
new file mode 100644
index 0000000..f0a5e67
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpandoObjectRecordWriter.cs
@@ -0,0 +1,49 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Writes expando objects.
+ /// </summary>
+ public class ExpandoObjectRecordWriter : RecordWriter
+ {
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public ExpandoObjectRecordWriter(CsvWriter writer) : base(writer) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Action{T}"/>
+ /// that will write the given record using the current writer row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ protected override Action<T> CreateWriteDelegate<T>(T record)
+ {
+ Action<T> action = r =>
+ {
+ var dict = ((IDictionary<string, object>)r).AsEnumerable();
+
+ if (Writer.Configuration.DynamicPropertySort != null)
+ {
+ dict = dict.OrderBy(pair => pair.Key, Writer.Configuration.DynamicPropertySort);
+ }
+
+ var values = dict.Select(pair => pair.Value);
+ foreach (var val in values)
+ {
+ Writer.WriteField(val);
+ }
+ };
+
+ return action;
+ }
+ }
+} \ No newline at end of file
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpressionManager.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpressionManager.cs
new file mode 100644
index 0000000..42ac8a6
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ExpressionManager.cs
@@ -0,0 +1,490 @@
+// 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;
+using CsvHelper.TypeConversion;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Manages expression creation.
+ /// </summary>
+ public class ExpressionManager
+ {
+ private readonly CsvReader reader;
+ private readonly CsvWriter writer;
+
+ /// <summary>
+ /// Initializes a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ public ExpressionManager(CsvReader reader)
+ {
+ this.reader = reader;
+ }
+
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public ExpressionManager(CsvWriter writer)
+ {
+ this.writer = writer;
+ }
+
+ /// <summary>
+ /// Creates the constructor arguments used to create a type.
+ /// </summary>
+ /// <param name="map">The mapping to create the arguments for.</param>
+ /// <param name="argumentExpressions">The arguments that will be added to the mapping.</param>
+ public virtual void CreateConstructorArgumentExpressionsForMapping(ClassMap map, List<Expression> argumentExpressions)
+ {
+ foreach (var parameterMap in map.ParameterMaps)
+ {
+ if (parameterMap.Data.IsConstantSet)
+ {
+ var constantExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Constant), parameterMap.Data.Parameter.ParameterType);
+ argumentExpressions.Add(constantExpression);
+
+ continue;
+ }
+
+ if (parameterMap.Data.Ignore)
+ {
+ Expression defaultExpression;
+ if (parameterMap.Data.IsDefaultSet)
+ {
+ defaultExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Default), parameterMap.Data.Parameter.ParameterType);
+ }
+ else if (parameterMap.Data.Parameter.HasDefaultValue)
+ {
+ defaultExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Parameter.DefaultValue), parameterMap.Data.Parameter.ParameterType);
+ }
+ else
+ {
+ defaultExpression = Expression.Default(parameterMap.Data.Parameter.ParameterType);
+ }
+
+ argumentExpressions.Add(defaultExpression);
+
+ continue;
+ }
+
+ if (parameterMap.ConstructorTypeMap != null)
+ {
+ // Constructor parameter type.
+ var arguments = new List<Expression>();
+ CreateConstructorArgumentExpressionsForMapping(parameterMap.ConstructorTypeMap, arguments);
+ var args = new GetConstructorArgs(parameterMap.ConstructorTypeMap.ClassType);
+ var constructorExpression = Expression.New(reader.Configuration.GetConstructor(args), arguments);
+
+ argumentExpressions.Add(constructorExpression);
+ }
+ else if (parameterMap.ReferenceMap != null)
+ {
+ // Reference type.
+
+ var referenceAssignments = new List<MemberAssignment>();
+ CreateMemberAssignmentsForMapping(parameterMap.ReferenceMap.Data.Mapping, referenceAssignments);
+
+ var referenceBody = CreateInstanceAndAssignMembers(parameterMap.ReferenceMap.Data.Parameter.ParameterType, referenceAssignments);
+ argumentExpressions.Add(referenceBody);
+ }
+ else
+ {
+ // Value type.
+
+ int index;
+ if (reader.Configuration.HasHeaderRecord && (parameterMap.Data.IsNameSet || !parameterMap.Data.IsIndexSet))
+ {
+ // Use name.
+ index = reader.GetFieldIndex(parameterMap.Data.Names, parameterMap.Data.NameIndex, parameterMap.Data.IsOptional);
+ if (index == -1)
+ {
+ if (parameterMap.Data.IsDefaultSet || parameterMap.Data.IsOptional)
+ {
+ var defaultExpression = CreateDefaultExpression(parameterMap, Expression.Constant(string.Empty));
+ argumentExpressions.Add(defaultExpression);
+ continue;
+ }
+
+ // Skip if the index was not found.
+ continue;
+ }
+ }
+ else if (!parameterMap.Data.IsIndexSet && parameterMap.Data.IsOptional)
+ {
+ // If there wasn't an index explicitly, use a default value since constructors need all
+ // arguments to be created.
+ var defaultExpression = CreateDefaultExpression(parameterMap, Expression.Constant(string.Empty));
+ argumentExpressions.Add(defaultExpression);
+ continue;
+ }
+ else
+ {
+ // Use index.
+ index = parameterMap.Data.Index;
+ }
+
+ // Get the field using the field index.
+ var method = typeof(IReaderRow).GetProperty("Item", typeof(string), new[] { typeof(int) }).GetGetMethod();
+ Expression fieldExpression = Expression.Call(Expression.Constant(reader), method, Expression.Constant(index, typeof(int)));
+
+ if (parameterMap.Data.IsDefaultSet)
+ {
+ fieldExpression = CreateDefaultExpression(parameterMap, fieldExpression);
+ }
+ else
+ {
+ fieldExpression = CreateTypeConverterExpression(parameterMap, fieldExpression);
+ }
+
+ argumentExpressions.Add(fieldExpression);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates the member assignments for the given <see cref="ClassMap"/>.
+ /// </summary>
+ /// <param name="mapping">The mapping to create the assignments for.</param>
+ /// <param name="assignments">The assignments that will be added to from the mapping.</param>
+ public virtual void CreateMemberAssignmentsForMapping(ClassMap mapping, List<MemberAssignment> assignments)
+ {
+ foreach (var memberMap in mapping.MemberMaps)
+ {
+ var fieldExpression = CreateGetFieldExpression(memberMap);
+ if (fieldExpression == null)
+ {
+ continue;
+ }
+
+ assignments.Add(Expression.Bind(memberMap.Data.Member, fieldExpression));
+ }
+
+ foreach (var referenceMap in mapping.ReferenceMaps)
+ {
+ if (!reader.CanRead(referenceMap))
+ {
+ continue;
+ }
+
+ Expression referenceBody;
+ if (referenceMap.Data.Mapping.ParameterMaps.Count > 0)
+ {
+ var arguments = new List<Expression>();
+ CreateConstructorArgumentExpressionsForMapping(referenceMap.Data.Mapping, arguments);
+ var args = new GetConstructorArgs(referenceMap.Data.Mapping.ClassType);
+ referenceBody = Expression.New(reader.Configuration.GetConstructor(args), arguments);
+ }
+ else
+ {
+ var referenceAssignments = new List<MemberAssignment>();
+ CreateMemberAssignmentsForMapping(referenceMap.Data.Mapping, referenceAssignments);
+ referenceBody = CreateInstanceAndAssignMembers(referenceMap.Data.Member.MemberType(), referenceAssignments);
+ }
+
+ assignments.Add(Expression.Bind(referenceMap.Data.Member, referenceBody));
+ }
+ }
+
+ /// <summary>
+ /// Creates an expression the represents getting the field for the given
+ /// member and converting it to the member's type.
+ /// </summary>
+ /// <param name="memberMap">The mapping for the member.</param>
+ public virtual Expression? CreateGetFieldExpression(MemberMap memberMap)
+ {
+ if (memberMap.Data.ReadingConvertExpression != null)
+ {
+ // The user is providing the expression to do the conversion.
+ Expression exp = Expression.Invoke(memberMap.Data.ReadingConvertExpression, Expression.Constant(new ConvertFromStringArgs(reader)));
+ return Expression.Convert(exp, memberMap.Data.Member.MemberType());
+ }
+
+ if (!reader.CanRead(memberMap))
+ {
+ return null;
+ }
+
+ if (memberMap.Data.IsConstantSet)
+ {
+ return Expression.Convert(Expression.Constant(memberMap.Data.Constant), memberMap.Data.Member.MemberType());
+ }
+
+ if (memberMap.Data.TypeConverter == null)
+ {
+ // Skip if the type isn't convertible.
+ return null;
+ }
+
+ int index;
+ if (reader.Configuration.HasHeaderRecord && (memberMap.Data.IsNameSet || !memberMap.Data.IsIndexSet))
+ {
+ // Use the name.
+ index = reader.GetFieldIndex(memberMap.Data.Names, memberMap.Data.NameIndex, memberMap.Data.IsOptional);
+ if (index == -1)
+ {
+ if (memberMap.Data.IsDefaultSet)
+ {
+ return CreateDefaultExpression(memberMap, Expression.Constant(string.Empty));
+ }
+
+ // Skip if the index was not found.
+ return null;
+ }
+ }
+ else
+ {
+ // Use the index.
+ index = memberMap.Data.Index;
+ }
+
+ // Get the field using the field index.
+ var method = typeof(IReaderRow).GetProperty("Item", typeof(string), new[] { typeof(int) }).GetGetMethod();
+ Expression fieldExpression = Expression.Call(Expression.Constant(reader), method, Expression.Constant(index, typeof(int)));
+
+ // Validate the field.
+ if (memberMap.Data.ValidateExpression != null)
+ {
+ var constructor = typeof(ValidateArgs).GetConstructor(new Type[] { typeof(string), typeof(IReaderRow) });
+ var args = Expression.New(constructor, fieldExpression, Expression.Constant(reader));
+ var validateExpression = Expression.IsFalse(Expression.Invoke(memberMap.Data.ValidateExpression, args));
+ var validationExceptionConstructor = typeof(FieldValidationException).GetConstructor(new Type[] { typeof(CsvContext), typeof(string), typeof(string) });
+ var messageExpression = Expression.Invoke(memberMap.Data.ValidateMessageExpression, args);
+ var newValidationExceptionExpression = Expression.New(validationExceptionConstructor, Expression.Constant(reader.Context), fieldExpression, messageExpression);
+ var throwExpression = Expression.Throw(newValidationExceptionExpression);
+ fieldExpression = Expression.Block(
+ // If the validate method returns false, throw an exception.
+ Expression.IfThen(validateExpression, throwExpression),
+ fieldExpression
+ );
+ }
+
+ if (memberMap.Data.IsDefaultSet)
+ {
+ return CreateDefaultExpression(memberMap, fieldExpression);
+ }
+
+ fieldExpression = CreateTypeConverterExpression(memberMap, fieldExpression);
+
+ return fieldExpression;
+ }
+
+ /// <summary>
+ /// Creates a member expression for the given member on the record.
+ /// This will recursively traverse the mapping to find the member
+ /// and create a safe member accessor for each level as it goes.
+ /// </summary>
+ /// <param name="recordExpression">The current member expression.</param>
+ /// <param name="mapping">The mapping to look for the member to map on.</param>
+ /// <param name="memberMap">The member map to look for on the mapping.</param>
+ /// <returns>An Expression to access the given member.</returns>
+ public virtual Expression? CreateGetMemberExpression(Expression recordExpression, ClassMap mapping, MemberMap memberMap)
+ {
+ if (mapping.MemberMaps.Any(mm => mm == memberMap))
+ {
+ // The member is on this level.
+ if (memberMap.Data.Member is PropertyInfo)
+ {
+ return Expression.Property(recordExpression, (PropertyInfo)memberMap.Data.Member);
+ }
+
+ if (memberMap.Data.Member is FieldInfo)
+ {
+ return Expression.Field(recordExpression, (FieldInfo)memberMap.Data.Member);
+ }
+ }
+
+ // The member isn't on this level of the mapping.
+ // We need to search down through the reference maps.
+ foreach (var refMap in mapping.ReferenceMaps)
+ {
+ var wrapped = refMap.Data.Member.GetMemberExpression(recordExpression);
+ var memberExpression = CreateGetMemberExpression(wrapped, refMap.Data.Mapping, memberMap);
+ if (memberExpression == null)
+ {
+ continue;
+ }
+
+ if (refMap.Data.Member.MemberType().GetTypeInfo().IsValueType)
+ {
+ return memberExpression;
+ }
+
+ var nullCheckExpression = Expression.Equal(wrapped, Expression.Constant(null));
+
+ var isValueType = memberMap.Data.Member.MemberType().GetTypeInfo().IsValueType;
+ var isGenericType = isValueType && memberMap.Data.Member.MemberType().GetTypeInfo().IsGenericType;
+ Type memberType;
+ if (isValueType && !isGenericType && !writer.Configuration.UseNewObjectForNullReferenceMembers)
+ {
+ memberType = typeof(Nullable<>).MakeGenericType(memberMap.Data.Member.MemberType());
+ memberExpression = Expression.Convert(memberExpression, memberType);
+ }
+ else
+ {
+ memberType = memberMap.Data.Member.MemberType();
+ }
+
+ var defaultValueExpression = isValueType && !isGenericType
+ ? (Expression)Expression.New(memberType)
+ : Expression.Constant(null, memberType);
+ var conditionExpression = Expression.Condition(nullCheckExpression, defaultValueExpression, memberExpression);
+ return conditionExpression;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Creates an instance of the given type using <see cref="IObjectResolver"/>, then assigns
+ /// the given member assignments to that instance.
+ /// </summary>
+ /// <param name="recordType">The type of the record we're creating.</param>
+ /// <param name="assignments">The member assignments that will be assigned to the created instance.</param>
+ /// <returns>A <see cref="BlockExpression"/> representing the instance creation and assignments.</returns>
+ public virtual BlockExpression CreateInstanceAndAssignMembers(Type recordType, List<MemberAssignment> assignments)
+ {
+ var expressions = new List<Expression>();
+ var createInstanceMethod = typeof(IObjectResolver).GetMethod(nameof(IObjectResolver.Resolve), new Type[] { typeof(Type), typeof(object[]) });
+ var instanceExpression = Expression.Convert(Expression.Call(Expression.Constant(ObjectResolver.Current), createInstanceMethod, Expression.Constant(recordType), Expression.Constant(new object[0])), recordType);
+ var variableExpression = Expression.Variable(instanceExpression.Type, "instance");
+ expressions.Add(Expression.Assign(variableExpression, instanceExpression));
+ expressions.AddRange(assignments.Select(b => Expression.Assign(Expression.MakeMemberAccess(variableExpression, b.Member), b.Expression)));
+ expressions.Add(variableExpression);
+ var variables = new ParameterExpression[] { variableExpression };
+ var blockExpression = Expression.Block(variables, expressions);
+
+ return blockExpression;
+ }
+
+ /// <summary>
+ /// Creates an expression that converts the field expression using a type converter.
+ /// </summary>
+ /// <param name="memberMap">The mapping for the member.</param>
+ /// <param name="fieldExpression">The field expression.</param>
+ public virtual Expression CreateTypeConverterExpression(MemberMap memberMap, Expression fieldExpression)
+ {
+ memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions { CultureInfo = reader.Configuration.CultureInfo }, reader.Context.TypeConverterOptionsCache.GetOptions(memberMap.Data.Member.MemberType()), memberMap.Data.TypeConverterOptions);
+
+ Expression typeConverterFieldExpression = Expression.Call(Expression.Constant(memberMap.Data.TypeConverter), nameof(ITypeConverter.ConvertFromString), null, fieldExpression, Expression.Constant(reader), Expression.Constant(memberMap.Data));
+ typeConverterFieldExpression = Expression.Convert(typeConverterFieldExpression, memberMap.Data.Member.MemberType());
+
+ return typeConverterFieldExpression;
+ }
+
+ /// <summary>
+ /// Creates an expression that converts the field expression using a type converter.
+ /// </summary>
+ /// <param name="parameterMap">The mapping for the parameter.</param>
+ /// <param name="fieldExpression">The field expression.</param>
+ public virtual Expression CreateTypeConverterExpression(ParameterMap parameterMap, Expression fieldExpression)
+ {
+ parameterMap.Data.TypeConverterOptions = TypeConverterOptions.Merge
+ (
+ new TypeConverterOptions { CultureInfo = reader.Configuration.CultureInfo },
+ reader.Context.TypeConverterOptionsCache.GetOptions(parameterMap.Data.Parameter.ParameterType),
+ parameterMap.Data.TypeConverterOptions
+ );
+
+ var memberMapData = new MemberMapData(null)
+ {
+ Constant = parameterMap.Data.Constant,
+ Default = parameterMap.Data.Default,
+ Ignore = parameterMap.Data.Ignore,
+ Index = parameterMap.Data.Index,
+ IsConstantSet = parameterMap.Data.IsConstantSet,
+ IsDefaultSet = parameterMap.Data.IsDefaultSet,
+ IsIndexSet = parameterMap.Data.IsIndexSet,
+ IsNameSet = parameterMap.Data.IsNameSet,
+ NameIndex = parameterMap.Data.NameIndex,
+ TypeConverter = parameterMap.Data.TypeConverter,
+ TypeConverterOptions = parameterMap.Data.TypeConverterOptions
+ };
+ memberMapData.Names.AddRange(parameterMap.Data.Names);
+
+ Expression typeConverterFieldExpression = Expression.Call(Expression.Constant(parameterMap.Data.TypeConverter), nameof(ITypeConverter.ConvertFromString), null, fieldExpression, Expression.Constant(reader), Expression.Constant(memberMapData));
+ typeConverterFieldExpression = Expression.Convert(typeConverterFieldExpression, parameterMap.Data.Parameter.ParameterType);
+
+ return typeConverterFieldExpression;
+ }
+
+ /// <summary>
+ /// Creates a default expression if field expression is empty.
+ /// </summary>
+ /// <param name="memberMap">The mapping for the member.</param>
+ /// <param name="fieldExpression">The field expression.</param>
+ public virtual Expression CreateDefaultExpression(MemberMap memberMap, Expression fieldExpression)
+ {
+ var typeConverterExpression = CreateTypeConverterExpression(memberMap, fieldExpression);
+
+ // Create default value expression.
+ Expression defaultValueExpression;
+ if (memberMap.Data.Member.MemberType() != typeof(string) && memberMap.Data.Default != null && memberMap.Data.Default.GetType() == typeof(string))
+ {
+ // The default is a string but the member type is not. Use a converter.
+ defaultValueExpression = Expression.Call(Expression.Constant(memberMap.Data.TypeConverter), nameof(ITypeConverter.ConvertFromString), null, Expression.Constant(memberMap.Data.Default), Expression.Constant(reader), Expression.Constant(memberMap.Data));
+ }
+ else
+ {
+ // The member type and default type match.
+ defaultValueExpression = Expression.Constant(memberMap.Data.Default);
+ }
+
+ defaultValueExpression = Expression.Convert(defaultValueExpression, memberMap.Data.Member.MemberType());
+
+ // If null, use string.Empty.
+ var coalesceExpression = Expression.Coalesce(fieldExpression, Expression.Constant(string.Empty));
+
+ // Check if the field is an empty string.
+ var checkFieldEmptyExpression = Expression.Equal(Expression.Convert(coalesceExpression, typeof(string)), Expression.Constant(string.Empty, typeof(string)));
+
+ // Use a default value if the field is an empty string.
+ fieldExpression = Expression.Condition(checkFieldEmptyExpression, defaultValueExpression, typeConverterExpression);
+
+ return fieldExpression;
+ }
+
+ /// <summary>
+ /// Creates a default expression if field expression is empty.
+ /// </summary>
+ /// <param name="parameterMap">The mapping for the parameter.</param>
+ /// <param name="fieldExpression">The field expression.</param>
+ public virtual Expression CreateDefaultExpression(ParameterMap parameterMap, Expression fieldExpression)
+ {
+ var typeConverterExpression = CreateTypeConverterExpression(parameterMap, fieldExpression);
+
+ // Create default value expression.
+ Expression defaultValueExpression;
+ if (parameterMap.Data.Parameter.ParameterType != typeof(string) && parameterMap.Data.Default != null && parameterMap.Data.Default.GetType() == typeof(string))
+ {
+ // The default is a string but the member type is not. Use a converter.
+ //defaultValueExpression = Expression.Call(Expression.Constant(parameterMap.Data.TypeConverter), nameof(ITypeConverter.ConvertFromString), null, Expression.Constant(parameterMap.Data.Default), Expression.Constant(reader), Expression.Constant(memberMap.Data));
+ defaultValueExpression = CreateTypeConverterExpression(parameterMap, Expression.Constant(parameterMap.Data.Default));
+ }
+ else
+ {
+ // The member type and default type match.
+ defaultValueExpression = Expression.Convert(Expression.Constant(parameterMap.Data.Default), parameterMap.Data.Parameter.ParameterType);
+ }
+
+ // If null, use string.Empty.
+ var coalesceExpression = Expression.Coalesce(fieldExpression, Expression.Constant(string.Empty));
+
+ // Check if the field is an empty string.
+ var checkFieldEmptyExpression = Expression.Equal(Expression.Convert(coalesceExpression, typeof(string)), Expression.Constant(string.Empty, typeof(string)));
+
+ // Use a default value if the field is an empty string.
+ fieldExpression = Expression.Condition(checkFieldEmptyExpression, defaultValueExpression, typeConverterExpression);
+
+ return fieldExpression;
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordCreator.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordCreator.cs
new file mode 100644
index 0000000..9adc951
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordCreator.cs
@@ -0,0 +1,66 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Creates objects.
+ /// </summary>
+ public class ObjectRecordCreator : RecordCreator
+ {
+ /// <summary>
+ /// Initializes a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader"></param>
+ public ObjectRecordCreator(CsvReader reader) : base(reader) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Func{T}"/>
+ /// that will create a record of the given type using the current
+ /// reader row.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ protected override Delegate CreateCreateRecordDelegate(Type recordType)
+ {
+ if (Reader.Context.Maps[recordType] == null)
+ {
+ Reader.Context.Maps.Add(Reader.Context.AutoMap(recordType));
+ }
+
+ var map = Reader.Context.Maps[recordType];
+
+ Expression body;
+
+ if (map.ParameterMaps.Count > 0)
+ {
+ // This is a constructor parameter type.
+ var arguments = new List<Expression>();
+ ExpressionManager.CreateConstructorArgumentExpressionsForMapping(map, arguments);
+
+ var args = new GetConstructorArgs(map.ClassType);
+ body = Expression.New(Reader.Configuration.GetConstructor(args), arguments);
+ }
+ else
+ {
+ var assignments = new List<MemberAssignment>();
+ ExpressionManager.CreateMemberAssignmentsForMapping(map, assignments);
+
+ if (assignments.Count == 0)
+ {
+ throw new ReaderException(Reader.Context, $"No members are mapped for type '{recordType.FullName}'.");
+ }
+
+ body = ExpressionManager.CreateInstanceAndAssignMembers(recordType, assignments);
+ }
+
+ var funcType = typeof(Func<>).MakeGenericType(recordType);
+
+ return Expression.Lambda(funcType, body).Compile();
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordWriter.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordWriter.cs
new file mode 100644
index 0000000..08fc79b
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/ObjectRecordWriter.cs
@@ -0,0 +1,124 @@
+// 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;
+using CsvHelper.TypeConversion;
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Writes objects.
+ /// </summary>
+ public class ObjectRecordWriter : RecordWriter
+ {
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public ObjectRecordWriter(CsvWriter writer) : base(writer) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Action{T}"/>
+ /// that will write the given record using the current writer row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ protected override Action<T> CreateWriteDelegate<T>(T record)
+ {
+ var type = Writer.GetTypeForRecord(record);
+
+ if (Writer.Context.Maps[type] == null)
+ {
+ Writer.Context.Maps.Add(Writer.Context.AutoMap(type));
+ }
+
+ var recordParameter = Expression.Parameter(typeof(T), "record");
+ var recordParameterConverted = Expression.Convert(recordParameter, type);
+
+ // Get a list of all the members so they will
+ // be sorted properly.
+ var members = new MemberMapCollection();
+ members.AddMembers(Writer.Context.Maps[type]);
+
+ if (members.Count == 0)
+ {
+ throw new WriterException(Writer.Context, $"No properties are mapped for type '{type.FullName}'.");
+ }
+
+ var delegates = new List<Action<T>>();
+
+ foreach (var memberMap in members)
+ {
+ if (memberMap.Data.WritingConvertExpression != null)
+ {
+ // The user is providing the expression to do the conversion.
+ var constructor = typeof(ConvertToStringArgs<T>).GetConstructor(new Type[] { typeof(T) });
+ var args = Expression.New(constructor, recordParameterConverted);
+ Expression exp = Expression.Invoke(memberMap.Data.WritingConvertExpression, args);
+ exp = Expression.Call(Expression.Constant(Writer), nameof(Writer.WriteField), null, exp);
+ delegates.Add(Expression.Lambda<Action<T>>(exp, recordParameter).Compile());
+ continue;
+ }
+
+ if (!Writer.CanWrite(memberMap))
+ {
+ continue;
+ }
+
+ Expression fieldExpression;
+
+ if (memberMap.Data.IsConstantSet)
+ {
+ if (memberMap.Data.Constant == null)
+ {
+ fieldExpression = Expression.Constant(string.Empty);
+ }
+ else
+ {
+ fieldExpression = Expression.Constant(memberMap.Data.Constant);
+ var typeConverterExpression = Expression.Constant(Writer.Context.TypeConverterCache.GetConverter(memberMap.Data.Constant.GetType()));
+ var method = typeof(ITypeConverter).GetMethod(nameof(ITypeConverter.ConvertToString));
+ fieldExpression = Expression.Convert(fieldExpression, typeof(object));
+ fieldExpression = Expression.Call(typeConverterExpression, method, fieldExpression, Expression.Constant(Writer), Expression.Constant(memberMap.Data));
+ }
+ }
+ else
+ {
+ if (memberMap.Data.TypeConverter == null)
+ {
+ // Skip if the type isn't convertible.
+ continue;
+ }
+
+ fieldExpression = ExpressionManager.CreateGetMemberExpression(recordParameterConverted, Writer.Context.Maps[type], memberMap);
+
+ var typeConverterExpression = Expression.Constant(memberMap.Data.TypeConverter);
+ memberMap.Data.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions { CultureInfo = Writer.Configuration.CultureInfo }, Writer.Context.TypeConverterOptionsCache.GetOptions(memberMap.Data.Member.MemberType()), memberMap.Data.TypeConverterOptions);
+
+ var method = typeof(ITypeConverter).GetMethod(nameof(ITypeConverter.ConvertToString));
+ fieldExpression = Expression.Convert(fieldExpression, typeof(object));
+ fieldExpression = Expression.Call(typeConverterExpression, method, fieldExpression, Expression.Constant(Writer), Expression.Constant(memberMap.Data));
+
+ if (type.GetTypeInfo().IsClass)
+ {
+ var areEqualExpression = Expression.Equal(recordParameterConverted, Expression.Constant(null));
+ fieldExpression = Expression.Condition(areEqualExpression, Expression.Constant(string.Empty), fieldExpression);
+ }
+ }
+
+ var writeFieldMethodCall = Expression.Call(Expression.Constant(Writer), nameof(Writer.WriteConvertedField), null, fieldExpression, Expression.Constant(memberMap.Data.Type));
+
+ delegates.Add(Expression.Lambda<Action<T>>(writeFieldMethodCall, recordParameter).Compile());
+ }
+
+ var action = CombineDelegates(delegates) ?? new Action<T>((T parameter) => { });
+
+ return action;
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordCreator.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordCreator.cs
new file mode 100644
index 0000000..3e1cfd0
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordCreator.cs
@@ -0,0 +1,49 @@
+// 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;
+using CsvHelper.TypeConversion;
+using System;
+using System.Linq.Expressions;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Creates primitive records.
+ /// </summary>
+ public class PrimitiveRecordCreator : RecordCreator
+ {
+ /// <summary>
+ /// Initializes a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ public PrimitiveRecordCreator(CsvReader reader) : base(reader) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Func{T}"/>
+ /// that will create a record of the given type using the current
+ /// reader row.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ protected override Delegate CreateCreateRecordDelegate(Type recordType)
+ {
+ var method = typeof(IReaderRow).GetProperty("Item", typeof(string), new[] { typeof(int) }).GetGetMethod();
+ Expression fieldExpression = Expression.Call(Expression.Constant(Reader), method, Expression.Constant(0, typeof(int)));
+
+ var memberMapData = new MemberMapData(null)
+ {
+ Index = 0,
+ TypeConverter = Reader.Context.TypeConverterCache.GetConverter(recordType)
+ };
+ memberMapData.TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions { CultureInfo = Reader.Configuration.CultureInfo }, Reader.Context.TypeConverterOptionsCache.GetOptions(recordType));
+
+ fieldExpression = Expression.Call(Expression.Constant(memberMapData.TypeConverter), "ConvertFromString", null, fieldExpression, Expression.Constant(Reader), Expression.Constant(memberMapData));
+ fieldExpression = Expression.Convert(fieldExpression, recordType);
+
+ var funcType = typeof(Func<>).MakeGenericType(recordType);
+
+ return Expression.Lambda(funcType, fieldExpression).Compile();
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordWriter.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordWriter.cs
new file mode 100644
index 0000000..128c5c3
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/PrimitiveRecordWriter.cs
@@ -0,0 +1,57 @@
+// 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;
+using CsvHelper.TypeConversion;
+using System;
+using System.Linq.Expressions;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Writes primitives.
+ /// </summary>
+ public class PrimitiveRecordWriter : RecordWriter
+ {
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public PrimitiveRecordWriter(CsvWriter writer) : base(writer) { }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Action{T}"/>
+ /// that will write the given record using the current writer row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ protected override Action<T> CreateWriteDelegate<T>(T record)
+ {
+ var type = Writer.GetTypeForRecord(record);
+
+ var recordParameter = Expression.Parameter(typeof(T), "record");
+
+ Expression fieldExpression = Expression.Convert(recordParameter, typeof(object));
+
+ var typeConverter = Writer.Context.TypeConverterCache.GetConverter(type);
+ var typeConverterExpression = Expression.Constant(typeConverter);
+ var method = typeof(ITypeConverter).GetMethod(nameof(ITypeConverter.ConvertToString));
+
+ var memberMapData = new MemberMapData(null)
+ {
+ Index = 0,
+ TypeConverter = typeConverter,
+ TypeConverterOptions = TypeConverterOptions.Merge(new TypeConverterOptions(), Writer.Context.TypeConverterOptionsCache.GetOptions(type))
+ };
+ memberMapData.TypeConverterOptions.CultureInfo = Writer.Configuration.CultureInfo;
+
+ fieldExpression = Expression.Call(typeConverterExpression, method, fieldExpression, Expression.Constant(Writer), Expression.Constant(memberMapData));
+ fieldExpression = Expression.Call(Expression.Constant(Writer), nameof(Writer.WriteConvertedField), null, fieldExpression, Expression.Constant(type));
+
+ var action = Expression.Lambda<Action<T>>(fieldExpression, recordParameter).Compile();
+
+ return action;
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreator.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreator.cs
new file mode 100644
index 0000000..d14f0d6
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreator.cs
@@ -0,0 +1,107 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Base implementation for classes that create records.
+ /// </summary>
+ public abstract class RecordCreator
+ {
+ private readonly Dictionary<Type, Delegate> createRecordFuncs = new Dictionary<Type, Delegate>();
+
+ /// <summary>
+ /// The reader.
+ /// </summary>
+ protected CsvReader Reader { get; private set; }
+
+ /// <summary>
+ /// The expression manager.
+ /// </summary>
+ protected ExpressionManager ExpressionManager { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ public RecordCreator(CsvReader reader)
+ {
+ Reader = reader;
+ ExpressionManager = new ExpressionManager(reader);
+ }
+
+ /// <summary>
+ /// Create a record of the given type using the current row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ public T Create<T>()
+ {
+ try
+ {
+ return ((Func<T>)GetCreateRecordDelegate(typeof(T))).Invoke();
+ }
+ catch (TargetInvocationException ex)
+ {
+ if (ex.InnerException != null)
+ {
+ throw ex.InnerException;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Create a record of the given type using the current row.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ public object? Create(Type recordType)
+ {
+ try
+ {
+ return GetCreateRecordDelegate(recordType).DynamicInvoke();
+ }
+ catch (TargetInvocationException ex)
+ {
+ if (ex.InnerException != null)
+ {
+ throw ex.InnerException;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the delegate to create a record for the given record type.
+ /// If the delegate doesn't exist, one will be created and cached.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ protected virtual Delegate GetCreateRecordDelegate(Type recordType)
+ {
+ if (!createRecordFuncs.TryGetValue(recordType, out Delegate func))
+ {
+ createRecordFuncs[recordType] = func = CreateCreateRecordDelegate(recordType);
+ }
+
+ return func;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Func{T}"/>
+ /// that will create a record of the given type using the current
+ /// reader row.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ protected abstract Delegate CreateCreateRecordDelegate(Type recordType);
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreatorFactory.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreatorFactory.cs
new file mode 100644
index 0000000..b785115
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordCreatorFactory.cs
@@ -0,0 +1,51 @@
+// 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 System;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Factory to create record creators.
+ /// </summary>
+ public class RecordCreatorFactory
+ {
+ private readonly CsvReader reader;
+ private readonly DynamicRecordCreator dynamicRecordCreator;
+ private readonly PrimitiveRecordCreator primitiveRecordCreator;
+ private readonly ObjectRecordCreator objectRecordCreator;
+
+ /// <summary>
+ /// Initializes a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ public RecordCreatorFactory(CsvReader reader)
+ {
+ this.reader = reader;
+ dynamicRecordCreator = new DynamicRecordCreator(reader);
+ primitiveRecordCreator = new PrimitiveRecordCreator(reader);
+ objectRecordCreator = new ObjectRecordCreator(reader);
+ }
+
+ /// <summary>
+ /// Creates a record creator for the given record type.
+ /// </summary>
+ /// <param name="recordType">The record type.</param>
+ public virtual RecordCreator MakeRecordCreator(Type recordType)
+ {
+ if (recordType == typeof(object))
+ {
+ return dynamicRecordCreator;
+ }
+
+ if (recordType.GetTypeInfo().IsPrimitive)
+ {
+ return primitiveRecordCreator;
+ }
+
+ return objectRecordCreator;
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordHydrator.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordHydrator.cs
new file mode 100644
index 0000000..7de4ddb
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordHydrator.cs
@@ -0,0 +1,126 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Hydrates members of an existing record.
+ /// </summary>
+ public class RecordHydrator
+ {
+ private readonly CsvReader reader;
+ private readonly ExpressionManager expressionManager;
+ private readonly Dictionary<Type, Delegate> hydrateRecordActions = new Dictionary<Type, Delegate>();
+
+ /// <summary>
+ /// Creates a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ public RecordHydrator(CsvReader reader)
+ {
+ this.reader = reader;
+ expressionManager = ObjectResolver.Current.Resolve<ExpressionManager>(reader);
+ }
+
+ /// <summary>
+ /// Hydrates members of the given record using the current reader row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ public void Hydrate<T>(T record)
+ {
+ try
+ {
+ GetHydrateRecordAction<T>()(record);
+ }
+ catch (TargetInvocationException ex)
+ {
+ if (ex.InnerException != null)
+ {
+ throw ex.InnerException;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the action delegate used to hydrate a custom class object's members with data from the reader.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ protected virtual Action<T> GetHydrateRecordAction<T>()
+ {
+ var recordType = typeof(T);
+
+ if (!hydrateRecordActions.TryGetValue(recordType, out Delegate action))
+ {
+ hydrateRecordActions[recordType] = action = CreateHydrateRecordAction<T>();
+ }
+
+ return (Action<T>)action;
+ }
+
+ /// <summary>
+ /// Creates the action delegate used to hydrate a record's members with data from the reader.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ protected virtual Action<T> CreateHydrateRecordAction<T>()
+ {
+ var recordType = typeof(T);
+
+ if (reader.Context.Maps[recordType] == null)
+ {
+ reader.Context.Maps.Add(reader.Context.AutoMap(recordType));
+ }
+
+ var mapping = reader.Context.Maps[recordType];
+
+ var recordTypeParameter = Expression.Parameter(recordType, "record");
+ var memberAssignments = new List<Expression>();
+
+ foreach (var memberMap in mapping.MemberMaps)
+ {
+ var fieldExpression = expressionManager.CreateGetFieldExpression(memberMap);
+ if (fieldExpression == null)
+ {
+ continue;
+ }
+
+ var memberTypeParameter = Expression.Parameter(memberMap.Data.Member.MemberType(), "member");
+ var memberAccess = Expression.MakeMemberAccess(recordTypeParameter, memberMap.Data.Member);
+ var memberAssignment = Expression.Assign(memberAccess, fieldExpression);
+ memberAssignments.Add(memberAssignment);
+ }
+
+ foreach (var referenceMap in mapping.ReferenceMaps)
+ {
+ if (!reader.CanRead(referenceMap))
+ {
+ continue;
+ }
+
+ var referenceAssignments = new List<MemberAssignment>();
+ expressionManager.CreateMemberAssignmentsForMapping(referenceMap.Data.Mapping, referenceAssignments);
+
+ var referenceBody = expressionManager.CreateInstanceAndAssignMembers(referenceMap.Data.Member.MemberType(), referenceAssignments);
+
+ var memberTypeParameter = Expression.Parameter(referenceMap.Data.Member.MemberType(), "referenceMember");
+ var memberAccess = Expression.MakeMemberAccess(recordTypeParameter, referenceMap.Data.Member);
+ var memberAssignment = Expression.Assign(memberAccess, referenceBody);
+ memberAssignments.Add(memberAssignment);
+ }
+
+ var body = Expression.Block(memberAssignments);
+
+ return Expression.Lambda<Action<T>>(body, recordTypeParameter).Compile();
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordManager.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordManager.cs
new file mode 100644
index 0000000..eef3d6e
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordManager.cs
@@ -0,0 +1,80 @@
+// 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 System;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Manages record manipulation.
+ /// </summary>
+ public class RecordManager
+ {
+ private readonly CsvReader reader;
+ private readonly RecordCreatorFactory recordCreatorFactory;
+ private readonly RecordHydrator recordHydrator;
+ private readonly RecordWriterFactory recordWriterFactory;
+
+ /// <summary>
+ /// Initializes a new instance using the given reader.
+ /// </summary>
+ /// <param name="reader"></param>
+ public RecordManager(CsvReader reader)
+ {
+ this.reader = reader;
+ recordCreatorFactory = ObjectResolver.Current.Resolve<RecordCreatorFactory>(reader);
+ recordHydrator = ObjectResolver.Current.Resolve<RecordHydrator>(reader);
+ }
+
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public RecordManager(CsvWriter writer)
+ {
+ recordWriterFactory = ObjectResolver.Current.Resolve<RecordWriterFactory>(writer);
+ }
+
+ /// <summary>
+ /// Creates a record of the given type using the current reader row.
+ /// </summary>
+ /// <typeparam name="T">The type of record to create.</typeparam>
+ public T Create<T>()
+ {
+ var recordCreator = recordCreatorFactory.MakeRecordCreator(typeof(T));
+ return recordCreator.Create<T>();
+ }
+
+ /// <summary>
+ /// Creates a record of the given type using the current reader row.
+ /// </summary>
+ /// <param name="recordType">The type of record to create.</param>
+ public object? Create(Type recordType)
+ {
+ var recordCreator = recordCreatorFactory.MakeRecordCreator(recordType);
+ return recordCreator.Create(recordType);
+ }
+
+ /// <summary>
+ /// Hydrates the given record using the current reader row.
+ /// </summary>
+ /// <typeparam name="T">The type of the record.</typeparam>
+ /// <param name="record">The record to hydrate.</param>
+ public void Hydrate<T>(T record)
+ {
+ recordHydrator.Hydrate(record);
+ }
+
+ /// <summary>
+ /// Writes the given record to the current writer row.
+ /// </summary>
+ /// <typeparam name="T">The type of the record.</typeparam>
+ /// <param name="record">The record.</param>
+ public void Write<T>(T record)
+ {
+ var recordWriter = recordWriterFactory.MakeRecordWriter(record);
+ recordWriter.Write(record);
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriter.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriter.cs
new file mode 100644
index 0000000..ca1194f
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriter.cs
@@ -0,0 +1,109 @@
+// 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 System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Base implementation for classes that write records.
+ /// </summary>
+ public abstract class RecordWriter
+ {
+ private readonly Dictionary<int, Delegate> typeActions = new Dictionary<int, Delegate>();
+
+ /// <summary>
+ /// Gets the writer.
+ /// </summary>
+ protected CsvWriter Writer { get; private set; }
+
+ /// <summary>
+ /// The expression manager.
+ /// </summary>
+ protected ExpressionManager ExpressionManager { get; private set; }
+
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public RecordWriter(CsvWriter writer)
+ {
+ Writer = writer;
+ ExpressionManager = ObjectResolver.Current.Resolve<ExpressionManager>(writer);
+ }
+
+ /// <summary>
+ /// Writes the record to the current row.
+ /// </summary>
+ /// <typeparam name="T">Type of the record.</typeparam>
+ /// <param name="record">The record.</param>
+ public void Write<T>(T record)
+ {
+ try
+ {
+ GetWriteDelegate(record)(record);
+ }
+ catch (TargetInvocationException ex)
+ {
+ if (ex.InnerException != null)
+ {
+ throw ex.InnerException;
+ }
+ else
+ {
+ throw;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the delegate to write the given record.
+ /// If the delegate doesn't exist, one will be created and cached.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ protected Action<T> GetWriteDelegate<T>(T record)
+ {
+ var type = typeof(T);
+ var typeKeyName = type.AssemblyQualifiedName;
+ if (type == typeof(object))
+ {
+ type = record.GetType();
+ typeKeyName += $"|{type.AssemblyQualifiedName}";
+ }
+
+ int typeKey = typeKeyName.GetHashCode();
+
+ if (!typeActions.TryGetValue(typeKey, out Delegate action))
+ {
+ typeActions[typeKey] = action = CreateWriteDelegate(record);
+ }
+
+ return (Action<T>)action;
+ }
+
+ /// <summary>
+ /// Creates a <see cref="Delegate"/> of type <see cref="Action{T}"/>
+ /// that will write the given record using the current writer row.
+ /// </summary>
+ /// <typeparam name="T">The record type.</typeparam>
+ /// <param name="record">The record.</param>
+ protected abstract Action<T> CreateWriteDelegate<T>(T record);
+
+ /// <summary>
+ /// Combines the delegates into a single multicast delegate.
+ /// This is needed because Silverlight doesn't have the
+ /// Delegate.Combine( params Delegate[] ) overload.
+ /// </summary>
+ /// <param name="delegates">The delegates to combine.</param>
+ /// <returns>A multicast delegate combined from the given delegates.</returns>
+ protected virtual Action<T> CombineDelegates<T>(IEnumerable<Action<T>> delegates)
+ {
+ return (Action<T>)delegates.Aggregate<Delegate, Delegate>(null, Delegate.Combine);
+ }
+ }
+}
diff --git a/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriterFactory.cs b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriterFactory.cs
new file mode 100644
index 0000000..4f27001
--- /dev/null
+++ b/ThirdParty/CsvHelper-master/src/CsvHelper/Expressions/RecordWriterFactory.cs
@@ -0,0 +1,61 @@
+// 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 System.Dynamic;
+using System.Reflection;
+
+namespace CsvHelper.Expressions
+{
+ /// <summary>
+ /// Factory to create record writers.
+ /// </summary>
+ public class RecordWriterFactory
+ {
+ private readonly CsvWriter writer;
+ private readonly ExpandoObjectRecordWriter expandoObjectRecordWriter;
+ private readonly DynamicRecordWriter dynamicRecordWriter;
+ private readonly PrimitiveRecordWriter primitiveRecordWriter;
+ private readonly ObjectRecordWriter objectRecordWriter;
+
+ /// <summary>
+ /// Initializes a new instance using the given writer.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ public RecordWriterFactory(CsvWriter writer)
+ {
+ this.writer = writer;
+ expandoObjectRecordWriter = new ExpandoObjectRecordWriter(writer);
+ dynamicRecordWriter = new DynamicRecordWriter(writer);
+ primitiveRecordWriter = new PrimitiveRecordWriter(writer);
+ objectRecordWriter = new ObjectRecordWriter(writer);
+ }
+
+ /// <summary>
+ /// Creates a new record writer for the given record.
+ /// </summary>
+ /// <typeparam name="T">The type of the record.</typeparam>
+ /// <param name="record">The record.</param>
+ public virtual RecordWriter MakeRecordWriter<T>(T record)
+ {
+ var type = writer.GetTypeForRecord(record);
+
+ if (record is ExpandoObject expandoObject)
+ {
+ return expandoObjectRecordWriter;
+ }
+
+ if (record is IDynamicMetaObjectProvider dynamicObject)
+ {
+ return dynamicRecordWriter;
+ }
+
+ if (type.GetTypeInfo().IsPrimitive)
+ {
+ return primitiveRecordWriter;
+ }
+
+ return objectRecordWriter;
+ }
+ }
+}