@@ -29,7 +29,7 @@ internal static class EngineAlgorithim
2929 // ---- IF Conditions ----
3030 foreach ( ReplaceIfOperationCode ifCondition in template . ReplaceIfConditionCodes )
3131 {
32- if ( ! propMap . TryGetValue ( ifCondition . IfPropertyName , out ExtractedObjProperty property ) )
32+ if ( ! TryResolveProperty ( propMap , ifCondition . IfPropertyName , out ExtractedObjProperty property ) )
3333 {
3434 result . ReplaceFirstOccurrence ( ifCondition . ReplaceRef , "[IF-CONDITION EXCEPTION]: unrecognized property: [" + ifCondition . IfPropertyName + "]" ) ;
3535 continue ;
@@ -89,7 +89,7 @@ internal static class EngineAlgorithim
8989 }
9090 else
9191 {
92- if ( rowMap . TryGetValue ( propName , out ExtractedObjProperty p ) )
92+ if ( TryResolveProperty ( rowMap , propName , out ExtractedObjProperty p ) )
9393 activeRow . ReplaceFirstOccurrence ( objLoopCode . ReplaceRef , p . GetPropertyDisplayString ( objLoopCode . GetFormattingCommand ( ) , options ) ) ;
9494 else
9595 activeRow . ReplaceFirstOccurrence ( objLoopCode . ReplaceRef , objLoopCode . ReplaceCommand ) ;
@@ -105,7 +105,7 @@ internal static class EngineAlgorithim
105105 // ---- Direct Replacements ----
106106 foreach ( ReplaceCode replaceCode in template . ReplaceCodes )
107107 {
108- if ( propMap . TryGetValue ( replaceCode . GetTargetPropertyName ( ) , out ExtractedObjProperty property ) )
108+ if ( TryResolveProperty ( propMap , replaceCode . GetTargetPropertyName ( ) , out ExtractedObjProperty property ) )
109109 result . ReplaceFirstOccurrence ( replaceCode . ReplaceRef , property . GetPropertyDisplayString ( replaceCode . GetFormattingCommand ( ) , options ) ) ;
110110 else
111111 result . ReplaceFirstOccurrence ( replaceCode . ReplaceRef , "{{ " + replaceCode . ReplaceCommand + " }}" ) ;
@@ -236,5 +236,78 @@ private static List<ExtractedObjProperty> GetObjPropertiesFromUnknown(object val
236236
237237 return result ;
238238 }
239+
240+ private static bool TryResolveProperty ( Dictionary < string , ExtractedObjProperty > propMap , string propertyPath , out ExtractedObjProperty result )
241+ {
242+ result = null ;
243+ if ( propMap == null || string . IsNullOrWhiteSpace ( propertyPath ) )
244+ return false ;
245+
246+ string path = propertyPath . Trim ( ) ;
247+ if ( propMap . TryGetValue ( path , out result ) )
248+ return true ;
249+
250+ int dotIndex = path . IndexOf ( '.' ) ;
251+ if ( dotIndex < 0 )
252+ return false ;
253+
254+ string rootName = path . Substring ( 0 , dotIndex ) . Trim ( ) ;
255+ string nestedPath = path . Substring ( dotIndex + 1 ) . Trim ( ) ;
256+ if ( string . IsNullOrEmpty ( rootName ) || string . IsNullOrEmpty ( nestedPath ) )
257+ return false ;
258+
259+ if ( ! propMap . TryGetValue ( rootName , out ExtractedObjProperty rootProperty ) )
260+ return false ;
261+
262+ return TryResolveNestedProperty ( rootProperty , nestedPath , path , out result ) ;
263+ }
264+
265+ private static bool TryResolveNestedProperty ( ExtractedObjProperty rootProperty , string nestedPath , string fullPath , out ExtractedObjProperty result )
266+ {
267+ result = null ;
268+ if ( rootProperty == null || string . IsNullOrWhiteSpace ( nestedPath ) )
269+ return false ;
270+
271+ string [ ] segments = nestedPath . Split ( new [ ] { '.' } , StringSplitOptions . RemoveEmptyEntries ) ;
272+ if ( segments . Length == 0 )
273+ return false ;
274+
275+ object currentValue = rootProperty . OriginalValue ;
276+ Type currentType = rootProperty . Type ;
277+
278+ for ( int i = 0 ; i < segments . Length ; i ++ )
279+ {
280+ if ( currentType == null )
281+ return false ;
282+
283+ string segment = segments [ i ] . Trim ( ) ;
284+ if ( string . IsNullOrEmpty ( segment ) )
285+ return false ;
286+
287+ PropertyInfo nextProperty = currentType
288+ . GetProperties ( BindingFlags . Public | BindingFlags . Instance )
289+ . FirstOrDefault ( p => p . GetIndexParameters ( ) . Length == 0 && string . Equals ( p . Name , segment , StringComparison . OrdinalIgnoreCase ) ) ;
290+
291+ if ( nextProperty == null )
292+ return false ;
293+
294+ object nextValue = currentValue == null ? null : nextProperty . GetValue ( currentValue , null ) ;
295+ if ( i == segments . Length - 1 )
296+ {
297+ result = new ExtractedObjProperty
298+ {
299+ Name = fullPath ,
300+ Type = nextProperty . PropertyType ,
301+ OriginalValue = nextValue
302+ } ;
303+ return true ;
304+ }
305+
306+ currentType = nextProperty . PropertyType ;
307+ currentValue = nextValue ;
308+ }
309+
310+ return false ;
311+ }
239312 }
240- }
313+ }
0 commit comments