77
88namespace TurboMapper . Impl
99{
10+ internal class MapperConfiguration
11+ {
12+ public List < PropertyMapping > Mappings { get ; set ; }
13+ public bool EnableDefaultMapping { get ; set ; }
14+
15+ public MapperConfiguration ( List < PropertyMapping > mappings , bool enableDefaultMapping )
16+ {
17+ Mappings = mappings ?? new List < PropertyMapping > ( ) ;
18+ EnableDefaultMapping = enableDefaultMapping ;
19+ }
20+ }
21+
1022 internal class Mapper : IMapper , IObjectMap
1123 {
12- private readonly Dictionary < Type , Dictionary < Type , List < PropertyMapping > > > _configurations ;
24+ private readonly Dictionary < Type , Dictionary < Type , MapperConfiguration > > _configurations ;
1325
1426 public Mapper ( )
1527 {
16- _configurations = new Dictionary < Type , Dictionary < Type , List < PropertyMapping > > > ( ) ;
28+ _configurations = new Dictionary < Type , Dictionary < Type , MapperConfiguration > > ( ) ;
1729 }
1830
1931 public void CreateMap < TSource , TTarget > ( List < PropertyMapping > mappings = null )
@@ -22,9 +34,20 @@ public void CreateMap<TSource, TTarget>(List<PropertyMapping> mappings = null)
2234 var targetType = typeof ( TTarget ) ;
2335
2436 if ( ! _configurations . ContainsKey ( sourceType ) )
25- _configurations [ sourceType ] = new Dictionary < Type , List < PropertyMapping > > ( ) ;
37+ _configurations [ sourceType ] = new Dictionary < Type , MapperConfiguration > ( ) ;
2638
27- _configurations [ sourceType ] [ targetType ] = mappings ?? new List < PropertyMapping > ( ) ;
39+ _configurations [ sourceType ] [ targetType ] = new MapperConfiguration ( mappings , true ) ;
40+ }
41+
42+ public void CreateMap < TSource , TTarget > ( List < PropertyMapping > mappings , bool enableDefaultMapping )
43+ {
44+ var sourceType = typeof ( TSource ) ;
45+ var targetType = typeof ( TTarget ) ;
46+
47+ if ( ! _configurations . ContainsKey ( sourceType ) )
48+ _configurations [ sourceType ] = new Dictionary < Type , MapperConfiguration > ( ) ;
49+
50+ _configurations [ sourceType ] [ targetType ] = new MapperConfiguration ( mappings , enableDefaultMapping ) ;
2851 }
2952
3053 public TTarget Map < TSource , TTarget > ( TSource source )
@@ -41,8 +64,11 @@ public TTarget Map<TSource, TTarget>(TSource source)
4164 if ( _configurations . ContainsKey ( sourceType ) &&
4265 _configurations [ sourceType ] . ContainsKey ( targetType ) )
4366 {
44- var mappings = _configurations [ sourceType ] [ targetType ] ;
45- ApplyCustomMappings ( source , target , mappings ) ;
67+ var config = _configurations [ sourceType ] [ targetType ] ;
68+ if ( config . EnableDefaultMapping )
69+ ApplyCustomMappings ( source , target , config . Mappings ) ;
70+ else
71+ ApplyCustomMappingsWithDefaultDisabled ( source , target , config . Mappings ) ;
4672 }
4773 else
4874 // Default name-based mapping
@@ -67,6 +93,19 @@ internal void ApplyCustomMappings<TSource, TTarget>(
6793 ApplyDefaultNameBasedMapping ( source , target , mappings ) ;
6894 }
6995
96+ internal void ApplyCustomMappingsWithDefaultDisabled < TSource , TTarget > (
97+ TSource source ,
98+ TTarget target ,
99+ List < PropertyMapping > mappings )
100+ {
101+ // Apply only custom mappings, no default mappings
102+ foreach ( var mapping in mappings )
103+ {
104+ var sourceValue = GetNestedValue ( source , mapping . SourcePropertyPath ) ;
105+ SetNestedValue ( target , mapping . TargetPropertyPath , sourceValue ) ;
106+ }
107+ }
108+
70109 private void ApplyDefaultNameBasedMapping < TSource , TTarget > (
71110 TSource source ,
72111 TTarget target ,
@@ -77,22 +116,48 @@ private void ApplyDefaultNameBasedMapping<TSource, TTarget>(
77116
78117 foreach ( var sourceProp in sourceProps )
79118 {
80- // Check if this property is already mapped in custom mappings
81- var isMapped = customMappings . Exists ( m =>
82- m . SourcePropertyPath . Split ( '.' ) . Last ( ) == sourceProp . Name ) ;
119+ // For default mapping, check if this source property maps to any target property in custom mappings
120+ // by checking if there's a custom mapping that targets a property with the same name as sourceProp.Name
121+ var targetProp = targetProps . FirstOrDefault ( p =>
122+ p . Name == sourceProp . Name &&
123+ p . CanWrite ) ;
83124
84- if ( ! isMapped )
125+ if ( targetProp != null )
85126 {
86- var targetProp = targetProps . FirstOrDefault ( p =>
87- p . Name == sourceProp . Name &&
88- p . CanWrite &&
89- p . PropertyType . IsAssignableFrom ( sourceProp . PropertyType ) ) ;
127+ // Check if this target property is already targeted by any custom mapping
128+ var isTargeted = customMappings . Exists ( m =>
129+ m . TargetPropertyPath . Split ( '.' ) . Last ( ) == targetProp . Name ) ;
90130
91- if ( targetProp != null )
131+ if ( ! isTargeted )
92132 {
93133 var sourceValue = sourceProp . GetValue ( source ) ;
94- var convertedValue = ConvertValue ( sourceValue , targetProp . PropertyType ) ;
95- targetProp . SetValue ( target , convertedValue ) ;
134+
135+ if ( IsComplexType ( sourceProp . PropertyType ) && IsComplexType ( targetProp . PropertyType ) )
136+ {
137+ // Handle nested object mapping
138+ if ( sourceValue != null )
139+ {
140+ var nestedTargetValue = targetProp . GetValue ( target ) ;
141+ if ( nestedTargetValue == null )
142+ {
143+ nestedTargetValue = Activator . CreateInstance ( targetProp . PropertyType ) ;
144+ targetProp . SetValue ( target , nestedTargetValue ) ;
145+ }
146+
147+ var nestedSourceValue = sourceValue ;
148+ ApplyNameBasedMapping ( nestedSourceValue , nestedTargetValue ) ;
149+ }
150+ else
151+ {
152+ targetProp . SetValue ( target , null ) ;
153+ }
154+ }
155+ else
156+ {
157+ // Handle simple types or type conversion
158+ var convertedValue = ConvertValue ( sourceValue , targetProp . PropertyType ) ;
159+ targetProp . SetValue ( target , convertedValue ) ;
160+ }
96161 }
97162 }
98163 }
@@ -113,6 +178,7 @@ internal void ApplyNameBasedMapping<TSource, TTarget>(TSource source, TTarget ta
113178 var sourceValue = sourceProp . GetValue ( source ) ;
114179
115180 if ( IsComplexType ( sourceProp . PropertyType ) && IsComplexType ( targetProp . PropertyType ) )
181+ {
116182 // Handle nested object mapping
117183 if ( sourceValue != null )
118184 {
@@ -123,14 +189,20 @@ internal void ApplyNameBasedMapping<TSource, TTarget>(TSource source, TTarget ta
123189 targetProp . SetValue ( target , nestedTargetValue ) ;
124190 }
125191
126- var nestedSourceValue = sourceValue ;
127- ApplyNameBasedMapping ( nestedSourceValue , nestedTargetValue ) ;
192+ // Recursively map the nested object properties
193+ ApplyNameBasedMapping ( sourceValue , nestedTargetValue ) ;
128194 }
129195 else
130196 {
131- var convertedValue = ConvertValue ( sourceValue , targetProp . PropertyType ) ;
132- targetProp . SetValue ( target , convertedValue ) ;
197+ targetProp . SetValue ( target , null ) ;
133198 }
199+ }
200+ else
201+ {
202+ // Handle simple types or type conversion
203+ var convertedValue = ConvertValue ( sourceValue , targetProp . PropertyType ) ;
204+ targetProp . SetValue ( target , convertedValue ) ;
205+ }
134206 }
135207 }
136208 }
@@ -192,7 +264,7 @@ private bool IsComplexType(Type type)
192264 return false ;
193265 if ( type . IsArray )
194266 return false ;
195- if ( type . GetInterface ( " IEnumerable" ) != null )
267+ if ( typeof ( System . Collections . IEnumerable ) . IsAssignableFrom ( type ) && type != typeof ( string ) )
196268 return false ;
197269 return true ;
198270 }
@@ -205,27 +277,78 @@ private object ConvertValue(object value, Type targetType)
205277 return value ;
206278
207279 if ( targetType . IsEnum )
208- return Enum . Parse ( targetType , value . ToString ( ) ) ;
280+ return Enum . Parse ( targetType , value . ToString ( ) , ignoreCase : true ) ;
209281
210282 if ( targetType == typeof ( string ) )
211283 return value . ToString ( ) ;
212284
213- if ( targetType . IsValueType )
214- return Convert . ChangeType ( value , targetType ) ;
285+ if ( targetType == typeof ( Guid ) )
286+ return Guid . Parse ( value . ToString ( ) ) ;
215287
216- // Handle complex types recursively
217- if ( IsComplexType ( targetType ) && value != null )
218- return Map ( value , targetType ) ;
288+ if ( targetType . IsValueType )
289+ {
290+ try
291+ {
292+ if ( targetType == typeof ( int ) && value is double doubleValue )
293+ return ( int ) doubleValue ; // Explicit truncation for double to int
294+ else if ( targetType == typeof ( int ) && value is float floatValue )
295+ return ( int ) floatValue ; // Explicit truncation for float to int
296+ else
297+ return Convert . ChangeType ( value , targetType ) ;
298+ }
299+ catch ( FormatException )
300+ {
301+ // If conversion fails, return default value for the target type
302+ return Activator . CreateInstance ( targetType ) ;
303+ }
304+ catch ( InvalidCastException )
305+ {
306+ // If conversion fails, return default value for the target type
307+ return Activator . CreateInstance ( targetType ) ;
308+ }
309+ }
219310
311+ // Handle complex types recursively - this is for when we need to convert
312+ // an object of one type to another (e.g., assigning Address object to AddressWithConfig property)
313+ if ( IsComplexType ( targetType ) && value != null && ! targetType . IsAssignableFrom ( value . GetType ( ) ) )
314+ {
315+ try
316+ {
317+ // Use the main Map method to convert the object from one type to another
318+ var sourceType = value . GetType ( ) ;
319+ var genericMapMethod = typeof ( Mapper ) . GetMethod ( nameof ( Map ) ,
320+ System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . Public ) ;
321+
322+ if ( genericMapMethod != null )
323+ {
324+ var specificMapMethod = genericMapMethod . MakeGenericMethod ( sourceType , targetType ) ;
325+ return specificMapMethod . Invoke ( this , new object [ ] { value } ) ;
326+ }
327+ }
328+ catch
329+ {
330+ // If mapping fails, return the original value
331+ return value ;
332+ }
333+ }
334+
220335 return value ;
221336 }
222337
223338 private object Map ( object source , Type targetType )
224339 {
225340 var sourceType = source . GetType ( ) ;
226- var mapMethod = typeof ( Mapper ) . GetMethod ( nameof ( Map ) , new [ ] { sourceType , targetType } ) ;
227- var genericMapMethod = mapMethod . MakeGenericMethod ( sourceType , targetType ) ;
228- return genericMapMethod . Invoke ( this , new object [ ] { source } ) ;
341+ // Get the generic Map method (TTarget Map<TSource, TTarget>(TSource source))
342+ var genericMapMethod = typeof ( Mapper ) . GetMethod ( nameof ( Map ) ,
343+ System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . Public ) ;
344+
345+ if ( genericMapMethod == null )
346+ {
347+ throw new InvalidOperationException ( $ "Could not find Map method for source type { sourceType } ") ;
348+ }
349+
350+ var specificMapMethod = genericMapMethod . MakeGenericMethod ( sourceType , targetType ) ;
351+ return specificMapMethod . Invoke ( this , new object [ ] { source } ) ;
229352 }
230353 }
231354}
0 commit comments