// 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.Linq.Expressions; using System.Reflection; namespace CsvHelper.Configuration { /// /// Maps class members to CSV fields. /// /// The of class to map. public abstract class ClassMap : ClassMap { /// /// Creates an instance of . /// public ClassMap() : base(typeof(TClass)) { } /// /// Maps a member to a CSV field. /// /// 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 virtual MemberMap Map(Expression> expression, bool useExistingMap = true) { var (classMap, member) = GetMemberMap(expression); var memberMap = classMap.Map(typeof(TClass), member, useExistingMap); ; return (MemberMap)memberMap; } /// /// Maps a member to a CSV field. /// /// 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 virtual MemberMap Map(Expression> expression, bool useExistingMap = true) { var (classMap, member) = GetMemberMap(expression); var memberMap = classMap.Map(typeof(TClass), member, useExistingMap); return memberMap; } /// /// Meant for internal use only. /// Maps a member to another class map. When this is used, accessing a property through /// sub-property mapping later won't work. You can only use one or the other. When using /// this, ConvertUsing will also not work. /// /// The type of the class map. /// The expression. /// Constructor arguments used to create the reference map. /// The reference mapping for the member. public virtual MemberReferenceMap References(Expression> expression, params object[] constructorArgs) where TClassMap : ClassMap { var member = ReflectionHelper.GetMember(expression); return References(typeof(TClassMap), member, constructorArgs); } private (ClassMap, MemberInfo) GetMemberMap(Expression> expression) { var stack = ReflectionHelper.GetMembers(expression); if (stack.Count == 0) { throw new InvalidOperationException($"No members were found in expression '{expression}'."); } ClassMap currentClassMap = this; MemberInfo member; if (stack.Count > 1) { // We need to add a reference map for every sub member. while (stack.Count > 1) { member = stack.Pop(); Type mapType; var property = member as PropertyInfo; var field = member as FieldInfo; if (property != null) { mapType = typeof(DefaultClassMap<>).MakeGenericType(property.PropertyType); } else if (field != null) { mapType = typeof(DefaultClassMap<>).MakeGenericType(field.FieldType); } else { throw new InvalidOperationException("The given expression was not a property or a field."); } var referenceMap = currentClassMap.References(mapType, member); currentClassMap = referenceMap.Data.Mapping; } } // Add the member map to the last reference map. member = stack.Pop(); return (currentClassMap, member); } } }