1- namespace MiniExcelLib . Core . Helpers ;
2-
3- /// <summary>
4- /// Helper class for extracting mapping metadata using reflection.
5- /// Consolidates reflection-based property extraction logic to reduce duplication and improve performance.
6- /// </summary>
7- internal static class MappingMetadataExtractor
8- {
9- /// <summary>
10- /// Extracts nested mapping information from a compiled mapping object.
11- /// This method minimizes reflection by extracting properties once at compile time.
12- /// </summary>
13- /// <param name="nestedMapping">The nested mapping object to extract information from</param>
14- /// <param name="itemType">The type of items in the nested mapping</param>
15- /// <returns>Nested mapping information or null if extraction fails</returns>
16- public static NestedMappingInfo ? ExtractNestedMappingInfo ( object nestedMapping , Type itemType )
17- {
18- // Use reflection minimally to extract properties from the nested mapping
19- // This is done once at compile time, not at runtime
20- var nestedMappingType = nestedMapping . GetType ( ) ;
21- var propsProperty = nestedMappingType . GetProperty ( "Properties" ) ;
22-
23- if ( propsProperty ? . GetValue ( nestedMapping ) is not IEnumerable properties )
24- return null ;
25-
26- var nestedInfo = new NestedMappingInfo
27- {
28- ItemType = itemType ,
29- ItemFactory = CollectionAccessor . CreateItemFactory ( itemType )
30- } ;
31-
32- var propertyList = ExtractPropertyList ( properties ) ;
33- nestedInfo . Properties = propertyList ;
34-
35- return nestedInfo ;
36- }
37-
38- /// <summary>
39- /// Extracts a list of property information from a collection of property mapping objects.
40- /// </summary>
41- /// <param name="properties">The collection of property mappings</param>
42- /// <returns>A list of nested property information</returns>
43- private static List < NestedPropertyInfo > ExtractPropertyList ( IEnumerable properties )
44- {
45- var propertyList = new List < NestedPropertyInfo > ( ) ;
46-
47- foreach ( var prop in properties )
48- {
49- var propType = prop . GetType ( ) ;
50- var nameProperty = propType . GetProperty ( "PropertyName" ) ;
51- var columnProperty = propType . GetProperty ( "CellColumn" ) ;
52- var getterProperty = propType . GetProperty ( "Getter" ) ;
53- var setterProperty = propType . GetProperty ( "Setter" ) ;
54- var typeProperty = propType . GetProperty ( "PropertyType" ) ;
55-
56- if ( nameProperty is null || columnProperty is null || getterProperty is null )
57- continue ;
58-
59- var name = nameProperty . GetValue ( prop ) as string ;
60- var column = ( int ) columnProperty . GetValue ( prop ) ! ;
61- var getter = getterProperty . GetValue ( prop ) as Func < object , object ? > ;
62- var setter = setterProperty ? . GetValue ( prop ) as Action < object , object ? > ;
63- var propTypeValue = typeProperty ? . GetValue ( prop ) as Type ;
64-
65- if ( name is not null && getter is not null )
66- {
67- propertyList . Add ( new NestedPropertyInfo
68- {
69- PropertyName = name ,
70- ColumnIndex = column ,
71- Getter = getter ,
72- Setter = setter ?? ( ( _ , _ ) => { } ) ,
73- PropertyType = propTypeValue ?? typeof ( object )
74- } ) ;
75- }
76- }
77-
78- return propertyList ;
79- }
80-
81- /// <summary>
82- /// Gets a specific property by name from a type.
83- /// </summary>
84- /// <param name="type">The type to search</param>
85- /// <param name="propertyName">The name of the property</param>
86- /// <returns>PropertyInfo if found, otherwise null</returns>
87- public static PropertyInfo ? GetPropertyByName ( Type type , string propertyName )
88- {
89- return type . GetProperty ( propertyName , BindingFlags . Public | BindingFlags . Instance ) ;
90- }
91-
92- private static bool IsSimpleType ( Type type )
93- {
94- return type == typeof ( string ) || type . IsValueType || type . IsPrimitive ;
95- }
96-
97- /// <summary>
98- /// Determines if a type is a complex type that likely has nested properties.
99- /// </summary>
100- /// <param name="type">The type to check</param>
101- /// <returns>True if the type is considered complex</returns>
102- public static bool IsComplexType ( Type type )
103- {
104- return ! IsSimpleType ( type ) && type != typeof ( object ) ;
105- }
1+ namespace MiniExcelLib . Core . Helpers ;
2+
3+ /// <summary>
4+ /// Helper class for extracting mapping metadata using reflection.
5+ /// Consolidates reflection-based property extraction logic to reduce duplication and improve performance.
6+ /// </summary>
7+ internal static class MappingMetadataExtractor
8+ {
9+ /// <summary>
10+ /// Extracts nested mapping information from a compiled mapping object.
11+ /// This method minimizes reflection by extracting properties once at compile time.
12+ /// </summary>
13+ /// <param name="nestedMapping">The nested mapping object to extract information from</param>
14+ /// <param name="itemType">The type of items in the nested mapping</param>
15+ /// <returns>Nested mapping information or null if extraction fails</returns>
16+ public static NestedMappingInfo ? ExtractNestedMappingInfo ( object nestedMapping , Type itemType )
17+ {
18+ // Use reflection minimally to extract properties from the nested mapping
19+ // This is done once at compile time, not at runtime
20+ var nestedMappingType = nestedMapping . GetType ( ) ;
21+ var propsProperty = nestedMappingType . GetProperty ( "Properties" ) ;
22+
23+ if ( propsProperty ? . GetValue ( nestedMapping ) is not IEnumerable properties )
24+ return null ;
25+
26+ var nestedInfo = new NestedMappingInfo
27+ {
28+ ItemType = itemType ,
29+ ItemFactory = CollectionAccessor . CreateItemFactory ( itemType )
30+ } ;
31+
32+ var propertyList = ExtractPropertyList ( properties , itemType ) ;
33+ nestedInfo . Properties = propertyList ;
34+
35+ var collectionsProperty = nestedMappingType . GetProperty ( "Collections" ) ;
36+ if ( collectionsProperty ? . GetValue ( nestedMapping ) is IEnumerable collectionMappings )
37+ {
38+ var nestedCollections = new Dictionary < string , NestedCollectionInfo > ( StringComparer . Ordinal ) ;
39+
40+ foreach ( var collection in collectionMappings )
41+ {
42+ if ( collection is not CompiledCollectionMapping compiledCollection )
43+ continue ;
44+
45+ var nestedItemType = compiledCollection . ItemType ?? typeof ( object ) ;
46+ var collectionInfo = new NestedCollectionInfo
47+ {
48+ PropertyName = compiledCollection . PropertyName ,
49+ StartColumn = compiledCollection . StartCellColumn ,
50+ StartRow = compiledCollection . StartCellRow ,
51+ Layout = compiledCollection . Layout ,
52+ RowSpacing = compiledCollection . RowSpacing ,
53+ ItemType = nestedItemType ,
54+ Getter = compiledCollection . Getter ,
55+ Setter = compiledCollection . Setter ,
56+ ListFactory = ( ) => CollectionAccessor . CreateTypedList ( nestedItemType ) ,
57+ ItemFactory = CollectionAccessor . CreateItemFactory ( nestedItemType )
58+ } ;
59+
60+ if ( compiledCollection . Registry is not null && nestedItemType != typeof ( object ) )
61+ {
62+ var childMapping = compiledCollection . Registry . GetCompiledMapping ( nestedItemType ) ;
63+ if ( childMapping is not null )
64+ {
65+ collectionInfo . NestedMapping = ExtractNestedMappingInfo ( childMapping , nestedItemType ) ;
66+ }
67+ }
68+
69+ nestedCollections [ collectionInfo . PropertyName ] = collectionInfo ;
70+ }
71+
72+ if ( nestedCollections . Count > 0 )
73+ {
74+ nestedInfo . Collections = nestedCollections ;
75+ }
76+ }
77+
78+ return nestedInfo ;
79+ }
80+
81+ /// <summary>
82+ /// Extracts a list of property information from a collection of property mapping objects.
83+ /// </summary>
84+ /// <param name="properties">The collection of property mappings</param>
85+ /// <returns>A list of nested property information</returns>
86+ private static readonly MethodInfo ? CreateTypedSetterMethod = typeof ( ConversionHelper )
87+ . GetMethods ( BindingFlags . Public | BindingFlags . Static )
88+ . FirstOrDefault ( m => m . Name == nameof ( ConversionHelper . CreateTypedPropertySetter ) && m . IsGenericMethodDefinition ) ;
89+
90+ private static List < NestedPropertyInfo > ExtractPropertyList ( IEnumerable properties , Type itemType )
91+ {
92+ var propertyList = new List < NestedPropertyInfo > ( ) ;
93+
94+ foreach ( var prop in properties )
95+ {
96+ var propType = prop . GetType ( ) ;
97+ var nameProperty = propType . GetProperty ( "PropertyName" ) ;
98+ var columnProperty = propType . GetProperty ( "CellColumn" ) ;
99+ var getterProperty = propType . GetProperty ( "Getter" ) ;
100+ var setterProperty = propType . GetProperty ( "Setter" ) ;
101+ var typeProperty = propType . GetProperty ( "PropertyType" ) ;
102+
103+ if ( nameProperty is null || columnProperty is null || getterProperty is null )
104+ continue ;
105+
106+ var name = nameProperty . GetValue ( prop ) as string ;
107+ var column = ( int ) columnProperty . GetValue ( prop ) ! ;
108+ var getter = getterProperty . GetValue ( prop ) as Func < object , object ? > ;
109+ var setter = setterProperty ? . GetValue ( prop ) as Action < object , object ? > ;
110+ var propTypeValue = typeProperty ? . GetValue ( prop ) as Type ;
111+
112+ if ( setter is null && name is not null )
113+ {
114+ var propertyInfo = itemType . GetProperty ( name , BindingFlags . Public | BindingFlags . Instance ) ;
115+ if ( propertyInfo ? . CanWrite == true )
116+ {
117+ setter = CreateSetterWithConversion ( itemType , propertyInfo )
118+ ?? CreateFallbackSetter ( propertyInfo ) ;
119+ }
120+ }
121+
122+ setter ??= ( _ , _ ) => { } ;
123+
124+ if ( name is not null && getter is not null )
125+ {
126+ propertyList . Add ( new NestedPropertyInfo
127+ {
128+ PropertyName = name ,
129+ ColumnIndex = column ,
130+ Getter = getter ,
131+ Setter = setter ,
132+ PropertyType = propTypeValue ?? typeof ( object )
133+ } ) ;
134+ }
135+ }
136+
137+ return propertyList ;
138+ }
139+
140+ private static Action < object , object ? > ? CreateSetterWithConversion ( Type itemType , PropertyInfo propertyInfo )
141+ {
142+ if ( CreateTypedSetterMethod is null )
143+ return null ;
144+
145+ try
146+ {
147+ var generic = CreateTypedSetterMethod . MakeGenericMethod ( itemType ) ;
148+ return generic . Invoke ( null , new object [ ] { propertyInfo } ) as Action < object , object ? > ;
149+ }
150+ catch
151+ {
152+ return null ;
153+ }
154+ }
155+
156+ private static Action < object , object ? > ? CreateFallbackSetter ( PropertyInfo propertyInfo )
157+ {
158+ try
159+ {
160+ var memberSetter = new MemberSetter ( propertyInfo ) ;
161+ return memberSetter . Invoke ;
162+ }
163+ catch
164+ {
165+ return null ;
166+ }
167+ }
168+
169+ /// <summary>
170+ /// Gets a specific property by name from a type.
171+ /// </summary>
172+ /// <param name="type">The type to search</param>
173+ /// <param name="propertyName">The name of the property</param>
174+ /// <returns>PropertyInfo if found, otherwise null</returns>
175+ public static PropertyInfo ? GetPropertyByName ( Type type , string propertyName )
176+ {
177+ return type . GetProperty ( propertyName , BindingFlags . Public | BindingFlags . Instance ) ;
178+ }
179+
180+ private static bool IsSimpleType ( Type type )
181+ {
182+ return type == typeof ( string ) || type . IsValueType || type . IsPrimitive ;
183+ }
184+
185+ /// <summary>
186+ /// Determines if a type is a complex type that likely has nested properties.
187+ /// </summary>
188+ /// <param name="type">The type to check</param>
189+ /// <returns>True if the type is considered complex</returns>
190+ public static bool IsComplexType ( Type type )
191+ {
192+ return ! IsSimpleType ( type ) && type != typeof ( object ) ;
193+ }
106194}
0 commit comments