Skip to content

Commit f80ad9a

Browse files
committed
chore: performance grade optimized
1 parent bef468f commit f80ad9a

7 files changed

Lines changed: 509 additions & 298 deletions

File tree

Lines changed: 2 additions & 297 deletions
Original file line numberDiff line numberDiff line change
@@ -1,313 +1,18 @@
1-
using ObjectSemantics.NET.Engine.Extensions;
21
using ObjectSemantics.NET.Engine.Models;
3-
using System;
4-
using System.Collections;
5-
using System.Collections.Concurrent;
62
using System.Collections.Generic;
7-
using System.Linq;
8-
using System.Reflection;
9-
using System.Text;
10-
using System.Text.RegularExpressions;
113

124
namespace ObjectSemantics.NET.Engine
135
{
146
internal static class EngineAlgorithim
157
{
16-
private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertyCache = new ConcurrentDictionary<Type, PropertyInfo[]>();
17-
18-
private static readonly Regex IfConditionRegex = new Regex(@"{{\s*#\s*if\s*\(\s*(?<param>\w+)\s*(?<operator>==|!=|>=|<=|>|<)\s*(?<value>[^)]+?)\s*\)\s*}}(?<code>[\s\S]*?)(?:{{\s*#\s*else\s*}}(?<else>[\s\S]*?))?{{\s*#\s*endif\s*}}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
19-
private static readonly Regex LoopBlockRegex = new Regex(@"{{\s*#\s*foreach\s*\(\s*(\w+)\s*\)\s*\}\}([\s\S]*?)\{\{\s*#\s*endforeach\s*}}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
20-
private static readonly Regex DirectParamRegex = new Regex(@"{{(.+?)}}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
21-
228
public static string GenerateFromTemplate<T>(T record, EngineRunnerTemplate template, Dictionary<string, object> parameterKeyValues = null, TemplateMapperOptions options = null) where T : new()
239
{
24-
List<ExtractedObjProperty> objProperties = GetObjectProperties(record, parameterKeyValues);
25-
Dictionary<string, ExtractedObjProperty> propMap = objProperties.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
26-
27-
StringBuilder result = new StringBuilder(template.Template ?? string.Empty, (template.Template?.Length ?? 0) * 2);
28-
29-
// ---- IF Conditions ----
30-
foreach (ReplaceIfOperationCode ifCondition in template.ReplaceIfConditionCodes)
31-
{
32-
if (!TryResolveProperty(propMap, ifCondition.IfPropertyName, out ExtractedObjProperty property))
33-
{
34-
result.ReplaceFirstOccurrence(ifCondition.ReplaceRef, "[IF-CONDITION EXCEPTION]: unrecognized property: [" + ifCondition.IfPropertyName + "]");
35-
continue;
36-
}
37-
38-
bool conditionPassed = property.IsPropertyValueConditionPassed(ifCondition.IfOperationValue, ifCondition.IfOperationType);
39-
string replacement;
40-
41-
if (conditionPassed)
42-
{
43-
EngineRunnerTemplate trueContent = GenerateRunnerTemplate(ifCondition.IfOperationTrueTemplate);
44-
replacement = GenerateFromTemplate(record, trueContent, parameterKeyValues, options);
45-
}
46-
else if (!string.IsNullOrEmpty(ifCondition.IfOperationFalseTemplate))
47-
{
48-
EngineRunnerTemplate falseContent = GenerateRunnerTemplate(ifCondition.IfOperationFalseTemplate);
49-
replacement = GenerateFromTemplate(record, falseContent, parameterKeyValues, options);
50-
}
51-
else
52-
{
53-
replacement = string.Empty;
54-
}
55-
56-
result.ReplaceFirstOccurrence(ifCondition.ReplaceRef, replacement);
57-
}
58-
59-
// ---- Object Loops ----
60-
foreach (ReplaceObjLoopCode objLoop in template.ReplaceObjLoopCodes)
61-
{
62-
if (!propMap.TryGetValue(objLoop.TargetObjectName, out ExtractedObjProperty targetObj) || !(targetObj.OriginalValue is IEnumerable enumerable))
63-
{
64-
result.ReplaceFirstOccurrence(objLoop.ReplaceRef, string.Empty);
65-
continue;
66-
}
67-
68-
StringBuilder loopResult = new StringBuilder();
69-
70-
foreach (object row in enumerable)
71-
{
72-
List<ExtractedObjProperty> rowProps = GetObjPropertiesFromUnknown(row);
73-
Dictionary<string, ExtractedObjProperty> rowMap = rowProps.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
74-
75-
StringBuilder activeRow = new StringBuilder(objLoop.ObjLoopTemplate);
76-
77-
foreach (ReplaceCode objLoopCode in objLoop.ReplaceObjCodes)
78-
{
79-
string propName = objLoopCode.GetTargetPropertyName();
80-
if (propName == ".")
81-
{
82-
ExtractedObjProperty tempProp = new ExtractedObjProperty
83-
{
84-
Name = ".",
85-
Type = row.GetType(),
86-
OriginalValue = row
87-
};
88-
activeRow.ReplaceFirstOccurrence(objLoopCode.ReplaceRef, tempProp.GetPropertyDisplayString(objLoopCode.GetFormattingCommand(), options));
89-
}
90-
else
91-
{
92-
if (TryResolveProperty(rowMap, propName, out ExtractedObjProperty p))
93-
activeRow.ReplaceFirstOccurrence(objLoopCode.ReplaceRef, p.GetPropertyDisplayString(objLoopCode.GetFormattingCommand(), options));
94-
else
95-
activeRow.ReplaceFirstOccurrence(objLoopCode.ReplaceRef, objLoopCode.ReplaceCommand);
96-
}
97-
}
98-
99-
loopResult.Append(activeRow);
100-
}
101-
102-
result.ReplaceFirstOccurrence(objLoop.ReplaceRef, loopResult.ToString());
103-
}
104-
105-
// ---- Direct Replacements ----
106-
foreach (ReplaceCode replaceCode in template.ReplaceCodes)
107-
{
108-
if (TryResolveProperty(propMap, replaceCode.GetTargetPropertyName(), out ExtractedObjProperty property))
109-
result.ReplaceFirstOccurrence(replaceCode.ReplaceRef, property.GetPropertyDisplayString(replaceCode.GetFormattingCommand(), options));
110-
else
111-
result.ReplaceFirstOccurrence(replaceCode.ReplaceRef, "{{ " + replaceCode.ReplaceCommand + " }}");
112-
}
113-
114-
return result.ToString();
10+
return EngineTemplateRenderer.Render(record, template, parameterKeyValues, options);
11511
}
11612

11713
internal static EngineRunnerTemplate GenerateRunnerTemplate(string fileContent)
11814
{
119-
EngineRunnerTemplate templatedContent = new EngineRunnerTemplate { Template = fileContent };
120-
long key = 0;
121-
122-
// ---- IF Conditions ----
123-
templatedContent.Template = IfConditionRegex.Replace(templatedContent.Template, m =>
124-
{
125-
key++;
126-
string refKey = "RIB_" + key;
127-
templatedContent.ReplaceIfConditionCodes.Add(new ReplaceIfOperationCode
128-
{
129-
ReplaceRef = refKey,
130-
IfPropertyName = m.Groups["param"].Value,
131-
IfOperationType = m.Groups["operator"].Value,
132-
IfOperationValue = m.Groups["value"].Value,
133-
IfOperationTrueTemplate = m.Groups["code"].Value,
134-
IfOperationFalseTemplate = m.Groups["else"].Success ? m.Groups["else"].Value : string.Empty
135-
});
136-
return refKey;
137-
});
138-
139-
// ---- FOREACH Loops ----
140-
templatedContent.Template = LoopBlockRegex.Replace(templatedContent.Template, m =>
141-
{
142-
key++;
143-
string refKey = "RLB_" + key;
144-
ReplaceObjLoopCode objLoop = new ReplaceObjLoopCode
145-
{
146-
ReplaceRef = refKey,
147-
TargetObjectName = m.Groups[1].Value?.Trim() ?? string.Empty
148-
};
149-
150-
string loopBlock = m.Groups[2].Value;
151-
loopBlock = DirectParamRegex.Replace(loopBlock, pm =>
152-
{
153-
key++;
154-
string loopRef = "RLBR_" + key;
155-
objLoop.ReplaceObjCodes.Add(new ReplaceCode
156-
{
157-
ReplaceCommand = pm.Groups[1].Value.Trim(),
158-
ReplaceRef = loopRef
159-
});
160-
return loopRef;
161-
});
162-
163-
objLoop.ObjLoopTemplate = loopBlock;
164-
templatedContent.ReplaceObjLoopCodes.Add(objLoop);
165-
return refKey;
166-
});
167-
168-
// ---- Direct Parameters ----
169-
templatedContent.Template = DirectParamRegex.Replace(templatedContent.Template, m =>
170-
{
171-
key++;
172-
string refKey = "RP_" + key;
173-
templatedContent.ReplaceCodes.Add(new ReplaceCode
174-
{
175-
ReplaceCommand = m.Groups[1].Value.Trim(),
176-
ReplaceRef = refKey
177-
});
178-
return refKey;
179-
});
180-
181-
return templatedContent;
182-
}
183-
184-
private static List<ExtractedObjProperty> GetObjectProperties<T>(T value, Dictionary<string, object> parameters) where T : new()
185-
{
186-
Type type = typeof(T);
187-
if (!PropertyCache.TryGetValue(type, out PropertyInfo[] props))
188-
{
189-
props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
190-
PropertyCache[type] = props;
191-
}
192-
193-
List<ExtractedObjProperty> result = new List<ExtractedObjProperty>(props.Length + (parameters != null ? parameters.Count : 0));
194-
195-
foreach (PropertyInfo prop in props)
196-
{
197-
result.Add(new ExtractedObjProperty
198-
{
199-
Type = prop.PropertyType,
200-
Name = prop.Name,
201-
OriginalValue = value == null ? null : prop.GetValue(value, null)
202-
});
203-
}
204-
205-
if (parameters != null)
206-
{
207-
foreach (KeyValuePair<string, object> p in parameters)
208-
result.Add(new ExtractedObjProperty { Type = p.Value.GetType(), Name = p.Key, OriginalValue = p.Value });
209-
}
210-
211-
return result;
212-
}
213-
214-
private static List<ExtractedObjProperty> GetObjPropertiesFromUnknown(object value)
215-
{
216-
if (value == null) return new List<ExtractedObjProperty>();
217-
218-
Type type = value.GetType();
219-
if (!PropertyCache.TryGetValue(type, out PropertyInfo[] props))
220-
{
221-
props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
222-
.Where(p => p.GetIndexParameters().Length == 0).ToArray();
223-
PropertyCache[type] = props;
224-
}
225-
226-
List<ExtractedObjProperty> result = new List<ExtractedObjProperty>(props.Length);
227-
foreach (PropertyInfo prop in props)
228-
{
229-
result.Add(new ExtractedObjProperty
230-
{
231-
Type = prop.PropertyType,
232-
Name = prop.Name,
233-
OriginalValue = prop.GetValue(value, null)
234-
});
235-
}
236-
237-
return result;
238-
}
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;
15+
return EngineTemplateCache.GetOrAdd(fileContent, EngineTemplateParser.Parse);
31116
}
31217
}
31318
}

0 commit comments

Comments
 (0)